Compare commits

...

5 Commits

Author SHA1 Message Date
f5d68d4982 UI Admin Menu Desa Sub Menu Profile 2025-05-15 17:47:43 +08:00
2844132ea0 Profile PPID, Visi Misi, dan Dasar Hukum 2025-05-13 17:54:23 +08:00
e5889ca903 ProfilePPID bagian Profile, VisiMisiPPD sudah ada seed dan bisa di edit 2025-05-13 11:51:48 +08:00
8a34a122d0 Tambahan Di menu PPID 2025-05-09 10:35:30 +08:00
795c79dd5f Tambahan Admin Di Menu PPID 2025-05-06 13:57:33 +08:00
122 changed files with 5153 additions and 1129 deletions

BIN
bun.lockb

Binary file not shown.

View File

@@ -16,6 +16,7 @@
"@cubejs-client/core": "^0.31.0",
"@elysiajs/cors": "^1.2.0",
"@elysiajs/eden": "^1.2.0",
"@elysiajs/static": "^1.3.0",
"@elysiajs/stream": "^1.1.0",
"@elysiajs/swagger": "^1.2.0",
"@mantine/carousel": "^7.16.2",
@@ -47,14 +48,16 @@
"elysia": "^1.2.12",
"embla-carousel-autoplay": "^8.5.2",
"embla-carousel-react": "^7.1.0",
"form-data": "^4.0.2",
"framer-motion": "^12.4.1",
"get-port": "^7.1.0",
"jotai": "^2.12.3",
"lodash": "^4.17.21",
"motion": "^12.4.1",
"nanoid": "^5.1.0",
"nanoid": "^5.1.5",
"next": "15.1.6",
"next-view-transitions": "^0.3.4",
"node-fetch": "^3.3.2",
"p-limit": "^6.2.0",
"prisma": "^6.3.1",
"react": "^19.0.0",

View File

@@ -0,0 +1,9 @@
[
{
"id": "1",
"biodata": "<p>I.B Surya Prabhawa Manuaba, S.H., M.H., adalah Perbekel Darmasaba periode 2021-2027, seorang advokat, pendiri Mantra Legal Consultants & Advocates, serta aktif di bidang musik dan akademis. Dia menempuh pendidikan hukum di Universitas Udayana dan Universitas Mahasaraswati Denpasar serta memiliki pengalaman luas di berbagai organisasi dan kepemimpinan.</p>",
"pengalaman": "<ul><li>2021 - 2027: Perbekel Desa Darmasaba</li><li>2015 - Sekarang: Founder & Managing Director Mantra Legal Consultants & Advocates</li><li>2020 - Sekarang: Founder Ugawa Record Music Studio</li><li>2010 - 2016: Dosen Fakultas Hukum Universitas Mahasaraswati Denpasar</li></ul>",
"pengalamanOrganisasi": "<ul> <li>1996 1997: Ketua OSIS SMP Negeri 1 Abiansemal</li><li>1999 2000: Ketua OSIS SMA Negeri 1 Mengwi</li> <li>2008 2009: Ketua BEM Universitas Mahasaraswati Denpasar</li> <li>2008 2010: Ketua Sekaa Taruna Sila Dharma, Banjar Tengah, Desa Adat Tegal, Darmasaba</li> <li>2020 Sekarang: Pengurus Young Lawyer Committee Peradi Denpasar</li> <li>2021 Sekarang: Dewan Kehormatan Himpunan Pengusaha Muda Indonesia (HIPMI) Badung</li> <li>2023 2028: Komite Tetap Advokasi Bidang Hukum dan Regulasi Kamar Dagang dan Industri Badung</li> </ul>",
"programUnggulan": "<h3>Pemberdayaan Ekonomi dan UMKM</h3> <ul> <li>Pelatihan dan pendampingan UMKM lokal</li> <li>Program bantuan modal usaha bagi pelaku usaha kecil</li><li>Digitalisasi UMKM untuk meningkatkan pemasaran produk lokal</li></ul>"
}
]

View File

@@ -0,0 +1,11 @@
[
{
"id": "1",
"sejarah" : "<p>Asal usul nama Darmasaba tertuang dalam lontar Usada Bali. Seperti di tulis dalam monografi Desa Darmasaba tahun 1980 silam, nama Darmasaba berkaitan dengan keturunan Danghyang Nirarta diceritakan, Sang kawi-wiku asal Daha (Jawa Timur) itu memiliki cucu bernama Ida Pedanda Sakti Manuaba yang tigggal di Desa Kendran Tegalalang Gianyar. Merasa tidak disenangi sang ayah, Ida Pedanda Sakti Manuaba pergi mengembara bersama dua orang pengiringnya. Pengembaraan sang pendeta sampai di pura Sarin Buana di Jimbaran. Saat mengadakan semedi di tempat ini sang pendeta melihat sinar api. Yang sangat jauh di utara. Timbul keinginan Ida Pedanda Manuaba untuk mengunjungi tempat itu. Sampailah sang Pedanda di pura Batan Bila Peguyangan. Disini Ida Pedanda Manuaba singgah menghadap Ida Pedanda Budha yang tinggal disana. Selanjutnya, kedua pendeta bersama-sama menuju arah utara dan singgah di Taman Cang Ana, sebuah taman milik Arya Lanang Blusung. Di tempat ini kedua pendeta bersama-sama melaksanakan semedi dan menetap untuk sementara waktu.</p>",
"visi" : "<p>Mewujudkan Desa Darmasaba yang sejahtera, unggul, religius, berbudaya, dan aman dengan berlandaskan Tri Hita Karana</p>",
"misi" : "<ul><li>Memperkokoh kerukunan hidup masyarakat dalam jalinan adat, budaya, olahraga, dan agama.</li><li>Meningkatkan kualitas pelayanan publik dengan menerapkan teknologi informasi dan komunikasi terintegrasi.</li><li>Meningkatkan tata kelola pemerintah desa dengan menerapkan prinsip good governance dan good clean government.</li><li>Meningkatkan kualitas pendidikan, kesehatan, Keluarga Berencana serta pengelolaan kependudukan.</li><li>Memperkuat usaha mikro kecil dan menengah (UMKM) dan BUMDesa sebagai pilar ekonomi masyarakat.</li><li>Mewujudkan tatanan kehidupan bermasyarakat yang menjunjung tinggi penegakan hukum dan HAM.</li><li>Meningkatkan perlindungan dan pengelolaan terhadap sumber daya alam dan lingkungan hidup.</li><li>Memperkuat daya saing desa melalui peningkatan mutu sumber daya manusia dan infrastruktur desa berbasis potensi desa.</li><li>Meningkatkan sinergisitas potensi budaya, pertanian dalam arti luas dan pariwisata.</li></ul>",
"lambang" : "<ul><li>Memperkokoh kerukunan hidup masyarakat dalam jalinan adat, budaya, olahraga, dan agama.</li><li>Meningkatkan kualitas pelayanan publik dengan menerapkan teknologi informasi dan komunikasi terintegrasi.</li><li>Meningkatkan tata kelola pemerintah desa dengan menerapkan prinsip good governance dan good clean government.</li><li>Meningkatkan kualitas pendidikan, kesehatan, Keluarga Berencana serta pengelolaan kependudukan.</li><li>Memperkuat usaha mikro kecil dan menengah (UMKM) dan BUMDesa sebagai pilar ekonomi masyarakat.</li><li>Mewujudkan tatanan kehidupan bermasyarakat yang menjunjung tinggi penegakan hukum dan HAM.</li><li>Meningkatkan perlindungan dan pengelolaan terhadap sumber daya alam dan lingkungan hidup.</li><li>Memperkuat daya saing desa melalui peningkatan mutu sumber daya manusia dan infrastruktur desa berbasis potensi desa.</li><li>Meningkatkan sinergisitas potensi budaya, pertanian dalam arti luas dan pariwisata.</li><li>Memperkuat daya saing desa melalui peningkatan mutu sumber daya manusia dan infrastruktur desa berbasis potensi desa.</li><li>Meningkatkan sinergisitas potensi budaya, pertanian dalam arti luas dan pariwisata.</li></ul>",
"maskot" : "<p>Pudak adalah bunga dari tanaman sejenis pandan (Pandanaceae). Bentuk bunga ini tersusun dalam beberapa lapisan, terbungkus oleh kelopak warna putih (semacam daun lonjong) yang ujungnya meruncing.</p><p>Bunga Pudak berwarna kuning dan akan terlihat jika kelopak atau pelepahnya telah mekar. Kekhasan dari bunga pudak, yaitu mempunyai aroma wangi yang semerbak nan lembut (tidak menyengat), dan dapat menebar keharuman sepanjang pagi atau pun sore hari. Tanaman ini dapat tumbuh di sepanjang pantai, aliran sungai, di atas batu-batu karang, dan juga di tanah ladang.</p><p>Dalam Kamus Jawa Kuna- Indonesia kata “Pudak” berarti bunga pandan atau Pandanus Moschatus (Mardiwarsito: 1981: 442). Selain itu bunga pudak juga dapat disebut ketaka atau ketaki (Mardiwarsito, 1981: 276). Sedangkan kata “Sategal” berasal dari kata dasar “Tegal” yang berarti ladang (Mardiwarsito, 1981: 593). Jadi Pudak Sategal dapat diartikan sebagai satu ladang luas yang dipenuhi bunga pudak dan menabar keharuman.</p><p>Pada sebuah kesempatan, Ida Pedanda Putu Pemaron menjelaskan mengenai makna dari istilah Pudak Sategal dengan sebuah analogi bahwa, sekuntum bunga pudak memiliki aroma wangi atau keharuman yang sangat kuat, apalagi jika satu ladang penuh bunga pudak, maka dapat dipastikan aroma keharumannya akan membumbung menyebar ke segala penjuru (Wawancara, 18 Mei 2019 di Geria Putra Mandara Kenderan, Tegallalang). “Pudak” ialah sebuah bunga yang memiliki aroma wangi atau keharuman yang semerbak, lembut, dan khas.</p><p>Garapan Tari Maskot Desa Darmasaba Sekar Pudak diwujudkan ke dalam bentuk tari kreasi yang ditarikan secara berkelompok dengan jumlah lima orang penari perempuan (putri).</p><p>Pemilihan penari perempuan dimaksudkan untuk mempresentasikan keindahan, keluwesan, dan keharuman dari bunga pudak. Sedangkan penetapan jumlah penari lima orang didasarkan atas pertimbangan kebutuhan koreografi agar dapat membentuk desain-desain komposisi lantai yang menarik dan dinamis, baik ketika ditarikan di area panggung yang luas atau pun area panggung yang kecil. Penyajian tari maskot ini dirancang dengan durasi waktu 9 menit.</p>",
"profilPerbekelId" : "1"
}
]

View File

@@ -0,0 +1,5 @@
[
{"name": "Melihat/Membaca/Mendengarkan/Mencatat"},
{"name": "Mendapatkan Salinan Informasi (Hardcopy)"},
{"name": "Mendapatkan Salinan Informasi (Softcopy)"}
]

View File

@@ -0,0 +1,5 @@
[
{ "name": "Mengambil Langsung" },
{ "name": "Dikirim Via Post" },
{ "name": "Dikirim Via Email" }
]

View File

@@ -0,0 +1,6 @@
[
{ "name": "Keuangan Desa" },
{ "name": "Pembangunan Desa" },
{ "name": "Data Demografi" },
{ "name": "Lainnya" }
]

View File

@@ -0,0 +1,8 @@
[
{
"id": "1",
"jenisInformasi": "Peraturan Desa",
"deskripsi": "Dokumen yang berisi kebijakan dan regulasi desa",
"tanggal": "15 Januari 2024"
}
]

View File

@@ -0,0 +1,7 @@
[
{
"id": "1",
"judul": "DASAR HUKUM PEMBENTUKAN PPID DESA DARMASABA",
"content" : "<ul><li>UU Nomor 14 Tahun 2008 tentang Keterbukaan Informasi Publik</li><li>PP Nomor 61 Tahun 2010 tentang Pelaksanaan UU 14 Tahun 2008 tentang Keterbukaan Informasi Publik</li><li>Permendagri Nomor 3 Tahun 2017 tentang Pedoman Pengelolaan Pelayanan Informasi dan Dokumentasi di Lingkungan Kemendagri dan Pemerintah Daerah</li><li>Peraturan Komisi Informasi Nomor 1 Tahun 2010 tentang Standar Layanan Informasi Publik</li><li>Peraturan Komisi Informasi Nomor 1 Tahun 2010 tentang Standar Layanan Informasi Publik</li><li>Peraturan Bupati Badung No. 42 Tahun 2017 tentang Pedoman Pengelolaan Pelayanan Informasi Publik dan Dokumentasi di Lingkungan Pemerintah Kabupaten Badung</li><li>Keputusan Bupati Badung Nomor 99/049/HK/2019 tentang Pengelola Layanan Informasi dan Dokumentasi Kabupaten Badung</li><li>Keputusan Perbekel Darmasaba Nomor 101 Tahun 2019 tentang Penetapan Pelaksana Teknis/Administrasi Pengelola Layanan Informasi Dan Dokumentasi di Desa Punggul</li><li>Peraturan Perbekel Darmasaba Nomor 12 Tahun 2019 tentang Pedoman Pengelolaan Pelayanan Informasi Publik dan Dokumentasi di Lingkungan Pemerintah Desa Darmasaba</li></ul>"
}
]

View File

@@ -0,0 +1,11 @@
[
{
"id": "1",
"name": "I.B Surya Prabhawa Manuaba, S.H., M.H.",
"biodata": "<p>I.B Surya Prabhawa Manuaba, S.H., M.H., adalah Perbekel Darmasaba periode 2021-2027, seorang advokat, pendiri Mantra Legal Consultants & Advocates, serta aktif di bidang musik dan akademis. Dia menempuh pendidikan hukum di Universitas Udayana dan Universitas Mahasaraswati Denpasar, serta memiliki pengalaman luas di berbagai organisasi dan kepemimpinan.</p>",
"riwayat": "<ul> <li>2021 - 2027: Perbekel Desa Darmasaba</li> <li>2015 - Sekarang: Founder & Managing Director Mantra Legal Consultants & Advocates</li> <li>2020 - Sekarang: Founder Ugawa Record Music Studio</li> <li>2010 - 2016: Dosen Fakultas Hukum Universitas Mahasaraswati Denpasar</li> </ul>",
"pengalaman": "<ul> <li>1996 1997: Ketua OSIS SMP Negeri 1 Abiansemal</li><li>1999 2000: Ketua OSIS SMA Negeri 1 Mengwi</li> <li>2008 2009: Ketua BEM Universitas Mahasaraswati Denpasar</li> <li>2008 2010: Ketua Sekaa Taruna Sila Dharma, Banjar Tengah, Desa Adat Tegal, Darmasaba</li> <li>2020 Sekarang: Pengurus Young Lawyer Committee Peradi Denpasar</li> <li>2021 Sekarang: Dewan Kehormatan Himpunan Pengusaha Muda Indonesia (HIPMI) Badung</li> <li>2023 2028: Komite Tetap Advokasi Bidang Hukum dan Regulasi Kamar Dagang dan Industri Badung</li> </ul>",
"unggulan": "<h3>Pemberdayaan Ekonomi dan UMKM</h3> <ul> <li>Pelatihan dan pendampingan UMKM lokal</li> <li>Program bantuan modal usaha bagi pelaku usaha kecil</li><li>Digitalisasi UMKM untuk meningkatkan pemasaran produk lokal</li></ul>",
"imageUrl": "/assets/images/ppid/profile-ppid/perbekel.png"
}
]

View File

@@ -0,0 +1,8 @@
[
{
"id": "1",
"misi": "<ol><li>Meningkatkan pengelolaan dan pelayanan informasi yang berkualitas, benar dan bertanggung jawab.</li><li>Membangun dan mengembangkan sistem penyediaan dan layanan informasi.</li><li>Meningkatkan dan mengembangkan kompetensi dan kualitas SDM dalam bidang pelayanan informasi.</li><li>Mewujudkan keterbukaan informasi Pemerintah Desa Punggul dengan proses yang cepat, tepat, mudah dan sederhana.</li></ol>",
"visi": "Memberikan pelayanan informasi yanng transparan dan akuntabel untuk memenuhi hak pemohon informasi sesuai dengan ketentuan peraturan perundang-undangan yang berlaku."
}
]

View File

@@ -47,7 +47,195 @@ model AppMenuChild {
appMenuId String?
}
//========================================= MENU PPID ========================================= //
// ========================================= VISI MISI PPID ========================================= //
model VisiMisiPPID {
id String @id @default(cuid())
visi String @db.Text
misi String @db.Text
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
deletedAt DateTime @default(now())
isActive Boolean @default(true)
}
// ========================================= DASAR HUKUM PPID ========================================= //
model DasarHukumPPID {
id String @id @default(cuid())
judul String @db.Text
content String @db.Text
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
deletedAt DateTime @default(now())
isActive Boolean @default(true)
}
// ========================================= PROFILE PPID ========================================= //
model ProfilePPID {
id String @id @default(cuid())
name String @db.Text
biodata String @db.Text
riwayat String @db.Text
pengalaman String @db.Text
unggulan String @db.Text
imageUrl String?
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
deletedAt DateTime @default(now())
isActive Boolean @default(true)
}
// ========================================= DAFTAR INFORMASI PUBLIK ========================================= //
model DaftarInformasiPublik {
id String @id @default(cuid())
nomor Int @default(autoincrement())
jenisInformasi String
deskripsi String
tanggal String
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
deletedAt DateTime @default(now())
isActive Boolean @default(true)
}
//=========================================PERMOHONAN INFORMASI PUBLIK========================= //
model PermohonanInformasiPublik {
id String @id @default(cuid())
nomor Int @default(autoincrement())
name String
nik String
notelp String
alamat String
email String
jenisInformasiDiminta JenisInformasiDiminta? @relation(fields: [jenisInformasiDimintaId], references: [id])
jenisInformasiDimintaId String?
caraMemperolehInformasi CaraMemperolehInformasi? @relation(fields: [caraMemperolehInformasiId], references: [id])
caraMemperolehInformasiId String?
caraMemperolehSalinanInformasi CaraMemperolehSalinanInformasi? @relation(fields: [caraMemperolehSalinanInformasiId], references: [id])
caraMemperolehSalinanInformasiId String?
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
deletedAt DateTime @default(now())
isActive Boolean @default(true)
}
model JenisInformasiDiminta {
id String @id @default(cuid())
name String @unique
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
deletedAt DateTime @default(now())
isActive Boolean @default(true)
PermohonanInformasiPublik PermohonanInformasiPublik[]
}
model CaraMemperolehInformasi {
id String @id @default(cuid())
name String @unique
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
deletedAt DateTime @default(now())
isActive Boolean @default(true)
PermohonanInformasiPublik PermohonanInformasiPublik[]
}
model CaraMemperolehSalinanInformasi {
id String @id @default(cuid())
name String @unique
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
deletedAt DateTime @default(now())
isActive Boolean @default(true)
PermohonanInformasiPublik PermohonanInformasiPublik[]
}
//=========================================PERMOHONAN INFORMASI KEBERATAN PUBLIK========================= //
model FormulirPermohonanKeberatan {
id String @id @default(cuid())
name String
email String
notelp String
alasan String
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
deletedAt DateTime @default(now())
isActive Boolean @default(true)
}
// ========================================= IKM ========================================= //
model IndeksKepuasanMasyarakat {
id Int @id @default(autoincrement())
label String
kepuasan String
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
deletedAt DateTime @default(now())
isActive Boolean @default(true)
}
model GrafikBerdasarkanJenisKelamin {
id String @id @default(cuid())
perempuan String
laki String
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
deletedAt DateTime @default(now())
isActive Boolean @default(true)
}
model GrafikBerdasarkanResponden {
id String @id @default(cuid())
sangatbaik String
baik String
kurangbaik String
tidakbaik String
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
deletedAt DateTime @default(now())
isActive Boolean @default(true)
}
model GrafikBerdasarkanUmur {
id String @id @default(cuid())
remaja String
dewasa String
orangtua String
lansia String
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
deletedAt DateTime @default(now())
isActive Boolean @default(true)
}
// ========================================= MENU DESA ========================================= //
// ========================================= PROFILE DESA ========================================= //
model ProfileDesa {
id String @id @default(cuid())
sejarah String @db.Text
visi String @db.Text
misi String @db.Text
lambang String @db.Text
maskot String @db.Text
ProfilPerbekel ProfilPerbekel? @relation(fields: [profilPerbekelId], references: [id])
profilPerbekelId String?
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
deletedAt DateTime @default(now())
isActive Boolean @default(true)
}
model ProfilPerbekel {
id String @id @default(cuid())
biodata String @db.Text
pengalaman String @db.Text
pengalamanOrganisasi String @db.Text
programUnggulan String @db.Text
ProfileDesa ProfileDesa[]
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
deletedAt DateTime @default(now())
isActive Boolean @default(true)
}
// ========================================= BERITA ========================================= //
model Berita {
id String @id @default(cuid())

View File

@@ -1,8 +1,16 @@
import prisma from '@/lib/prisma'
import categoryPengumuman from './data/category-pengumuman.json'
import katagoryBerita from './data/katagory-berita.json'
import caraMemperolehInformasi from './data/list-caraMemperolehInformasi.json'
import caraMemperolehSalinanInformasi from './data/list-caraMemperolehSalinanInformasi.json'
import jenisInformasiDiminta from './data/list-jenisInfromasi.json'
import layanan from './data/list-layanan.json'
import potensi from './data/list-potensi.json'
import katagoryBerita from './data/katagory-berita.json'
import categoryPengumuman from './data/category-pengumuman.json'
import prisma from '@/lib/prisma';
import profilePPID from './data/ppid/profile-ppid/profilePPid.json'
import visiMisiPPID from './data/ppid/visi-misi-ppid/visimisiPPID.json'
import dasarHukumPPID from './data/ppid/dasar-hukum-ppid/dasarhukumPPID.json'
import profileDesa from './data/desa/profile/profile_desa.json'
import profilePerbekel from './data/desa/profile/profil_perbekel.json'
(async () => {
for (const l of layanan) {
await prisma.layanan.upsert({
@@ -67,6 +75,162 @@ import prisma from '@/lib/prisma';
}
console.log("category pengumuman success ...")
for (const j of jenisInformasiDiminta) {
await prisma.jenisInformasiDiminta.upsert({
where: {
name: j.name
},
update: {
name: j.name
},
create: {
name: j.name
}
})
}
console.log("jenis informasi diminta success ...")
for (const c of caraMemperolehInformasi) {
await prisma.caraMemperolehInformasi.upsert({
where: {
name: c.name
},
update: {
name: c.name
},
create: {
name: c.name
}
})
}
console.log("cara memperoleh informasi success ...")
for (const c of caraMemperolehSalinanInformasi) {
await prisma.caraMemperolehSalinanInformasi.upsert({
where: {
name: c.name
},
update: {
name: c.name
},
create: {
name: c.name
}
})
}
console.log("cara memperoleh salinan informasi success ...")
for (const c of profilePPID) {
await prisma.profilePPID.upsert({
where: {
id: c.id
},
update: {
name: c.name,
biodata: c.biodata,
riwayat: c.riwayat,
pengalaman: c.pengalaman,
unggulan: c.unggulan,
imageUrl: c.imageUrl
},
create: {
id: c.id,
name: c.name,
biodata: c.biodata,
riwayat: c.riwayat,
pengalaman: c.pengalaman,
unggulan: c.unggulan,
imageUrl: c.imageUrl
}
})
}
console.log("profile PPID success ...")
for (const v of visiMisiPPID) {
await prisma.visiMisiPPID.upsert({
where: {
id: v.id,
},
update: {
misi: v.misi,
visi: v.visi
},
create: {
id: v.id,
misi: v.misi,
visi: v.visi
}
})
}
console.log("visi misi PPID success ...")
for (const v of dasarHukumPPID) {
await prisma.dasarHukumPPID.upsert({
where: {
id: v.id,
},
update: {
judul: v.judul,
content: v.content
},
create: {
id: v.id,
judul: v.judul,
content: v.content
}
})
}
console.log("dasar hukum PPID success ...")
for (const v of profileDesa) {
await prisma.profileDesa.upsert({
where: {
id: v.id,
},
update: {
sejarah: v.sejarah,
visi: v.visi,
misi: v.misi,
lambang: v.lambang,
maskot: v.maskot,
profilPerbekelId: v.profilPerbekelId
},
create: {
id: v.id,
sejarah: v.sejarah,
visi: v.visi,
misi: v.misi,
lambang: v.lambang,
maskot: v.maskot,
profilPerbekelId: v.profilPerbekelId
}
})
}
console.log("profile desa success ...")
for (const v of profilePerbekel) {
await prisma.profilPerbekel.upsert({
where: {
id: v.id,
},
update: {
biodata: v.biodata,
pengalaman: v.pengalaman,
pengalamanOrganisasi: v.pengalamanOrganisasi,
programUnggulan: v.programUnggulan
},
create: {
id: v.id,
biodata: v.biodata,
pengalaman: v.pengalaman,
pengalamanOrganisasi: v.pengalamanOrganisasi,
programUnggulan: v.programUnggulan
}
})
}
console.log("profile perbekel success ...")
})().then(() => prisma.$disconnect()).catch((e) => {
console.error(e)
prisma.$disconnect()

Binary file not shown.

After

Width:  |  Height:  |  Size: 275 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 275 KiB

View File

@@ -87,4 +87,4 @@ const stateDashboardBerita = proxy({
berita,
});
export default stateDashboardBerita;
export default stateDashboardBerita;

View File

@@ -0,0 +1,65 @@
import ApiFetch from "@/lib/api-fetch";
import { Prisma } from "@prisma/client";
import { toast } from "react-toastify";
import { proxy } from "valtio";
import { z } from "zod";
const templateDaftarInformasi = z.object({
jenisInformasi: z.string().min(3, "Jenis Informasi minimal 3 karakter"),
deskripsi: z.string().min(3, "Deskripsi minimal 3 karakter"),
tanggal: z.string().min(3, "Tanggal minimal 3 karakter"),
})
type DaftarInformasi = Prisma.DaftarInformasiPublikGetPayload<{
select: {
jenisInformasi: true;
deskripsi: true;
tanggal: true;
};
}>;
const daftarInformasi = proxy({
create: {
form: {} as DaftarInformasi,
loading: false,
async create() {
const cek = templateDaftarInformasi.safeParse(daftarInformasi.create.form);
if (!cek.success) {
const err = `[${cek.error.issues
.map((v) => `${v.path.join(".")}`)
.join("\n")}] required`;
return toast.error(err);
}
try {
daftarInformasi.create.loading = true;
const res = await ApiFetch.api.ppid.daftarinformasipublik["create"].post(daftarInformasi.create.form);
if (res.status === 200) {
daftarInformasi.findMany.load();
return toast.success("success create");
}
return toast.error("failed create");
} catch (error) {
console.log((error as Error).message);
} finally {
daftarInformasi.create.loading = false;
}
},
},
findMany: {
data: null as
| Prisma.DaftarInformasiPublikGetPayload<{ omit: { isActive: true } }>[]
| null,
async load() {
const res = await ApiFetch.api.ppid.daftarinformasipublik["find-many"].get();
if (res.status === 200) {
daftarInformasi.findMany.data = res.data?.data ?? [];
}
}
}
});
const stateDaftarInformasiPublik = proxy({
daftarInformasi
})
export default stateDaftarInformasiPublik;

View File

@@ -0,0 +1,82 @@
import ApiFetch from "@/lib/api-fetch";
import { Prisma } from "@prisma/client";
import { toast } from "react-toastify";
import { proxy } from "valtio";
import { z } from "zod";
const templateForm = z.object({
judul: z.string().min(3, "Judul minimal 3 karakter"),
content: z.string().min(3, "Content minimal 3 karakter"),
});
type DasarHukumForm = Prisma.DasarHukumPPIDGetPayload<{
select: {
id: true;
judul: true;
content: true;
};
}>;
const stateDasarHukumPPID = proxy({
findById: {
data: null as DasarHukumForm | null,
loading: false,
initialize() {
stateDasarHukumPPID.findById.data = {
id: '',
judul: '',
content: '',
} as DasarHukumForm;
},
async load(id: string) {
try {
stateDasarHukumPPID.findById.loading = true;
const res = await ApiFetch.api.ppid.dasarhukumppid["find-by-id"].get({
query: { id },
});
if (res.status === 200) {
stateDasarHukumPPID.findById.data = res.data?.data ?? null;
} else {
toast.error("Gagal mengambil data dasar hukum");
}
} catch (error) {
console.error((error as Error).message);
toast.error("Terjadi kesalahan saat mengambil data dasar hukum");
} finally {
stateDasarHukumPPID.findById.loading = false;
}
},
},
update: {
loading: false,
async save(data: DasarHukumForm) {
const cek = templateForm.safeParse(data);
if (!cek.success) {
const errors = cek.error.issues
.map((issue) => `${issue.path.join(".")}: ${issue.message}`)
.join(", ");
toast.error(`Form tidak valid: ${errors}`);
return;
}
try {
stateDasarHukumPPID.update.loading = true;
const res = await ApiFetch.api.ppid.dasarhukumppid["update"].post(data);
if (res.status === 200) {
toast.success("Data dasar hukum berhasil diubah");
await stateDasarHukumPPID.findById.load(data.id);
} else {
toast.error("Gagal mengubah data dasar hukum");
}
} catch (error) {
console.error((error as Error).message);
toast.error("Terjadi kesalahan saat mengubah data dasar hukum");
} finally {
stateDasarHukumPPID.update.loading = false;
}
},
},
});
export default stateDasarHukumPPID;

View File

@@ -0,0 +1,77 @@
import ApiFetch from "@/lib/api-fetch";
import { Prisma } from "@prisma/client";
import { toast } from "react-toastify";
import { proxy } from "valtio";
import { z } from "zod";
const templateGrafikJenisKelamin = z.object({
laki: z.string().min(2, "Data laki-laki harus diisi"),
perempuan: z.string().min(2, "Data perempuan harus diisi"),
});
type GrafikJenisKelamin = Prisma.GrafikBerdasarkanJenisKelaminGetPayload<{
select: {
laki: true;
perempuan: true;
};
}>;
const defaultForm: GrafikJenisKelamin = {
laki: "",
perempuan: "",
};
const grafikBerdasarkanJenisKelamin = proxy({
create: {
form: defaultForm,
loading: false,
async create() {
const cek = templateGrafikJenisKelamin.safeParse(
grafikBerdasarkanJenisKelamin.create.form
);
if (!cek.success) {
const err = `[${cek.error.issues
.map((v) => `${v.path.join(".")}`)
.join("\n")}] required`;
return toast.error(err);
}
try {
grafikBerdasarkanJenisKelamin.create.loading = true;
const res = await ApiFetch.api.ppid.grafikberdasarkanjeniskelamin[
"create"
].post(grafikBerdasarkanJenisKelamin.create.form);
if (res.status === 200) {
grafikBerdasarkanJenisKelamin.create.form = defaultForm;
grafikBerdasarkanJenisKelamin.findMany.load();
return toast.success("success create");
}
return toast.error("failed create");
} catch (error) {
console.log((error as Error).message);
} finally {
grafikBerdasarkanJenisKelamin.create.loading = false;
}
},
},
findMany: {
data: null as
| Prisma.GrafikBerdasarkanJenisKelaminGetPayload<{
omit: { isActive: true };
}>[]
| null,
loading: false,
async load() {
const res = await ApiFetch.api.ppid.grafikberdasarkanjeniskelamin[
"find-many"
].get();
if (res.status === 200) {
grafikBerdasarkanJenisKelamin.findMany.data = res.data?.data ?? [];
}
},
},
});
const stateGrafikBerdasarkanJenisKelamin = proxy({
grafikBerdasarkanJenisKelamin,
});
export default stateGrafikBerdasarkanJenisKelamin;

View File

@@ -0,0 +1,84 @@
import ApiFetch from "@/lib/api-fetch";
import { Prisma } from "@prisma/client";
import { toast } from "react-toastify";
import { proxy } from "valtio";
import { z } from "zod";
const templateGrafikResponden = z.object({
sangatbaik: z.string().min(1, "Data sangat baik harus diisi"),
baik: z.string().min(1, "Data baik harus diisi"),
kurangbaik: z.string().min(1, "Data kurang baik harus diisi"),
tidakbaik: z.string().min(1, "Data tidak baik harus diisi"),
});
type GrafikResponden = Prisma.GrafikBerdasarkanRespondenGetPayload<{
select: {
sangatbaik: true;
baik: true;
kurangbaik: true;
tidakbaik: true;
};
}>;
const defaultForm: GrafikResponden = {
sangatbaik: "",
baik: "",
kurangbaik: "",
tidakbaik: "",
};
const grafikBerdasarkanResponden = proxy({
create: {
form: defaultForm,
loading: false,
async create() {
const cek = templateGrafikResponden.safeParse(
grafikBerdasarkanResponden.create.form
);
if (!cek.success) {
const err = `[${cek.error.issues
.map((v) => `${v.path.join(".")}`)
.join("\n")}] required`;
return toast.error(err);
}
try {
grafikBerdasarkanResponden.create.loading = true;
const res = await ApiFetch.api.ppid.grafikberdasarkanresponden[
"create"
].post(grafikBerdasarkanResponden.create.form);
if (res.status === 200) {
grafikBerdasarkanResponden.create.form = defaultForm;
grafikBerdasarkanResponden.findMany.load();
return toast.success("success create");
}
return toast.error("failed create");
} catch (error) {
console.log((error as Error).message);
} finally {
grafikBerdasarkanResponden.create.loading = false;
}
},
},
findMany: {
data: null as
| Prisma.GrafikBerdasarkanRespondenGetPayload<{
omit: { isActive: true };
}>[]
| null,
loading: false,
async load() {
const res = await ApiFetch.api.ppid.grafikberdasarkanresponden[
"find-many"
].get();
if (res.status === 200) {
grafikBerdasarkanResponden.findMany.data = res.data?.data ?? [];
}
},
},
});
const stateGrafikResponden = proxy({
grafikBerdasarkanResponden,
});
export default stateGrafikResponden;

View File

@@ -0,0 +1,84 @@
import ApiFetch from "@/lib/api-fetch";
import { Prisma } from "@prisma/client";
import { toast } from "react-toastify";
import { proxy } from "valtio";
import { z } from "zod";
const templateGrafikUmur = z.object({
remaja: z.string().min(2, "Data remaja harus diisi"),
dewasa: z.string().min(2, "Data dewasa harus diisi"),
orangtua: z.string().min(2, "Data orangtua harus diisi"),
lansia: z.string().min(2, "Data lansia harus diisi"),
});
type GrafikUmur = Prisma.GrafikBerdasarkanUmurGetPayload<{
select: {
remaja: true;
dewasa: true;
orangtua: true;
lansia: true;
};
}>;
const defaultForm: GrafikUmur = {
remaja: "",
dewasa: "",
orangtua: "",
lansia: "",
};
const grafikBerdasarkanUmur = proxy({
create: {
form: defaultForm,
loading: false,
async create() {
const cek = templateGrafikUmur.safeParse(
grafikBerdasarkanUmur.create.form
);
if (!cek.success) {
const err = `[${cek.error.issues
.map((v) => `${v.path.join(".")}`)
.join("\n")}] required`;
return toast.error(err);
}
try {
grafikBerdasarkanUmur.create.loading = true;
const res = await ApiFetch.api.ppid.grafikberdasarkanumur[
"create"
].post(grafikBerdasarkanUmur.create.form);
if (res.status === 200) {
grafikBerdasarkanUmur.create.form = defaultForm;
grafikBerdasarkanUmur.findMany.load();
return toast.success("success create");
}
return toast.error("failed create");
} catch (error) {
console.log((error as Error).message);
} finally {
grafikBerdasarkanUmur.create.loading = false;
}
},
},
findMany: {
data: null as
| Prisma.GrafikBerdasarkanUmurGetPayload<{
omit: { isActive: true };
}>[]
| null,
loading: false,
async load() {
const res = await ApiFetch.api.ppid.grafikberdasarkanumur[
"find-many"
].get();
if (res.status === 200) {
grafikBerdasarkanUmur.findMany.data = res.data?.data ?? [];
}
},
},
})
const stateGrafikBerdasarkanUmur = proxy({
grafikBerdasarkanUmur,
})
export default stateGrafikBerdasarkanUmur;

View File

@@ -0,0 +1,76 @@
import ApiFetch from "@/lib/api-fetch";
import { Prisma } from "@prisma/client";
import { toast } from "react-toastify";
import { proxy } from "valtio";
import { z } from "zod";
const templateGrafikHasilKepuasanMasyarakat = z.object({
label: z.string().min(2, "Label harus diisi"),
kepuasan: z.string().min(2, "Kepuasan harus diisi"),
});
type GrafikHasilKepuasanMasyarakat = Prisma.IndeksKepuasanMasyarakatGetPayload<{
select: {
label: true;
kepuasan: true;
};
}>;
const defaultForm: GrafikHasilKepuasanMasyarakat = {
label: "",
kepuasan: "",
};
const grafikHasilKepuasanMasyarakat = proxy({
create: {
form: defaultForm,
loading: false,
async create() {
const cek = templateGrafikHasilKepuasanMasyarakat.safeParse(
grafikHasilKepuasanMasyarakat.create.form
);
if (!cek.success) {
const err = `[${cek.error.issues
.map((v) => `${v.path.join(".")}`)
.join("\n")}] required`;
return toast.error(err);
}
try {
grafikHasilKepuasanMasyarakat.create.loading = true;
const res = await ApiFetch.api.ppid.grafikhasilkepuasamanmasyarakat["create"].post(
grafikHasilKepuasanMasyarakat.create.form
);
if (res.status === 200) {
grafikHasilKepuasanMasyarakat.create.form = {
label: "",
kepuasan: ""
};
grafikHasilKepuasanMasyarakat.findMany.load();
return toast.success("success create");
}
return toast.error("failed create");
} catch (error) {
console.log((error as Error).message);
} finally {
grafikHasilKepuasanMasyarakat.create.loading = false;
}
},
},
findMany: {
data: null as
| Prisma.IndeksKepuasanMasyarakatGetPayload<{ omit: { isActive: true } }>[]
| null,
async load() {
const res = await ApiFetch.api.ppid.grafikhasilkepuasamanmasyarakat["find-many"].get();
if (res.status === 200) {
grafikHasilKepuasanMasyarakat.findMany.data = res.data?.data ?? [];
}
}
}
});
const stateGrafikHasilKepuasanMasyarakat = proxy({
grafikHasilKepuasanMasyarakat,
});
export default stateGrafikHasilKepuasanMasyarakat;

View File

@@ -0,0 +1,125 @@
import ApiFetch from "@/lib/api-fetch";
import { Prisma } from "@prisma/client";
import { toast } from "react-toastify";
import { proxy } from "valtio";
import { z } from "zod";
const templateForm = z.object({
name: z.string().min(3, "Nama minimal 3 karakter"),
nik: z.string().min(3, "NIK minimal 3 karakter"),
notelp: z.string().min(3, "Nomor Telepon minimal 3 karakter"),
alamat: z.string().min(3, "Alamat minimal 3 karakter"),
email: z.string().min(3, "Email minimal 3 karakter"),
jenisInformasiDimintaId: z.string().nonempty(),
caraMemperolehInformasiId: z.string().nonempty(),
caraMemperolehSalinanInformasiId: z.string().nonempty(),
})
const jenisInformasiDiminta = proxy({
findMany: {
data: null as
| null
| Prisma.JenisInformasiDimintaGetPayload<{ omit: { isActive: true } }>[],
async load(){
const res = await ApiFetch.api.ppid.permohonaninformasipublik.jenisInformasi["find-many"].get();
if (res.status === 200) {
jenisInformasiDiminta.findMany.data = res.data?.data ?? [];
}
}
}
})
const caraMemperolehInformasi = proxy({
findMany: {
data: null as
| null
| Prisma.CaraMemperolehInformasiGetPayload<{ omit: { isActive: true } }>[],
async load() {
const res = await ApiFetch.api.ppid.permohonaninformasipublik.memperolehInformasi["find-many"].get();
if (res.status === 200) {
caraMemperolehInformasi.findMany.data = res.data?.data ?? [];
}
}
}
})
const caraMemperolehSalinanInformasi = proxy({
findMany: {
data: null as
| null
| Prisma.CaraMemperolehSalinanInformasiGetPayload<{ omit: { isActive: true } }>[],
async load() {
const res = await ApiFetch.api.ppid.permohonaninformasipublik.salinanInformasi["find-many"].get();
if (res.status === 200) {
caraMemperolehSalinanInformasi.findMany.data = res.data?.data ?? [];
}
}
}
})
console.log(caraMemperolehSalinanInformasi)
type PermohonanInformasiPublikForm = Prisma.PermohonanInformasiPublikGetPayload<{
select: {
name: true;
nik: true;
notelp: true;
alamat: true;
email: true;
jenisInformasiDimintaId: true;
caraMemperolehInformasiId: true;
caraMemperolehSalinanInformasiId: true;
};
}>;
const statepermohonanInformasiPublik = proxy({
create: {
form: {} as PermohonanInformasiPublikForm,
loading: false,
async create(){
const cek = templateForm.safeParse(statepermohonanInformasiPublik.create.form);
if(!cek.success) {
const err = `[${cek.error.issues
.map((v) => `${v.path.join(".")}`)
.join("\n")}] required`;
return toast.error(err);
}
try {
statepermohonanInformasiPublik.create.loading = true;
const res = await ApiFetch.api.ppid.permohonaninformasipublik["create"].post(statepermohonanInformasiPublik.create.form);
if (res.status === 200) {
statepermohonanInformasiPublik.findMany.load();
return toast.success("success create");
}
return toast.error("failed create");
} catch (error) {
console.log((error as Error).message);
} finally {
statepermohonanInformasiPublik.create.loading = false;
}
}
},
findMany: {
data: null as
| Prisma.PermohonanInformasiPublikGetPayload<{ include: {
caraMemperolehSalinanInformasi: true,
jenisInformasiDiminta: true,
caraMemperolehInformasi: true,
} }>[]
| null,
async load() {
const res = await ApiFetch.api.ppid.permohonaninformasipublik["find-many"].get();
if (res.status === 200) {
statepermohonanInformasiPublik.findMany.data = res.data?.data ?? [];
}
}
}
})
const statepermohonanInformasiPublikForm = proxy({
statepermohonanInformasiPublik,
jenisInformasiDiminta,
caraMemperolehInformasi,
caraMemperolehSalinanInformasi,
})
export default statepermohonanInformasiPublikForm;

View File

@@ -0,0 +1,64 @@
import ApiFetch from "@/lib/api-fetch";
import { Prisma } from "@prisma/client";
import { toast } from "react-toastify";
import { proxy } from "valtio";
import { z } from "zod";
const templateForm = z.object({
name: z.string().min(3, "Nama minimal 3 karakter"),
email: z.string().min(3, "Email minimal 3 karakter"),
notelp: z.string().min(3, "Nomor Telepon minimal 3 karakter"),
alasan: z.string().min(3, "Alasan minimal 3 karakter"),
})
type PermohonanKeberatanInformasiForm = Prisma.FormulirPermohonanKeberatanGetPayload<{
select: {
name: true;
email: true;
notelp: true;
alasan: true;
};
}>;
const permohonanKeberatanInformasi = proxy({
create: {
form: {} as PermohonanKeberatanInformasiForm,
loading: false,
async create(){
const cek = templateForm.safeParse(permohonanKeberatanInformasi.create.form);
if(!cek.success) {
const err = `[${cek.error.issues
.map((v) => `${v.path.join(".")}`)
.join("\n")}] required`;
return toast.error(err);
}
try {
permohonanKeberatanInformasi.create.loading = true;
const res = await ApiFetch.api.ppid.permohonankeberataninformasipublik["create"].post(permohonanKeberatanInformasi.create.form);
if (res.status === 200) {
permohonanKeberatanInformasi.findMany.load();
return toast.success("success create");
}
return toast.error("failed create");
} catch (error) {
console.log((error as Error).message);
} finally {
permohonanKeberatanInformasi.create.loading = false;
}
},
},
findMany: {
data: null as
| Prisma.FormulirPermohonanKeberatanGetPayload<{omit: {isActive: true}}>[]
| null,
async load() {
const res = await ApiFetch.api.ppid.permohonankeberataninformasipublik["find-many"].get();
if (res.status === 200) {
permohonanKeberatanInformasi.findMany.data = res.data?.data ?? [];
}
}
}
});
export default permohonanKeberatanInformasi;

View File

@@ -0,0 +1,125 @@
import ApiFetch from "@/lib/api-fetch";
import { Prisma } from "@prisma/client";
import { toast } from "react-toastify";
import { proxy } from "valtio";
import { z } from "zod";
// Schema validasi form
const templateForm = z.object({
name: z.string().min(3, "Nama minimal 3 karakter"),
biodata: z.string().min(3, "Biodata minimal 3 karakter"),
riwayat: z.string().min(3, "Riwayat minimal 3 karakter"),
pengalaman: z.string().min(3, "Pengalaman minimal 3 karakter"),
unggulan: z.string().min(3, "Unggulan minimal 3 karakter"),
});
// Type ambil dari Prisma
type ProfilePPIDForm = Prisma.ProfilePPIDGetPayload<{
select: {
id: true;
name: true;
biodata: true;
riwayat: true;
pengalaman: true;
unggulan: true;
imageUrl: true;
};
}>;
// Proxy utama
const stateProfilePPID = proxy({
findById: {
data: null as ProfilePPIDForm | null,
loading: false,
initialize() {
stateProfilePPID.findById.data = {
id: '',
name: '',
biodata: '',
riwayat: '',
pengalaman: '',
unggulan: '',
imageUrl:''
} as ProfilePPIDForm;
},
async load(id: string) {
try {
stateProfilePPID.findById.loading = true;
const res = await ApiFetch.api.ppid.profileppid["find-by-id"].get({
query: { id },
});
if (res.status === 200) {
stateProfilePPID.findById.data = res.data?.data ?? null;
} else {
toast.error("Gagal mengambil data profile");
}
} catch (error) {
console.error((error as Error).message);
toast.error("Terjadi kesalahan saat mengambil data profile");
} finally {
stateProfilePPID.findById.loading = false;
}
},
},
update: {
loading: false,
async save(data: ProfilePPIDForm) {
const cek = templateForm.safeParse(data);
if (!cek.success) {
const errors = cek.error.issues
.map((issue) => `${issue.path.join(".")}: ${issue.message}`)
.join(", ");
toast.error(`Form tidak valid: ${errors}`);
return;
}
try {
stateProfilePPID.update.loading = true;
const res = await ApiFetch.api.ppid.profileppid["update"].post(data);
if (res.status === 200) {
toast.success("Berhasil update profile");
await stateProfilePPID.findById.load(data.id);
} else {
toast.error("Gagal update profile");
}
} catch (error) {
console.error((error as Error).message);
toast.error("Terjadi kesalahan saat update profile");
} finally {
stateProfilePPID.update.loading = false;
}
},
},
uploadImage: {
loading: false,
async save(file: File, id: string) {
if (!file || !id) {
toast.error("File atau ID harus disertakan");
return;
}
try {
stateProfilePPID.uploadImage.loading = true;
const form = new FormData();
form.append("file", file);
form.append("id", id);
const res = await ApiFetch.api.ppid.profileppid["edit-img"].post(form);
if (res.status === 200) {
toast.success("Berhasil mengunggah gambar");
await stateProfilePPID.findById.load(id);
} else {
toast.error("Gagal mengunggah gambar");
}
} catch (error) {
console.error((error as Error).message);
toast.error("Terjadi kesalahan saat mengunggah gambar");
} finally {
stateProfilePPID.uploadImage.loading = false;
}
}
}
});
export default stateProfilePPID;

View File

@@ -0,0 +1,81 @@
import ApiFetch from "@/lib/api-fetch";
import { Prisma } from "@prisma/client";
import { toast } from "react-toastify";
import { proxy } from "valtio";
import { z } from "zod";
const templateForm = z.object({
misi: z.string().min(3, "Misi minimal 3 karakter"),
visi: z.string().min(3, "Visi minimal 3 karakter"),
});
type VisiMisiPPIDForm = Prisma.VisiMisiPPIDGetPayload<{
select: {
id: true;
misi: true;
visi: true;
};
}>;
const stateVisiMisiPPID = proxy({
findById: {
data: null as VisiMisiPPIDForm | null,
loading: false,
initialize() {
stateVisiMisiPPID.findById.data = {
id: "",
misi: "",
visi: "",
} as VisiMisiPPIDForm;
},
async load(id: string) {
try {
stateVisiMisiPPID.findById.loading = true;
const res = await ApiFetch.api.ppid.visimisippid["find-by-id"].get({
query: { id },
});
if (res.status === 200) {
stateVisiMisiPPID.findById.data = res.data?.data ?? null;
} else {
toast.error("Gagal mengambil data visi misi");
}
} catch (error) {
console.error((error as Error).message);
toast.error("Terjadi kesalahan saat mengambil data visi misi");
} finally {
stateVisiMisiPPID.findById.loading = false;
}
},
},
update: {
loading: false,
async save(data: VisiMisiPPIDForm) {
const cek = templateForm.safeParse(data);
if (!cek.success) {
const errors = cek.error.issues
.map((issue) => `${issue.path.join(".")}: ${issue.message}`)
.join(", ");
toast.error(`Form tidak valid: ${errors}`);
return;
}
try {
stateVisiMisiPPID.update.loading = true;
const res = await ApiFetch.api.ppid.visimisippid["update"].post(data);
if (res.status === 200) {
toast.success("Berhasil update visi misi");
await stateVisiMisiPPID.findById.load(data.id);
} else {
toast.error("Gagal update visi misi");
}
} catch (error) {
console.error((error as Error).message);
toast.error("Terjadi kesalahan saat update visi misi");
} finally {
stateVisiMisiPPID.update.loading = false;
}
},
},
});
export default stateVisiMisiPPID;

View File

@@ -0,0 +1,93 @@
'use client'
import colors from '@/con/colors';
import { Button, Stack } from '@mantine/core';
import { Link, RichTextEditor } from '@mantine/tiptap';
import Highlight from '@tiptap/extension-highlight';
import SubScript from '@tiptap/extension-subscript';
import Superscript from '@tiptap/extension-superscript';
import TextAlign from '@tiptap/extension-text-align';
import Underline from '@tiptap/extension-underline';
import { useEditor } from '@tiptap/react';
import StarterKit from '@tiptap/starter-kit';
const content =
'<h2 style="text-align: center;">Welcome to Mantine rich text editor</h2><p><code>RichTextEditor</code> component focuses on usability and is designed to be as simple as possible to bring a familiar editing experience to regular users. <code>RichTextEditor</code> is based on <a href="https://tiptap.dev/" rel="noopener noreferrer" target="_blank">Tiptap.dev</a> and supports all of its features:</p><ul><li>General text formatting: <strong>bold</strong>, <em>italic</em>, <u>underline</u>, <s>strike-through</s> </li><li>Headings (h1-h6)</li><li>Sub and super scripts (<sup>&lt;sup /&gt;</sup> and <sub>&lt;sub /&gt;</sub> tags)</li><li>Ordered and bullet lists</li><li>Text align&nbsp;</li><li>And all <a href="https://tiptap.dev/extensions" target="_blank" rel="noopener noreferrer">other extensions</a></li></ul>';
export function DesaEditor({showSubmit = true} : {
showSubmit: boolean
}) {
const editor = useEditor({
extensions: [
StarterKit,
Underline,
Link,
Superscript,
SubScript,
Highlight,
TextAlign.configure({ types: ['heading', 'paragraph'] }),
],
immediatelyRender: false,
content,
});
return (
<Stack>
<RichTextEditor editor={editor}>
<RichTextEditor.Toolbar sticky stickyOffset={60}>
<RichTextEditor.ControlsGroup>
<RichTextEditor.Bold />
<RichTextEditor.Italic />
<RichTextEditor.Underline />
<RichTextEditor.Strikethrough />
<RichTextEditor.ClearFormatting />
<RichTextEditor.Highlight />
<RichTextEditor.Code />
</RichTextEditor.ControlsGroup>
<RichTextEditor.ControlsGroup>
<RichTextEditor.H1 />
<RichTextEditor.H2 />
<RichTextEditor.H3 />
<RichTextEditor.H4 />
</RichTextEditor.ControlsGroup>
<RichTextEditor.ControlsGroup>
<RichTextEditor.Blockquote />
<RichTextEditor.Hr />
<RichTextEditor.BulletList />
<RichTextEditor.OrderedList />
<RichTextEditor.Subscript />
<RichTextEditor.Superscript />
</RichTextEditor.ControlsGroup>
<RichTextEditor.ControlsGroup>
<RichTextEditor.Link />
<RichTextEditor.Unlink />
</RichTextEditor.ControlsGroup>
<RichTextEditor.ControlsGroup>
<RichTextEditor.AlignLeft />
<RichTextEditor.AlignCenter />
<RichTextEditor.AlignJustify />
<RichTextEditor.AlignRight />
</RichTextEditor.ControlsGroup>
<RichTextEditor.ControlsGroup>
<RichTextEditor.Undo />
<RichTextEditor.Redo />
</RichTextEditor.ControlsGroup>
</RichTextEditor.Toolbar>
<RichTextEditor.Content />
</RichTextEditor>
{showSubmit && (
<Button
mt={10}
bg={colors['blue-button']}
>
Submit
</Button>
)}
</Stack>
);
}

View File

@@ -0,0 +1,95 @@
'use client'
import { Button, Stack } from '@mantine/core';
import { Link, RichTextEditor } from '@mantine/tiptap';
import Highlight from '@tiptap/extension-highlight';
import SubScript from '@tiptap/extension-subscript';
import Superscript from '@tiptap/extension-superscript';
import TextAlign from '@tiptap/extension-text-align';
import Underline from '@tiptap/extension-underline';
import { useEditor } from '@tiptap/react';
import StarterKit from '@tiptap/starter-kit';
function DesaEditorText({ onSubmit, onChange, showSubmit = true, initialContent = '', }: {
onSubmit?: (val: string) => void,
onChange: (val: string) => void,
showSubmit?: boolean,
initialContent?: string }) {
const editor = useEditor({
extensions: [
StarterKit,
Underline,
Link,
Superscript,
SubScript,
Highlight,
TextAlign.configure({ types: ['heading', 'paragraph'] }),
],
immediatelyRender: false,
content: initialContent,
onUpdate : ({editor}) => {
onChange(editor.getHTML())
}
});
return (
<Stack>
<RichTextEditor editor={editor}>
<RichTextEditor.Toolbar sticky stickyOffset={60}>
<RichTextEditor.ControlsGroup>
<RichTextEditor.Bold />
<RichTextEditor.Italic />
<RichTextEditor.Underline />
<RichTextEditor.Strikethrough />
<RichTextEditor.ClearFormatting />
<RichTextEditor.Highlight />
<RichTextEditor.Code />
</RichTextEditor.ControlsGroup>
<RichTextEditor.ControlsGroup>
<RichTextEditor.H1 />
<RichTextEditor.H2 />
<RichTextEditor.H3 />
<RichTextEditor.H4 />
</RichTextEditor.ControlsGroup>
<RichTextEditor.ControlsGroup>
<RichTextEditor.Blockquote />
<RichTextEditor.Hr />
<RichTextEditor.BulletList />
<RichTextEditor.OrderedList />
<RichTextEditor.Subscript />
<RichTextEditor.Superscript />
</RichTextEditor.ControlsGroup>
<RichTextEditor.ControlsGroup>
<RichTextEditor.Link />
<RichTextEditor.Unlink />
</RichTextEditor.ControlsGroup>
<RichTextEditor.ControlsGroup>
<RichTextEditor.AlignLeft />
<RichTextEditor.AlignCenter />
<RichTextEditor.AlignJustify />
<RichTextEditor.AlignRight />
</RichTextEditor.ControlsGroup>
<RichTextEditor.ControlsGroup>
<RichTextEditor.Undo />
<RichTextEditor.Redo />
</RichTextEditor.ControlsGroup>
</RichTextEditor.Toolbar>
<RichTextEditor.Content />
</RichTextEditor>
{showSubmit && (
<Button onClick={() => {
if (!editor) return
onSubmit?.(editor?.getHTML())
}}>Submit</Button>
)}
</Stack>
);
}
export default DesaEditorText;

View File

@@ -10,9 +10,9 @@ import { BeritaEditor } from './_com/BeritaEditor';
function Page() {
return (
<Stack>
<SimpleGrid cols={2}>
<BeritaList />
<SimpleGrid cols={{base: 1, md: 2}}>
<BeritaCreate />
<BeritaList />
</SimpleGrid>
</Stack>
);

View File

@@ -1,10 +1,52 @@
import colors from '@/con/colors';
import { Stack, Title, Tabs, TabsList, TabsTab, TabsPanel } from '@mantine/core';
import React from 'react';
import SejarahDesa from './ui/sejarah_desa/page';
import VisiMisiDesa from './ui/visi_misi_desa/page';
import LambangDesa from './ui/lambang_desa/page';
import MaskotDesa from './ui/maskot_desa/page';
import ProfilePerbekel from './ui/profile_perbekel/page';
function Page() {
return (
<div>
Profile
</div>
<Stack >
<Title order={3}>Profile Desa</Title>
<Tabs color={colors['blue-button']} variant='pills' defaultValue={"Sejarah Desa"}>
<TabsList p={"xs"} bg={"#BBC8E7FF"}>
<TabsTab value="Sejarah Desa">
Sejarah Desa
</TabsTab>
<TabsTab value="Visi Misi Desa">
Visi Misi Desa
</TabsTab>
<TabsTab value="Lambang Desa">
Lambang Desa
</TabsTab>
<TabsTab value="Maskot Desa">
Maskot Desa
</TabsTab>
<TabsTab value="Profile Perbekel">
Profile Perbekel
</TabsTab>
</TabsList>
<TabsPanel value="Sejarah Desa">
<SejarahDesa/>
</TabsPanel>
<TabsPanel value="Visi Misi Desa">
<VisiMisiDesa/>
</TabsPanel>
<TabsPanel value="Lambang Desa">
<LambangDesa/>
</TabsPanel>
<TabsPanel value="Maskot Desa">
<MaskotDesa/>
</TabsPanel>
<TabsPanel value="Profile Perbekel">
<ProfilePerbekel/>
</TabsPanel>
</Tabs>
</Stack>
);
}

View File

@@ -0,0 +1,39 @@
import colors from '@/con/colors';
import { Box, SimpleGrid, Paper, Stack, Title, Group, Button, Text } from '@mantine/core';
import React from 'react';
import { DesaEditor } from '../../../_com/desaEditor';
function LambangDesa() {
return (
<Box py={10}>
<SimpleGrid cols={{ base: 1, md: 2 }}>
<Box>
<Paper bg={colors['white-1']} p={'md'}>
<Stack>
<Title order={3}>Lambang Desa</Title>
<Text fw={"bold"}>Deskripsi Lambang Desa</Text>
<DesaEditor showSubmit={false} />
<Group>
<Button
mt={10}
bg={colors['blue-button']}
>
Submit
</Button>
</Group>
</Stack>
</Paper>
</Box>
<Box>
<Paper bg={colors['white-1']} p={'md'}>
<Stack>
<Title order={3}>List Lambang Desa</Title>
</Stack>
</Paper>
</Box>
</SimpleGrid>
</Box>
);
}
export default LambangDesa;

View File

@@ -0,0 +1,39 @@
import colors from '@/con/colors';
import { Box, SimpleGrid, Paper, Stack, Title, Group, Button, Text } from '@mantine/core';
import React from 'react';
import { DesaEditor } from '../../../_com/desaEditor';
function MaskotDesa() {
return (
<Box py={10}>
<SimpleGrid cols={{ base: 1, md: 2 }}>
<Box>
<Paper bg={colors['white-1']} p={'md'}>
<Stack>
<Title order={3}>Maskot Desa</Title>
<Text fw={"bold"}>Deskripsi Maskot Desa</Text>
<DesaEditor showSubmit={false} />
<Group>
<Button
mt={10}
bg={colors['blue-button']}
>
Submit
</Button>
</Group>
</Stack>
</Paper>
</Box>
<Box>
<Paper bg={colors['white-1']} p={'md'}>
<Stack>
<Title order={3}>List Maskot Desa</Title>
</Stack>
</Paper>
</Box>
</SimpleGrid>
</Box>
);
}
export default MaskotDesa;

View File

@@ -0,0 +1,49 @@
import colors from '@/con/colors';
import { Box, SimpleGrid, Paper, Stack, Title, Group, Button, TextInput, Text } from '@mantine/core';
import React from 'react';
import { DesaEditor } from '../../../_com/desaEditor';
function ProfilePerbekel() {
return (
<Box py={10}>
<SimpleGrid cols={{ base: 1, md: 2 }}>
<Box>
<Paper bg={colors['white-1']} p={'md'}>
<Stack>
<Title order={3}>Profil Perbekel</Title>
<TextInput
label="Nama Perbekel"
placeholder="masukkan nama perbekel"
/>
<Text fz={"sm"} fw={"bold"}>Biodata</Text>
<DesaEditor showSubmit={false} />
<Text fz={"sm"} fw={"bold"}>Pengalaman</Text>
<DesaEditor showSubmit={false} />
<Text fz={"sm"} fw={"bold"}>Pengalaman Organisasi</Text>
<DesaEditor showSubmit={false} />
<Text fz={"sm"} fw={"bold"}>Program Unggulan</Text>
<DesaEditor showSubmit={false} />
<Group>
<Button
mt={10}
bg={colors['blue-button']}
>
Submit
</Button>
</Group>
</Stack>
</Paper>
</Box>
<Box>
<Paper bg={colors['white-1']} p={'md'}>
<Stack>
<Title order={3}>List Profil Perbekel</Title>
</Stack>
</Paper>
</Box>
</SimpleGrid>
</Box>
);
}
export default ProfilePerbekel;

View File

@@ -0,0 +1,38 @@
import colors from '@/con/colors';
import { Box, Button, Group, Paper, SimpleGrid, Stack, Text, Title } from '@mantine/core';
import { DesaEditor } from '../../../_com/desaEditor';
function SejarahDesa() {
return (
<Box py={10}>
<SimpleGrid cols={{ base: 1, md: 2 }}>
<Box>
<Paper bg={colors['white-1']} p={'md'}>
<Stack>
<Title order={3}>Sejarah Desa</Title>
<Text fw={"bold"}>Deskripsi Sejarah Desa</Text>
<DesaEditor showSubmit={false}/>
<Group>
<Button
mt={10}
bg={colors['blue-button']}
>
Submit
</Button>
</Group>
</Stack>
</Paper>
</Box>
<Box>
<Paper bg={colors['white-1']} p={'md'}>
<Stack>
<Title order={3}>List Sejarah Desa</Title>
</Stack>
</Paper>
</Box>
</SimpleGrid>
</Box>
);
}
export default SejarahDesa;

View File

@@ -0,0 +1,64 @@
import colors from '@/con/colors';
import { Box, Button, Group, Paper, SimpleGrid, Stack, Text, Title } from '@mantine/core';
import { DesaEditor } from '../../../_com/desaEditor';
function VisiMisiDesa() {
return (
<Box py={10}>
<SimpleGrid cols={{ base: 1, md: 2 }}>
<Box>
<Paper bg={colors['white-1']} p={'md'}>
<Stack>
<Title order={3}>Visi Desa</Title>
<Text fw={"bold"}>Deskripsi Visi Desa</Text>
<DesaEditor showSubmit={false} />
<Group>
<Button
mt={10}
bg={colors['blue-button']}
>
Submit
</Button>
</Group>
</Stack>
</Paper>
</Box>
<Box>
<Paper bg={colors['white-1']} p={'md'}>
<Stack>
<Title order={3}>List Visi Desa</Title>
</Stack>
</Paper>
</Box>
</SimpleGrid>
<SimpleGrid py={30} cols={{ base: 1, md: 2 }}>
<Box>
<Paper bg={colors['white-1']} p={'md'}>
<Stack>
<Title order={3}>Misi Desa</Title>
<Text fw={"bold"}>Deskripsi Misi Desa</Text>
<DesaEditor showSubmit={false}/>
<Group>
<Button
mt={10}
bg={colors['blue-button']}
>
Submit
</Button>
</Group>
</Stack>
</Paper>
</Box>
<Box>
<Paper bg={colors['white-1']} p={'md'}>
<Stack>
<Title order={3}>List Misi Desa</Title>
</Stack>
</Paper>
</Box>
</SimpleGrid>
</Box>
);
}
export default VisiMisiDesa;

View File

@@ -0,0 +1,93 @@
'use client'
import { Button, Stack } from '@mantine/core';
import { Link, RichTextEditor } from '@mantine/tiptap';
import Highlight from '@tiptap/extension-highlight';
import SubScript from '@tiptap/extension-subscript';
import Superscript from '@tiptap/extension-superscript';
import TextAlign from '@tiptap/extension-text-align';
import Underline from '@tiptap/extension-underline';
import { useEditor } from '@tiptap/react';
import StarterKit from '@tiptap/starter-kit';
export function PPIDTextEditor({ onSubmit, onChange, showSubmit = true, initialContent = '', }: {
onSubmit?: (val: string) => void,
onChange: (val: string) => void,
showSubmit?: boolean,
initialContent?: string }) {
const editor = useEditor({
extensions: [
StarterKit,
Underline,
Link,
Superscript,
SubScript,
Highlight,
TextAlign.configure({ types: ['heading', 'paragraph'] }),
],
immediatelyRender: false,
content: initialContent,
onUpdate : ({editor}) => {
onChange(editor.getHTML())
}
});
return (
<Stack>
<RichTextEditor editor={editor}>
<RichTextEditor.Toolbar sticky stickyOffset={60}>
<RichTextEditor.ControlsGroup>
<RichTextEditor.Bold />
<RichTextEditor.Italic />
<RichTextEditor.Underline />
<RichTextEditor.Strikethrough />
<RichTextEditor.ClearFormatting />
<RichTextEditor.Highlight />
<RichTextEditor.Code />
</RichTextEditor.ControlsGroup>
<RichTextEditor.ControlsGroup>
<RichTextEditor.H1 />
<RichTextEditor.H2 />
<RichTextEditor.H3 />
<RichTextEditor.H4 />
</RichTextEditor.ControlsGroup>
<RichTextEditor.ControlsGroup>
<RichTextEditor.Blockquote />
<RichTextEditor.Hr />
<RichTextEditor.BulletList />
<RichTextEditor.OrderedList />
<RichTextEditor.Subscript />
<RichTextEditor.Superscript />
</RichTextEditor.ControlsGroup>
<RichTextEditor.ControlsGroup>
<RichTextEditor.Link />
<RichTextEditor.Unlink />
</RichTextEditor.ControlsGroup>
<RichTextEditor.ControlsGroup>
<RichTextEditor.AlignLeft />
<RichTextEditor.AlignCenter />
<RichTextEditor.AlignJustify />
<RichTextEditor.AlignRight />
</RichTextEditor.ControlsGroup>
<RichTextEditor.ControlsGroup>
<RichTextEditor.Undo />
<RichTextEditor.Redo />
</RichTextEditor.ControlsGroup>
</RichTextEditor.Toolbar>
<RichTextEditor.Content />
</RichTextEditor>
{showSubmit && (
<Button onClick={() => {
if (!editor) return
onSubmit?.(editor?.getHTML())
}}>Submit</Button>
)}
</Stack>
);
}

View File

@@ -0,0 +1,94 @@
'use client'
import { Button, Stack } from '@mantine/core';
import { Link, RichTextEditor } from '@mantine/tiptap';
import Highlight from '@tiptap/extension-highlight';
import SubScript from '@tiptap/extension-subscript';
import Superscript from '@tiptap/extension-superscript';
import TextAlign from '@tiptap/extension-text-align';
import Underline from '@tiptap/extension-underline';
import { useEditor } from '@tiptap/react';
import StarterKit from '@tiptap/starter-kit';
const content =
'<p>Dokumen yang berisi kebijakan dan regulasi desa</p>';
export function PPIDEditor({ onSubmit, onChange, showSubmit = true }: {
onSubmit?: (val: string) => void,
onChange: (val: string) => void,
showSubmit?: boolean }) {
const editor = useEditor({
extensions: [
StarterKit,
Underline,
Link,
Superscript,
SubScript,
Highlight,
TextAlign.configure({ types: ['heading', 'paragraph'] }),
],
immediatelyRender: false,
content,
onUpdate : ({editor}) => {
onChange(editor.getHTML())
}
});
return (
<Stack>
<RichTextEditor editor={editor}>
<RichTextEditor.Toolbar sticky stickyOffset={60}>
<RichTextEditor.ControlsGroup>
<RichTextEditor.Bold />
<RichTextEditor.Italic />
<RichTextEditor.Underline />
<RichTextEditor.Strikethrough />
<RichTextEditor.ClearFormatting />
<RichTextEditor.Highlight />
<RichTextEditor.Code />
</RichTextEditor.ControlsGroup>
<RichTextEditor.ControlsGroup>
<RichTextEditor.H1 />
<RichTextEditor.H2 />
<RichTextEditor.H3 />
<RichTextEditor.H4 />
</RichTextEditor.ControlsGroup>
<RichTextEditor.ControlsGroup>
<RichTextEditor.Blockquote />
<RichTextEditor.Hr />
<RichTextEditor.BulletList />
<RichTextEditor.OrderedList />
<RichTextEditor.Subscript />
<RichTextEditor.Superscript />
</RichTextEditor.ControlsGroup>
<RichTextEditor.ControlsGroup>
<RichTextEditor.Link />
<RichTextEditor.Unlink />
</RichTextEditor.ControlsGroup>
<RichTextEditor.ControlsGroup>
<RichTextEditor.AlignLeft />
<RichTextEditor.AlignCenter />
<RichTextEditor.AlignJustify />
<RichTextEditor.AlignRight />
</RichTextEditor.ControlsGroup>
<RichTextEditor.ControlsGroup>
<RichTextEditor.Undo />
<RichTextEditor.Redo />
</RichTextEditor.ControlsGroup>
</RichTextEditor.Toolbar>
<RichTextEditor.Content />
</RichTextEditor>
{showSubmit && (
<Button onClick={() => {
if (!editor) return
onSubmit?.(editor?.getHTML())
}}>Submit</Button>
)}
</Stack>
);
}

View File

@@ -1,11 +1,116 @@
import React from 'react';
'use client'
import colors from '@/con/colors';
import { Box, Button, Group, Paper, SimpleGrid, Skeleton, Stack, Table, TableTbody, TableTd, TableTh, TableThead, TableTr, TextInput, Title } from '@mantine/core';
import { useShallowEffect } from '@mantine/hooks';
import { useProxy } from 'valtio/utils';
import stateDaftarInformasiPublik from '../../_state/ppid/daftar_informasi_publik/daftarInformasiPublik';
import { PPIDTextEditor } from '../_com/PPIDTextEditor';
function Page() {
const daftarInformasi = useProxy(stateDaftarInformasiPublik.daftarInformasi)
const submit = () => {
if (daftarInformasi.create.form.jenisInformasi &&
daftarInformasi.create.form.deskripsi &&
daftarInformasi.create.form.tanggal) {
daftarInformasi.create.create()
}
}
return (
<div>
daftar-informasi-publik-desa-darmasaba
</div>
<Box>
<SimpleGrid cols={{ base: 1, md: 2 }}>
<Box>
<Paper bg={colors['white-1']} p={'md'}>
<Stack gap={"xs"}>
<Title fw={"bold"} order={3}>Daftar Informasi Publik Desa Darmasaba</Title>
<TextInput
label="Jenis Informasi"
placeholder="masukkan jenis informasi"
onChange={(val) => {
daftarInformasi.create.form.jenisInformasi = val.target.value
}}
/>
<PPIDTextEditor
showSubmit={false}
onChange={(val) => {
daftarInformasi.create.form.deskripsi = val
}}
/>
<TextInput
label="Tanggal Publikasi"
placeholder="masukkan tanggal publikasi"
onChange={(val) => {
daftarInformasi.create.form.tanggal = val.target.value
}}
/>
<Group>
<Button
bg={colors['blue-button']}
onClick={submit}
>Submit</Button>
</Group>
</Stack>
</Paper>
</Box>
<Box>
<ListDaftarInformasi />
</Box>
</SimpleGrid>
</Box>
);
}
function ListDaftarInformasi() {
const listData = useProxy(stateDaftarInformasiPublik.daftarInformasi)
useShallowEffect(() => {
listData.findMany.load()
}, [])
if (!listData.findMany.data) return <Stack>
{Array.from({ length: 10 }).map((v, k) => <Skeleton key={k} h={40} />)}
</Stack>
return (
<Box>
<Paper bg={colors['white-1']} p={'md'}>
<Stack gap={"xs"}>
<Title fw={"bold"} order={3}>List Daftar Informasi Publik Desa Darmasaba</Title>
<Table
suppressHydrationWarning
striped
highlightOnHover
withTableBorder
withColumnBorders
bg={colors['white-1']}
>
<TableThead>
<TableTr>
<TableTh>
No
</TableTh>
<TableTh>
Jenis Informasi
</TableTh>
<TableTh>
Deskripsi
</TableTh>
<TableTh>
Tanggal Publikasi
</TableTh>
</TableTr>
</TableThead>
<TableTbody>
{listData.findMany.data?.map((item) => (
<TableTr key={item.id}>
<TableTd>{item.nomor}</TableTd>
<TableTd>{item.jenisInformasi}</TableTd>
<TableTd dangerouslySetInnerHTML={{ __html: item.deskripsi }}></TableTd>
<TableTd>{item.tanggal}</TableTd>
</TableTr>
))}
</TableTbody>
</Table>
</Stack>
</Paper>
</Box>
)
}
export default Page;

View File

@@ -0,0 +1,64 @@
'use client'
import { Box, Button, Group, Stack, Text } from '@mantine/core';
import { useProxy } from 'valtio/utils';
import stateDasarHukumPPID from '../../../_state/ppid/dasar_hukum/dasarHukum';
import dynamic from 'next/dynamic';
import colors from '@/con/colors';
import { useShallowEffect } from '@mantine/hooks';
const PPIDTextEditor = dynamic(() => import('../../_com/PPIDTextEditor').then(mod => mod.PPIDTextEditor), {
ssr: false,
});
function CreateDasarHukum() {
const dasarHukumState = useProxy(stateDasarHukumPPID)
useShallowEffect(() => {
if (!dasarHukumState.findById.data) {
dasarHukumState.findById.load(""); // biar masuk ke `findFirst` route kamu
}
}, []);
const submit = () => {
if (dasarHukumState.findById.data?.judul && dasarHukumState.findById.data?.content) {
dasarHukumState.update.save(dasarHukumState.findById.data)
}
}
return (
<Box>
<Stack gap={"xs"}>
<Text fw={"bold"}>Judul</Text>
<PPIDTextEditor
showSubmit={false}
key={`judul-${dasarHukumState.findById.data?.id ?? 'new'}`}
onChange={(val) => {
if (dasarHukumState.findById.data) {
dasarHukumState.findById.data.judul = val
}
}}
initialContent={dasarHukumState.findById.data?.judul ?? ''}
/>
<Text fw={"bold"}>Content</Text>
<PPIDTextEditor
showSubmit={false}
key={`content-${dasarHukumState.findById.data?.id ?? 'baru'}`}
onChange={(val) => {
if (dasarHukumState.findById.data) {
dasarHukumState.findById.data.content = val
}
}}
initialContent={dasarHukumState.findById.data?.content ?? ''}
/>
<Group>
<Button
bg={colors['blue-button']}
onClick={submit}
loading={dasarHukumState.update.loading}
>
Submit
</Button>
</Group>
</Stack>
</Box>
);
}
export default CreateDasarHukum;

View File

@@ -0,0 +1,40 @@
'use client'
import React from 'react';
import { useProxy } from 'valtio/utils';
import stateDasarHukumPPID from '../../../_state/ppid/dasar_hukum/dasarHukum';
import { useShallowEffect } from '@mantine/hooks';
import { Box, Paper, Skeleton, Stack, Text, Title } from '@mantine/core';
import colors from '@/con/colors';
function ListDataDasarHukum() {
const dataList = useProxy(stateDasarHukumPPID)
useShallowEffect(() => {
dataList.findById.load("")
}, [])
if(!dataList.findById.data) return <Stack>
{Array.from({length: 10}).map((v, k) => <Skeleton key={k} h={40} />)}
</Stack>
const dataArray = Array.isArray(dataList.findById.data)
? dataList.findById.data
: [dataList.findById.data];
return (
<Paper bg={colors['white-1']} p={'md'} radius={10}>
<Stack gap={"xs"}>
<Title order={3}>List Dasar Hukum PPID</Title>
{dataArray.map((item) => (
<Box key={item.id}>
<Text fw={"bold"}>Judul</Text>
<Text dangerouslySetInnerHTML={{ __html: item.judul }}></Text>
<Text fw={"bold"}>Content</Text>
<Text dangerouslySetInnerHTML={{ __html: item.content }}></Text>
</Box>
))}
</Stack>
</Paper>
);
}
export default ListDataDasarHukum;

View File

@@ -1,11 +1,24 @@
import React from 'react';
'use client'
import colors from '@/con/colors';
import { Box, Paper, SimpleGrid, Stack, Title } from '@mantine/core';
import CreateDasarHukum from './create/page';
import ListDataDasarHukum from './listData/page';
function Page() {
return (
<div>
dasar-hukum
</div>
);
<Stack gap={"xs"}>
<SimpleGrid cols={{ base: 1, md: 2 }}>
<Box>
<Paper bg={colors['white-1']} p={'md'} radius={10}>
<Title order={3}>Dasar Hukum PPID</Title>
<CreateDasarHukum/>
</Paper>
</Box>
<ListDataDasarHukum/>
</SimpleGrid>
</Stack>
)
}
export default Page;

View File

@@ -0,0 +1,142 @@
/* eslint-disable @typescript-eslint/no-explicit-any */
'use client'
import stateGrafikBerdasarkanJenisKelamin from '@/app/admin/(dashboard)/_state/ppid/indeks_kepuasan_masyarakat/grafikBerdasarkanJenisKelamin';
import colors from '@/con/colors';
import { Box, Button, Center, Flex, Paper, Stack, Text, TextInput, Title } from '@mantine/core';
import { useShallowEffect } from '@mantine/hooks';
import { useEffect, useState } from 'react';
import { Cell, Pie, PieChart } from 'recharts';
import { useProxy } from 'valtio/utils';
function GrafikBerdasarkanJenisKelamin() {
const grafikBerdasarkanJenisKelamin = useProxy(stateGrafikBerdasarkanJenisKelamin.grafikBerdasarkanJenisKelamin)
const [donutData, setDonutData] = useState<any[]>([]);
const [mounted, setMounted] = useState(false);
useEffect(() => {
setMounted(true);
}, [])
const updateChartData = (data: any) => {
if (data && data.length > 0) {
const totalLaki = data.reduce((acc: number, cur: any) => acc + Number(cur.laki || 0), 0);
const totalPerempuan = data.reduce((acc: number, cur: any) => acc + Number(cur.perempuan || 0), 0);
setDonutData([
{ name: 'Laki-laki', value: totalLaki, color: colors['blue-button'], key: 'laki-laki' },
{ name: 'Perempuan', value: totalPerempuan, color: '#FF6384', key: 'perempuan' }
]);
}
};
useShallowEffect(() => {
fetchData();
}, []);
const fetchData = async () => {
await grafikBerdasarkanJenisKelamin.findMany.load();
if (grafikBerdasarkanJenisKelamin.findMany.data) {
updateChartData(grafikBerdasarkanJenisKelamin.findMany.data);
}
};
const handleSubmit = async () => {
try {
// Simpan data baru
await grafikBerdasarkanJenisKelamin.create.create();
// Muat ulang data
await grafikBerdasarkanJenisKelamin.findMany.load();
// Update chart dengan data baru
if (grafikBerdasarkanJenisKelamin.findMany.data) {
updateChartData(grafikBerdasarkanJenisKelamin.findMany.data);
}
// Reset form setelah submit
grafikBerdasarkanJenisKelamin.create.form.laki = '';
grafikBerdasarkanJenisKelamin.create.form.perempuan = '';
} catch (error) {
console.error("Error submitting data:", error);
}
};
return (
<Box>
<Box py={15}>
<Paper bg={colors['white-1']} w={{ base: '100%', md: '50%' }} p={'md'}>
<Title order={3}>Grafik Hasil Kepuasan Masyarakat Terhadap Pelayanan Publik</Title>
<TextInput
w={{ base: '100%', md: '50%' }}
label="Laki-laki"
placeholder="masukkan jumlah laki-laki"
value={grafikBerdasarkanJenisKelamin.create.form.laki}
onChange={(val) => {
grafikBerdasarkanJenisKelamin.create.form.laki = val.currentTarget.value;
}}
/>
<TextInput
w={{ base: '100%', md: '50%' }}
label="Perempuan"
type="number"
placeholder="masukkan jumlah perempuan"
value={grafikBerdasarkanJenisKelamin.create.form.perempuan}
onChange={(val) => {
grafikBerdasarkanJenisKelamin.create.form.perempuan = val.currentTarget.value;
}}
/>
<Button
mt={10}
bg={colors['blue-button']}
onClick={handleSubmit}
>
Submit
</Button>
</Paper>
</Box>
{/* Chart */}
<Box>
<Paper bg={colors['white-1']} w={{ base: '100%', md: '50%' }} p={'md'}>
<Stack>
<Title pb={10} order={3}>Grafik Berdasarkan Jenis Kelamin Responden</Title>
{mounted && donutData.length > 0 && (
<Box style={{ width: '100%', height: 'auto', minHeight: 900 }}>
<Center>
<PieChart
width={1000} height={400}
data={donutData}
>
<Pie
dataKey="value"
nameKey="name"
data={donutData}
innerRadius={120}
outerRadius={230}
label={true}
>
{donutData.map((entry, index) => (
<Cell key={`cell-${index}`} fill={entry.color} />
))}
</Pie>
</PieChart>
</Center>
<Flex gap={"md"} align={"center"}>
<Box bg={'#FF6384'} w={20} h={20} />
<Text>Perempuan: {donutData.find((entry) => entry.name === 'Perempuan')?.value}</Text>
</Flex>
<Flex gap={"md"} align={"center"}>
<Box bg={colors['blue-button']} w={20} h={20} />
<Text>Laki-laki: {donutData.find((entry) => entry.name === 'Laki-laki')?.value}</Text>
</Flex>
</Box>
)}
</Stack>
</Paper>
</Box>
</Box>
);
}
export default GrafikBerdasarkanJenisKelamin;

View File

@@ -0,0 +1,173 @@
'use client'
/* eslint-disable @typescript-eslint/no-explicit-any */
import stateGrafikResponden from '@/app/admin/(dashboard)/_state/ppid/indeks_kepuasan_masyarakat/grafikBerdasarkanResponden';
import colors from '@/con/colors';
import { Box, Button, Center, Flex, Group, Paper, Stack, Text, TextInput, Title } from '@mantine/core';
import { useShallowEffect } from '@mantine/hooks';
import React, { useEffect, useState } from 'react';
import { PieChart, Pie, Cell } from 'recharts';
import { useProxy } from 'valtio/utils';
function GrafikBerdasarkanResponden() {
const grafikBerdasarkanResponden = useProxy(stateGrafikResponden.grafikBerdasarkanResponden)
const [donutData, setDonutData] = useState<any[]>([]);
const [mounted, setMounted] = useState(false);
useEffect(() => {
setMounted(true);
}, [])
const updateChartData = (data: any) => {
if (data && data.length > 0) {
const totalSangatBaik = data.reduce((acc: number, cur: any) => acc + Number(cur.sangatbaik || 0), 0);
const totalBaik = data.reduce((acc: number, cur: any) => acc + Number(cur.baik || 0), 0);
const totalKurangBaik = data.reduce((acc: number, cur: any) => acc + Number(cur.kurangbaik || 0), 0);
const totalTidakBaik = data.reduce((acc: number, cur: any) => acc + Number(cur.tidakbaik || 0), 0);
setDonutData([
{ name: 'sangatbaik', value: totalSangatBaik, color: colors['blue-button'], key: 'sangatbaik' },
{ name: 'baik', value: totalBaik, color: '#10A85AFF', key: 'baik' },
{ name: 'kurangbaik', value: totalKurangBaik, color: '#B3AA12FF', key: 'kurangbaik' },
{ name: 'tidakbaik', value: totalTidakBaik, color: '#B21313FF', key: 'tidakbaik' }
]);
}
};
useShallowEffect(() => {
fetchData();
}, []);
const fetchData = async () => {
await grafikBerdasarkanResponden.findMany.load();
if (grafikBerdasarkanResponden.findMany.data) {
updateChartData(grafikBerdasarkanResponden.findMany.data);
}
};
const handleSubmit = async () => {
try {
// Simpan data baru
await grafikBerdasarkanResponden.create.create();
// Muat ulang data
await grafikBerdasarkanResponden.findMany.load();
// Update chart dengan data baru
if (grafikBerdasarkanResponden.findMany.data) {
updateChartData(grafikBerdasarkanResponden.findMany.data);
}
// Reset form setelah submit
grafikBerdasarkanResponden.create.form.sangatbaik = '';
grafikBerdasarkanResponden.create.form.baik = '';
grafikBerdasarkanResponden.create.form.kurangbaik = '';
grafikBerdasarkanResponden.create.form.tidakbaik = '';
} catch (error) {
console.error("Error submitting data:", error);
}
};
return (
<Box>
<Box py={15}>
<Paper bg={colors['white-1']} w={{ base: '100%', md: '50%' }} p={'md'}>
<Stack gap={"xs"}>
<Title order={3}>Grafik Berdasarkan Responden</Title>
<TextInput
w={{ base: '100%', md: '50%' }}
label="Sangat Baik"
value={grafikBerdasarkanResponden.create.form.sangatbaik}
placeholder="masukkan jumlah respon sangat baik"
onChange={(val) => {
grafikBerdasarkanResponden.create.form.sangatbaik = val.currentTarget.value;
}}
/>
<TextInput
w={{ base: '100%', md: '50%' }}
label="Baik"
value={grafikBerdasarkanResponden.create.form.baik}
placeholder="masukkan jumlah respon baik"
onChange={(val) => {
grafikBerdasarkanResponden.create.form.baik = val.currentTarget.value;
}}
/>
<TextInput
w={{ base: '100%', md: '50%' }}
label="Kurang Baik"
value={grafikBerdasarkanResponden.create.form.kurangbaik}
placeholder="masukkan jumlah respon kurang baik"
onChange={(val) => {
grafikBerdasarkanResponden.create.form.kurangbaik = val.currentTarget.value;
}}
/>
<TextInput
w={{ base: '100%', md: '50%' }}
label="Tidak Baik"
value={grafikBerdasarkanResponden.create.form.tidakbaik}
placeholder="masukkan jumlah respon tidak baik"
onChange={(val) => {
grafikBerdasarkanResponden.create.form.tidakbaik = val.currentTarget.value;
}}
/>
<Group>
<Button
bg={colors['blue-button']}
onClick={handleSubmit}
>
Submit
</Button>
</Group>
</Stack>
</Paper>
</Box>
{/* Chart */}
<Box>
<Paper bg={colors['white-1']} w={{ base: '100%', md: '50%' }} p={'md'}>
<Stack>
<Title pb={10} order={3}>Grafik Berdasarkan Responden</Title>
{mounted && donutData.length > 0 && (
<Box style={{ width: '100%', height: 'auto', minHeight: 900 }}>
<Center>
<PieChart
width={1000} height={400}
data={donutData}
>
<Pie
dataKey="value"
nameKey="name"
data={donutData}
innerRadius={120}
outerRadius={230}
label={true}
>
{donutData.map((entry, index) => (
<Cell key={`cell-${index}`} fill={entry.color} />
))}
</Pie>
</PieChart>
</Center>
<Flex gap={"md"} align={"center"}>
<Box bg={colors['blue-button']} w={20} h={20} />
<Text>Sangat Baik: {donutData.find((entry) => entry.name === 'sangatbaik')?.value}</Text>
</Flex>
<Flex gap={"md"} align={"center"}>
<Box bg={'#10A85AFF'} w={20} h={20} />
<Text>Baik: {donutData.find((entry) => entry.name === 'baik')?.value}</Text>
</Flex>
<Flex gap={"md"} align={"center"}>
<Box bg={'#B3AA12FF'} w={20} h={20} />
<Text>Kurang Baik: {donutData.find((entry) => entry.name === 'kurangbaik')?.value}</Text>
</Flex>
<Flex gap={"md"} align={"center"}>
<Box bg={'#B21313FF'} w={20} h={20} />
<Text>Tidak Baik: {donutData.find((entry) => entry.name === 'tidakbaik')?.value}</Text>
</Flex>
</Box>
)}
</Stack>
</Paper>
</Box>
</Box>
);
}
export default GrafikBerdasarkanResponden;

View File

@@ -0,0 +1,164 @@
'use client'
/* eslint-disable @typescript-eslint/no-explicit-any */
import colors from '@/con/colors';
import { Box, Button, Center, Flex, Group, Paper, Stack, Text, TextInput, Title } from '@mantine/core';
import React, { useEffect, useState } from 'react';
import { useProxy } from 'valtio/utils';
import stateGrafikBerdasarkanUmur from '@/app/admin/(dashboard)/_state/ppid/indeks_kepuasan_masyarakat/grafikBerdasarkanUmur';
import { useShallowEffect } from '@mantine/hooks';
import { PieChart, Pie, Cell } from 'recharts';
function GrafikBerdasarakanUmur() {
const grafikBerdasarkanUmur = useProxy(stateGrafikBerdasarkanUmur.grafikBerdasarkanUmur)
const [donutData, setDonutData] = useState<any[]>([]);
const [mounted, setMounted] = useState(false);
useEffect(() => {
setMounted(true);
}, []);
const updateChartData = (data: any) => {
if (data && data.length > 0) {
const totalRemaja = data.reduce((acc: number, cur: any) => acc + Number(cur.remaja || 0), 0);
const totalDewasa = data.reduce((acc: number, cur: any) => acc + Number(cur.dewasa || 0), 0);
const totalOrangtua = data.reduce((acc: number, cur: any) => acc + Number(cur.orangtua || 0), 0);
const totalLansia = data.reduce((acc: number, cur: any) => acc + Number(cur.lansia || 0), 0);
setDonutData([
{ name: 'Remaja', value: totalRemaja, color: colors['blue-button'], key: 'remaja' },
{ name: 'Dewasa', value: totalDewasa, color: '#D32711FF', key: 'dewasa' },
{ name: 'Orangtua', value: totalOrangtua, color: '#B46B04FF', key: 'orangtua' },
{ name: 'Lansia', value: totalLansia, color: '#038617FF', key: 'lansia' }
]);
}
};
useShallowEffect(() => {
fetchData();
}, []);
const fetchData = async () => {
await grafikBerdasarkanUmur.findMany.load();
if (grafikBerdasarkanUmur.findMany.data) {
updateChartData(grafikBerdasarkanUmur.findMany.data);
}
}
const handleSubmit = async () => {
try {
await grafikBerdasarkanUmur.create.create();
await grafikBerdasarkanUmur.findMany.load();
if (grafikBerdasarkanUmur.findMany.data) {
updateChartData(grafikBerdasarkanUmur.findMany.data);
}
} catch (error) {
console.error("Error submitting data:", error);
}
}
return (
<Box>
<Box py={15}>
<Paper bg={colors['white-1']} w={{ base: '100%', md: '50%' }} p={'md'}>
<Stack gap={"xs"}>
<Title order={3}>Grafik Berdasarkan Umur Responden</Title>
<TextInput
w={{ base: '100%', md: '50%' }}
label="Remaja"
placeholder="masukkan jumlah responden remaja"
value={grafikBerdasarkanUmur.create.form.remaja}
onChange={(val) => {
grafikBerdasarkanUmur.create.form.remaja = val.currentTarget.value;
}}
/>
<TextInput
w={{ base: '100%', md: '50%' }}
label="Dewasa"
placeholder="masukkan jumlah responden dewasa"
value={grafikBerdasarkanUmur.create.form.dewasa}
onChange={(val) => {
grafikBerdasarkanUmur.create.form.dewasa = val.currentTarget.value;
}}
/>
<TextInput
w={{ base: '100%', md: '50%' }}
label="Orangtua"
placeholder="masukkan jumlah responden orangtua"
value={grafikBerdasarkanUmur.create.form.orangtua}
onChange={(val) => {
grafikBerdasarkanUmur.create.form.orangtua = val.currentTarget.value;
}}
/>
<TextInput
w={{ base: '100%', md: '50%' }}
label="Lansia"
placeholder="masukkan jumlah responden lansia"
value={grafikBerdasarkanUmur.create.form.lansia}
onChange={(val) => {
grafikBerdasarkanUmur.create.form.lansia = val.currentTarget.value;
}}
/>
<Group>
<Button
mt={10}
bg={colors['blue-button']}
onClick={handleSubmit}
>
Submit
</Button>
</Group>
</Stack>
</Paper>
</Box>
{/* Chart */}
<Box>
<Paper bg={colors['white-1']} w={{ base: '100%', md: '50%' }} p={'md'}>
<Stack>
<Title pb={10} order={3}>Grafik Berdasarkan Umur Responden</Title>
{mounted && donutData.length > 0 && (
<Box style={{ width: '100%', height: 'auto', minHeight: 900 }}>
<Center>
<PieChart
width={1000} height={400}
data={donutData}
>
<Pie
dataKey="value"
nameKey="name"
data={donutData}
innerRadius={120}
outerRadius={230}
label={true}
>
{donutData.map((entry, index) => (
<Cell key={`cell-${index}`} fill={entry.color} />
))}
</Pie>
</PieChart>
</Center>
<Flex gap={"md"} align={"center"}>
<Box bg={colors['blue-button']} w={20} h={20} />
<Text>17 - 25 tahun: {donutData.find((entry) => entry.name === 'remaja')?.value}</Text>
</Flex>
<Flex gap={"md"} align={"center"}>
<Box bg={'#D32711FF'} w={20} h={20} />
<Text>26 - 45 tahun: {donutData.find((entry) => entry.name === 'dewasa')?.value}</Text>
</Flex>
<Flex gap={"md"} align={"center"}>
<Box bg={'#B46B04FF'} w={20} h={20} />
<Text>46 - 60 tahun: {donutData.find((entry) => entry.name === 'orangtua')?.value}</Text>
</Flex>
<Flex gap={"md"} align={"center"}>
<Box bg={'#038617FF'} w={20} h={20} />
<Text>di atas 60 tahun: {donutData.find((entry) => entry.name === 'lansia')?.value}</Text>
</Flex>
</Box>
)}
</Stack>
</Paper>
</Box>
</Box>
);
}
export default GrafikBerdasarakanUmur;

View File

@@ -0,0 +1,96 @@
/* eslint-disable @typescript-eslint/no-explicit-any */
'use client'
import stateGrafikHasilKepuasanMasyarakat from '@/app/admin/(dashboard)/_state/ppid/indeks_kepuasan_masyarakat/grafikHasilKepuasan';
import colors from '@/con/colors';
import { Box, Button, Paper, Stack, TextInput, Title } from '@mantine/core';
import { useMediaQuery, useShallowEffect } from '@mantine/hooks';
import React, { useEffect, useState } from 'react';
import { Bar, BarChart, Legend, Tooltip, XAxis, YAxis } from 'recharts';
import { useProxy } from 'valtio/utils';
function GrafikHasilKepuasan() {
const grafikHasilKepuasan = useProxy(stateGrafikHasilKepuasanMasyarakat.grafikHasilKepuasanMasyarakat)
const [chartData, setChartData] = useState<any[]>([]);
const [mounted, setMounted] = useState(false);
const isTablet = useMediaQuery('(max-width: 1024px)')
const isMobile = useMediaQuery('(max-width: 768px)')
useEffect(() => {
setMounted(true);
}, [])
useShallowEffect(() => {
const fetchData = async () => {
await grafikHasilKepuasan.findMany.load();
if (grafikHasilKepuasan.findMany.data && grafikHasilKepuasan.findMany.data.length > 0) {
setChartData(grafikHasilKepuasan.findMany.data);
}
};
fetchData();
}, []);
return (
<Box>
<Box py={15}>
<Paper bg={colors['white-1']} w={{ base: '100%', md: '50%' }} p={'md'}>
<Stack>
<Title order={3}>Grafik Hasil Kepuasan Masyarakat Terhadap Pelayanan Publik</Title>
<TextInput
w={{ base: '100%', md: '50%' }}
label="Label"
placeholder="masukkan label"
value={grafikHasilKepuasan.create.form.label}
onChange={(val) => {
grafikHasilKepuasan.create.form.label = val.currentTarget.value;
}}
/>
<TextInput
w={{ base: '100%', md: '50%' }}
label="Jumlah Kepuasan"
type="number"
placeholder="masukkan jumlah kepuasan"
value={grafikHasilKepuasan.create.form.kepuasan}
onChange={(val) => {
grafikHasilKepuasan.create.form.kepuasan = val.currentTarget.value;
}}
/>
<Button
mt={10}
bg={colors['blue-button']}
onClick={async () => {
await grafikHasilKepuasan.create.create();
await grafikHasilKepuasan.findMany.load();
if (grafikHasilKepuasan.findMany.data) {
setChartData(grafikHasilKepuasan.findMany.data);
}
}}
>
Submit
</Button>
</Stack>
</Paper>
</Box>
{/* Chart */}
<Box style={{ width: '100%', minWidth: 300, height: 400, minHeight: 300 }}>
<Paper style={{ width: '100%', minWidth: 300, height: 400, minHeight: 300 }} bg={colors['white-1']} p={'md'}>
<Stack gap={"xs"}>
<Title pb={10} order={3}>Data Kepuasan Masyarakat</Title>
{mounted && chartData.length > 0 && (
<BarChart width={isMobile ? 300 : isTablet ? 600 : 900} height={380} data={chartData} >
<XAxis dataKey="label" />
<YAxis />
<Tooltip />
<Legend />
<Bar dataKey="kepuasan" fill={colors['blue-button']} name="Kepuasan" />
</BarChart>
)}
</Stack>
</Paper>
</Box>
</Box>
);
}
export default GrafikHasilKepuasan;

View File

@@ -1,10 +1,45 @@
import { Stack, Tabs, TabsList, TabsPanel, TabsTab, Title } from '@mantine/core';
import React from 'react';
import colors from '@/con/colors';
import GrafikHasilKepuasan from './_ui/grafik_hasil_kepuasan_masyarakat/page';
import GrafikBerdasarkanJenisKelamin from './_ui/grafik_berdasarkan_jenis_kelamin_responden/page';
import GrafikBerdasarkanResponden from './_ui/grafik_berdasarkan_responden/page';
import GrafikBerdasarakanUmur from './_ui/grafik_berdasarkan_umur/page';
function Page() {
return (
<div>
ikm-desa-darmasaba
</div>
<Stack >
<Title order={3}>Indeks Kepuasan Masyarakat (IKM) Desa Darmasaba</Title>
<Tabs color={colors['blue-button']} variant='pills' defaultValue={"Grafik Hasil Kepuasan Masyarakat Terhadap Pelayanan Publik"}>
<TabsList p={"xs"} bg={"#BBC8E7FF"}>
<TabsTab value="Grafik Hasil Kepuasan Masyarakat Terhadap Pelayanan Publik">
Grafik Hasil Kepuasan Masyarakat Terhadap Pelayanan Publik
</TabsTab>
<TabsTab value="Grafik Berdasarkan Jenis Kelamin Responden">
Grafik Berdasarkan Jenis Kelamin Responden
</TabsTab>
<TabsTab value="Grafik Berdasarkan Pilihan Responden">
Grafik Berdasarkan Pilihan Responden
</TabsTab>
<TabsTab value="Grafik Berdasarkan Umur Responden">
Grafik Berdasarkan Umur Responden
</TabsTab>
</TabsList>
<TabsPanel value="Grafik Hasil Kepuasan Masyarakat Terhadap Pelayanan Publik">
<GrafikHasilKepuasan/>
</TabsPanel>
<TabsPanel value="Grafik Berdasarkan Jenis Kelamin Responden">
<GrafikBerdasarkanJenisKelamin/>
</TabsPanel>
<TabsPanel value="Grafik Berdasarkan Pilihan Responden">
<GrafikBerdasarkanResponden/>
</TabsPanel>
<TabsPanel value="Grafik Berdasarkan Umur Responden">
<GrafikBerdasarakanUmur/>
</TabsPanel>
</Tabs>
</Stack>
);
}

View File

@@ -1,11 +1,57 @@
import React from 'react';
'use client'
import colors from '@/con/colors';
import { Box, Paper, Skeleton, Stack, Table, TableTbody, TableTd, TableTh, TableThead, TableTr, Title } from '@mantine/core';
import { useProxy } from 'valtio/utils';
import { useShallowEffect } from '@mantine/hooks';
import statepermohonanInformasiPublikForm from '../../_state/ppid/permohonan_informasi_publik/permohonanInformasiPublik';
function Page() {
const permohonanInformasiPublikState = useProxy(statepermohonanInformasiPublikForm)
useShallowEffect(() => {
permohonanInformasiPublikState.statepermohonanInformasiPublik.findMany.load()
}, [])
if (!permohonanInformasiPublikState.statepermohonanInformasiPublik.findMany.data) return <Stack pos={"relative"} bg={colors.Bg} py={"xl"} gap={"22"}>
{Array.from({ length: 10 }).map((v, k) => <Skeleton key={k} h={40} />)}
</Stack>
return (
<div>
permohonan-informasi-publik
</div>
<Box py={5}>
<Paper bg={colors['white-1']} p={'md'}>
<Stack gap={"xs"}>
<Title order={3}>Permohonan Informasi Publik</Title>
<Box p={"xl"}>
<Table striped withRowBorders withColumnBorders withTableBorder>
<TableThead>
<TableTr>
<TableTh>No</TableTh>
<TableTh>Nama</TableTh>
<TableTh>NIK</TableTh>
<TableTh>Telepon</TableTh>
<TableTh>Email</TableTh>
<TableTh>Jenis Informasi</TableTh>
<TableTh>Cara Memperoleh Informasi</TableTh>
<TableTh>Cara Memperoleh Salinan Informasi</TableTh>
</TableTr>
</TableThead>
<TableTbody>
{permohonanInformasiPublikState.statepermohonanInformasiPublik.findMany.data?.map((item, index) => (
<TableTr key={item.id}>
<TableTd>{index + 1}</TableTd>
<TableTd>{item.name}</TableTd>
<TableTd>{item.nik}</TableTd>
<TableTd>{item.notelp}</TableTd>
<TableTd>{item.email}</TableTd>
<TableTd>{item.jenisInformasiDiminta?.name}</TableTd>
<TableTd>{item.caraMemperolehInformasi?.name}</TableTd>
<TableTd>{item.caraMemperolehSalinanInformasi?.name}</TableTd>
</TableTr>
))}
</TableTbody>
</Table> </Box>
</Stack>
</Paper>
</Box>
);
}
export default Page;
export default Page;

View File

@@ -1,11 +1,49 @@
import React from 'react';
'use client'
import colors from '@/con/colors';
import { Box, Paper, Skeleton, Stack, Table, TableTbody, TableTd, TableTh, TableThead, TableTr, Title } from '@mantine/core';
import { useShallowEffect } from '@mantine/hooks';
import { useProxy } from 'valtio/utils';
import statePermohonanKeberatan from '../../_state/ppid/permohonan_keberatan_informasi_publik/permohonanKeberatanInformasi';
function Page() {
const listState = useProxy(statePermohonanKeberatan)
useShallowEffect(() => {
listState.findMany.load()
}, [])
if (!listState.findMany.data) return <Stack>
{Array.from({ length: 10 }).map((v, k) => <Skeleton key={k} h={40} />)}
</Stack>
return (
<div>
permohonan-keberatan-informasi-publik
</div>
<Box py={10}>
<Paper bg={colors['white-1']} p={'md'}>
<Stack gap={"xs"}>
<Title order={3}>Permohonan Keberatan Informasi Publik</Title>
<Table striped withRowBorders withColumnBorders withTableBorder>
<TableThead>
<TableTr>
<TableTh>No</TableTh>
<TableTh>Nama</TableTh>
<TableTh>Email</TableTh>
<TableTh>Telepon</TableTh>
<TableTh>Alasan</TableTh>
</TableTr>
</TableThead>
<TableTbody>
{listState.findMany.data?.map((item, index) => (
<TableTr key={item.id}>
<TableTd>{index + 1}</TableTd>
<TableTd>{item.name}</TableTd>
<TableTd>{item.email}</TableTd>
<TableTd>{item.notelp}</TableTd>
<TableTd dangerouslySetInnerHTML={{ __html: item.alasan }} />
</TableTr>
))}
</TableTbody>
</Table>
</Stack>
</Paper>
</Box>
);
}
export default Page;

View File

@@ -0,0 +1,10 @@
const biodata = {
id: "1",
name: "<p>I.B Surya Prabhawa Manuaba, S.H., M.H.</p>",
biodata: "<h2>Biodata</h2> <p>....</p>",
riwayat: "<h2>Riwayat Karir</h2> <ul>...</ul>",
pengalaman: "<h2>Pengalaman Organisasi</h2> <ul>...</ul>",
unggulan: "<h2>Program Kerja Unggulan</h2> <h3>...</h3>",
}
export default biodata

View File

@@ -0,0 +1,76 @@
'use client'
import { Box, Group, Text } from '@mantine/core';
import { useState } from 'react';
import ApiFetch from '@/lib/api-fetch';
import { Dropzone, MIME_TYPES } from '@mantine/dropzone';
import { IconPhoto, IconUpload, IconX } from '@tabler/icons-react';
import { useProxy } from 'valtio/utils';
import stateProfilePPID from '../../../_state/ppid/profile_ppid/profile_PPID';
import { PPIDTextEditor } from '../../_com/PPIDTextEditor';
function Biodata() {
const biodataState = useProxy(stateProfilePPID)
const [loading, setLoading] = useState(false);
return (<Box>
<Text fw={"bold"}>Biodata</Text>
<Dropzone
mb={20}
onDrop={async (droppedFiles) => {
setLoading(true);
for (const file of droppedFiles) {
await ApiFetch.api.ppid.profileppid["edit-img"].post({
file: file,
id: biodataState.findById.data?.id,
});
}
setLoading(false);
if (biodataState.findById.data?.id) {
biodataState.findById.load(biodataState.findById.data.id);
}
}}
accept={[MIME_TYPES.jpeg, MIME_TYPES.png, MIME_TYPES.webp]}
loading={loading}
>
<Group justify="center" gap="md" mih={150} style={{ pointerEvents: 'none' }}>
<Dropzone.Accept>
<IconUpload size={50} stroke={1.5} />
</Dropzone.Accept>
<Dropzone.Reject>
<IconX size={50} stroke={1.5} />
</Dropzone.Reject>
<Dropzone.Idle>
<IconPhoto size={50} stroke={1.5} />
</Dropzone.Idle>
<div>
<Text size="xl" inline>
Drag & drop gambar di sini atau klik untuk pilih file
</Text>
<Text size="sm" c="dimmed" inline mt={7}>
Maksimal ukuran file 5MB. Format: JPG, PNG, WebP
</Text>
</div>
</Group>
</Dropzone>
<PPIDTextEditor
key={biodataState.findById.data?.id ?? 'new'}
showSubmit={false}
onChange={(val) => {
if (biodataState.findById.data) {
biodataState.findById.data.biodata = val;
}
}}
initialContent={biodataState.findById.data?.biodata ?? ''}
/>
</Box>
);
}
export default Biodata;

View File

@@ -0,0 +1,65 @@
import colors from '@/con/colors';
import { Stack, Skeleton, Paper, Title, Box, Text, Image } from '@mantine/core';
import { useShallowEffect } from '@mantine/hooks';
import React from 'react';
import { useProxy } from 'valtio/utils';
import stateProfilePPID from '../../_state/ppid/profile_ppid/profile_PPID';
function ProfileList() {
const allList = useProxy(stateProfilePPID)
useShallowEffect(() => {
allList.findById.load("1") // Assuming "1" is your default ID, adjust as needed
}, [])
if (!allList.findById.data) return <Stack>
{Array.from({ length: 10 }).map((v, k) => <Skeleton key={k} h={40} />)}
</Stack>
const dataArray = Array.isArray(allList.findById.data)
? allList.findById.data
: [allList.findById.data];
return (
<Paper bg={colors['white-1']} p={'md'}>
<Stack gap={"xs"}>
<Title order={3}>List Profile PPID</Title>
{dataArray.map((item) => (
<Box key={item.id}>
{item.imageUrl && (
<Box mb={20}>
<Text fw={"bold"} mb={5}>Preview Gambar:</Text>
<Image
src={item.imageUrl}
alt="Profile"
w={200}
/>
</Box>
)}
<Box>
<Text fw={"bold"}>Nama</Text>
<Text dangerouslySetInnerHTML={{ __html: item.name }}></Text>
</Box>
<Box>
<Text fw={"bold"}>Biodata</Text>
<Text dangerouslySetInnerHTML={{ __html: item.biodata }}></Text>
</Box>
<Box>
<Text fw={"bold"}>Riwayat</Text>
<Text dangerouslySetInnerHTML={{ __html: item.riwayat }}></Text>
</Box>
<Box>
<Text fw={"bold"}>Pengalaman</Text>
<Text dangerouslySetInnerHTML={{ __html: item.pengalaman }}></Text>
</Box>
<Box>
<Text fw={"bold"}>Program Kerja Unggulan</Text>
<Text dangerouslySetInnerHTML={{ __html: item.unggulan }}></Text>
</Box>
</Box>
))}
</Stack>
</Paper>
)
}
export default ProfileList;

View File

@@ -1,11 +1,79 @@
import React from 'react';
'use client'
import colors from '@/con/colors';
import { Box, Button, Group, Paper, SimpleGrid, Stack, Text, TextInput, Title } from '@mantine/core';
import Biodata from './biodata/page';
import PengalamanOrganisasi from './pengalaman_organisasi/page';
import RiwayatKarir from './riwayat_karir/page';
import ProgramKerjaUnggulan from './program_kerja_unggulan/page';
import { useProxy } from 'valtio/utils';
import stateProfilePPID from '../../_state/ppid/profile_ppid/profile_PPID';
import { useShallowEffect } from '@mantine/hooks';
import ProfileList from './listPage';
function Page() {
return (
<div>
profile-ppid
</div>
<Box>
<SimpleGrid cols={{ base: 1, md: 2 }}>
<ProfileCreate />
<ProfileList />
</SimpleGrid>
</Box>
);
}
export default Page;
function ProfileCreate() {
const allState = useProxy(stateProfilePPID)
// Initialize data if it doesn't exist
useShallowEffect(() => {
if (!allState.findById.data) {
allState.findById.initialize()
}
}, [])
const submit = () => {
if (
allState.findById.data?.name &&
allState.findById.data?.biodata &&
allState.findById.data?.riwayat &&
allState.findById.data?.pengalaman &&
allState.findById.data?.unggulan
) {
allState.update.save(allState.findById.data)
}
}
return (
<Paper bg={colors['white-1']} p={'md'} radius={10}>
<Stack gap={"xs"}>
<Title order={3}>Profile PPID</Title>
<TextInput
label={<Text fw={"bold"}>Nama Perbekel</Text>}
placeholder="masukkan nama perbekel"
value={allState.findById.data?.name || ''}
onChange={(val) => {
if (allState.findById.data) {
allState.findById.data.name = val.currentTarget.value
}
}}
/>
<Biodata />
<RiwayatKarir />
<PengalamanOrganisasi />
<ProgramKerjaUnggulan />
<Group>
<Button
bg={colors['blue-button']}
onClick={submit}
loading={allState.update.loading}
>
Submit
</Button>
</Group>
</Stack>
</Paper>
)
}
export default Page;

View File

@@ -0,0 +1,26 @@
'use client'
import { Box, Text } from '@mantine/core';
import React from 'react';
import { useProxy } from 'valtio/utils';
import stateProfilePPID from '../../../_state/ppid/profile_ppid/profile_PPID';
import { PPIDTextEditor } from '../../_com/PPIDTextEditor';
function PengalamanOrganisasi() {
const pengalamanOrganisasiState = useProxy(stateProfilePPID)
return (<Box>
<Text fw={"bold"}>Pengalaman Organisasi</Text>
<PPIDTextEditor
key={pengalamanOrganisasiState.findById.data?.id ?? 'new'}
showSubmit={false}
onChange={(val) => {
if (pengalamanOrganisasiState.findById.data) {
pengalamanOrganisasiState.findById.data.pengalaman = val;
}
}}
initialContent={pengalamanOrganisasiState.findById.data?.pengalaman ?? ''}
/>
</Box>
);
}
export default PengalamanOrganisasi;

View File

@@ -0,0 +1,26 @@
'use client'
import { Box, Text } from '@mantine/core';
import React from 'react';
import { useProxy } from 'valtio/utils';
import stateProfilePPID from '../../../_state/ppid/profile_ppid/profile_PPID';
import { PPIDTextEditor } from '../../_com/PPIDTextEditor';
function ProgramKerjaUnggulan() {
const programKerjaUnggulanState = useProxy(stateProfilePPID)
return (<Box>
<Text fw={"bold"}>Program Kerja Unggulan</Text>
<PPIDTextEditor
key={programKerjaUnggulanState.findById.data?.id ?? 'new'}
showSubmit={false}
onChange={(val) => {
if (programKerjaUnggulanState.findById.data) {
programKerjaUnggulanState.findById.data.unggulan = val;
}
}}
initialContent={programKerjaUnggulanState.findById.data?.unggulan ?? ''}
/>
</Box>
);
}
export default ProgramKerjaUnggulan;

View File

@@ -0,0 +1,33 @@
'use client';
import { Box, Text } from '@mantine/core';
import React from 'react';
import { useProxy } from 'valtio/utils';
import dynamic from 'next/dynamic';
import stateProfilePPID from '../../../_state/ppid/profile_ppid/profile_PPID';
// ini penting
const PPIDTextEditor = dynamic(() => import('../../_com/PPIDTextEditor').then(mod => mod.PPIDTextEditor), {
ssr: false, // disable server side rendering
});
function RiwayatKarir() {
const riwayatKarirState = useProxy(stateProfilePPID);
return (
<Box>
<Text fw={"bold"}>Riwayat Karir</Text>
<PPIDTextEditor
key={riwayatKarirState.findById.data?.id ?? 'new'}
showSubmit={false}
onChange={(val) => {
if (riwayatKarirState.findById.data) {
riwayatKarirState.findById.data.riwayat = val;
}
}}
initialContent={riwayatKarirState.findById.data?.riwayat ?? ''}
/>
</Box>
);
}
export default RiwayatKarir;

View File

@@ -0,0 +1,26 @@
'use client'
import { Box, Text } from '@mantine/core';
import { useProxy } from 'valtio/utils';
import { PPIDTextEditor } from '../../_com/PPIDTextEditor';
import stateVisiMisiPPID from '../../../_state/ppid/visi_misi_ppid/visimisiPPID';
function MisiPPID() {
const misiState = useProxy(stateVisiMisiPPID)
return (
<Box>
<Text fw={"bold"}>Misi PPID</Text>
<PPIDTextEditor
key={misiState.findById.data?.id ?? 'new'}
showSubmit={false}
onChange={(val) => {
if(misiState.findById.data){
misiState.findById.data.misi = val
}
}}
initialContent={misiState.findById.data?.misi ?? ''}
/>
</Box>
);
}
export default MisiPPID;

View File

@@ -1,11 +1,83 @@
import React from 'react';
'use client'
import colors from '@/con/colors';
import { Box, Button, Group, Paper, SimpleGrid, Skeleton, Stack, Text, Title } from '@mantine/core';
import { useShallowEffect } from '@mantine/hooks';
import { useProxy } from 'valtio/utils';
import stateVisiMisiPPID from '../../_state/ppid/visi_misi_ppid/visimisiPPID';
import MisiPPID from './misiPPID/page';
import VisiPPID from './visiPPID/page';
function Page() {
return (
<div>
visi-misi-ppid
</div>
<Box>
<SimpleGrid cols={{ base: 1, md: 2 }}>
<VisiMisiPPIDCreate />
<VisiMisiPPIDList />
</SimpleGrid>
</Box>
);
}
function VisiMisiPPIDCreate() {
const visiMisi = useProxy(stateVisiMisiPPID)
useShallowEffect(() => {
if(!visiMisi.findById.data){
visiMisi.findById.initialize()
}
}, [])
const submit = () => {
if(visiMisi.findById.data?.misi && visiMisi.findById.data?.visi){
visiMisi.update.save(visiMisi.findById.data)
}
}
return (
<Paper bg={colors['white-1']} p={'md'} radius={10}>
<Stack gap={"xs"}>
<Title order={3}>Visi Misi PPID</Title>
<VisiPPID />
<MisiPPID />
<Group>
<Button
bg={colors['blue-button']}
onClick={submit}
loading={visiMisi.update.loading}
>
Submit
</Button>
</Group>
</Stack>
</Paper>
)
}
function VisiMisiPPIDList() {
const listVisiMisi = useProxy(stateVisiMisiPPID)
useShallowEffect(() => {
listVisiMisi.findById.load("1")
}, [])
if(!listVisiMisi.findById.data) return <Stack>
{Array.from({length: 10}).map((v, k) => <Skeleton key={k} h={40} />)}
</Stack>
return (
<Paper bg={colors['white-1']} p={'md'} radius={10}>
<Stack gap={"xs"}>
<Title order={3}>List Visi Misi PPID</Title>
<Box>
<Text fw={"bold"}>Visi PPID</Text>
<Text dangerouslySetInnerHTML={{__html: listVisiMisi.findById.data?.visi}}></Text>
</Box>
<Box>
<Text fw={"bold"}>Misi PPID</Text>
<Text dangerouslySetInnerHTML={{__html: listVisiMisi.findById.data?.misi}}></Text>
</Box>
</Stack>
</Paper>
)
}
export default Page;

View File

@@ -0,0 +1,26 @@
'use client'
import { Box, Text } from '@mantine/core';
import { useProxy } from 'valtio/utils';
import { PPIDTextEditor } from '../../_com/PPIDTextEditor';
import stateVisiMisiPPID from '../../../_state/ppid/visi_misi_ppid/visimisiPPID';
function VisiPPID() {
const visiState = useProxy(stateVisiMisiPPID)
return (
<Box>
<Text fw={"bold"}>Visi PPID</Text>
<PPIDTextEditor
key={visiState.findById.data?.id ?? 'new'}
showSubmit={false}
onChange={(val) => {
if(visiState.findById.data){
visiState.findById.data.visi = val
}
}}
initialContent={visiState.findById.data?.visi ?? ''}
/>
</Box>
);
}
export default VisiPPID;

View File

@@ -1,9 +1,11 @@
import Elysia from "elysia";
import Berita from "./berita";
import Pengumuman from "./pengumuman";
import ProfileDesa from "./profile/profile_desa";
const Desa = new Elysia({ prefix: "/api/desa", tags: ["Desa"] })
.use(Berita)
.use(Pengumuman)
.use(ProfileDesa)
export default Desa;

View File

@@ -0,0 +1,12 @@
import prisma from "@/lib/prisma";
export default async function profileDesaFindMany() {
const res = await prisma.profileDesa.findMany({
include: {
ProfilPerbekel: true,
},
});
return {
data: res,
};
}

View File

@@ -0,0 +1,51 @@
import Elysia, { t } from "elysia";
import profileDesaFindMany from "./find-many";
import lambangDesaUpdate from "./lambangDesa";
import maskotDesaUpdate from "./maskotDesa";
import profilePerbekelUpdate from "./profilePerbekel";
import sejarahDesaUpdate from "./sejarahDesa";
import visimisiDesaUpdate from "./visimisiDesa";
const ProfileDesa = new Elysia({
prefix: "/profile",
tags: ["Desa/Profile"]
})
.get("/find-many", profileDesaFindMany)
.post("/profilePerbekel/update", profilePerbekelUpdate, {
body: t.Object({
id: t.String(),
biodata: t.String(),
pengalaman: t.String(),
pengalamanOrganisasi: t.String(),
programUnggulan: t.String(),
})
})
.post("/visimisiDesa/update", visimisiDesaUpdate, {
body: t.Object({
id: t.String(),
visi: t.String(),
misi: t.String(),
})
})
.post("/sejarahDesa/update", sejarahDesaUpdate, {
body: t.Object({
id: t.String(),
sejarah: t.String(),
})
})
.post("/lambangDesa/update", lambangDesaUpdate, {
body: t.Object({
id: t.String(),
lambang: t.String(),
})
})
.post("/maskotDesa/update", maskotDesaUpdate, {
body: t.Object({
id: t.String(),
maskot: t.String(),
})
})
export default ProfileDesa

View File

@@ -0,0 +1,28 @@
import prisma from "@/lib/prisma";
import { Prisma } from "@prisma/client";
import { Context } from "elysia";
type FormCreate = Prisma.ProfileDesaGetPayload<{
select: {
id: true;
lambang: true;
}
}>
export default async function lambangDesaUpdate(context: Context) {
const body = context.body as FormCreate;
await prisma.profileDesa.update({
where: {
id: body.id
},
data: {
lambang: body.lambang,
}
})
return {
success: true,
message: "Profile Desa Berhasil Diupdate",
}
}

View File

@@ -0,0 +1,29 @@
import prisma from "@/lib/prisma";
import { Prisma } from "@prisma/client";
import { Context } from "elysia";
type FormCreate = Prisma.ProfileDesaGetPayload<{
select: {
id: true;
maskot: true;
}
}>
export default async function maskotDesaUpdate(context: Context) {
const body = context.body as FormCreate;
await prisma.profileDesa.update({
where: {
id: body.id
},
data: {
maskot: body.maskot,
}
})
return {
success: true,
message: "Profile Desa Berhasil Diupdate",
}
}

View File

@@ -0,0 +1,33 @@
import prisma from "@/lib/prisma";
import { Prisma } from "@prisma/client";
import { Context } from "elysia";
type FormCreate = Prisma.ProfilPerbekelGetPayload<{
select: {
id: true;
biodata: true;
pengalaman: true;
pengalamanOrganisasi: true;
programUnggulan: true;
}
}>
export default async function profilePerbekelUpdate(context: Context) {
const body = context.body as FormCreate;
await prisma.profilPerbekel.update({
where: {
id: body.id
},
data: {
biodata: body.biodata,
pengalaman: body.pengalaman,
pengalamanOrganisasi: body.pengalamanOrganisasi,
programUnggulan: body.programUnggulan,
}
})
return {
success: true,
message: "Profile Perbekel Berhasil Diupdate",
}
}

View File

@@ -0,0 +1,29 @@
import prisma from "@/lib/prisma";
import { Prisma } from "@prisma/client";
import { Context } from "elysia";
type FormCreate = Prisma.ProfileDesaGetPayload<{
select: {
id: true;
sejarah: true;
}
}>
export default async function sejarahDesaUpdate(context: Context) {
const body = context.body as FormCreate;
await prisma.profileDesa.update({
where: {
id: body.id
},
data: {
sejarah: body.sejarah,
}
})
return {
success: true,
message: "Profile Desa Berhasil Diupdate",
}
}

View File

@@ -0,0 +1,29 @@
import prisma from "@/lib/prisma";
import { Prisma } from "@prisma/client";
import { Context } from "elysia";
type FormCreate = Prisma.ProfileDesaGetPayload<{
select: {
id: true;
visi: true;
misi: true;
}
}>
export default async function visimisiDesaUpdate(context: Context) {
const body = context.body as FormCreate;
await prisma.profileDesa.update({
where: {
id: body.id
},
data: {
visi: body.visi,
misi: body.misi,
}
})
return {
success: true,
message: "Profile Desa Berhasil Diupdate",
}
}

View File

@@ -0,0 +1,32 @@
import prisma from "@/lib/prisma";
import { Prisma } from "@prisma/client";
import { Context } from "elysia";
type FormCreate = Prisma.DaftarInformasiPublikGetPayload<{
select: {
jenisInformasi: true;
deskripsi: true;
tanggal: true;
}
}>
export default async function daftarInformasiPublikCreate(context: Context) {
const body = context.body as FormCreate;
const jumlahData = await prisma.daftarInformasiPublik.count({
where: { isActive: true }, // hitung data aktif aja
})
await prisma.daftarInformasiPublik.create({
data: {
nomor: jumlahData + 1,
jenisInformasi: body.jenisInformasi,
deskripsi: body.deskripsi,
tanggal: body.tanggal,
},
})
return {
success: true,
message: "Success create daftar informasi publik",
data: {
...body,
},
};
}

View File

@@ -0,0 +1,9 @@
import prisma from "@/lib/prisma";
export default async function daftarInformasiPublikFindMany() {
const res = await prisma.daftarInformasiPublik.findMany();
return {
data: res
}
}

View File

@@ -0,0 +1,18 @@
import Elysia, { t } from "elysia";
import daftarInformasiPublikCreate from "./create";
import daftarInformasiPublikFindMany from "./find-many";
const DaftarInformasiPublik = new Elysia({
prefix: "/daftarinformasipublik",
tags: ["PPID/Daftar Informasi Publik"]
})
.get("/find-many", daftarInformasiPublikFindMany)
.post("/create", daftarInformasiPublikCreate, {
body: t.Object({
jenisInformasi: t.String(),
deskripsi: t.String(),
tanggal: t.String(),
}),
})
export default DaftarInformasiPublik

View File

@@ -0,0 +1,33 @@
import prisma from "@/lib/prisma";
import { Context } from "elysia";
export default async function dasarHukumPPIDFindById(context: Context) {
try {
const id = context?.params?.slugs?.[0];
// If no ID provided, get the first profile
if (!id) {
const data = await prisma.dasarHukumPPID.findFirst();
return {
success: true,
data,
};
}
const data = await prisma.dasarHukumPPID.findUniqueOrThrow({
where: { id },
});
return {
success: true,
data,
};
} catch (error) {
console.error("Error fetching profilePPID:", error);
return {
success: false,
message: error instanceof Error ? error.message : "Unknown error",
};
}
}

View File

@@ -0,0 +1,18 @@
import Elysia, { t } from "elysia";
import dasarHukumPPIDFindById from "./find-by-id";
import dasarHukumPPIDUpdate from "./update";
const DasarHukumPPID = new Elysia({
prefix: "/dasarhukumppid",
tags: ["PPID/Dasar Hukum PPID"]
})
.get("/find-by-id", dasarHukumPPIDFindById)
.post("/update", dasarHukumPPIDUpdate, {
body: t.Object({
id: t.String(),
judul: t.String(),
content: t.String(),
})
})
export default DasarHukumPPID

View File

@@ -0,0 +1,29 @@
import prisma from "@/lib/prisma";
import { Prisma } from "@prisma/client";
import { Context } from "elysia";
type FormCreate = Prisma.DasarHukumPPIDGetPayload<{
select: {
id: true;
judul: true;
content: true;
}
}>
export default async function dasarHukumPPIDUpdate(context: Context) {
const body = context.body as FormCreate;
await prisma.dasarHukumPPID.update({
where: {
id: body.id
},
data: {
judul: body.judul,
content: body.content,
}
})
return {
success: true,
message: "Dasar Hukum PPID Berhasil Dibuat",
}
}

View File

@@ -0,0 +1,27 @@
import prisma from "@/lib/prisma";
import { Prisma } from "@prisma/client";
import { Context } from "elysia";
type FormCreate = Prisma.GrafikBerdasarkanJenisKelaminGetPayload<{
select: {
perempuan: true;
laki: true;
}
}>
export default async function grafikBerdasarkanJenisKelaminCreate(context: Context) {
const body = context.body as FormCreate;
await prisma.grafikBerdasarkanJenisKelamin.create({
data: {
perempuan: body.perempuan,
laki: body.laki,
},
});
return {
success: true,
message: "Success create grafik berdasarkan jenis kelamin",
data: {
...body,
},
};
}

View File

@@ -0,0 +1,8 @@
import prisma from "@/lib/prisma";
export default async function grafikBerdasarkanJenisKelaminFindMany() {
const res = await prisma.grafikBerdasarkanJenisKelamin.findMany();
return {
data: res
}
}

View File

@@ -0,0 +1,17 @@
import Elysia, { t } from "elysia";
import grafikBerdasarkanJenisKelaminCreate from "./create";
import grafikBerdasarkanJenisKelaminFindMany from "./find-many";
const GrafikBerdasarkanJenisKelamin = new Elysia({
prefix: "/grafikberdasarkanjeniskelamin",
tags: ["PPID/IKM/grafikberdasarkanjeniskelamin"],
})
.get("/find-many", grafikBerdasarkanJenisKelaminFindMany)
.post("/create", grafikBerdasarkanJenisKelaminCreate, {
body: t.Object({
perempuan: t.String(),
laki: t.String(),
}),
});
export default GrafikBerdasarkanJenisKelamin;

View File

@@ -0,0 +1,31 @@
import prisma from "@/lib/prisma";
import { Prisma } from "@prisma/client";
import { Context } from "elysia";
type FormCreate = Prisma.GrafikBerdasarkanUmurGetPayload<{
select: {
remaja: true;
dewasa: true;
orangtua: true;
lansia: true;
}
}>
export async function grafikBerdasarkanUmurCreate(context: Context) {
const body = context.body as FormCreate;
await prisma.grafikBerdasarkanUmur.create({
data: {
remaja: body.remaja,
dewasa: body.dewasa,
orangtua: body.orangtua,
lansia: body.lansia,
},
});
return {
success: true,
message: "Success create grafik berdasarkan umur",
data: {
...body,
},
};
}

View File

@@ -0,0 +1,8 @@
import prisma from "@/lib/prisma";
export async function grafikBerdasarkanUmurFindMany(){
const res = await prisma.grafikBerdasarkanUmur.findMany();
return {
data: res
}
}

View File

@@ -0,0 +1,19 @@
import Elysia, { t } from "elysia";
import { grafikBerdasarkanUmurFindMany } from "./find-many";
import { grafikBerdasarkanUmurCreate } from "./create";
const GrafikBerdasarkanUmur = new Elysia({
prefix: "/grafikberdasarkanumur",
tags: ["PPID/IKM/grafikberdasarkanumur"]
})
.get("/find-many", grafikBerdasarkanUmurFindMany)
.post("/create", grafikBerdasarkanUmurCreate, {
body: t.Object({
remaja: t.String(),
dewasa: t.String(),
orangtua: t.String(),
lansia: t.String(),
}),
});
export default GrafikBerdasarkanUmur;

View File

@@ -0,0 +1,27 @@
import prisma from "@/lib/prisma";
import { Prisma } from "@prisma/client";
import { Context } from "elysia";
type FormCreate = Prisma.IndeksKepuasanMasyarakatGetPayload<{
select: {
label: true;
kepuasan: true;
};
}>;
export default async function grafikHasilKepuasanMasyarakatCreate(context: Context) {
const body = context.body as FormCreate;
await prisma.indeksKepuasanMasyarakat.create({
data: {
label: body.label,
kepuasan: body.kepuasan,
},
});
return {
success: true,
message: "Success create grafik hasil kepuasan masyarakat",
data: {
...body,
},
};
}

View File

@@ -0,0 +1,8 @@
import prisma from "@/lib/prisma";
export default async function grafikHasilKepuasanMasyarakatFindMany() {
const res = await prisma.indeksKepuasanMasyarakat.findMany();
return {
data: res,
};
}

View File

@@ -0,0 +1,17 @@
import Elysia, { t } from "elysia";
import grafikHasilKepuasanMasyarakatCreate from "./create";
import grafikHasilKepuasanMasyarakatFindMany from "./find-many";
const GrafikHasilKepuasanMasyarakat = new Elysia({
prefix: "/grafikhasilkepuasamanmasyarakat",
tags: ["PPID/IKM/grafikhasilkepuasanmasyarakat"],
})
.get("/find-many", grafikHasilKepuasanMasyarakatFindMany)
.post("/create", grafikHasilKepuasanMasyarakatCreate, {
body: t.Object({
label: t.String(),
kepuasan: t.String(),
}),
});
export default GrafikHasilKepuasanMasyarakat;

View File

@@ -0,0 +1,31 @@
import prisma from "@/lib/prisma";
import { Prisma } from "@prisma/client";
import { Context } from "elysia";
type FormCreate = Prisma.GrafikBerdasarkanRespondenGetPayload<{
select: {
sangatbaik: true;
baik: true;
kurangbaik: true;
tidakbaik: true
};
}>;
export default async function grafikRespondenCreate(context: Context) {
const body = context.body as FormCreate;
await prisma.grafikBerdasarkanResponden.create({
data: {
sangatbaik: body.sangatbaik,
baik: body.baik,
kurangbaik: body.kurangbaik,
tidakbaik: body.tidakbaik,
},
});
return {
success: true,
message: "Success create grafik berdasarkan responden",
data: {
...body,
},
};
}

View File

@@ -0,0 +1,8 @@
import prisma from "@/lib/prisma";
export default async function grafikRespondenFindMany(){
const res = await prisma.grafikBerdasarkanResponden.findMany();
return{
data: res
}
}

View File

@@ -0,0 +1,20 @@
import Elysia, { t } from "elysia";
import grafikRespondenCreate from "./create";
import grafikRespondenFindMany from "./find-many";
const GrafikBerdasarkanResponden = new Elysia({
prefix: "/grafikberdasarkanresponden",
tags: ["PPID/IKM/grafikberdasarkanresponden"]
})
.get("/find-many", grafikRespondenFindMany)
.post("/create", grafikRespondenCreate, {
body: t.Object({
sangatbaik: t.String(),
baik: t.String(),
kurangbaik: t.String(),
tidakbaik: t.String(),
}),
})
export default GrafikBerdasarkanResponden

View File

@@ -0,0 +1,33 @@
import Elysia from "elysia";
import DaftarInformasiPublik from "./daftar_informasi_publik";
import GrafikHasilKepuasanMasyarakat from "./ikm/grafik_hasil_kepuasan_masyarakat";
import GrafikBerdasarkanJenisKelamin from "./ikm/grafik_berdasarkan_jenis_kelamin";
import GrafikBerdasarkanResponden from "./ikm/grafik_responden";
import GrafikBerdasarkanUmur from "./ikm/grafik_berdasarkan_umur";
import PermohonanInformasiPublik from "./permohonan_informasi_publik";
import PermohonanKeberatanInformasiPublik from "./permohonan_keberatan_informasi_publik";
import ProfilePPID from "./profile_ppid";
import VisiMisiPPID from "./visi_misi_ppid/visi_misi_ppid";
import DasarHukumPPID from "./dasar_hukum";
const PPID = new Elysia({ prefix: "/api/ppid", tags: ["PPID"] })
.use(ProfilePPID)
.use(DaftarInformasiPublik)
.use(GrafikHasilKepuasanMasyarakat)
.use(GrafikBerdasarkanJenisKelamin)
.use(GrafikBerdasarkanResponden)
.use(GrafikBerdasarkanUmur)
.use(PermohonanInformasiPublik)
.use(PermohonanKeberatanInformasiPublik)
.use(VisiMisiPPID)
.use(DasarHukumPPID)
export default PPID

View File

@@ -0,0 +1,41 @@
import prisma from "@/lib/prisma";
import { Prisma } from "@prisma/client";
import { Context } from "elysia";
type FormCreate = Prisma.PermohonanInformasiPublikGetPayload<{
select: {
name: true;
nik: true;
email: true;
notelp: true;
alamat: true;
jenisInformasiDimintaId: true;
caraMemperolehInformasiId: true;
caraMemperolehSalinanInformasiId: true;
}
}>
export default async function permohonanInformasiPublikCreate(context: Context) {
const body = context.body as FormCreate;
await prisma.permohonanInformasiPublik.create({
data: {
name: body.name,
nik: body.nik,
email: body.email,
notelp: body.notelp,
alamat: body.alamat,
jenisInformasiDimintaId: body.jenisInformasiDimintaId,
caraMemperolehInformasiId: body.caraMemperolehInformasiId,
caraMemperolehSalinanInformasiId: body.caraMemperolehSalinanInformasiId,
}
})
return {
success: true,
message: "Permohonan Informasi Publik Berhasil Dibuat",
data: {
...body,
}
}
}

View File

@@ -0,0 +1,14 @@
import prisma from "@/lib/prisma";
export default async function permohonanInformasiPublikFindMany() {
const res = await prisma.permohonanInformasiPublik.findMany({
include: {
jenisInformasiDiminta: true,
caraMemperolehInformasi: true,
caraMemperolehSalinanInformasi: true,
}
});
return {
data: res,
};
}

View File

@@ -0,0 +1,29 @@
import Elysia, { t } from "elysia";
import permohonanInformasiPublikCreate from "./create";
import permohonanInformasiPublikFindMany from "./find-many";
import jenisInformasiFindMany from "./jenisInformasi";
import memperolehInformasiFindMany from "./memperolehInformasi";
import salinanInformasiFindMany from "./salinanInformasi";
const PermohonanInformasiPublik = new Elysia({
prefix: "/permohonaninformasipublik",
tags: ["PPID/Permohonan Informasi Publik"]
})
.get("/jenisInformasi/find-many", jenisInformasiFindMany)
.get("/memperolehInformasi/find-many", memperolehInformasiFindMany)
.get("/salinanInformasi/find-many", salinanInformasiFindMany)
.get("/find-many", permohonanInformasiPublikFindMany)
.post("/create", permohonanInformasiPublikCreate, {
body: t.Object({
name: t.String(),
nik: t.String(),
notelp: t.String(),
alamat: t.String(),
email: t.String(),
jenisInformasiDimintaId: t.Union([t.String(), t.Null()]),
caraMemperolehInformasiId: t.Union([t.String(), t.Null()]),
caraMemperolehSalinanInformasiId: t.Union([t.String(), t.Null()]),
}),
})
export default PermohonanInformasiPublik

View File

@@ -0,0 +1,8 @@
import prisma from "@/lib/prisma";
export default async function jenisInformasiFindMany() {
const res = await prisma.jenisInformasiDiminta.findMany();
return {
data: res,
};
}

View File

@@ -0,0 +1,8 @@
import prisma from "@/lib/prisma";
export default async function memperolehInformasiFindMany() {
const res = await prisma.caraMemperolehInformasi.findMany();
return {
data: res,
};
}

View File

@@ -0,0 +1,9 @@
import prisma from "@/lib/prisma";
export default async function salinanInformasiFindMany() {
const res = await prisma.caraMemperolehSalinanInformasi.findMany();
return {
data: res,
};
}

View File

@@ -0,0 +1,33 @@
import prisma from "@/lib/prisma";
import { Prisma } from "@prisma/client";
import { Context } from "elysia";
type FormCreate = Prisma.FormulirPermohonanKeberatanGetPayload<{
select: {
name: true;
email: true;
notelp: true;
alasan: true;
}
}>
export default async function permohonanKeberatanInformasiPublikCreate(context: Context) {
const body = context.body as FormCreate;
await prisma.formulirPermohonanKeberatan.create({
data: {
name: body.name,
email: body.email,
notelp: body.notelp,
alasan: body.alasan,
}
})
return {
success: true,
message: "Permohonan Keberatan Informasi Publik Berhasil Dibuat",
data: {
...body,
}
}
}

View File

@@ -0,0 +1,8 @@
import prisma from "@/lib/prisma";
export default async function permohonanKeberatanInformasiPublikFindMany() {
const res = await prisma.formulirPermohonanKeberatan.findMany();
return {
data: res,
};
}

View File

@@ -0,0 +1,19 @@
import Elysia, { t } from "elysia";
import permohonanKeberatanInformasiPublikCreate from "./create";
import permohonanKeberatanInformasiPublikFindMany from "./find-many";
const PermohonanKeberatanInformasiPublik = new Elysia({
prefix: "/permohonankeberataninformasipublik",
tags: ["PPID/Permohonan Keberatan Informasi Publik"],
})
.get("/find-many", permohonanKeberatanInformasiPublikFindMany)
.post("/create", permohonanKeberatanInformasiPublikCreate, {
body: t.Object({
name: t.String(),
email: t.String(),
notelp: t.String(),
alasan: t.String(),
}),
})
export default PermohonanKeberatanInformasiPublik;

View File

@@ -0,0 +1,82 @@
import prisma from "@/lib/prisma";
import { Context } from "elysia";
import { writeFile, unlink } from "fs/promises";
import path from "path";
import fs from "fs";
import { mkdir } from "fs/promises";
interface ProfilePPIDBody {
id: string;
file: Blob & { name: string; type: string };
}
export default async function editImageProfilePPID(context: Context<{ body: ProfilePPIDBody }>) {
const { id, file } = context.body;
if (!file || !id) {
return {
success: false,
message: "File atau ID harus disertakan",
};
}
// Validasi ekstensi file
const allowedTypes = ["image/jpeg", "image/png", "image/webp"];
if (!allowedTypes.includes(file.type)) {
return {
success: false,
message: "Tipe file tidak diizinkan. Hanya JPG, PNG, atau WEBP",
};
}
const bytes = await file.arrayBuffer();
const buffer = Buffer.from(bytes);
const filename = `${id}_${Date.now()}_${file.name}`;
const folderPath = path.resolve("public", "assets", "images", "ppid", "profile-ppid");
try {
await mkdir(folderPath, { recursive: true });
console.log('Folder path:', folderPath);
const filePath = path.join(folderPath, filename);
console.log('File path:', filePath);
await writeFile(filePath, buffer);
if (!fs.existsSync(filePath)) {
return {
success: false,
message: "Failed to write file to disk",
};
}
const imageUrl = `/assets/images/ppid/profile-ppid/${filename}`;
// Hapus file lama (opsional, kalau mau bersih)
const oldData = await prisma.profilePPID.findUnique({ where: { id } });
if (oldData?.imageUrl) {
const oldPath = path.resolve("public", oldData.imageUrl.replace(/^\//, ""));
if (fs.existsSync(oldPath)) {
await unlink(oldPath).catch(() => {});
}
}
// Update DB
await prisma.profilePPID.update({
where: { id },
data: { imageUrl },
});
return {
success: true,
message: "Gambar berhasil diunggah",
url: imageUrl,
};
} catch (error) {
console.error('Error uploading file:', error);
return {
success: false,
message: "Gagal mengunggah gambar",
};
}
}

View File

@@ -0,0 +1,33 @@
import prisma from "@/lib/prisma";
import { Context } from "elysia";
export default async function profilePPIDFindById(context: Context) {
try {
const id = context?.params?.slugs?.[0];
// If no ID provided, get the first profile
if (!id) {
const data = await prisma.profilePPID.findFirst();
return {
success: true,
data,
};
}
const data = await prisma.profilePPID.findUniqueOrThrow({
where: { id },
});
return {
success: true,
data,
};
} catch (error) {
console.error("Error fetching profilePPID:", error);
return {
success: false,
message: error instanceof Error ? error.message : "Unknown error",
};
}
}

View File

@@ -0,0 +1,24 @@
import Elysia, { t } from "elysia";
import profilePPIDFindById from "./find-by-id";
import profilePPIDUpdate from "./update";
import editImageProfilePPID from "./edit-img";
const ProfilePPID = new Elysia({
prefix: "/profileppid",
tags: ["PPID/Profile PPID"]
})
.get("/find-by-id", profilePPIDFindById)
.post("/update", profilePPIDUpdate, {
body: t.Object({
id: t.String(),
name: t.String(),
biodata: t.String(),
riwayat: t.String(),
pengalaman: t.String(),
unggulan: t.String(),
})
})
.post("/edit-img", editImageProfilePPID)
export default ProfilePPID;

View File

@@ -0,0 +1,38 @@
import prisma from "@/lib/prisma";
import { Prisma } from "@prisma/client";
import { Context } from "elysia";
type FormCreate = Prisma.ProfilePPIDGetPayload<{
select: {
id: true;
name: true;
biodata: true;
riwayat: true;
pengalaman: true;
unggulan: true;
}
}>
export default async function profilePPIDUpdate(context: Context) {
const body = context.body as FormCreate;
await prisma.profilePPID.update({
where: {
id: body.id
},
data: {
name: body.name,
biodata: body.biodata,
riwayat: body.riwayat,
pengalaman: body.pengalaman,
unggulan: body.unggulan,
}
})
return {
success: true,
message: "Profile PPID Berhasil Dibuat",
data: {
...body,
}
}
}

View File

@@ -0,0 +1,33 @@
import prisma from "@/lib/prisma";
import { Context } from "elysia";
export default async function visimisiPPIDFindById(context: Context) {
try {
const id = context?.params?.slugs?.[0];
// If no ID provided, get the first profile
if (!id) {
const data = await prisma.visiMisiPPID.findFirst();
return {
success: true,
data,
};
}
const data = await prisma.visiMisiPPID.findUniqueOrThrow({
where: { id },
});
return {
success: true,
data,
};
} catch (error) {
console.error("Error fetching visiPPID:", error);
return {
success: false,
message: error instanceof Error ? error.message : "Unknown error",
};
}
}

View File

@@ -0,0 +1,20 @@
import Elysia from "elysia";
import visimisiPPIDFindById from "./find-by-id";
import visimisiPPIDUpdate from "./update";
import { t } from "elysia";
const VisiMisiPPID = new Elysia({
prefix: "/visimisippid",
tags: ["PPID/Visi Misi PPID"]
})
.get("/find-by-id", visimisiPPIDFindById)
.post("/update", visimisiPPIDUpdate, {
body: t.Object({
id: t.String(),
misi: t.String(),
visi: t.String(),
})
})
export default VisiMisiPPID

Some files were not shown because too many files have changed in this diff Show More