Fix Compres Gambar && seed gambar profile - landing page
This commit is contained in:
@@ -23,6 +23,7 @@ export default function SpashScreen() {
|
||||
<Paper p={"md"} miw={320}>
|
||||
<Flex>
|
||||
<Image
|
||||
loading="lazy"
|
||||
src={images["darmasaba-icon"]}
|
||||
alt="darmasaba"
|
||||
w={100}
|
||||
|
||||
@@ -23,7 +23,12 @@ type ProgramInovasiForm = Prisma.ProgramInovasiGetPayload<{
|
||||
|
||||
const programInovasi = proxy({
|
||||
create: {
|
||||
form: {} as ProgramInovasiForm,
|
||||
form: {
|
||||
name: "",
|
||||
description: "",
|
||||
imageId: "",
|
||||
link: ""
|
||||
} as ProgramInovasiForm,
|
||||
loading: false,
|
||||
async create() {
|
||||
// Ensure all required fields are non-null
|
||||
|
||||
@@ -72,7 +72,7 @@ const sdgsDesa = proxy({
|
||||
].get({
|
||||
query,
|
||||
});
|
||||
|
||||
|
||||
if (res.status === 200 && res.data?.success) {
|
||||
sdgsDesa.findMany.data = res.data.data || [];
|
||||
sdgsDesa.findMany.total = res.data.total || 0;
|
||||
@@ -94,7 +94,7 @@ const sdgsDesa = proxy({
|
||||
},
|
||||
},
|
||||
findUnique: {
|
||||
data: null as Prisma.SDGSDesaGetPayload<{
|
||||
data: null as Prisma.SdgsDesaGetPayload<{
|
||||
include: {
|
||||
image: true;
|
||||
};
|
||||
|
||||
@@ -91,7 +91,7 @@ function EditKolaborasiInovasi() {
|
||||
|
||||
await sdgsState.edit.update();
|
||||
toast.success("sdgs desa berhasil diperbarui!");
|
||||
router.push("/admin/landing-page/sdgs");
|
||||
router.push("/admin/landing-page/sdgs-desa");
|
||||
} catch (error) {
|
||||
console.error("Error updating sdgs desa:", error);
|
||||
toast.error("Terjadi kesalahan saat memperbarui sdgs desa");
|
||||
@@ -107,7 +107,7 @@ function EditKolaborasiInovasi() {
|
||||
</Button>
|
||||
</Tooltip>
|
||||
<Title order={4} ml="sm" c="dark">
|
||||
Edit SDGs Desa
|
||||
Edit Sdgs Desa
|
||||
</Title>
|
||||
</Group>
|
||||
|
||||
@@ -122,7 +122,7 @@ function EditKolaborasiInovasi() {
|
||||
<Stack gap="md">
|
||||
<Box>
|
||||
<Text fw="bold" fz="sm" mb={6}>
|
||||
Gambar SDGs Desa
|
||||
Gambar Sdgs Desa
|
||||
</Text>
|
||||
<Dropzone
|
||||
onDrop={(files) => {
|
||||
@@ -172,8 +172,8 @@ function EditKolaborasiInovasi() {
|
||||
</Box>
|
||||
|
||||
<TextInput
|
||||
label="Nama SDGs Desa"
|
||||
placeholder="Masukkan nama SDGs Desa"
|
||||
label="Nama Sdgs Desa"
|
||||
placeholder="Masukkan nama Sdgs Desa"
|
||||
value={formData.name}
|
||||
onChange={(e) => setFormData({ ...formData, name: e.target.value })}
|
||||
required
|
||||
@@ -27,7 +27,7 @@ function DetailSDGSDesa() {
|
||||
sdgsState.delete.byId(selectedId)
|
||||
setModalHapus(false)
|
||||
setSelectedId(null)
|
||||
router.push("/admin/landing-page/sdgs")
|
||||
router.push("/admin/landing-page/sdgs-desa")
|
||||
}
|
||||
}
|
||||
|
||||
@@ -62,13 +62,13 @@ function DetailSDGSDesa() {
|
||||
>
|
||||
<Stack gap="md">
|
||||
<Text fz="2xl" fw="bold" c={colors['blue-button']}>
|
||||
Detail SDGs Desa
|
||||
Detail Sdgs Desa
|
||||
</Text>
|
||||
|
||||
<Paper bg="#ECEEF8" p="md" radius="md" shadow="xs">
|
||||
<Stack gap="md">
|
||||
<Box>
|
||||
<Text fz="lg" fw="bold" mb={4}>Nama SDGs Desa</Text>
|
||||
<Text fz="lg" fw="bold" mb={4}>Nama Sdgs Desa</Text>
|
||||
<Text fz="md" c="dimmed">{data.name || '-'}</Text>
|
||||
</Box>
|
||||
|
||||
@@ -82,7 +82,7 @@ function DetailSDGSDesa() {
|
||||
{data.image?.link ? (
|
||||
<Image
|
||||
src={data.image.link}
|
||||
alt={data.name || 'Gambar SDGs Desa'}
|
||||
alt={data.name || 'Gambar Sdgs Desa'}
|
||||
w={200}
|
||||
h={200}
|
||||
radius="md"
|
||||
@@ -94,7 +94,7 @@ function DetailSDGSDesa() {
|
||||
</Box>
|
||||
|
||||
<Group gap="sm" mt="md">
|
||||
<Tooltip label="Hapus SDGs Desa" withArrow position="top">
|
||||
<Tooltip label="Hapus Sdgs Desa" withArrow position="top">
|
||||
<Button
|
||||
color="red"
|
||||
onClick={() => {
|
||||
@@ -110,10 +110,10 @@ function DetailSDGSDesa() {
|
||||
</Button>
|
||||
</Tooltip>
|
||||
|
||||
<Tooltip label="Edit SDGs Desa" withArrow position="top">
|
||||
<Tooltip label="Edit Sdgs Desa" withArrow position="top">
|
||||
<Button
|
||||
color="green"
|
||||
onClick={() => router.push(`/admin/landing-page/sdgs/${data.id}/edit`)}
|
||||
onClick={() => router.push(`/admin/landing-page/sdgs-desa/${data.id}/edit`)}
|
||||
variant="light"
|
||||
radius="md"
|
||||
size="md"
|
||||
@@ -131,7 +131,7 @@ function DetailSDGSDesa() {
|
||||
opened={modalHapus}
|
||||
onClose={() => setModalHapus(false)}
|
||||
onConfirm={handleHapus}
|
||||
text="Apakah Anda yakin ingin menghapus SDGs Desa ini?"
|
||||
text="Apakah Anda yakin ingin menghapus Sdgs Desa ini?"
|
||||
/>
|
||||
</Box>
|
||||
);
|
||||
@@ -53,7 +53,7 @@ function CreateSDGsDesa() {
|
||||
await stateSDGSDesa.create.create();
|
||||
|
||||
resetForm();
|
||||
router.push("/admin/landing-page/sdgs")
|
||||
router.push("/admin/landing-page/sdgs-desa")
|
||||
}
|
||||
return (
|
||||
<Box px={{ base: 'sm', md: 'lg' }} py="md">
|
||||
@@ -64,7 +64,7 @@ function CreateSDGsDesa() {
|
||||
</Button>
|
||||
</Tooltip>
|
||||
<Title order={4} ml="sm" c="dark">
|
||||
Tambah SDGs Desa
|
||||
Tambah Sdgs Desa
|
||||
</Title>
|
||||
</Group>
|
||||
|
||||
@@ -79,7 +79,7 @@ function CreateSDGsDesa() {
|
||||
<Stack gap="md">
|
||||
<Box>
|
||||
<Text fw="bold" fz="sm" mb={6}>
|
||||
Gambar SDGs Desa
|
||||
Gambar Sdgs Desa
|
||||
</Text>
|
||||
<Dropzone
|
||||
onDrop={(files) => {
|
||||
@@ -143,10 +143,10 @@ function CreateSDGsDesa() {
|
||||
<TextInput
|
||||
label={
|
||||
<Text fw="bold" fz="sm" mb={4}>
|
||||
Nama SDGs Desa
|
||||
Nama Sdgs Desa
|
||||
</Text>
|
||||
}
|
||||
placeholder="Masukkan nama SDGs Desa"
|
||||
placeholder="Masukkan nama Sdgs Desa"
|
||||
value={stateSDGSDesa.create.form.name}
|
||||
onChange={(val) => {
|
||||
stateSDGSDesa.create.form.name = val.target.value;
|
||||
@@ -15,8 +15,8 @@ function SdgsDesa() {
|
||||
return (
|
||||
<Box>
|
||||
<HeaderSearch
|
||||
title='SDGs Desa'
|
||||
placeholder='Cari SDGs Desa...'
|
||||
title='Sdgs Desa'
|
||||
placeholder='Cari Sdgs Desa...'
|
||||
searchIcon={<IconSearch size={20} />}
|
||||
value={search}
|
||||
onChange={(e) => setSearch(e.currentTarget.value)}
|
||||
@@ -58,13 +58,13 @@ function ListSdgsDesa({ search }: { search: string }) {
|
||||
<Box py={10}>
|
||||
<Paper withBorder bg={colors['white-1']} p="lg" shadow="md" radius="md">
|
||||
<Group justify="space-between" mb="md">
|
||||
<Title order={4}>Daftar SDGs Desa</Title>
|
||||
<Tooltip label="Tambah SDGs Desa" withArrow>
|
||||
<Title order={4}>Daftar Sdgs Desa</Title>
|
||||
<Tooltip label="Tambah Sdgs Desa" withArrow>
|
||||
<Button
|
||||
leftSection={<IconPlus size={18} />}
|
||||
color={colors['blue-button']}
|
||||
variant="light"
|
||||
onClick={() => router.push('/admin/landing-page/sdgs/create')}
|
||||
onClick={() => router.push('/admin/landing-page/sdgs-desa/create')}
|
||||
>
|
||||
Tambah Baru
|
||||
</Button>
|
||||
@@ -74,7 +74,7 @@ function ListSdgsDesa({ search }: { search: string }) {
|
||||
<Table>
|
||||
<TableThead>
|
||||
<TableTr>
|
||||
<TableTh style={{ width: '60%' }}>Nama SDGs Desa</TableTh>
|
||||
<TableTh style={{ width: '60%' }}>Nama Sdgs Desa</TableTh>
|
||||
<TableTh style={{ width: '20%' }}>Jumlah</TableTh>
|
||||
<TableTh style={{ width: '20%', textAlign: 'center' }}>Aksi</TableTh>
|
||||
</TableTr>
|
||||
@@ -82,7 +82,7 @@ function ListSdgsDesa({ search }: { search: string }) {
|
||||
<TableTbody>
|
||||
<TableTr>
|
||||
<TableTd colSpan={3} style={{ textAlign: 'center', padding: '2rem' }}>
|
||||
<Text c="dimmed">Tidak ada data SDGs Desa</Text>
|
||||
<Text c="dimmed">Tidak ada data Sdgs Desa</Text>
|
||||
</TableTd>
|
||||
</TableTr>
|
||||
</TableTbody>
|
||||
@@ -97,13 +97,13 @@ function ListSdgsDesa({ search }: { search: string }) {
|
||||
<Box py={10}>
|
||||
<Paper withBorder bg={colors['white-1']} p="lg" shadow="md" radius="md">
|
||||
<Group justify="space-between" mb="md">
|
||||
<Title order={4}>Daftar SDGs Desa</Title>
|
||||
<Tooltip label="Tambah SDGs Desa" withArrow>
|
||||
<Title order={4}>Daftar Sdgs Desa</Title>
|
||||
<Tooltip label="Tambah Sdgs Desa" withArrow>
|
||||
<Button
|
||||
leftSection={<IconPlus size={18} />}
|
||||
color={colors['blue-button']}
|
||||
variant="light"
|
||||
onClick={() => router.push('/admin/landing-page/sdgs/create')}
|
||||
onClick={() => router.push('/admin/landing-page/sdgs-desa/create')}
|
||||
>
|
||||
Tambah Baru
|
||||
</Button>
|
||||
@@ -113,7 +113,7 @@ function ListSdgsDesa({ search }: { search: string }) {
|
||||
<Table highlightOnHover>
|
||||
<TableThead>
|
||||
<TableTr>
|
||||
<TableTh style={{ width: '60%' }}>Nama SDGs Desa</TableTh>
|
||||
<TableTh style={{ width: '60%' }}>Nama Sdgs Desa</TableTh>
|
||||
<TableTh style={{ width: '20%' }}>Jumlah</TableTh>
|
||||
<TableTh style={{ width: '20%', textAlign: 'center' }}>Aksi</TableTh>
|
||||
</TableTr>
|
||||
@@ -137,7 +137,7 @@ function ListSdgsDesa({ search }: { search: string }) {
|
||||
variant="light"
|
||||
color="blue"
|
||||
size="sm"
|
||||
onClick={() => router.push(`/admin/landing-page/sdgs/${item.id}`)}
|
||||
onClick={() => router.push(`/admin/landing-page/sdgs-desa/${item.id}`)}
|
||||
>
|
||||
<IconDeviceImacCog size={18} />
|
||||
</Button>
|
||||
@@ -21,8 +21,8 @@ export const navBar = [
|
||||
},
|
||||
{
|
||||
id: "Landing_Page_4",
|
||||
name: "SDGs Desa",
|
||||
path: "/admin/landing-page/sdgs"
|
||||
name: "Sdgs Desa",
|
||||
path: "/admin/landing-page/sdgs-desa"
|
||||
},
|
||||
{
|
||||
id: "Landing_Page_5",
|
||||
|
||||
@@ -3,6 +3,8 @@ import { Context } from "elysia";
|
||||
import fs from "fs/promises";
|
||||
import path from "path";
|
||||
import { nanoid } from "nanoid";
|
||||
import sharp from "sharp";
|
||||
import zlib from "zlib";
|
||||
|
||||
const UPLOAD_DIR = process.env.WIBU_UPLOAD_DIR;
|
||||
|
||||
@@ -11,6 +13,7 @@ const fileStorageCreate = async (context: Context) => {
|
||||
name: string;
|
||||
file: File;
|
||||
};
|
||||
|
||||
const file = body.file;
|
||||
const name = body.name;
|
||||
|
||||
@@ -18,7 +21,6 @@ const fileStorageCreate = async (context: Context) => {
|
||||
if (!name) return { status: 400, body: "No name provided" };
|
||||
if (!UPLOAD_DIR) return { status: 500, body: "UPLOAD_DIR is not defined" };
|
||||
|
||||
// Tentukan kategori berdasarkan mimeType
|
||||
const isImage = file.type.startsWith("image/");
|
||||
const category = isImage ? "image" : "document";
|
||||
|
||||
@@ -26,25 +28,54 @@ const fileStorageCreate = async (context: Context) => {
|
||||
const rootPath = path.join(UPLOAD_DIR, pathName);
|
||||
await fs.mkdir(rootPath, { recursive: true });
|
||||
|
||||
const ext = file.name.split(".").pop();
|
||||
const newName = nanoid() + "." + ext;
|
||||
// Convert File ke Buffer
|
||||
const buffer = Buffer.from(await file.arrayBuffer());
|
||||
let finalName = nanoid();
|
||||
let finalMimeType = file.type;
|
||||
|
||||
if (isImage) {
|
||||
// Simpan sebagai WebP untuk kompresi maksimal
|
||||
const mobileBuffer = await sharp(buffer)
|
||||
.resize({ width: 720 })
|
||||
.webp({ quality: 80 })
|
||||
.toBuffer();
|
||||
|
||||
const desktopBuffer = await sharp(buffer)
|
||||
.resize({ width: 1920, withoutEnlargement: true })
|
||||
.webp({ quality: 80 })
|
||||
.toBuffer();
|
||||
|
||||
const mobileName = `${finalName}-mobile.webp`;
|
||||
const desktopName = `${finalName}-desktop.webp`;
|
||||
|
||||
await fs.writeFile(path.join(rootPath, mobileName), mobileBuffer);
|
||||
await fs.writeFile(path.join(rootPath, desktopName), desktopBuffer);
|
||||
|
||||
// Simpan metadata untuk versi desktop sebagai default
|
||||
finalName = desktopName;
|
||||
finalMimeType = "image/webp";
|
||||
} else {
|
||||
// Kompres dokumen (opsional pakai gzip)
|
||||
const gzBuffer = zlib.gzipSync(buffer);
|
||||
const docName = `${finalName}.gz`;
|
||||
|
||||
await fs.writeFile(path.join(rootPath, docName), gzBuffer);
|
||||
|
||||
finalName = docName;
|
||||
finalMimeType = "application/gzip";
|
||||
}
|
||||
|
||||
const data = await prisma.fileStorage.create({
|
||||
data: {
|
||||
name: newName,
|
||||
name: finalName,
|
||||
realName: file.name,
|
||||
path: rootPath,
|
||||
mimeType: file.type,
|
||||
mimeType: finalMimeType,
|
||||
category,
|
||||
link: `/api/fileStorage/findUnique/${newName}`,
|
||||
link: `/api/fileStorage/findUnique/${finalName}`,
|
||||
},
|
||||
});
|
||||
|
||||
await fs.writeFile(
|
||||
path.join(rootPath, newName),
|
||||
Buffer.from(await file.arrayBuffer())
|
||||
);
|
||||
|
||||
return { data };
|
||||
};
|
||||
|
||||
|
||||
@@ -11,7 +11,7 @@ export default async function sdgsDesaCreate(context: Context) {
|
||||
const body = context.body as FormCreate;
|
||||
|
||||
try {
|
||||
const result = await prisma.sDGSDesa.create({
|
||||
const result = await prisma.sdgsDesa.create({
|
||||
data: {
|
||||
name: body.name,
|
||||
jumlah: body.jumlah,
|
||||
|
||||
@@ -9,7 +9,7 @@ export default async function sdgsDesaDelete(context: Context) {
|
||||
throw new Error("ID tidak ditemukan dalam parameter");
|
||||
}
|
||||
|
||||
const deleted = await prisma.sDGSDesa.delete({
|
||||
const deleted = await prisma.sdgsDesa.delete({
|
||||
where: { id },
|
||||
});
|
||||
|
||||
|
||||
@@ -21,7 +21,7 @@ async function sdgsDesaFindMany(context: Context) {
|
||||
|
||||
try {
|
||||
const [data, total] = await Promise.all([
|
||||
prisma.sDGSDesa.findMany({
|
||||
prisma.sdgsDesa.findMany({
|
||||
where,
|
||||
include: {
|
||||
image: true,
|
||||
@@ -30,7 +30,7 @@ async function sdgsDesaFindMany(context: Context) {
|
||||
take: limit,
|
||||
orderBy: { jumlah: "desc" }, // opsional, kalau mau urut berdasarkan waktu
|
||||
}),
|
||||
prisma.sDGSDesa.count({
|
||||
prisma.sdgsDesa.count({
|
||||
where,
|
||||
}),
|
||||
]);
|
||||
|
||||
@@ -9,7 +9,7 @@ export default async function sdgsDesaFindUnique(context: Context) {
|
||||
throw new Error("ID tidak ditemukan dalam parameter");
|
||||
}
|
||||
|
||||
const data = await prisma.sDGSDesa.findUnique({
|
||||
const data = await prisma.sdgsDesa.findUnique({
|
||||
where: { id },
|
||||
include: {
|
||||
image: true,
|
||||
|
||||
@@ -21,7 +21,7 @@ export default async function sdgsDesaUpdate(context: Context) {
|
||||
}
|
||||
|
||||
try {
|
||||
const updated = await prisma.sDGSDesa.update({
|
||||
const updated = await prisma.sdgsDesa.update({
|
||||
where: { id },
|
||||
data: {
|
||||
name: body.name,
|
||||
|
||||
@@ -7,7 +7,7 @@ import BackButton from '../../(pages)/desa/layanan/_com/BackButto';
|
||||
import colors from '@/con/colors';
|
||||
|
||||
function Page() {
|
||||
const [sdgsDesa, setSdgsDesa] = useState<Prisma.SDGSDesaGetPayload<{ include: { image: true } }>[]>([]);
|
||||
const [sdgsDesa, setSdgsDesa] = useState<Prisma.SdgsDesaGetPayload<{ include: { image: true } }>[]>([]);
|
||||
const [loading, setLoading] = useState(true);
|
||||
|
||||
useEffect(() => {
|
||||
|
||||
@@ -9,7 +9,7 @@ import { IconMoodSad } from "@tabler/icons-react"
|
||||
export default function SDGS() {
|
||||
const theme = useMantineTheme()
|
||||
const mobile = useMediaQuery(`(max-width: ${theme.breakpoints.sm})`)
|
||||
const [sdgsDesa, setSdgsDesa] = useState<Prisma.SDGSDesaGetPayload<{ include: { image: true } }>[] | null>(null)
|
||||
const [sdgsDesa, setSdgsDesa] = useState<Prisma.SdgsDesaGetPayload<{ include: { image: true } }>[] | null>(null)
|
||||
|
||||
useEffect(() => {
|
||||
const fetchSdgsDesa = async () => {
|
||||
|
||||
Reference in New Issue
Block a user