UI Admin Menu Desa Sub Menu Profile

This commit is contained in:
2025-05-15 17:47:43 +08:00
parent 2844132ea0
commit f5d68d4982
51 changed files with 1603 additions and 865 deletions

BIN
bun.lockb

Binary file not shown.

View File

@@ -48,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,8 @@
[
{
"id": "1",
"jenisInformasi": "Peraturan Desa",
"deskripsi": "Dokumen yang berisi kebijakan dan regulasi desa",
"tanggal": "15 Januari 2024"
}
]

View File

@@ -208,6 +208,34 @@ model GrafikBerdasarkanUmur {
}
// ========================================= 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

@@ -9,6 +9,8 @@ import potensi from './data/list-potensi.json'
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({
@@ -181,6 +183,54 @@ import dasarHukumPPID from './data/ppid/dasar-hukum-ppid/dasarhukumPPID.json'
}
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()

View File

Before

Width:  |  Height:  |  Size: 275 KiB

After

Width:  |  Height:  |  Size: 275 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 275 KiB

View File

@@ -56,6 +56,7 @@ const caraMemperolehSalinanInformasi = proxy({
}
}
})
console.log(caraMemperolehSalinanInformasi)
type PermohonanInformasiPublikForm = Prisma.PermohonanInformasiPublikGetPayload<{
select: {
@@ -70,12 +71,12 @@ type PermohonanInformasiPublikForm = Prisma.PermohonanInformasiPublikGetPayload<
};
}>;
const permohonanInformasiPublikForm = proxy({
const statepermohonanInformasiPublik = proxy({
create: {
form: {} as PermohonanInformasiPublikForm,
loading: false,
async create(){
const cek = templateForm.safeParse(permohonanInformasiPublikForm.create.form);
const cek = templateForm.safeParse(statepermohonanInformasiPublik.create.form);
if(!cek.success) {
const err = `[${cek.error.issues
.map((v) => `${v.path.join(".")}`)
@@ -83,38 +84,42 @@ const permohonanInformasiPublikForm = proxy({
return toast.error(err);
}
try {
permohonanInformasiPublikForm.create.loading = true;
const res = await ApiFetch.api.ppid.permohonaninformasipublik["create"].post(permohonanInformasiPublikForm.create.form);
statepermohonanInformasiPublik.create.loading = true;
const res = await ApiFetch.api.ppid.permohonaninformasipublik["create"].post(statepermohonanInformasiPublik.create.form);
if (res.status === 200) {
permohonanInformasiPublikForm.findMany.load();
statepermohonanInformasiPublik.findMany.load();
return toast.success("success create");
}
return toast.error("failed create");
} catch (error) {
console.log((error as Error).message);
} finally {
permohonanInformasiPublikForm.create.loading = false;
statepermohonanInformasiPublik.create.loading = false;
}
}
},
findMany: {
data: null as
| Prisma.PermohonanInformasiPublikGetPayload<{ omit: { isActive: true } }>[]
| 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) {
permohonanInformasiPublikForm.findMany.data = res.data?.data ?? [];
statepermohonanInformasiPublik.findMany.data = res.data?.data ?? [];
}
}
}
})
const statePermohonanInformasi = proxy({
permohonanInformasiPublikForm,
const statepermohonanInformasiPublikForm = proxy({
statepermohonanInformasiPublik,
jenisInformasiDiminta,
caraMemperolehInformasi,
caraMemperolehSalinanInformasi
caraMemperolehSalinanInformasi,
})
export default statePermohonanInformasi;
export default statepermohonanInformasiPublikForm;

View File

@@ -20,12 +20,12 @@ type PermohonanKeberatanInformasiForm = Prisma.FormulirPermohonanKeberatanGetPay
};
}>;
const permohonanKeberatanInformasiForm = proxy({
const permohonanKeberatanInformasi = proxy({
create: {
form: {} as PermohonanKeberatanInformasiForm,
loading: false,
async create(){
const cek = templateForm.safeParse(permohonanKeberatanInformasiForm.create.form);
const cek = templateForm.safeParse(permohonanKeberatanInformasi.create.form);
if(!cek.success) {
const err = `[${cek.error.issues
.map((v) => `${v.path.join(".")}`)
@@ -33,17 +33,17 @@ const permohonanKeberatanInformasiForm = proxy({
return toast.error(err);
}
try {
permohonanKeberatanInformasiForm.create.loading = true;
const res = await ApiFetch.api.ppid.permohonankeberataninformasipublik["create"].post(permohonanKeberatanInformasiForm.create.form);
permohonanKeberatanInformasi.create.loading = true;
const res = await ApiFetch.api.ppid.permohonankeberataninformasipublik["create"].post(permohonanKeberatanInformasi.create.form);
if (res.status === 200) {
permohonanKeberatanInformasiForm.findMany.load();
permohonanKeberatanInformasi.findMany.load();
return toast.success("success create");
}
return toast.error("failed create");
} catch (error) {
console.log((error as Error).message);
} finally {
permohonanKeberatanInformasiForm.create.loading = false;
permohonanKeberatanInformasi.create.loading = false;
}
},
},
@@ -54,15 +54,11 @@ const permohonanKeberatanInformasiForm = proxy({
async load() {
const res = await ApiFetch.api.ppid.permohonankeberataninformasipublik["find-many"].get();
if (res.status === 200) {
permohonanKeberatanInformasiForm.findMany.data = res.data?.data ?? [];
permohonanKeberatanInformasi.findMany.data = res.data?.data ?? [];
}
}
}
});
const statePermohonanKeberatan = proxy({
permohonanKeberatanInformasiForm,
})
export default statePermohonanKeberatan;
export default permohonanKeberatanInformasi;

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

@@ -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

@@ -10,7 +10,7 @@ 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>';
'<p>Dokumen yang berisi kebijakan dan regulasi desa</p>';
export function PPIDEditor({ onSubmit, onChange, showSubmit = true }: {
onSubmit?: (val: string) => void,

View File

@@ -1,11 +1,10 @@
'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 React from 'react';
import { useShallowEffect } from '@mantine/hooks';
import { useProxy } from 'valtio/utils';
import stateDaftarInformasiPublik from '../../_state/ppid/daftar_informasi_publik/daftarInformasiPublik';
import { PPIDEditor } from '../_com/ppid_Editor';
import colors from '@/con/colors';
import { useShallowEffect } from '@mantine/hooks';
import { PPIDTextEditor } from '../_com/PPIDTextEditor';
function Page() {
const daftarInformasi = useProxy(stateDaftarInformasiPublik.daftarInformasi)
@@ -30,7 +29,7 @@ function Page() {
daftarInformasi.create.form.jenisInformasi = val.target.value
}}
/>
<PPIDEditor
<PPIDTextEditor
showSubmit={false}
onChange={(val) => {
daftarInformasi.create.form.deskripsi = val
@@ -68,46 +67,50 @@ function ListDaftarInformasi() {
if (!listData.findMany.data) return <Stack>
{Array.from({ length: 10 }).map((v, k) => <Skeleton key={k} h={40} />)}
</Stack>
return <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>
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

@@ -1,4 +1,4 @@
import { Stack, Tabs, TabsList, TabsPanel, TabsTab } from '@mantine/core';
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';
@@ -8,9 +8,10 @@ import GrafikBerdasarakanUmur from './_ui/grafik_berdasarkan_umur/page';
function Page() {
return (
<Stack>
<Tabs color={colors['blue-button']} variant='pills' defaultValue={"Grafik Hasil Kepuasan Masyarakat Terhadap Pelayanan Publik"}>
<TabsList>
<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>

View File

@@ -1,41 +0,0 @@
import { Prisma } from '@prisma/client';
import React from 'react';
import { useProxy } from 'valtio/utils';
import statePermohonanInformasi from '../../_state/ppid/permohonan_informasi_publik/permohonanInformasiPublik';
import { useShallowEffect } from '@mantine/hooks';
import { Group, Select, Skeleton } from '@mantine/core';
function JenisInformasi({ onChange }: {
onChange: (value: Prisma.JenisInformasiDimintaGetPayload<{
select: {
id: true,
name: true
}
}>) => void
}) {
const jenisInformasiState = useProxy(statePermohonanInformasi.jenisInformasiDiminta)
useShallowEffect(() => {
jenisInformasiState.findMany.load()
}, [])
if (!jenisInformasiState.findMany.data) return <Skeleton h={40} />
return (
<Group>
<Select
placeholder='pilih jenis informasi'
label={'select jenis informasi'}
data={jenisInformasiState.findMany.data.map((item) => ({
value: item.id,
label: item.name
}))}
onChange={(v) => {
const data = jenisInformasiState.findMany.data?.find((item) => item.id === v)
if (!data) return
onChange(data)
}}
/>
</Group>
);
}
export default JenisInformasi;

View File

@@ -1,38 +0,0 @@
import { Group, Select } from '@mantine/core';
import { useShallowEffect } from '@mantine/hooks';
import { Prisma } from '@prisma/client';
import React from 'react';
import { useProxy } from 'valtio/utils';
import statePermohonanInformasi from '../../_state/ppid/permohonan_informasi_publik/permohonanInformasiPublik';
function MemperolehInformasi({ onChange }: {
onChange: (value: Prisma.CaraMemperolehInformasiGetPayload<{
select: {
id: true,
name: true
}
}>) => void
}) {
const memperolehInformasiState = useProxy(statePermohonanInformasi.caraMemperolehInformasi)
useShallowEffect(() => {
memperolehInformasiState.findMany.load()
}, [])
return (
<Group>
<Select
placeholder='pilih katagori'
label={"select katagori"}
data={memperolehInformasiState.findMany.data?.map((item) => ({
value: item.id,
label: item.name
}))} onChange={(v) => {
const data = memperolehInformasiState.findMany.data?.find((item) => item.id === v)
if (!data) return
onChange(data)
}} />
</Group>
);
}
export default MemperolehInformasi;

View File

@@ -1,45 +0,0 @@
import { Group, Select, Skeleton } from '@mantine/core';
import { useShallowEffect } from '@mantine/hooks';
import { Prisma } from '@prisma/client';
import React from 'react';
import { useProxy } from 'valtio/utils';
import statePermohonanInformasi from '../../_state/ppid/permohonan_informasi_publik/permohonanInformasiPublik';
function MemperolehSalinan({ onChange }: {
onChange: (value: Prisma.CaraMemperolehSalinanInformasiGetPayload<{
select: {
id: true,
name: true
}
}>) => void
}) {
const memperolehSalinanInformasiState = useProxy(statePermohonanInformasi.caraMemperolehSalinanInformasi)
useShallowEffect(() => {
memperolehSalinanInformasiState.findMany.load()
}, [])
if (!memperolehSalinanInformasiState.findMany.data) return <Skeleton h={40} />
return (
<Group>
<Select
placeholder='pilih salinan informasi'
label={'select salinan informasi'}
data={memperolehSalinanInformasiState.findMany.data.map((item) => ({
value: item.id,
label: item.name
}))}
onChange={(v) => {
if (!v) return
const selectedItem = memperolehSalinanInformasiState.findMany.data?.find(item => item.id === v)
if (selectedItem) {
onChange({
id: selectedItem.id,
name: selectedItem.name
})
}
}}
/>
</Group>
);
}
export default MemperolehSalinan;

View File

@@ -1,115 +1,57 @@
'use client'
import { Box, Button, Group, Paper, SimpleGrid, Stack, TextInput, Title } from '@mantine/core';
import { useProxy } from 'valtio/utils';
import statePermohonanInformasi from '../../_state/ppid/permohonan_informasi_publik/permohonanInformasiPublik';
import JenisInformasi from './jenisInformasi';
import MemperolehInformasi from './memperolehInformasi';
import MemperolehSalinan from './memperolehSalinan';
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 (
<Box>
<Stack>
<SimpleGrid cols={{ base: 1, md: 2 }}>
<PermohonanInformasiPublikCreate />
</SimpleGrid>
</Stack>
<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>
);
}
function PermohonanInformasiPublikCreate() {
const permohonanInformasiPublikState = useProxy(statePermohonanInformasi)
const submitForms = () => {
// Tambahkan log untuk debugging
console.log("Form data sebelum submit:", {
name: permohonanInformasiPublikState.permohonanInformasiPublikForm.create.form.name,
nik: permohonanInformasiPublikState.permohonanInformasiPublikForm.create.form.nik,
notelp: permohonanInformasiPublikState.permohonanInformasiPublikForm.create.form.notelp,
alamat: permohonanInformasiPublikState.permohonanInformasiPublikForm.create.form.alamat,
email: permohonanInformasiPublikState.permohonanInformasiPublikForm.create.form.email,
jenisInformasiDimintaId: permohonanInformasiPublikState.jenisInformasiDiminta,
caraMemperolehInformasiId: permohonanInformasiPublikState.caraMemperolehInformasi,
caraMemperolehSalinanInformasiId: permohonanInformasiPublikState.caraMemperolehSalinanInformasi
});
if (permohonanInformasiPublikState.permohonanInformasiPublikForm.create.form.name &&
permohonanInformasiPublikState.permohonanInformasiPublikForm.create.form.nik &&
permohonanInformasiPublikState.permohonanInformasiPublikForm.create.form.notelp &&
permohonanInformasiPublikState.permohonanInformasiPublikForm.create.form.alamat &&
permohonanInformasiPublikState.permohonanInformasiPublikForm.create.form.email &&
permohonanInformasiPublikState.jenisInformasiDiminta &&
permohonanInformasiPublikState.caraMemperolehInformasi &&
permohonanInformasiPublikState.caraMemperolehSalinanInformasi) {
permohonanInformasiPublikState.permohonanInformasiPublikForm.create.create()
} else {
console.log("Validasi gagal, form tidak lengkap");
// Tampilkan pesan error ke pengguna di sini
}
}
return (
<Box py={5}>
<Paper bg={colors['white-1']} p={'md'}>
<Stack gap={"10"}>
<Title order={3}>Permohonan Informasi Publik</Title>
<TextInput
label="Nama Lengkap"
placeholder="masukkan nama lengkap"
onChange={(val) => {
permohonanInformasiPublikState.permohonanInformasiPublikForm.create.form.name = val.target.value
}}
/>
<TextInput
label="NIK"
placeholder="masukkan NIK"
onChange={(val) => {
permohonanInformasiPublikState.permohonanInformasiPublikForm.create.form.nik = val.target.value
}}
/>
<TextInput
label="No.Telp"
placeholder="masukkan no telp"
onChange={(val) => {
permohonanInformasiPublikState.permohonanInformasiPublikForm.create.form.notelp = val.target.value
}}
/>
<TextInput
label="Alamat"
placeholder="masukkan alamat"
onChange={(val) => {
permohonanInformasiPublikState.permohonanInformasiPublikForm.create.form.alamat = val.target.value
}}
/>
<TextInput
label="Email"
placeholder="masukkan email"
onChange={(val) => {
permohonanInformasiPublikState.permohonanInformasiPublikForm.create.form.email = val.target.value
}}
/>
<JenisInformasi
onChange={(val) => {
permohonanInformasiPublikState.permohonanInformasiPublikForm.create.form.jenisInformasiDimintaId = val.id
}}
/>
<MemperolehInformasi
onChange={(val) => {
permohonanInformasiPublikState.permohonanInformasiPublikForm.create.form.caraMemperolehInformasiId = val.id
}}
/>
<MemperolehSalinan
onChange={(val) => {
permohonanInformasiPublikState.permohonanInformasiPublikForm.create.form.caraMemperolehSalinanInformasiId = val.id
}}
/>
<Group>
<Button onClick={submitForms}>Submit</Button>
</Group>
</Stack>
</Paper>
</Box >
)
}
export default Page;

View File

@@ -1,73 +1,12 @@
'use client'
import colors from '@/con/colors';
import { Box, Button, Group, Paper, SimpleGrid, Skeleton, Stack, Text, TextInput, Title } from '@mantine/core';
import React from 'react';
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';
import { useShallowEffect } from '@mantine/hooks';
function Page() {
return (
<Box>
<SimpleGrid cols={{ base: 1, md: 2 }}>
<PermohonanKeberatanInformasiCreate />
<PermohonanKeberatanInformasiList />
</SimpleGrid>
</Box>
);
}
function PermohonanKeberatanInformasiCreate() {
const state = useProxy(statePermohonanKeberatan.permohonanKeberatanInformasiForm)
const submit = () => {
if (state.create.form.name && state.create.form.email && state.create.form.notelp && state.create.form.alasan) {
state.create.create()
}
}
return (
<Box py={10}>
<Paper bg={colors['white-1']} p={'md'}>
<Stack gap={"xs"}>
<Title order={3}>Permohonan Keberatan Informasi Publik</Title>
<TextInput
label="Nama"
placeholder="masukkan nama lengkap"
onChange={(val) => {
state.create.form.name = val.target.value
}}
/>
<TextInput
label="Email"
placeholder="masukkan email"
onChange={(val) => {
state.create.form.email = val.target.value
}}
/>
<TextInput
label="Nomor Telepon"
placeholder="masukkan nomor telepon"
onChange={(val) => {
state.create.form.notelp = val.target.value
}}
/>
<TextInput
label="Alasan Keberatan"
placeholder="masukkan alasan keberatan"
onChange={(val) => {
state.create.form.alasan = val.target.value
}}
/>
<Group>
<Button onClick={submit} bg={colors['blue-button']}>Kirim Permohonan</Button>
</Group>
</Stack>
</Paper>
</Box>
)
}
function PermohonanKeberatanInformasiList() {
const listState = useProxy(statePermohonanKeberatan.permohonanKeberatanInformasiForm)
const listState = useProxy(statePermohonanKeberatan)
useShallowEffect(() => {
listState.findMany.load()
}, [])
@@ -75,24 +14,36 @@ function PermohonanKeberatanInformasiList() {
if (!listState.findMany.data) return <Stack>
{Array.from({ length: 10 }).map((v, k) => <Skeleton key={k} h={40} />)}
</Stack>
return (
<Box py={10}>
<Paper bg={colors['white-1']} p={'md'}>
<Stack gap={"xs"}>
<Title order={3}>Permohonan Keberatan Informasi Publik</Title>
{listState.findMany.data?.map((item) => (
<Box key={item.id}>
<Text>Nama: {item.name}</Text>
<Text>Email: {item.email}</Text>
<Text>Telepon: {item.notelp}</Text>
<Text>Alasan: {item.alasan}</Text>
</Box>
))}
<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

@@ -1,18 +1,18 @@
'use client'
import { Box, Group, Text } from '@mantine/core';
import React, { useState } from 'react';
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';
import { Dropzone, MIME_TYPES } from '@mantine/dropzone';
import { IconUpload, IconX, IconPhoto } from '@tabler/icons-react';
import ApiFetch from '@/lib/api-fetch';
function Biodata() {
const biodataState = useProxy(stateProfilePPID)
const [loading, setLoading] = useState(false);
return (<Box>
<Text fw={"bold"}>Biodata</Text>
<Dropzone
@@ -55,6 +55,9 @@ function Biodata() {
</div>
</Group>
</Dropzone>
<PPIDTextEditor
key={biodataState.findById.data?.id ?? 'new'}
showSubmit={false}
@@ -67,8 +70,7 @@ function Biodata() {
/>
</Box>
);
}
export default Biodata;
export default Biodata;

View File

@@ -25,19 +25,16 @@ function ProfileList() {
<Title order={3}>List Profile PPID</Title>
{dataArray.map((item) => (
<Box key={item.id}>
<Box>
<Text fw={"bold"}>Gambar</Text>
{item.imageUrl ? (
{item.imageUrl && (
<Box mb={20}>
<Text fw={"bold"} mb={5}>Preview Gambar:</Text>
<Image
src={item.imageUrl}
alt="Foto Profil"
alt="Profile"
w={200}
radius="md"
/>
) : (
<Text c="dimmed">Belum ada foto</Text>
)}
</Box>
</Box>
)}
<Box>
<Text fw={"bold"}>Nama</Text>
<Text dangerouslySetInnerHTML={{ __html: item.name }}></Text>

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

@@ -11,9 +11,12 @@ type FormCreate = Prisma.DaftarInformasiPublikGetPayload<{
}>
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,

View File

@@ -26,6 +26,7 @@ export default async function permohonanInformasiPublikCreate(context: Context)
alamat: body.alamat,
jenisInformasiDimintaId: body.jenisInformasiDimintaId,
caraMemperolehInformasiId: body.caraMemperolehInformasiId,
caraMemperolehSalinanInformasiId: body.caraMemperolehSalinanInformasiId,
}
})

View File

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

View File

@@ -1,50 +1,27 @@
'use client'
import stateDaftarInformasiPublik from '@/app/admin/(dashboard)/_state/ppid/daftar_informasi_publik/daftarInformasiPublik';
import colors from '@/con/colors';
import { Stack, Box, Text, Center, Image, TextInput, TableTd, TableTr, TableTbody, TableTh, TableThead, Table, ActionIcon } from '@mantine/core';
import React from 'react';
import { Box, Center, Image, Skeleton, Stack, Table, TableTbody, TableTd, TableTh, TableThead, TableTr, Text, TextInput } from '@mantine/core';
import { useShallowEffect } from '@mantine/hooks';
import { IconSearch } from '@tabler/icons-react';
import { useProxy } from 'valtio/utils';
import BackButton from '../../desa/layanan/_com/BackButto';
import { IconDownload, IconSearch } from '@tabler/icons-react';
const dataTable = [
{
id: 1,
nomer: '1',
jenis: "Peraturan Desa",
deskripsi: "Dokumen yang berisi kebijakan dan regulasi desa",
tanggal: "15 Januari 2024",
unduh: <IconDownload size={20} />
},
{
id: 2,
nomer: '2',
jenis: "Laporan Keuangan",
deskripsi: "Laporan Kegunaan anggaran desa secara transparan",
tanggal: "20 Januari 2024",
unduh: <IconDownload size={20} />
},
{
id: 3,
nomer: '3',
jenis: "Program & Kegiatan",
deskripsi: "Informasi mengenai program pembangunan & kegiatan desa",
tanggal: "30 Januari 2024",
unduh: <IconDownload size={20} />
},
]
function Page() {
const rows = dataTable.map((element) => (
<TableTr key={element.jenis}>
<TableTd ta={'center'}>{element.nomer}</TableTd>
<TableTd >{element.jenis}</TableTd>
<TableTd>{element.deskripsi}</TableTd>
<TableTd>{element.tanggal}</TableTd>
<TableTd ta={'center'}>
<ActionIcon c={colors['blue-button']} variant='transparent'>
{element.unduh}
</ActionIcon>
</TableTd>
</TableTr>
))
const listData = useProxy(stateDaftarInformasiPublik.daftarInformasi)
useShallowEffect(() => {
listData.findMany.load()
}, [])
if (!listData.findMany.data) return <Stack pos={"relative"} bg={colors.Bg} py={"xl"} gap={"22"}>
<Box px={{ base: 'md', md: 100 }}>
<Skeleton h={40} />
</Box>
<Skeleton h={40}/>
<Skeleton h={40}/>
<Box px={{ base: 'md', md: 100 }}>
<Skeleton h={40} />
</Box>
</Stack>
return (
<Stack pos={"relative"} bg={colors.Bg} py={"xl"} gap={"22"}>
<Box px={{ base: 'md', md: 100 }}>
@@ -71,10 +48,18 @@ function Page() {
<TableTh ta={'center'}>Jenis Informasi</TableTh>
<TableTh ta={'center'}>Deskripsi</TableTh>
<TableTh ta={'center'}>Tanggal Publikasi</TableTh>
<TableTh ta={'center'}>Unduh</TableTh>
</TableTr>
</TableThead>
<TableTbody bg={colors['white-1']}>{rows}</TableTbody>
<TableTbody bg={colors['white-1']}>
{listData.findMany.data?.map((item) => (
<TableTr key={item.id}>
<TableTd ta={'center'}>{item.nomor}</TableTd>
<TableTd>{item.jenisInformasi}</TableTd>
<TableTd dangerouslySetInnerHTML={{ __html: item.deskripsi }}></TableTd>
<TableTd>{item.tanggal}</TableTd>
</TableTr>
))}
</TableTbody>
</Table>
</Stack>
<Text pt={20} fz={'h4'} fw={"bold"}>Kontak PPID</Text>

View File

@@ -0,0 +1,87 @@
/* eslint-disable @typescript-eslint/no-explicit-any */
'use client'
import React, { useEffect, useState } from 'react';
import { Box, Center, Flex, Skeleton, Stack, Text, Title } from '@mantine/core';
import { Cell, Pie, PieChart } from 'recharts';
import colors from '@/con/colors';
import { useShallowEffect } from '@mantine/hooks';
import { useProxy } from 'valtio/utils';
import stateGrafikBerdasarkanJenisKelamin from '@/app/admin/(dashboard)/_state/ppid/indeks_kepuasan_masyarakat/grafikBerdasarkanJenisKelamin';
function GrafikBerdasarkanJenisKelamin() {
const grafikBerdasarkanJenisKelamin = useProxy(stateGrafikBerdasarkanJenisKelamin.grafikBerdasarkanJenisKelamin)
const [mounted, setMounted] = useState(false);
const [donutData, setDonutData] = useState<any[]>([]);
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);
}
};
if(!grafikBerdasarkanJenisKelamin.findMany.data) return <Stack>
<Title pb={10} order={3}>Grafik Berdasarkan Jenis Kelamin Responden</Title>
<Skeleton h={500} />
</Stack>
return (
<Stack gap={"xl"}>
<Title pb={10} order={3}>Grafik Berdasarkan Jenis Kelamin Responden</Title>
{mounted && donutData.length > 0 && (
<Box style={{ width: '100%', height: 'auto', minHeight: 300 }}>
<Center>
<PieChart
width={1000} height={530}
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>
);
}
export default GrafikBerdasarkanJenisKelamin;

View File

@@ -0,0 +1,96 @@
/* eslint-disable @typescript-eslint/no-explicit-any */
'use client'
import stateGrafikResponden from '@/app/admin/(dashboard)/_state/ppid/indeks_kepuasan_masyarakat/grafikBerdasarkanResponden';
import colors from '@/con/colors';
import { Stack, Title, Box, Center, Flex, Text, Skeleton } 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);
}
};
if (!grafikBerdasarkanResponden.findMany.data) return <Stack>
<Title pb={10} order={3}>Grafik Berdasarkan Responden</Title>
<Skeleton h={500} />
</Stack>
return (
<Stack>
<Title pb={10} order={3}>Grafik Berdasarkan Responden</Title>
{mounted && donutData.length > 0 && (
<Box style={{ width: '100%', height: 'auto', minHeight: 300 }}>
<Center>
<PieChart
width={1000} height={530}
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>
);
}
export default GrafikBerdasarkanResponden;

View File

@@ -0,0 +1,97 @@
/* eslint-disable @typescript-eslint/no-explicit-any */
'use client'
import stateGrafikBerdasarkanUmur from '@/app/admin/(dashboard)/_state/ppid/indeks_kepuasan_masyarakat/grafikBerdasarkanUmur';
import colors from '@/con/colors';
import { Stack, Title, Box, Center, Flex, Text, Skeleton } 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 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);
}
}
if(!grafikBerdasarkanUmur.findMany.data) return <Stack>
<Title pb={10} order={3}>Grafik Berdasarkan Umur Responden</Title>
<Skeleton h={500} />
</Stack>
return (
<Stack>
<Title pb={10} order={3}>Grafik Berdasarkan Umur Responden</Title>
{mounted && donutData.length > 0 && (
<Box style={{ width: '100%', height: 'auto', minHeight: 300 }}>
<Center>
<PieChart
width={1000} height={530}
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>
);
}
export default GrafikBerdasarakanUmur;

View File

@@ -0,0 +1,57 @@
/* 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, Skeleton, Stack, Text, Title } from '@mantine/core';
import { useMediaQuery, useShallowEffect } from '@mantine/hooks';
import React, { useEffect, useState } from 'react';
import { BarChart, Tooltip, XAxis, YAxis, Legend, Bar } 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();
}, []);
if(!grafikHasilKepuasan.findMany.data) return <Stack>
<Title pb={10} order={3}>Grafik Hasil Kepuasan Masyarakat Terhadap Pelayanan Publik</Title>
<Skeleton h={500} />
</Stack>
return (
<Stack gap={"xl"}>
<Text fw={"bold"} fz={{ base: 'h4', md: 'h3' }} ta={"center"}>
Grafik Hasil Kepuasan Masyarakat Terhadap Pelayanan Publik
</Text>
<Box style={{ width: '100%', minWidth: 300, height: 400, minHeight: 300 }}>
{mounted && chartData.length > 0 && (
<BarChart width={isMobile ? 300 : isTablet ? 400 : 450} height={380} data={chartData} >
<XAxis dataKey="label" />
<YAxis />
<Tooltip />
<Legend style={{justifyContent: 'center'}} />
<Bar dataKey="kepuasan" fill={colors['blue-button']} name="Kepuasan" />
</BarChart>
)}
</Box>
</Stack>
);
}
export default GrafikHasilKepuasan;

View File

@@ -1,38 +1,11 @@
import colors from '@/con/colors';
import { Stack, Box, Paper, Text, Center, Flex, ColorSwatch, } from '@mantine/core';
import React from 'react';
import { Box, Paper, Stack, Text } from '@mantine/core';
import BackButton from '../../desa/layanan/_com/BackButto';
import { BarChart, DonutChart } from '@mantine/charts';
import GrafikBerdasarkanJenisKelamin from './grafik_berdasarkan_jenis_kelamin/page';
import GrafikBerdasarkanResponden from './grafik_berdasarkan_pilihan_responden/page';
import GrafikBerdasarakanUmur from './grafik_berdasarkan_umur_responden/page';
import GrafikHasilKepuasan from './grafik_hasil_kepuasan_masyarakat/page';
const dataBar = [
{ pelayanan: 'Persyaratan', kepuasan: 90 },
{ pelayanan: 'Prosedur', kepuasan: 98 },
{ pelayanan: 'Kecepatan', kepuasan: 92 },
{ pelayanan: 'Biaya / Tarif', kepuasan: 85 },
{ pelayanan: 'Produk Layanan', kepuasan: 89 },
{ pelayanan: 'Kompetensi Pelaksana', kepuasan: 91 },
{ pelayanan: 'Perilaku Pelaksana', kepuasan: 90 },
{ pelayanan: 'Penanganan Pengaduan', kepuasan: 93 },
{ pelayanan: 'Sarana dan Prasarana', kepuasan: 91 },
]
const dataJenisKelamin = [
{ name: 'Perempuan', value: 90, color: '#3291CB' },
{ name: 'Laki - Laki', value: 10, color: colors['blue-button'] },
]
const dataResponden = [
{ name: 'Sangat Baik', value: 60, color: 'green' },
{ name: 'Baik', value: 20, color: 'blue' },
{ name: 'Kurang Baik', value: 10, color: 'orange' },
{ name: 'Tidak Baik', value: 10, color: 'red' },
]
const dataUmur = [
{ name: '17 - 25 tahun', value: 60, color: 'green' },
{ name: '26 - 45 tahun', value: 20, color: 'blue' },
{ name: '46 - 60 tahun', value: 10, color: 'orange' },
{ name: 'di atas 60 tahun', value: 10, color: 'red' },
]
function Page() {
return (
<Stack pos={"relative"} bg={colors.Bg} py={"xl"} gap={"22"}>
@@ -48,116 +21,23 @@ function Page() {
</Text>
</Box>
<Box px={{ base: "md", md: 100 }}>
<Paper p={"xl"} bg={colors['white-trans-1']}>
<Text fw={"bold"} fz={{ base: 'h4', md: 'h3' }} ta={"center"}>
Grafik Hasil Kepuasan Masyarakat Terhadap Pelayanan Publik
</Text>
<BarChart
py={30}
h={500}
data={dataBar}
dataKey='pelayanan'
orientation='vertical'
yAxisProps={{ width: 80 }}
barProps={{ radius: 10 }}
series={[{ name: 'kepuasan', color: colors['blue-button'] }]}
/>
<Paper bg={colors['white-1']} p={'xl'}>
<GrafikHasilKepuasan />
</Paper>
</Box>
<Box px={{ base: "md", md: 100 }}>
<Paper p={"xl"} bg={colors['white-trans-1']}>
<Text fw={"bold"} fz={{ base: 'h4', md: 'h3' }} ta={"center"}>
Grafik Berdasarkan Jenis Kelamin Responden
</Text>
<Center>
<DonutChart size={250} thickness={30} withLabelsLine labelsType="percent" withLabels data={dataJenisKelamin} />
</Center>
<Flex gap={"md"} justify={"center"} align={"center"}>
<Box>
<Flex gap={10} align={"center"}>
<Text>Perempuan</Text>
<ColorSwatch color='#3291CB' />
</Flex>
</Box>
<Box>
<Flex gap={10} align={"center"}>
<Text>Laki - Laki</Text>
<ColorSwatch color={colors['blue-button']} />
</Flex>
</Box>
</Flex>
<GrafikBerdasarkanJenisKelamin/>
</Paper>
</Box>
<Box px={{ base: "md", md: 100 }}>
<Paper p={"xl"} bg={colors['white-trans-1']}>
<Text fw={"bold"} fz={{ base: 'h4', md: 'h3' }} ta={"center"}>
Grafik Berdasarkan Pilihan Responden
</Text>
<Center>
<DonutChart size={250} thickness={30} withLabelsLine labelsType="percent" withLabels data={dataResponden} />
</Center>
<Flex gap={"md"} justify={"center"} align={"center"}>
<Box>
<Flex gap={{ base: 5, md: 10 }} align={"center"}>
<Text fz={{ base: 'xs', md: 'lg' }}>Sangat Baik</Text>
<ColorSwatch color='#40BE57' />
</Flex>
</Box>
<Box>
<Flex gap={{ base: 5, md: 10 }} align={"center"}>
<Text fz={{ base: 'xs', md: 'lg' }}>Baik</Text>
<ColorSwatch color={'#228AE7'} />
</Flex>
</Box>
<Box>
<Flex gap={{ base: 5, md: 10 }} align={"center"}>
<Text fz={{ base: 'xs', md: 'lg' }}>Kurang Baik</Text>
<ColorSwatch color={'#FD7F12'} />
</Flex>
</Box>
<Box>
<Flex gap={{ base: 5, md: 10 }} align={"center"}>
<Text fz={{ base: 'xs', md: 'lg' }}>Tidak Baik</Text>
<ColorSwatch color={'#F85253'} />
</Flex>
</Box>
</Flex>
<GrafikBerdasarkanResponden/>
</Paper>
</Box>
<Box px={{ base: "md", md: 100 }}>
<Paper p={"xl"} bg={colors['white-trans-1']}>
<Text fw={"bold"} fz={{ base: 'h4', md: 'h3' }} ta={"center"}>
Grafik Berdasarkan Umur Responden
</Text>
<Center>
<DonutChart size={250} thickness={30} withLabelsLine labelsType="percent" withLabels data={dataUmur} />
</Center>
<Flex gap={"md"} justify={"center"} align={"center"}>
<Box>
<Flex gap={{ base: 0, md: 10 }} align={"center"}>
<Text fz={{ base: 'xs', md: 'lg' }}>17 25 tahun</Text>
<ColorSwatch color='#40BE57' />
</Flex>
</Box>
<Box>
<Flex gap={{ base: 0, md: 10 }} align={"center"}>
<Text fz={{ base: 'xs', md: 'lg' }}>26 45 tahun</Text>
<ColorSwatch color={'#228AE7'} />
</Flex>
</Box>
<Box>
<Flex gap={{ base: 0, md: 10 }} align={"center"}>
<Text fz={{ base: 'xs', md: 'lg' }}>46 60 tahun</Text>
<ColorSwatch color={'#FD7F12'} />
</Flex>
</Box>
<Box>
<Flex gap={{ base: 0, md: 10 }} align={"center"}>
<Text fz={{ base: 'xs', md: 'lg' }}>di atas 60 tahun</Text>
<ColorSwatch color={'#F85253'} />
</Flex>
</Box>
</Flex>
<GrafikBerdasarakanUmur/>
</Paper>
</Box>
</Stack>

View File

@@ -0,0 +1,48 @@
// components/jenisInformasiSelector.tsx
'use client';
import { Skeleton, Group, Select } from '@mantine/core';
import { useShallowEffect } from '@mantine/hooks';
import { Prisma } from '@prisma/client';
import React from 'react';
import { useProxy } from 'valtio/utils';
import statePermohonanInformasi from '@/app/admin/(dashboard)/_state/ppid/permohonan_informasi_publik/permohonanInformasiPublik';
export default function JenisInformasiSelector({ onChange }: {
onChange: (value: Prisma.JenisInformasiDimintaGetPayload<{
select: {
id: true;
name: true
}
}>) => void;
}) {
const jenisInformasiState = useProxy(statePermohonanInformasi);
useShallowEffect(() => {
jenisInformasiState.jenisInformasiDiminta.findMany.load();
}, []);
if (!jenisInformasiState.jenisInformasiDiminta.findMany.data) {
return <Skeleton h={40} />;
}
// Add a check for data before mapping
const data = jenisInformasiState.jenisInformasiDiminta.findMany.data;
if (!data) return <Skeleton h={40} />;
return (
<Group>
<Select
placeholder='pilih jenis informasi'
label='Jenis Informasi'
data={data.map((item) => ({
value: item.id,
label: item.name,
}))}
onChange={(v) => {
const selectedData = data.find((item) => item.id === v);
if (selectedData) onChange(selectedData);
}}
/>
</Group>
);
}

View File

@@ -0,0 +1,46 @@
import statePermohonanInformasi from '@/app/admin/(dashboard)/_state/ppid/permohonan_informasi_publik/permohonanInformasiPublik';
import { Group, Select, Skeleton } from '@mantine/core';
import { useShallowEffect } from '@mantine/hooks';
import { Prisma } from '@prisma/client';
import React from 'react';
import { useProxy } from 'valtio/utils';
function MemperolehInformasi({ onChange }: {
onChange: (value: Prisma.CaraMemperolehInformasiGetPayload<{
select: {
id: true,
name: true
}
}>) => void
}) {
const memperolehInformasiState = useProxy(statePermohonanInformasi)
useShallowEffect(() => {
memperolehInformasiState.caraMemperolehInformasi.findMany.load()
}, [])
if (!memperolehInformasiState.caraMemperolehInformasi.findMany.data)
return <Skeleton h={40} />
const data = memperolehInformasiState.caraMemperolehInformasi.findMany.data;
if (!data) return <Skeleton h={40} />
return (
<Group>
<Select
placeholder='pilih cara memperoleh informasi'
label={"Cara Memperoleh Informasi"}
data={data.map((item) => ({
value: item.id,
label: item.name,
}))}
onChange={(v) => {
const selectedData = data.find((item) => item.id === v);
if (selectedData) onChange(selectedData);
}}
/>
</Group>
);
}
export default MemperolehInformasi;

View File

@@ -1,123 +1,39 @@
'use client'
import statePermohonanInformasi from '@/app/admin/(dashboard)/_state/ppid/permohonan_informasi_publik/permohonanInformasiPublik';
import colors from '@/con/colors';
import { ActionIcon, Box, Button, Center, Checkbox, Combobox, ComboboxOption, Group, Input, InputBase, Paper, SimpleGrid, Stack, Text, Textarea, TextInput, useCombobox } from '@mantine/core';
import { ActionIcon, Box, Button, Center, Checkbox, Group, Paper, SimpleGrid, Stack, Text, TextInput } from '@mantine/core';
import { IconDownload } from '@tabler/icons-react';
import BackButton from '../../desa/layanan/_com/BackButto';
import { useRouter } from 'next/navigation';
import { useState } from 'react';
import { useProxy } from 'valtio/utils';
import BackButton from '../../desa/layanan/_com/BackButto';
import JenisInformasiSelector from './jenis_infromasi/jenisInformasiSelector';
import MemperolehInformasi from './memperoleh_informasi/memperolehInfromasi';
import MemperolehSalinan from './salinan_informasi/salinanInformasi';
const data = [
{
id: 1,
number: '1',
title: "Langkah 1",
desc: "Pemohon informasi publik mengajukan permohonan informasi kepada badan publik baik langsung maupun melalui surat elektronik",
},
{
id: 2,
number: '2',
title: "Langkah 2",
desc: "Isi formulir permohonan informasi dengan data diri (nama, alamat, telepon), jenis, format, dan cara penyampaian informasi, serta lampiran fotokopi kartu identitas.",
},
{
id: 3,
number: '3',
title: "Langkah 3",
desc: "PPID akan memproses permohonan sesuai dengan ketentuan",
},
{
id: 4,
number: '4',
title: "Langkah 4",
desc: "Petugas PPID menyampaikan informasi sesuai permohonan kepada pemohon informasi.",
},
]
{ id: 1, number: '1', title: "Langkah 1", desc: "Pemohon informasi publik mengajukan permohonan informasi kepada badan publik baik langsung maupun melalui surat elektronik" },
{ id: 2, number: '2', title: "Langkah 2", desc: "Isi formulir permohonan informasi dengan data diri (nama, alamat, telepon), jenis, format, dan cara penyampaian informasi, serta lampiran fotokopi kartu identitas." },
{ id: 3, number: '3', title: "Langkah 3", desc: "PPID akan memproses permohonan sesuai dengan ketentuan" },
{ id: 4, number: '4', title: "Langkah 4", desc: "Petugas PPID menyampaikan informasi sesuai permohonan kepada pemohon informasi." },
];
const jenisInformasi = [
{
id: 1,
jenis: 'Keuangan Desa'
},
{
id: 2,
jenis: 'Pembangunan Desa'
},
{
id: 3,
jenis: 'Data Demografi'
},
{
id: 4,
jenis: 'Lainnya'
},
]
const memperolehInformasi = [
{
id: 1,
jenis: 'Melihat/Membaca/Mendengarkan/Mencatat'
},
{
id: 2,
jenis: 'Mendapatkan Salinan Informasi (Hardcopy)'
},
{
id: 3,
jenis: 'Mendapatkan Salinan Informasi (Softcopy)'
}
]
const mendapatkanInformasi = [
{
id: 1,
jenis: 'Mengambil Langsung'
},
{
id: 2,
jenis: 'Dikirim via Post'
},
{
id: 3,
jenis: 'Dikirim via Email'
}
]
function Page() {
const combobox = useCombobox({
onDropdownClose: () => combobox.resetSelectedOption(),
});
const permohonanInformasiPublikState = useProxy(statePermohonanInformasi);
const router = useRouter();
const combobox2 = useCombobox({
onDropdownClose: () => combobox2.resetSelectedOption(),
});
const submitForms = () => {
const { create } = permohonanInformasiPublikState.statepermohonanInformasiPublik;
const combobox3 = useCombobox({
onDropdownClose: () => combobox3.resetSelectedOption(),
});
if (create.form.name && create.form.nik && create.form.notelp && create.form.alamat && create.form.email &&
create.form.jenisInformasiDimintaId && create.form.caraMemperolehInformasiId && create.form.caraMemperolehSalinanInformasiId) {
create.create();
router.push('/darmasaba/permohonan/berhasil');
} else {
console.log("Validasi gagal, form tidak lengkap");
// Display error message to user
}
};
const [value, setValue] = useState<string | null>(null);
const [value2, setValue2] = useState<string | null>(null);
const [value3, setValue3] = useState<string | null>(null);
const options = jenisInformasi.map((item) => (
<ComboboxOption value={item.jenis} key={item.id}>
{item.jenis}
</ComboboxOption>
))
const options2 = memperolehInformasi.map((item) => (
<ComboboxOption value={item.jenis} key={item.id}>
{item.jenis}
</ComboboxOption>
))
const options3 = mendapatkanInformasi.map((item) => (
<ComboboxOption value={item.jenis} key={item.id}>
{item.jenis}
</ComboboxOption>
))
const router = useRouter()
return (
<Stack pos={"relative"} bg={colors.Bg} py={"xl"} gap={"22"}>
<Box px={{ base: 'md', md: 100 }}>
@@ -130,33 +46,20 @@ function Page() {
<Stack gap={'lg'}>
<Paper p={"xl"} bg={colors['white-trans-1']}>
<Text pb={30} ta={'center'} fw={"bold"} fz={{ base: 'h3', md: 'h2' }}>Tata Cara Permohonan</Text>
<SimpleGrid
pb={30}
cols={{
base: 1,
md: 2,
lg: 3,
xl: 4
}}>
{data.map((v, k) => {
return (
<Paper key={k} p={"xl"} bg={colors['blue-button']}>
<Stack justify='space-between'>
<Center>
<ActionIcon bg={colors['white-1']} radius={150} size={50}>
<Text c={colors['blue-button']} ta={"center"} fw={"bold"} fz={{ base: "h3", md: "h2" }}>{v.number}</Text>
</ActionIcon>
</Center>
<Text ta={"center"} c={colors['white-1']} fw={"bold"} fz={"h3"}>
{v.title}
</Text>
<Text ta={"center"} c={colors['white-1']} fz={'h4'}>
{v.desc}
</Text>
</Stack>
</Paper>
)
})}
<SimpleGrid pb={30} cols={{ base: 1, md: 2, lg: 3, xl: 4 }}>
{data.map((v, k) => (
<Paper key={k} p={"xl"} bg={colors['blue-button']}>
<Stack justify='space-between'>
<Center>
<ActionIcon bg={colors['white-1']} radius={150} size={50}>
<Text c={colors['blue-button']} ta={"center"} fw={"bold"} fz={{ base: "h3", md: "h2" }}>{v.number}</Text>
</ActionIcon>
</Center>
<Text ta={"center"} c={colors['white-1']} fw={"bold"} fz={"h3"}>{v.title}</Text>
<Text ta={"center"} c={colors['white-1']} fz={'h4'}>{v.desc}</Text>
</Stack>
</Paper>
))}
</SimpleGrid>
<Center pb={30}>
<Button fz={"h5"} bg={colors['blue-button']} leftSection={<IconDownload size={20} color={colors['white-1']} />}>
@@ -165,132 +68,45 @@ function Page() {
</Center>
<Group justify='center'>
<Paper p={'xl'} bg={colors['white-1']}>
<Text fw={"bold"} fz={{ base: 'h4', md: 'h3' }} ta={"center"}>Formulir Permohonan Informasi</Text>
<TextInput
styles={{ input: { padding: 20 } }}
py={10}
placeholder='Nama Lengkap'
/>
<TextInput
styles={{ input: { padding: 20 } }}
placeholder='NIK'
/>
<TextInput
styles={{ input: { padding: 20 } }}
py={10}
placeholder='No.Telepon'
/>
<TextInput
styles={{ input: { padding: 20 } }}
placeholder="Alamat"
/>
<TextInput
py={10}
styles={{ input: { padding: 20 } }}
placeholder="Email"
/>
<TextInput
styles={{ input: { padding: 20 } }}
placeholder="Alamat"
/>
{/* ComboBox */}
<Box py={10}>
<Text fz={'sm'}>Jenis Informasi yang diminta</Text>
<Combobox
store={combobox}
withinPortal={false}
onOptionSubmit={(val) => {
setValue(val);
combobox.closeDropdown();
<Stack gap={"xs"}>
<Text fw={"bold"} fz={{ base: 'h4', md: 'h3' }} ta={"center"}>Formulir Permohonan Informasi</Text>
<TextInput label="Nama Lengkap" placeholder="masukkan nama lengkap" onChange={(val) => {
permohonanInformasiPublikState.statepermohonanInformasiPublik.create.form.name = val.target.value;
}} />
<TextInput label="NIK" placeholder="masukkan NIK" onChange={(val) => {
permohonanInformasiPublikState.statepermohonanInformasiPublik.create.form.nik = val.target.value;
}} />
<TextInput label="No.Telp" placeholder="masukkan no telp" onChange={(val) => {
permohonanInformasiPublikState.statepermohonanInformasiPublik.create.form.notelp = val.target.value;
}} />
<TextInput label="Alamat" placeholder="masukkan alamat" onChange={(val) => {
permohonanInformasiPublikState.statepermohonanInformasiPublik.create.form.alamat = val.target.value;
}} />
<TextInput label="Email" placeholder="masukkan email" onChange={(val) => {
permohonanInformasiPublikState.statepermohonanInformasiPublik.create.form.email = val.target.value;
}} />
<JenisInformasiSelector
onChange={(val) => {
permohonanInformasiPublikState.statepermohonanInformasiPublik.create.form.jenisInformasiDimintaId = val.id;
}}
>
<Combobox.Target>
<InputBase
component="button"
type="button"
pointer
rightSection={<Combobox.Chevron />}
onClick={() => combobox.toggleDropdown()}
rightSectionPointerEvents="none"
>
{value || <Input.Placeholder>--Pilih Jenis Informasi--</Input.Placeholder>}
</InputBase>
</Combobox.Target>
<Combobox.Dropdown>
<Combobox.Options>{options}</Combobox.Options>
</Combobox.Dropdown>
</Combobox>
</Box>
<Textarea
placeholder='Tujuan Penggunaan Informasi'
/>
{/* ComboBox2 */}
<Box py={10}>
<Text fz={'sm'}>Cara Memperoleh Informasi</Text>
<Combobox
store={combobox2}
withinPortal={false}
onOptionSubmit={(val) => {
setValue2(val);
combobox2.closeDropdown();
}}
>
<Combobox.Target>
<InputBase
component="button"
type="button"
pointer
rightSection={<Combobox.Chevron />}
onClick={() => combobox2.toggleDropdown()}
rightSectionPointerEvents="none"
>
{value2 || <Input.Placeholder>--Pilih Cara Memperoleh--</Input.Placeholder>}
</InputBase>
</Combobox.Target>
<Combobox.Dropdown>
<Combobox.Options>{options2}</Combobox.Options>
</Combobox.Dropdown>
</Combobox>
</Box>
{/* ComboBox3 */}
<Box pb={10}>
<Text fz={'sm'}>Cara Mendapatkan Salinan Informasi</Text>
<Combobox
store={combobox3}
withinPortal={false}
onOptionSubmit={(val) => {
setValue3(val);
combobox3.closeDropdown();
}}
>
<Combobox.Target>
<InputBase
component="button"
type="button"
pointer
rightSection={<Combobox.Chevron />}
onClick={() => combobox3.toggleDropdown()}
rightSectionPointerEvents="none"
>
{value3 || <Input.Placeholder>--Pilih Cara Mendapatkan--</Input.Placeholder>}
</InputBase>
</Combobox.Target>
<Combobox.Dropdown>
<Combobox.Options>{options3}</Combobox.Options>
</Combobox.Dropdown>
</Combobox>
</Box>
<Box pb={10}>
<Checkbox
label="Saya menyatakan bahwa informasi yang saya berikan adalah benar dan akan menggunakan informasi yang diminta"
/>
</Box>
<Button onClick={() => router.push('/darmasaba/permohonan/berhasil')} bg={"green"} fullWidth>
Kirim Permohonan
</Button>
<MemperolehInformasi
onChange={(val) => {
permohonanInformasiPublikState.statepermohonanInformasiPublik.create.form.caraMemperolehInformasiId = val.id;
}}
/>
<MemperolehSalinan
onChange={(val) => {
permohonanInformasiPublikState.statepermohonanInformasiPublik.create.form.caraMemperolehSalinanInformasiId = val.id;
}}
/>
<Box py={10}>
<Checkbox label="Saya menyatakan bahwa informasi yang saya berikan adalah benar dan akan menggunakan informasi yang diminta" />
</Box>
<Group>
<Button bg={colors['blue-button']} onClick={submitForms}>Kirim Permohonan</Button>
</Group>
</Stack>
</Paper>
</Group>
</Paper>
@@ -300,4 +116,4 @@ function Page() {
);
}
export default Page;
export default Page;

View File

@@ -0,0 +1,43 @@
import statePermohonanInformasi from '@/app/admin/(dashboard)/_state/ppid/permohonan_informasi_publik/permohonanInformasiPublik';
import { Skeleton, Group, Select } from '@mantine/core';
import { useShallowEffect } from '@mantine/hooks';
import { Prisma } from '@prisma/client';
import React from 'react';
import { useProxy } from 'valtio/utils';
function MemperolehSalinan({ onChange }: {
onChange: (value: Prisma.CaraMemperolehSalinanInformasiGetPayload<{
select: {
id: true,
name: true
}
}>) => void
}) {
const memperolehSalinanInformasiState = useProxy(statePermohonanInformasi)
useShallowEffect(() => {
memperolehSalinanInformasiState.caraMemperolehSalinanInformasi.findMany.load()
}, [])
if (!memperolehSalinanInformasiState.caraMemperolehSalinanInformasi.findMany.data) return <Skeleton h={40} />
const data = memperolehSalinanInformasiState.caraMemperolehSalinanInformasi.findMany.data
if (!data) return <Skeleton h={40} />
return (
<Group>
<Select
placeholder='pilih cara memperoleh salinan informasi'
label={'Cara Memperoleh Salinan Informasi'}
data={data.map((item) => ({
value: item.id,
label: item.name,
}))}
onChange={(v) => {
const selectedData = data.find((item) => item.id === v);
if (selectedData) onChange(selectedData);
}}
/>
</Group>
);
}
export default MemperolehSalinan;

View File

@@ -1,9 +1,12 @@
'use client'
import permohonanKeberatanInformasi from '@/app/admin/(dashboard)/_state/ppid/permohonan_keberatan_informasi_publik/permohonanKeberatanInformasi';
import { PPIDTextEditor } from '@/app/admin/(dashboard)/ppid/_com/PPIDTextEditor';
import colors from '@/con/colors';
import { Box, Button, Center, Group, Paper, SimpleGrid, Stack, Text, Textarea, TextInput } from '@mantine/core';
import { Box, Button, Center, Group, Paper, SimpleGrid, Stack, Text, TextInput } from '@mantine/core';
import { IconFileCheck, IconForms, IconHourglassOff, IconPhoneRinging } from '@tabler/icons-react';
import BackButton from '../../desa/layanan/_com/BackButto';
import { useRouter } from 'next/navigation';
import { useProxy } from 'valtio/utils';
import BackButton from '../../desa/layanan/_com/BackButto';
const data = [
{
@@ -32,6 +35,15 @@ const data = [
},
]
function Page() {
const stateKeberatan = useProxy(permohonanKeberatanInformasi)
const submit = () => {
if (stateKeberatan.create.form.name && stateKeberatan.create.form.email && stateKeberatan.create.form.notelp && stateKeberatan.create.form.alasan) {
stateKeberatan.create.create()
router.push('/darmasaba/permohonan/berhasil')
} else {
console.log("Validasi gagal, form tidak lengkap")
}
}
const router = useRouter();
return (
<Stack pos={"relative"} bg={colors.Bg} py={"xl"} gap={"22"}>
@@ -76,33 +88,41 @@ function Page() {
</SimpleGrid>
<Group justify='center'>
<Paper p={'xl'} bg={colors['white-1']}>
<Text fw={"bold"} fz={{ base: 'h4', md: 'h3' }} ta={"center"}>Formulir Permohonan Keberatan</Text>
<TextInput
label='Nama'
styles={{ input: { padding: 20 } }}
py={10}
placeholder='Masukkan nama lengkap'
/>
<TextInput
label='Email'
styles={{ input: { padding: 20 } }}
placeholder='Masukkan email'
/>
<TextInput
label='Nomor Telepon'
styles={{ input: { padding: 20 } }}
py={10}
placeholder='Masukkan nomor telepon'
/>
<Textarea
pb={20}
label='Alasan Keberatan'
styles={{ input: { padding: 20 } }}
placeholder="Alasan keberatan"
/>
<Button onClick={() => router.push('/darmasaba/permohonan/berhasil')} bg={"green"} fullWidth>
Kirim Permohonan
</Button>
<Stack gap={"xs"}>
<Text fw={"bold"} fz={{ base: 'h4', md: 'h3' }} ta={"center"}>Formulir Permohonan Keberatan</Text>
<TextInput
label="Nama"
placeholder="masukkan nama lengkap"
onChange={(val) => {
stateKeberatan.create.form.name = val.target.value
}}
/>
<TextInput
label="Email"
placeholder="masukkan email"
onChange={(val) => {
stateKeberatan.create.form.email = val.target.value
}}
/>
<TextInput
label="Nomor Telepon"
placeholder="masukkan nomor telepon"
onChange={(val) => {
stateKeberatan.create.form.notelp = val.target.value
}}
/>
<Text fz={"sm"}>Alasan Permohonan Keberatan</Text>
<PPIDTextEditor
showSubmit={false}
onChange={(val) => {
stateKeberatan.create.form.alasan = val
}}
/>
<Group>
<Button onClick={submit} bg={'green'}>Kirim Permohonan</Button>
</Group>
</Stack>
</Paper>
</Group>
<Text pt={20} ta={"center"} fz={'h3'} fw={"bold"}>Kontak PPID</Text>