Fix Compres Gambar && seed gambar profile - landing page

This commit is contained in:
2025-09-12 11:55:40 +08:00
parent 6a7bd386ae
commit a5d841bb6b
65 changed files with 386 additions and 156 deletions

BIN
bun.lockb

Binary file not shown.

View File

@@ -80,9 +80,11 @@
"react-transition-group": "^4.4.5", "react-transition-group": "^4.4.5",
"readdirp": "^4.1.1", "readdirp": "^4.1.1",
"recharts": "^2.15.3", "recharts": "^2.15.3",
"sharp": "^0.34.3",
"swr": "^2.3.2", "swr": "^2.3.2",
"uuid": "^11.1.0", "uuid": "^11.1.0",
"valtio": "^2.1.3", "valtio": "^2.1.3",
"zlib": "^1.0.5",
"zod": "^3.24.3" "zod": "^3.24.3"
}, },
"devDependencies": { "devDependencies": {

View File

@@ -0,0 +1,137 @@
[
{
"id": "cmff0rr4z0002vn0twp333m2",
"name": "S6RIjFaPvdQm3oq4rM4X9-desktop.webp",
"realName": "bares.png",
"path": "uploads/images",
"mimeType": "image/webp",
"link": "/api/fileStorage/findUnique/S6RIjFaPvdQm3oq4rM4X9-desktop.webp",
"category": "image"
},
{
"id": "cmff0tnf00003vn0t3kgzi0u0",
"name": "_pVNEmThU5ICGa8gv3gh_-desktop.webp",
"realName": "bicara-darma.png",
"path": "uploads/images",
"mimeType": "image/webp",
"link": "/api/fileStorage/findUnique/_pVNEmThU5ICGa8gv3gh_-desktop.webp",
"category": "image"
},
{
"id": "cmff0uykf0004vn0trmmxpgfh",
"name": "bv6rdKvjxkkjUSGLQ0lvB-desktop.webp",
"realName": "daves.png",
"path": "uploads/images",
"mimeType": "image/webp",
"link": "/api/fileStorage/findUnique/bv6rdKvjxkkjUSGLQ0lvB-desktop.webp",
"category": "image"
},
{
"id": "cmff0z34f0005vn0tjtvq519p",
"name": "Z4hWaV04CvoE20MjccQsV-desktop.webp",
"realName": "mangan.png",
"path": "uploads/images",
"mimeType": "image/webp",
"link": "/api/fileStorage/findUnique/Z4hWaV04CvoE20MjccQsV-desktop.webp",
"category": "image"
},
{
"id": "cmff38cyq000bvn0t9f01cz3f",
"name": "LvLAtOqWojx4sn6NjJWB9-desktop.webp",
"realName": "gelah-melah.png",
"path": "uploads/images",
"mimeType": "image/webp",
"link": "/api/fileStorage/findUnique/LvLAtOqWojx4sn6NjJWB9-desktop.webp",
"category": "image"
},
{
"id": "cmff0zqvd0007vn0tv6o5hjcq",
"name": "gR2mcvAQVgJ2-rM5coYJj-desktop.webp",
"realName": "inovasi-desa-darmasaba.png",
"path": "uploads/images",
"mimeType": "image/webp",
"link": "/api/fileStorage/findUnique/gR2mcvAQVgJ2-rM5coYJj-desktop.webp",
"category": "image"
},
{
"id": "cmff1013m0008vn0th7t0d64d",
"name": "JpL-9F8-IGztMn8E2ce02-desktop.webp",
"realName": "pdkt.png",
"path": "uploads/images",
"mimeType": "image/webp",
"link": "/api/fileStorage/findUnique/JpL-9F8-IGztMn8E2ce02-desktop.webp",
"category": "image"
},
{
"id": "cmff10cwq0009vn0tse8dzu3j",
"name": "bxAk4AsGbJTC705_IVdes-desktop.webp",
"realName": "sajjiana-dharma-raksaka.png",
"path": "uploads/images",
"mimeType": "image/webp",
"link": "/api/fileStorage/findUnique/bxAk4AsGbJTC705_IVdes-desktop.webp",
"category": "image"
},
{
"id": "cmff2w5ly000avn0telhct71k",
"name": "Vbj_osnMJUkGEQGDTLwV--desktop.webp",
"realName": "perbekel.png",
"path": "uploads/images",
"mimeType": "image/webp",
"link": "/api/fileStorage/findUnique/Vbj_osnMJUkGEQGDTLwV--desktop.webp",
"category": "image"
},
{
"id": "cmff3joae0000vn6h8sgs0ilg",
"name": "7hox9spUxj56hY_EBYLnj-desktop.webp",
"realName": "youtube.png",
"path": "uploads/images",
"mimeType": "image/webp",
"link": "/api/fileStorage/findUnique/7hox9spUxj56hY_EBYLnj-desktop.webp",
"category": "image"
},
{
"id": "cmff3ll130001vn6hkhls3f5y",
"name": "ChihV7_1eS-AGtSg9UwMv-desktop.webp",
"realName": "gmail.png",
"path": "uploads/images",
"mimeType": "image/webp",
"link": "/api/fileStorage/findUnique/ChihV7_1eS-AGtSg9UwMv-desktop.webp",
"category": "image"
},
{
"id": "cmff3mtat0002vn6hs8vyyhdd",
"name": "z8v9ZREwOJHKGIRYauROt-desktop.webp",
"realName": "facebook.png",
"path": "uploads/images",
"mimeType": "image/webp",
"link": "/api/fileStorage/findUnique/z8v9ZREwOJHKGIRYauROt-desktop.webp",
"category": "image"
},
{
"id": "cmff3nv180003vn6h5jvedidq",
"name": "BLjMxTKoCNE31uOURR3IU-desktop.webp",
"realName": "telephone-call.png",
"path": "uploads/images",
"mimeType": "image/webp",
"link": "/api/fileStorage/findUnique/BLjMxTKoCNE31uOURR3IU-desktop.webp",
"category": "image"
},
{
"id": "cmff3oouh0004vn6hd94brzv9",
"name": "hkJYAeTNWK_vYaYS20w3I-desktop.webp",
"realName": "instagram.png",
"path": "uploads/images",
"mimeType": "image/webp",
"link": "/api/fileStorage/findUnique/hkJYAeTNWK_vYaYS20w3I-desktop.webp",
"category": "image"
},
{
"id": "cmff3q12g0005vn6h5ojov2qa",
"name": "6XEoZ9SFu59COpil03Gya-desktop.webp",
"realName": "tiktok.png",
"path": "uploads/images",
"mimeType": "image/webp",
"link": "/api/fileStorage/findUnique/6XEoZ9SFu59COpil03Gya-desktop.webp",
"category": "image"
}
]

View File

@@ -2,31 +2,37 @@
{ {
"id": "cmds8w2q60002vnbe6i8qhkuo", "id": "cmds8w2q60002vnbe6i8qhkuo",
"name": "Telephone Desa Darmasaba", "name": "Telephone Desa Darmasaba",
"iconUrl": "081239580000" "iconUrl": "081239580000",
"imageId": "cmff3nv180003vn6h5jvedidq"
}, },
{ {
"id": "cmds8z7u20005vnbegyyvnbk0", "id": "cmds8z7u20005vnbegyyvnbk0",
"name": "Email Desa Darmasaba", "name": "Email Desa Darmasaba",
"iconUrl": "desadarmasaba@badungkab.go.id" "iconUrl": "desadarmasaba@badungkab.go.id",
"imageId": "cmff3ll130001vn6hkhls3f5y"
}, },
{ {
"id": "cmds9023u0008vnbe3oxmhwyf", "id": "cmds9023u0008vnbe3oxmhwyf",
"name": "Desa Darmasaba", "name": "Desa Darmasaba",
"iconUrl": "https://www.youtube.com/channel/UCtPw9WOQO7d2HIKzKgel4Xg" "iconUrl": "https://www.youtube.com/channel/UCtPw9WOQO7d2HIKzKgel4Xg",
"imageId": "cmff3joae0000vn6h8sgs0ilg"
}, },
{ {
"id": "cmds90oul000bvnbe2bqkptoi", "id": "cmds90oul000bvnbe2bqkptoi",
"name": "Pemerintah Desa Darmasaba", "name": "Pemerintah Desa Darmasaba",
"iconUrl": "https://www.facebook.com/DarmasabaDesaku" "iconUrl": "https://www.facebook.com/DarmasabaDesaku",
"imageId": "cmff3mtat0002vn6hs8vyyhdd"
}, },
{ {
"id": "cmds91i4e000evnbe8gtf1gub", "id": "cmds91i4e000evnbe8gtf1gub",
"name": "ddarmasaba", "name": "ddarmasaba",
"iconUrl": "https://www.instagram.com/ddarmasaba/" "iconUrl": "https://www.instagram.com/ddarmasaba/",
"imageId": "cmff3oouh0004vn6hd94brzv9"
}, },
{ {
"id": "cmds92de5000hvnbemlu6sq5x", "id": "cmds92de5000hvnbemlu6sq5x",
"name": "desa.darmasaba", "name": "desa.darmasaba",
"iconUrl": "https://www.tiktok.com/@desa.darmasaba?is_from_webapp=1&sender_device=pc" "iconUrl": "https://www.tiktok.com/@desa.darmasaba?is_from_webapp=1&sender_device=pc",
"imageId": "cmff3q12g0005vn6h5ojov2qa"
} }
] ]

View File

@@ -2,6 +2,7 @@
{ {
"id": "edit", "id": "edit",
"name": "I.B Surya Prabhawa Manuaba, S.H., M.H.", "name": "I.B Surya Prabhawa Manuaba, S.H., M.H.",
"position": "Perbekel Darmasaba periode 2021-2027" "position": "Perbekel Darmasaba periode 2021-2027",
"imageId": "cmff2w5ly000avn0telhct71k"
} }
] ]

View File

@@ -1,50 +1,51 @@
[ [
{
"id": "cmdr7039z0002vn5rttctt9hn",
"name": "Davest",
"description": "Darmasaba Village Festval",
"link": "https://darmasaba.desa.id/berita/55862-rakor-davest-2024"
},
{ {
"id": "cmdr755pf0005vn5rp8tyuubw", "id": "cmdr755pf0005vn5rp8tyuubw",
"name": "Dmangan", "name": "Dmangan",
"description": "Darmasaba Aman Pangan", "description": "Darmasaba Aman Pangan",
"link": "https://darmasaba.desa.id/berita/61452-kader-d-mangan-berhasil-meraih-prestasi-dalam-ajang-lomba-banjar-bali-quis-bbq-tahun-2024" "link": "https://darmasaba.desa.id/berita/61452-kader-d-mangan-berhasil-meraih-prestasi-dalam-ajang-lomba-banjar-bali-quis-bbq-tahun-2024",
"imageId" : "cmff0z34f0005vn0tjtvq519p"
}, },
{ {
"id": "cmdr76nqk0008vn5rdddvcxnr", "id": "cmdr76nqk0008vn5rdddvcxnr",
"name": "Bicara Darmasaba", "name": "Bicara Darmasaba",
"description": "Bicara Darmasaba", "description": "Bicara Darmasaba",
"link": "https://darmasaba.desa.id/berita/42506-bicara-darmasaba" "link": "https://darmasaba.desa.id/berita/42506-bicara-darmasaba",
"imageId" : "cmff0tnf00003vn0t3kgzi0u0"
}, },
{ {
"id": "cmdr77vbw000bvn5rvpmoq31s", "id": "cmdr77vbw000bvn5rvpmoq31s",
"name": "Bares", "name": "Bares",
"description": "Darmasaba Recycling Stock/Exchange", "description": "Darmasaba Recycling Stock/Exchange",
"link": "http://darmasaba.desa.id/berita/56722-bares" "link": "http://darmasaba.desa.id/berita/56722-bares",
"imageId" : "cmff0rr4z0002vn0twp333m2"
}, },
{ {
"id": "cmdr7bxtp000evn5rmy85wihx", "id": "cmdr7bxtp000evn5rmy85wihx",
"name": "Sajjana Dharma Raksaka", "name": "Sajjana Dharma Raksaka",
"description": "Sajjana Dharma Raksaka", "description": "Sajjana Dharma Raksaka",
"link": "https://ppid.badungkab.go.id/storage/dokumen/5RS9dldGkrgzMQq6bKdZsqsVRHI8gffWv4PGfb3r.pdf" "link": "https://ppid.badungkab.go.id/storage/dokumen/5RS9dldGkrgzMQq6bKdZsqsVRHI8gffWv4PGfb3r.pdf",
"imageId" : "cmff10cwq0009vn0tse8dzu3j"
}, },
{ {
"id": "cmdr7dlnk000hvn5r9lur3z35", "id": "cmdr7dlnk000hvn5r9lur3z35",
"name": "PDKT", "name": "PDKT",
"description": "Perangkat Desa Kuat Teknologi", "description": "Perangkat Desa Kuat Teknologi",
"link": "https://darmasaba.desa.id/berita/53752-p-d-k-t" "link": "https://darmasaba.desa.id/berita/53752-p-d-k-t",
"imageId" : "cmff1013m0008vn0th7t0d64d"
}, },
{ {
"id": "cmdr7ftob000mvn5rfhgdtg8v", "id": "cmdr7ftob000mvn5rfhgdtg8v",
"name": "GM", "name": "GM",
"description": "Galah Melah", "description": "Galah Melah",
"link": "https://darmasaba.desa.id/berita/52880-galah-melah" "link": "https://darmasaba.desa.id/berita/52880-galah-melah",
"imageId" : "cmff38cyq000bvn0t9f01cz3f"
}, },
{ {
"id": "cmdr7glue000pvn5r6onzslju", "id": "cmdr7glue000pvn5r6onzslju",
"name": "Inovasi Desa Darmasaba", "name": "Inovasi Desa Darmasaba",
"description": "Inovasi Desa Darmasaba", "description": "Inovasi Desa Darmasaba",
"link": "https://darmasaba.desa.id/produk-lokal-desa" "link": "https://darmasaba.desa.id/produk-lokal-desa",
"imageId" : "cmff0zqvd0007vn0tv6o5hjcq"
} }
] ]

View File

@@ -92,7 +92,7 @@ model FileStorage {
PejabatDesa PejabatDesa[] PejabatDesa PejabatDesa[]
MediaSosial MediaSosial[] MediaSosial MediaSosial[]
DesaAntiKorupsi DesaAntiKorupsi[] DesaAntiKorupsi DesaAntiKorupsi[]
SDGSDesa SDGSDesa[] SDGSDesa SdgsDesa[]
APBDesImage APBDes[] @relation("APBDesImage") APBDesImage APBDes[] @relation("APBDesImage")
APBDesFile APBDes[] @relation("APBDesFile") APBDesFile APBDes[] @relation("APBDesFile")
PrestasiDesa PrestasiDesa[] PrestasiDesa PrestasiDesa[]
@@ -168,7 +168,7 @@ model KategoriDesaAntiKorupsi {
} }
//========================================= SDGS Desa ========================================= // //========================================= SDGS Desa ========================================= //
model SDGSDesa { model SdgsDesa {
id String @id @default(cuid()) id String @id @default(cuid())
name String @unique name String @unique
jumlah String jumlah String

View File

@@ -1,3 +1,4 @@
/* eslint-disable @typescript-eslint/no-unused-vars */
import prisma from "@/lib/prisma"; import prisma from "@/lib/prisma";
import profilePejabatDesa from "./data/landing-page/profile/profile.json"; import profilePejabatDesa from "./data/landing-page/profile/profile.json";
import programInovasi from "./data/landing-page/profile/programInovasi.json"; import programInovasi from "./data/landing-page/profile/programInovasi.json";
@@ -15,7 +16,7 @@ import posisiOrganisasiPPID from "./data/ppid/struktur-ppid/posisi-organisasi-PP
import visiMisiPPID from "./data/ppid/visi-misi-ppid/visimisiPPID.json"; import visiMisiPPID from "./data/ppid/visi-misi-ppid/visimisiPPID.json";
import dasarHukumPPID from "./data/ppid/dasar-hukum-ppid/dasarhukumPPID.json"; import dasarHukumPPID from "./data/ppid/dasar-hukum-ppid/dasarhukumPPID.json";
import jenisKelamin from "./data/ppid/ikm/jenis-kelamin/jenis-kelamin.json"; import jenisKelamin from "./data/ppid/ikm/jenis-kelamin/jenis-kelamin.json";
import daftarInformasiPublik from "./data/ppid/daftar-informasi-publik-desa-darmasaba/daftarInformasi.json" import daftarInformasiPublik from "./data/ppid/daftar-informasi-publik-desa-darmasaba/daftarInformasi.json";
import pilihanRatingResponden from "./data/ppid/ikm/pilihan-rating-responden/rating-responden.json"; import pilihanRatingResponden from "./data/ppid/ikm/pilihan-rating-responden/rating-responden.json";
import umurResponden from "./data/ppid/ikm/umur-responden/umur-responden.json"; import umurResponden from "./data/ppid/ikm/umur-responden/umur-responden.json";
import categoryPengumuman from "./data/category-pengumuman.json"; import categoryPengumuman from "./data/category-pengumuman.json";
@@ -54,63 +55,90 @@ import programUnggulan from "./data/pendidikan/program-pendidikan-anak/program-u
import tujuanProgram from "./data/pendidikan/program-pendidikan-anak/tujuan-program.json"; import tujuanProgram from "./data/pendidikan/program-pendidikan-anak/tujuan-program.json";
import roles from "./data/user/roles.json"; import roles from "./data/user/roles.json";
import users from "./data/user/users.json"; import users from "./data/user/users.json";
import fileStorage from "./data/file-storage.json";
(async () => { (async () => {
// =========== USER & ROLE =========== // =========== USER & ROLE ===========
// In your seed.ts // In your seed.ts
// =========== ROLES =========== // =========== ROLES ===========
console.log("🔄 Seeding roles..."); console.log("🔄 Seeding roles...");
for (const r of roles) { for (const r of roles) {
await prisma.role.upsert({ await prisma.role.upsert({
where: { id: r.id }, where: { id: r.id },
update: { update: {
name: r.name, name: r.name,
description: r.description, description: r.description,
permissions: r.permissions, permissions: r.permissions,
isActive: r.isActive, isActive: r.isActive,
}, },
create: { create: {
id: r.id, id: r.id,
name: r.name, name: r.name,
description: r.description, description: r.description,
permissions: r.permissions, permissions: r.permissions,
isActive: r.isActive, isActive: r.isActive,
}, },
}); });
}
console.log("✅ Roles seeded");
// =========== USERS ===========
console.log("🔄 Seeding users...");
for (const u of users) {
// First verify the role exists
const roleExists = await prisma.role.findUnique({
where: { id: u.roleId }
});
if (!roleExists) {
console.error(`❌ Role with id ${u.roleId} not found for user ${u.nama}`);
continue;
} }
console.log("✅ Roles seeded");
await prisma.user.upsert({ // =========== USERS ===========
where: { id: u.id }, console.log("🔄 Seeding users...");
update: { for (const u of users) {
username: u.nama, // First verify the role exists
nomor: u.nomor, const roleExists = await prisma.role.findUnique({
roleId: u.roleId, where: { id: u.roleId },
isActive: u.isActive, });
},
create: { if (!roleExists) {
id: u.id, console.error(`❌ Role with id ${u.roleId} not found for user ${u.nama}`);
username: u.nama, continue;
nomor: u.nomor, }
roleId: u.roleId,
isActive: u.isActive, await prisma.user.upsert({
}, where: { id: u.id },
}); update: {
} username: u.nama,
console.log("✅ Users seeded"); nomor: u.nomor,
roleId: u.roleId,
isActive: u.isActive,
},
create: {
id: u.id,
username: u.nama,
nomor: u.nomor,
roleId: u.roleId,
isActive: u.isActive,
},
});
}
console.log("✅ Users seeded");
// =========== FILE STORAGE ===========
console.log("🔄 Seeding file storage...");
for (const f of fileStorage) {
await prisma.fileStorage.upsert({
where: { id: f.id },
update: {
name: f.name,
realName: f.realName,
path: f.path,
mimeType: f.mimeType,
link: f.link,
category: f.category,
},
create: {
id: f.id,
name: f.name,
realName: f.realName,
path: f.path,
mimeType: f.mimeType,
link: f.link,
category: f.category,
},
});
}
console.log("✅ File storage seeded");
// =========== LANDING PAGE =========== // =========== LANDING PAGE ===========
// =========== SUBMENU PROFILE =========== // =========== SUBMENU PROFILE ===========
// =========== PROFILE PEJABAT DESA =========== // =========== PROFILE PEJABAT DESA ===========
@@ -120,11 +148,13 @@ console.log("✅ Users seeded");
update: { update: {
name: p.name, name: p.name,
position: p.position, position: p.position,
imageId: p.imageId,
}, },
create: { create: {
id: p.id, id: p.id,
name: p.name, name: p.name,
position: p.position, position: p.position,
imageId: p.imageId,
}, },
}); });
} }
@@ -134,18 +164,35 @@ console.log("✅ Users seeded");
// =========== PROGRAM INOVASI =========== // =========== PROGRAM INOVASI ===========
for (const p of programInovasi) { for (const p of programInovasi) {
let imageId: string | null = null;
if (p.imageId) {
const imageExists = await prisma.fileStorage.findUnique({
where: { id: p.imageId },
});
if (imageExists) {
imageId = p.imageId;
} else {
console.warn(
`⚠️ imageId ${p.imageId} tidak ditemukan untuk ProgramInovasi ${p.name}`
);
}
}
await prisma.programInovasi.upsert({ await prisma.programInovasi.upsert({
where: { id: p.id }, where: { id: p.id },
update: { update: {
name: p.name, name: p.name,
description: p.description, description: p.description,
link: p.link, link: p.link,
imageId: p.imageId,
}, },
create: { create: {
id: p.id, id: p.id,
name: p.name, name: p.name,
description: p.description, description: p.description,
link: p.link, link: p.link,
imageId: p.imageId,
}, },
}); });
} }
@@ -158,11 +205,13 @@ console.log("✅ Users seeded");
update: { update: {
name: p.name, name: p.name,
iconUrl: p.iconUrl, iconUrl: p.iconUrl,
imageId: p.imageId,
}, },
create: { create: {
id: p.id, id: p.id,
name: p.name, name: p.name,
iconUrl: p.iconUrl, iconUrl: p.iconUrl,
imageId: p.imageId,
}, },
}); });
} }
@@ -308,11 +357,8 @@ console.log("✅ Users seeded");
// =========== SDGSDesa =========== // =========== SDGSDesa ===========
for (const l of sdgsDesa) { for (const l of sdgsDesa) {
await prisma.sDGSDesa.upsert({ await prisma.sdgsDesa.upsert({
where: { where: { id: l.id },
name: l.name,
jumlah: l.jumlah,
},
update: { update: {
name: l.name, name: l.name,
jumlah: l.jumlah, jumlah: l.jumlah,
@@ -558,29 +604,29 @@ console.log("✅ Users seeded");
} }
console.log("dasar hukum PPID success ..."); console.log("dasar hukum PPID success ...");
// =========== SUBMENU DAFTAR INFORMASI PUBLIK PPID =========== // =========== SUBMENU DAFTAR INFORMASI PUBLIK PPID ===========
for (const v of daftarInformasiPublik) { for (const v of daftarInformasiPublik) {
// Convert string date to Date object // Convert string date to Date object
const tanggal = new Date(v.tanggal); const tanggal = new Date(v.tanggal);
await prisma.daftarInformasiPublik.upsert({ await prisma.daftarInformasiPublik.upsert({
where: { where: {
id: v.id, id: v.id,
}, },
update: { update: {
jenisInformasi: v.jenisInformasi, jenisInformasi: v.jenisInformasi,
deskripsi: v.deskripsi, deskripsi: v.deskripsi,
tanggal: tanggal, tanggal: tanggal,
}, },
create: { create: {
id: v.id, id: v.id,
jenisInformasi: v.jenisInformasi, jenisInformasi: v.jenisInformasi,
deskripsi: v.deskripsi, deskripsi: v.deskripsi,
tanggal: tanggal, tanggal: tanggal,
}, },
}); });
} }
console.log("daftar informasi publik PPID success ..."); console.log("daftar informasi publik PPID success ...");
for (const l of pelayananPerizinanBerusaha) { for (const l of pelayananPerizinanBerusaha) {
await prisma.pelayananPerizinanBerusaha.upsert({ await prisma.pelayananPerizinanBerusaha.upsert({

Binary file not shown.

Before

Width:  |  Height:  |  Size: 275 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 275 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 275 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 275 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 275 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 275 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 275 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 275 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 275 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 275 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 275 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 275 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 275 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 275 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 275 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 275 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 275 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 275 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 275 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 275 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 275 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 275 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 275 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 275 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 275 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 275 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 275 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 275 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 275 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 275 KiB

View File

@@ -23,6 +23,7 @@ export default function SpashScreen() {
<Paper p={"md"} miw={320}> <Paper p={"md"} miw={320}>
<Flex> <Flex>
<Image <Image
loading="lazy"
src={images["darmasaba-icon"]} src={images["darmasaba-icon"]}
alt="darmasaba" alt="darmasaba"
w={100} w={100}

View File

@@ -23,7 +23,12 @@ type ProgramInovasiForm = Prisma.ProgramInovasiGetPayload<{
const programInovasi = proxy({ const programInovasi = proxy({
create: { create: {
form: {} as ProgramInovasiForm, form: {
name: "",
description: "",
imageId: "",
link: ""
} as ProgramInovasiForm,
loading: false, loading: false,
async create() { async create() {
// Ensure all required fields are non-null // Ensure all required fields are non-null

View File

@@ -72,7 +72,7 @@ const sdgsDesa = proxy({
].get({ ].get({
query, query,
}); });
if (res.status === 200 && res.data?.success) { if (res.status === 200 && res.data?.success) {
sdgsDesa.findMany.data = res.data.data || []; sdgsDesa.findMany.data = res.data.data || [];
sdgsDesa.findMany.total = res.data.total || 0; sdgsDesa.findMany.total = res.data.total || 0;
@@ -94,7 +94,7 @@ const sdgsDesa = proxy({
}, },
}, },
findUnique: { findUnique: {
data: null as Prisma.SDGSDesaGetPayload<{ data: null as Prisma.SdgsDesaGetPayload<{
include: { include: {
image: true; image: true;
}; };

View File

@@ -91,7 +91,7 @@ function EditKolaborasiInovasi() {
await sdgsState.edit.update(); await sdgsState.edit.update();
toast.success("sdgs desa berhasil diperbarui!"); toast.success("sdgs desa berhasil diperbarui!");
router.push("/admin/landing-page/sdgs"); router.push("/admin/landing-page/sdgs-desa");
} catch (error) { } catch (error) {
console.error("Error updating sdgs desa:", error); console.error("Error updating sdgs desa:", error);
toast.error("Terjadi kesalahan saat memperbarui sdgs desa"); toast.error("Terjadi kesalahan saat memperbarui sdgs desa");
@@ -107,7 +107,7 @@ function EditKolaborasiInovasi() {
</Button> </Button>
</Tooltip> </Tooltip>
<Title order={4} ml="sm" c="dark"> <Title order={4} ml="sm" c="dark">
Edit SDGs Desa Edit Sdgs Desa
</Title> </Title>
</Group> </Group>
@@ -122,7 +122,7 @@ function EditKolaborasiInovasi() {
<Stack gap="md"> <Stack gap="md">
<Box> <Box>
<Text fw="bold" fz="sm" mb={6}> <Text fw="bold" fz="sm" mb={6}>
Gambar SDGs Desa Gambar Sdgs Desa
</Text> </Text>
<Dropzone <Dropzone
onDrop={(files) => { onDrop={(files) => {
@@ -172,8 +172,8 @@ function EditKolaborasiInovasi() {
</Box> </Box>
<TextInput <TextInput
label="Nama SDGs Desa" label="Nama Sdgs Desa"
placeholder="Masukkan nama SDGs Desa" placeholder="Masukkan nama Sdgs Desa"
value={formData.name} value={formData.name}
onChange={(e) => setFormData({ ...formData, name: e.target.value })} onChange={(e) => setFormData({ ...formData, name: e.target.value })}
required required

View File

@@ -27,7 +27,7 @@ function DetailSDGSDesa() {
sdgsState.delete.byId(selectedId) sdgsState.delete.byId(selectedId)
setModalHapus(false) setModalHapus(false)
setSelectedId(null) setSelectedId(null)
router.push("/admin/landing-page/sdgs") router.push("/admin/landing-page/sdgs-desa")
} }
} }
@@ -62,13 +62,13 @@ function DetailSDGSDesa() {
> >
<Stack gap="md"> <Stack gap="md">
<Text fz="2xl" fw="bold" c={colors['blue-button']}> <Text fz="2xl" fw="bold" c={colors['blue-button']}>
Detail SDGs Desa Detail Sdgs Desa
</Text> </Text>
<Paper bg="#ECEEF8" p="md" radius="md" shadow="xs"> <Paper bg="#ECEEF8" p="md" radius="md" shadow="xs">
<Stack gap="md"> <Stack gap="md">
<Box> <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> <Text fz="md" c="dimmed">{data.name || '-'}</Text>
</Box> </Box>
@@ -82,7 +82,7 @@ function DetailSDGSDesa() {
{data.image?.link ? ( {data.image?.link ? (
<Image <Image
src={data.image.link} src={data.image.link}
alt={data.name || 'Gambar SDGs Desa'} alt={data.name || 'Gambar Sdgs Desa'}
w={200} w={200}
h={200} h={200}
radius="md" radius="md"
@@ -94,7 +94,7 @@ function DetailSDGSDesa() {
</Box> </Box>
<Group gap="sm" mt="md"> <Group gap="sm" mt="md">
<Tooltip label="Hapus SDGs Desa" withArrow position="top"> <Tooltip label="Hapus Sdgs Desa" withArrow position="top">
<Button <Button
color="red" color="red"
onClick={() => { onClick={() => {
@@ -110,10 +110,10 @@ function DetailSDGSDesa() {
</Button> </Button>
</Tooltip> </Tooltip>
<Tooltip label="Edit SDGs Desa" withArrow position="top"> <Tooltip label="Edit Sdgs Desa" withArrow position="top">
<Button <Button
color="green" 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" variant="light"
radius="md" radius="md"
size="md" size="md"
@@ -131,7 +131,7 @@ function DetailSDGSDesa() {
opened={modalHapus} opened={modalHapus}
onClose={() => setModalHapus(false)} onClose={() => setModalHapus(false)}
onConfirm={handleHapus} onConfirm={handleHapus}
text="Apakah Anda yakin ingin menghapus SDGs Desa ini?" text="Apakah Anda yakin ingin menghapus Sdgs Desa ini?"
/> />
</Box> </Box>
); );

View File

@@ -53,7 +53,7 @@ function CreateSDGsDesa() {
await stateSDGSDesa.create.create(); await stateSDGSDesa.create.create();
resetForm(); resetForm();
router.push("/admin/landing-page/sdgs") router.push("/admin/landing-page/sdgs-desa")
} }
return ( return (
<Box px={{ base: 'sm', md: 'lg' }} py="md"> <Box px={{ base: 'sm', md: 'lg' }} py="md">
@@ -64,7 +64,7 @@ function CreateSDGsDesa() {
</Button> </Button>
</Tooltip> </Tooltip>
<Title order={4} ml="sm" c="dark"> <Title order={4} ml="sm" c="dark">
Tambah SDGs Desa Tambah Sdgs Desa
</Title> </Title>
</Group> </Group>
@@ -79,7 +79,7 @@ function CreateSDGsDesa() {
<Stack gap="md"> <Stack gap="md">
<Box> <Box>
<Text fw="bold" fz="sm" mb={6}> <Text fw="bold" fz="sm" mb={6}>
Gambar SDGs Desa Gambar Sdgs Desa
</Text> </Text>
<Dropzone <Dropzone
onDrop={(files) => { onDrop={(files) => {
@@ -143,10 +143,10 @@ function CreateSDGsDesa() {
<TextInput <TextInput
label={ label={
<Text fw="bold" fz="sm" mb={4}> <Text fw="bold" fz="sm" mb={4}>
Nama SDGs Desa Nama Sdgs Desa
</Text> </Text>
} }
placeholder="Masukkan nama SDGs Desa" placeholder="Masukkan nama Sdgs Desa"
value={stateSDGSDesa.create.form.name} value={stateSDGSDesa.create.form.name}
onChange={(val) => { onChange={(val) => {
stateSDGSDesa.create.form.name = val.target.value; stateSDGSDesa.create.form.name = val.target.value;

View File

@@ -15,8 +15,8 @@ function SdgsDesa() {
return ( return (
<Box> <Box>
<HeaderSearch <HeaderSearch
title='SDGs Desa' title='Sdgs Desa'
placeholder='Cari SDGs Desa...' placeholder='Cari Sdgs Desa...'
searchIcon={<IconSearch size={20} />} searchIcon={<IconSearch size={20} />}
value={search} value={search}
onChange={(e) => setSearch(e.currentTarget.value)} onChange={(e) => setSearch(e.currentTarget.value)}
@@ -58,13 +58,13 @@ function ListSdgsDesa({ search }: { search: string }) {
<Box py={10}> <Box py={10}>
<Paper withBorder bg={colors['white-1']} p="lg" shadow="md" radius="md"> <Paper withBorder bg={colors['white-1']} p="lg" shadow="md" radius="md">
<Group justify="space-between" mb="md"> <Group justify="space-between" mb="md">
<Title order={4}>Daftar SDGs Desa</Title> <Title order={4}>Daftar Sdgs Desa</Title>
<Tooltip label="Tambah SDGs Desa" withArrow> <Tooltip label="Tambah Sdgs Desa" withArrow>
<Button <Button
leftSection={<IconPlus size={18} />} leftSection={<IconPlus size={18} />}
color={colors['blue-button']} color={colors['blue-button']}
variant="light" variant="light"
onClick={() => router.push('/admin/landing-page/sdgs/create')} onClick={() => router.push('/admin/landing-page/sdgs-desa/create')}
> >
Tambah Baru Tambah Baru
</Button> </Button>
@@ -74,7 +74,7 @@ function ListSdgsDesa({ search }: { search: string }) {
<Table> <Table>
<TableThead> <TableThead>
<TableTr> <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%' }}>Jumlah</TableTh>
<TableTh style={{ width: '20%', textAlign: 'center' }}>Aksi</TableTh> <TableTh style={{ width: '20%', textAlign: 'center' }}>Aksi</TableTh>
</TableTr> </TableTr>
@@ -82,7 +82,7 @@ function ListSdgsDesa({ search }: { search: string }) {
<TableTbody> <TableTbody>
<TableTr> <TableTr>
<TableTd colSpan={3} style={{ textAlign: 'center', padding: '2rem' }}> <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> </TableTd>
</TableTr> </TableTr>
</TableTbody> </TableTbody>
@@ -97,13 +97,13 @@ function ListSdgsDesa({ search }: { search: string }) {
<Box py={10}> <Box py={10}>
<Paper withBorder bg={colors['white-1']} p="lg" shadow="md" radius="md"> <Paper withBorder bg={colors['white-1']} p="lg" shadow="md" radius="md">
<Group justify="space-between" mb="md"> <Group justify="space-between" mb="md">
<Title order={4}>Daftar SDGs Desa</Title> <Title order={4}>Daftar Sdgs Desa</Title>
<Tooltip label="Tambah SDGs Desa" withArrow> <Tooltip label="Tambah Sdgs Desa" withArrow>
<Button <Button
leftSection={<IconPlus size={18} />} leftSection={<IconPlus size={18} />}
color={colors['blue-button']} color={colors['blue-button']}
variant="light" variant="light"
onClick={() => router.push('/admin/landing-page/sdgs/create')} onClick={() => router.push('/admin/landing-page/sdgs-desa/create')}
> >
Tambah Baru Tambah Baru
</Button> </Button>
@@ -113,7 +113,7 @@ function ListSdgsDesa({ search }: { search: string }) {
<Table highlightOnHover> <Table highlightOnHover>
<TableThead> <TableThead>
<TableTr> <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%' }}>Jumlah</TableTh>
<TableTh style={{ width: '20%', textAlign: 'center' }}>Aksi</TableTh> <TableTh style={{ width: '20%', textAlign: 'center' }}>Aksi</TableTh>
</TableTr> </TableTr>
@@ -137,7 +137,7 @@ function ListSdgsDesa({ search }: { search: string }) {
variant="light" variant="light"
color="blue" color="blue"
size="sm" size="sm"
onClick={() => router.push(`/admin/landing-page/sdgs/${item.id}`)} onClick={() => router.push(`/admin/landing-page/sdgs-desa/${item.id}`)}
> >
<IconDeviceImacCog size={18} /> <IconDeviceImacCog size={18} />
</Button> </Button>

View File

@@ -21,8 +21,8 @@ export const navBar = [
}, },
{ {
id: "Landing_Page_4", id: "Landing_Page_4",
name: "SDGs Desa", name: "Sdgs Desa",
path: "/admin/landing-page/sdgs" path: "/admin/landing-page/sdgs-desa"
}, },
{ {
id: "Landing_Page_5", id: "Landing_Page_5",

View File

@@ -3,6 +3,8 @@ import { Context } from "elysia";
import fs from "fs/promises"; import fs from "fs/promises";
import path from "path"; import path from "path";
import { nanoid } from "nanoid"; import { nanoid } from "nanoid";
import sharp from "sharp";
import zlib from "zlib";
const UPLOAD_DIR = process.env.WIBU_UPLOAD_DIR; const UPLOAD_DIR = process.env.WIBU_UPLOAD_DIR;
@@ -11,6 +13,7 @@ const fileStorageCreate = async (context: Context) => {
name: string; name: string;
file: File; file: File;
}; };
const file = body.file; const file = body.file;
const name = body.name; const name = body.name;
@@ -18,7 +21,6 @@ const fileStorageCreate = async (context: Context) => {
if (!name) return { status: 400, body: "No name provided" }; if (!name) return { status: 400, body: "No name provided" };
if (!UPLOAD_DIR) return { status: 500, body: "UPLOAD_DIR is not defined" }; if (!UPLOAD_DIR) return { status: 500, body: "UPLOAD_DIR is not defined" };
// Tentukan kategori berdasarkan mimeType
const isImage = file.type.startsWith("image/"); const isImage = file.type.startsWith("image/");
const category = isImage ? "image" : "document"; const category = isImage ? "image" : "document";
@@ -26,25 +28,54 @@ const fileStorageCreate = async (context: Context) => {
const rootPath = path.join(UPLOAD_DIR, pathName); const rootPath = path.join(UPLOAD_DIR, pathName);
await fs.mkdir(rootPath, { recursive: true }); await fs.mkdir(rootPath, { recursive: true });
const ext = file.name.split(".").pop(); // Convert File ke Buffer
const newName = nanoid() + "." + ext; 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({ const data = await prisma.fileStorage.create({
data: { data: {
name: newName, name: finalName,
realName: file.name, realName: file.name,
path: rootPath, path: rootPath,
mimeType: file.type, mimeType: finalMimeType,
category, 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 }; return { data };
}; };

View File

@@ -11,7 +11,7 @@ export default async function sdgsDesaCreate(context: Context) {
const body = context.body as FormCreate; const body = context.body as FormCreate;
try { try {
const result = await prisma.sDGSDesa.create({ const result = await prisma.sdgsDesa.create({
data: { data: {
name: body.name, name: body.name,
jumlah: body.jumlah, jumlah: body.jumlah,

View File

@@ -9,7 +9,7 @@ export default async function sdgsDesaDelete(context: Context) {
throw new Error("ID tidak ditemukan dalam parameter"); throw new Error("ID tidak ditemukan dalam parameter");
} }
const deleted = await prisma.sDGSDesa.delete({ const deleted = await prisma.sdgsDesa.delete({
where: { id }, where: { id },
}); });

View File

@@ -21,7 +21,7 @@ async function sdgsDesaFindMany(context: Context) {
try { try {
const [data, total] = await Promise.all([ const [data, total] = await Promise.all([
prisma.sDGSDesa.findMany({ prisma.sdgsDesa.findMany({
where, where,
include: { include: {
image: true, image: true,
@@ -30,7 +30,7 @@ async function sdgsDesaFindMany(context: Context) {
take: limit, take: limit,
orderBy: { jumlah: "desc" }, // opsional, kalau mau urut berdasarkan waktu orderBy: { jumlah: "desc" }, // opsional, kalau mau urut berdasarkan waktu
}), }),
prisma.sDGSDesa.count({ prisma.sdgsDesa.count({
where, where,
}), }),
]); ]);

View File

@@ -9,7 +9,7 @@ export default async function sdgsDesaFindUnique(context: Context) {
throw new Error("ID tidak ditemukan dalam parameter"); throw new Error("ID tidak ditemukan dalam parameter");
} }
const data = await prisma.sDGSDesa.findUnique({ const data = await prisma.sdgsDesa.findUnique({
where: { id }, where: { id },
include: { include: {
image: true, image: true,

View File

@@ -21,7 +21,7 @@ export default async function sdgsDesaUpdate(context: Context) {
} }
try { try {
const updated = await prisma.sDGSDesa.update({ const updated = await prisma.sdgsDesa.update({
where: { id }, where: { id },
data: { data: {
name: body.name, name: body.name,

View File

@@ -7,7 +7,7 @@ import BackButton from '../../(pages)/desa/layanan/_com/BackButto';
import colors from '@/con/colors'; import colors from '@/con/colors';
function Page() { 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); const [loading, setLoading] = useState(true);
useEffect(() => { useEffect(() => {

View File

@@ -9,7 +9,7 @@ import { IconMoodSad } from "@tabler/icons-react"
export default function SDGS() { export default function SDGS() {
const theme = useMantineTheme() const theme = useMantineTheme()
const mobile = useMediaQuery(`(max-width: ${theme.breakpoints.sm})`) 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(() => { useEffect(() => {
const fetchSdgsDesa = async () => { const fetchSdgsDesa = async () => {

Binary file not shown.

Before

Width:  |  Height:  |  Size: 47 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1009 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 274 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 495 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 366 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.5 MiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 44 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 275 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.5 MiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.1 MiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 142 KiB