Compare commits

...

6 Commits

37 changed files with 1694 additions and 999 deletions

View File

@@ -223,7 +223,7 @@ model KategoriPrestasiDesa {
model Responden { model Responden {
id String @id @default(cuid()) id String @id @default(cuid())
name String @unique name String @unique
tanggal DateTime // misal: 2025-05-01 tanggal String // misal: 2025-05-01
jenisKelamin JenisKelaminResponden @relation(fields: [jenisKelaminId], references: [id]) jenisKelamin JenisKelaminResponden @relation(fields: [jenisKelaminId], references: [id])
jenisKelaminId String jenisKelaminId String
rating PilihanRatingResponden @relation(fields: [ratingId], references: [id]) rating PilihanRatingResponden @relation(fields: [ratingId], references: [id])

View File

@@ -1,3 +1,4 @@
/* eslint-disable @typescript-eslint/no-explicit-any */
import ApiFetch from "@/lib/api-fetch"; import ApiFetch from "@/lib/api-fetch";
import { Prisma } from "@prisma/client"; import { Prisma } from "@prisma/client";
import { toast } from "react-toastify"; import { toast } from "react-toastify";
@@ -61,13 +62,39 @@ const lowonganKerjaState = proxy({
findMany: { findMany: {
data: null as data: null as
| Prisma.LowonganPekerjaanGetPayload<{ | Prisma.LowonganPekerjaanGetPayload<{
omit: { isActive: true }; omit: {
isActive: true;
};
}>[] }>[]
| null, | null,
async load() { page: 1,
const res = await ApiFetch.api.ekonomi.lowongankerja["find-many"].get(); totalPages: 1,
if (res.status === 200) { loading: false,
lowonganKerjaState.findMany.data = res.data?.data ?? []; search: "",
load: async (page = 1, limit = 10, search = "") => {
lowonganKerjaState.findMany.loading = true; // ✅ Akses langsung via nama path
lowonganKerjaState.findMany.page = page;
lowonganKerjaState.findMany.search = search;
try {
const query: any = { page, limit };
if (search) query.search = search;
const res = await ApiFetch.api.ekonomi.lowongankerja["find-many"].get({ query });
if (res.status === 200 && res.data?.success) {
lowonganKerjaState.findMany.data = res.data.data ?? [];
lowonganKerjaState.findMany.totalPages = res.data.totalPages ?? 1;
} else {
lowonganKerjaState.findMany.data = [];
lowonganKerjaState.findMany.totalPages = 1;
}
} catch (err) {
console.error("Gagal fetch lowongan kerja paginated:", err);
lowonganKerjaState.findMany.data = [];
lowonganKerjaState.findMany.totalPages = 1;
} finally {
lowonganKerjaState.findMany.loading = false;
} }
}, },
}, },

View File

@@ -1,3 +1,4 @@
/* eslint-disable @typescript-eslint/no-explicit-any */
import ApiFetch from "@/lib/api-fetch"; import ApiFetch from "@/lib/api-fetch";
import { Prisma } from "@prisma/client"; import { Prisma } from "@prisma/client";
import { toast } from "react-toastify"; import { toast } from "react-toastify";
@@ -53,22 +54,47 @@ const pasarDesa = proxy({
}, },
}, },
findMany: { findMany: {
data: null as Array< data: null as
Prisma.PasarDesaGetPayload<{ | Prisma.PasarDesaGetPayload<{
include: { include: {
image: true; image: true;
KategoriToPasar: { KategoriToPasar: {
include: { include: {
kategori: true; kategori: true;
};
}; };
}; };
}; }>[]
}> | null,
> | null, page: 1,
async load() { totalPages: 1,
const res = await ApiFetch.api.ekonomi.pasardesa["find-many"].get(); loading: false,
if (res.status === 200) { search: "",
pasarDesa.findMany.data = res.data?.data ?? []; load: async (page = 1, limit = 10, search = "", categoryId?: string) => {
pasarDesa.findMany.loading = true;
pasarDesa.findMany.page = page;
pasarDesa.findMany.search = search;
try {
const query: any = { page, limit };
if (search) query.search = search;
if (categoryId) query.categoryId = categoryId;
const res = await ApiFetch.api.ekonomi.pasardesa["find-many"].get({ query });
if (res.status === 200 && res.data?.success) {
pasarDesa.findMany.data = res.data.data ?? [];
pasarDesa.findMany.totalPages = res.data.totalPages ?? 1;
} else {
pasarDesa.findMany.data = [];
pasarDesa.findMany.totalPages = 1;
}
} catch (err) {
console.error("Gagal fetch keamanan lingkungan paginated:", err);
pasarDesa.findMany.data = [];
pasarDesa.findMany.totalPages = 1;
} finally {
pasarDesa.findMany.loading = false;
} }
}, },
}, },
@@ -272,14 +298,41 @@ const kategoriProduk = proxy({
}, },
}, },
findMany: { findMany: {
data: null as Array<{ data: null as
id: string; | Prisma.KategoriProdukGetPayload<{
nama: string; omit: {
}> | null, isActive: true;
async load() { };
const res = await ApiFetch.api.ekonomi.kategoriproduk["find-many"].get(); }>[]
if (res.status === 200) { | null,
kategoriProduk.findMany.data = res.data?.data ?? []; page: 1,
totalPages: 1,
loading: false,
search2: "",
load: async (page = 1, limit = 10, search2 = "") => {
kategoriProduk.findMany.loading = true; // ✅ Akses langsung via nama path
kategoriProduk.findMany.page = page;
kategoriProduk.findMany.search2 = search2;
try {
const query: any = { page, limit };
if (search2) query.search2 = search2;
const res = await ApiFetch.api.ekonomi.kategoriproduk["find-many"].get({ query });
if (res.status === 200 && res.data?.success) {
kategoriProduk.findMany.data = res.data.data ?? [];
kategoriProduk.findMany.totalPages = res.data.totalPages ?? 1;
} else {
kategoriProduk.findMany.data = [];
kategoriProduk.findMany.totalPages = 1;
}
} catch (err) {
console.error("Gagal fetch kategori produk paginated:", err);
kategoriProduk.findMany.data = [];
kategoriProduk.findMany.totalPages = 1;
} finally {
kategoriProduk.findMany.loading = false;
} }
}, },
}, },

View File

@@ -1,3 +1,4 @@
/* eslint-disable @typescript-eslint/no-explicit-any */
import ApiFetch from "@/lib/api-fetch"; import ApiFetch from "@/lib/api-fetch";
import { Prisma } from "@prisma/client"; import { Prisma } from "@prisma/client";
import { toast } from "react-toastify"; import { toast } from "react-toastify";
@@ -53,15 +54,39 @@ const keamananLingkunganState = proxy({
findMany: { findMany: {
data: null as data: null as
| Prisma.KeamananLingkunganGetPayload<{ | Prisma.KeamananLingkunganGetPayload<{
include: { image: true }; include: {
image: true;
};
}>[] }>[]
| null, | null,
async load() { page: 1,
const res = await ApiFetch.api.keamanan.keamananlingkungan[ totalPages: 1,
"find-many" loading: false,
].get(); search: "",
if (res.status === 200) { load: async (page = 1, limit = 10, search = "") => {
keamananLingkunganState.findMany.data = res.data?.data ?? []; keamananLingkunganState.findMany.loading = true; // ✅ Akses langsung via nama path
keamananLingkunganState.findMany.page = page;
keamananLingkunganState.findMany.search = search;
try {
const query: any = { page, limit };
if (search) query.search = search;
const res = await ApiFetch.api.keamanan.keamananlingkungan["find-many"].get({ query });
if (res.status === 200 && res.data?.success) {
keamananLingkunganState.findMany.data = res.data.data ?? [];
keamananLingkunganState.findMany.totalPages = res.data.totalPages ?? 1;
} else {
keamananLingkunganState.findMany.data = [];
keamananLingkunganState.findMany.totalPages = 1;
}
} catch (err) {
console.error("Gagal fetch keamanan lingkungan paginated:", err);
keamananLingkunganState.findMany.data = [];
keamananLingkunganState.findMany.totalPages = 1;
} finally {
keamananLingkunganState.findMany.loading = false;
} }
}, },
}, },

View File

@@ -1,3 +1,4 @@
/* eslint-disable @typescript-eslint/no-explicit-any */
import ApiFetch from "@/lib/api-fetch"; import ApiFetch from "@/lib/api-fetch";
import { Prisma } from "@prisma/client"; import { Prisma } from "@prisma/client";
import { toast } from "react-toastify"; import { toast } from "react-toastify";
@@ -63,13 +64,41 @@ const polsekTerdekatState = proxy({
findMany: { findMany: {
data: null as data: null as
| Prisma.PolsekTerdekatGetPayload<{ | Prisma.PolsekTerdekatGetPayload<{
include: { layananPolsek: true }; include: {
layananPolsek: true;
};
}>[] }>[]
| null, | null,
async load() { page: 1,
const res = await ApiFetch.api.keamanan.polsekterdekat["find-many"].get(); totalPages: 1,
if (res.status === 200) { loading: false,
polsekTerdekatState.findMany.data = res.data?.data ?? []; search: "",
load: async (page = 1, limit = 10, search = "") => {
polsekTerdekatState.findMany.loading = true; // ✅ Akses langsung via nama path
polsekTerdekatState.findMany.page = page;
polsekTerdekatState.findMany.search = search;
try {
const query: any = { page, limit };
if (search) query.search = search;
const res = await ApiFetch.api.keamanan.polsekterdekat["find-many"].get(
{ query }
);
if (res.status === 200 && res.data?.success) {
polsekTerdekatState.findMany.data = res.data.data ?? [];
polsekTerdekatState.findMany.totalPages = res.data.totalPages ?? 1;
} else {
polsekTerdekatState.findMany.data = [];
polsekTerdekatState.findMany.totalPages = 1;
}
} catch (err) {
console.error("Gagal fetch polsek terdekat paginated:", err);
polsekTerdekatState.findMany.data = [];
polsekTerdekatState.findMany.totalPages = 1;
} finally {
polsekTerdekatState.findMany.loading = false;
} }
}, },
}, },
@@ -237,6 +266,29 @@ const polsekTerdekatState = proxy({
polsekTerdekatState.edit.form = { ...defaultForm }; polsekTerdekatState.edit.form = { ...defaultForm };
}, },
}, },
findFirst: {
data: null as Prisma.PolsekTerdekatGetPayload<{
include: {
layananPolsek: true;
};
}> | null,
loading: false,
load: async () => { // Changed to arrow function
polsekTerdekatState.findFirst.loading = true;
try {
const res = await ApiFetch.api.keamanan.polsekterdekat["find-first"].get();
if (res.status === 200 && res.data?.success) {
polsekTerdekatState.findFirst.data = res.data.data || null;
} else {
polsekTerdekatState.findFirst.data = null;
}
} catch (err) {
console.error("Gagal fetch polsek terdekat terbaru:", err);
} finally {
polsekTerdekatState.findFirst.loading = false;
}
}
}
}); });
export default polsekTerdekatState; export default polsekTerdekatState;

View File

@@ -1,3 +1,4 @@
/* eslint-disable @typescript-eslint/no-explicit-any */
import ApiFetch from "@/lib/api-fetch"; import ApiFetch from "@/lib/api-fetch";
import { Prisma } from "@prisma/client"; import { Prisma } from "@prisma/client";
import { toast } from "react-toastify"; import { toast } from "react-toastify";
@@ -53,15 +54,39 @@ const tipsKeamananState = proxy({
findMany: { findMany: {
data: null as data: null as
| Prisma.MenuTipsKeamananGetPayload<{ | Prisma.MenuTipsKeamananGetPayload<{
include: { image: true }; include: {
image: true;
};
}>[] }>[]
| null, | null,
async load() { page: 1,
const res = await ApiFetch.api.keamanan.menutipskeamanan[ totalPages: 1,
"find-many" loading: false,
].get(); search: "",
if (res.status === 200) { load: async (page = 1, limit = 10, search = "") => {
tipsKeamananState.findMany.data = res.data?.data ?? []; tipsKeamananState.findMany.loading = true; // ✅ Akses langsung via nama path
tipsKeamananState.findMany.page = page;
tipsKeamananState.findMany.search = search;
try {
const query: any = { page, limit };
if (search) query.search = search;
const res = await ApiFetch.api.keamanan.menutipskeamanan["find-many"].get({ query });
if (res.status === 200 && res.data?.success) {
tipsKeamananState.findMany.data = res.data.data ?? [];
tipsKeamananState.findMany.totalPages = res.data.totalPages ?? 1;
} else {
tipsKeamananState.findMany.data = [];
tipsKeamananState.findMany.totalPages = 1;
}
} catch (err) {
console.error("Gagal fetch menu tips keamanan paginated:", err);
tipsKeamananState.findMany.data = [];
tipsKeamananState.findMany.totalPages = 1;
} finally {
tipsKeamananState.findMany.loading = false;
} }
}, },
}, },

View File

@@ -181,7 +181,13 @@ const responden = proxy({
headers: { headers: {
"Content-Type": "application/json", "Content-Type": "application/json",
}, },
body: JSON.stringify(this.form), body: JSON.stringify({
name: this.form.name,
tanggal: this.form.tanggal,
jenisKelaminId: this.form.jenisKelaminId,
ratingId: this.form.ratingId,
kelompokUmurId: this.form.kelompokUmurId,
}),
}); });
const result = await response.json(); const result = await response.json();
if (!response.ok || !result?.success) { if (!response.ok || !result?.success) {

View File

@@ -49,35 +49,38 @@ const daftarInformasiPublik = proxy({
}, },
}, },
findMany: { findMany: {
data: null as any[] | null, data: null as
| Prisma.DaftarInformasiPublikGetPayload<{
omit: {
isActive: true;
};
}>[]
| null,
page: 1, page: 1,
totalPages: 1, totalPages: 1,
total: 0,
loading: false, loading: false,
load: async (page = 1, limit = 10) => { // Change to arrow function search: "",
daftarInformasiPublik.findMany.loading = true; // Use the full path to access the property load: async (page = 1, limit = 10, search = "") => {
daftarInformasiPublik.findMany.loading = true; // ✅ Akses langsung via nama path
daftarInformasiPublik.findMany.page = page; daftarInformasiPublik.findMany.page = page;
try { daftarInformasiPublik.findMany.search = search;
const res = await ApiFetch.api.ppid.daftarinformasipublik[
"find-many"
].get({
query: { page, limit },
});
try {
const query: any = { page, limit };
if (search) query.search = search;
const res = await ApiFetch.api.ppid.daftarinformasipublik["find-many"].get({ query });
if (res.status === 200 && res.data?.success) { if (res.status === 200 && res.data?.success) {
daftarInformasiPublik.findMany.data = res.data.data || []; daftarInformasiPublik.findMany.data = res.data.data ?? [];
daftarInformasiPublik.findMany.total = res.data.total || 0; daftarInformasiPublik.findMany.totalPages = res.data.totalPages ?? 1;
daftarInformasiPublik.findMany.totalPages = res.data.totalPages || 1;
} else { } else {
console.error("Failed to load daftar informasi publik:", res.data?.message);
daftarInformasiPublik.findMany.data = []; daftarInformasiPublik.findMany.data = [];
daftarInformasiPublik.findMany.total = 0;
daftarInformasiPublik.findMany.totalPages = 1; daftarInformasiPublik.findMany.totalPages = 1;
} }
} catch (error) { } catch (err) {
console.error("Error loading daftar informasi publik:", error); console.error("Gagal fetch daftar informasi publik paginated:", err);
daftarInformasiPublik.findMany.data = []; daftarInformasiPublik.findMany.data = [];
daftarInformasiPublik.findMany.total = 0;
daftarInformasiPublik.findMany.totalPages = 1; daftarInformasiPublik.findMany.totalPages = 1;
} finally { } finally {
daftarInformasiPublik.findMany.loading = false; daftarInformasiPublik.findMany.loading = false;

View File

@@ -55,17 +55,17 @@ function EditLowonganKerja() {
const handleSubmit = async () => { const handleSubmit = async () => {
try { try {
lowonganKerjaState.update.form = { // Set the ID for the update
...lowonganKerjaState.update.form, lowonganState.update.id = params?.id as string;
posisi: formData.posisi,
namaPerusahaan: formData.namaPerusahaan, // Update the form state
lokasi: formData.lokasi, lowonganState.update.form = {
tipePekerjaan: formData.tipePekerjaan, ...lowonganState.update.form,
gaji: formData.gaji, ...formData
deskripsi: formData.deskripsi, };
kualifikasi: formData.kualifikasi,
} // Call the update function
await lowonganState.update.update() await lowonganState.update.update();
toast.success("Lowongan kerja berhasil diperbarui!"); toast.success("Lowongan kerja berhasil diperbarui!");
router.push("/admin/ekonomi/lowongan-kerja-lokal"); router.push("/admin/ekonomi/lowongan-kerja-lokal");
} catch (error) { } catch (error) {
@@ -88,7 +88,7 @@ function EditLowonganKerja() {
<TextInput <TextInput
value={formData.posisi} value={formData.posisi}
onChange={(val) => { onChange={(val) => {
formData.posisi = val.target.value; setFormData(prev => ({ ...prev, posisi: val.target.value }));
}} }}
label={<Text fw={"bold"} fz={"sm"}>Posisi</Text>} label={<Text fw={"bold"} fz={"sm"}>Posisi</Text>}
placeholder='Masukkan posisi' placeholder='Masukkan posisi'
@@ -96,7 +96,7 @@ function EditLowonganKerja() {
<TextInput <TextInput
value={formData.namaPerusahaan} value={formData.namaPerusahaan}
onChange={(val) => { onChange={(val) => {
formData.namaPerusahaan = val.target.value; setFormData(prev => ({ ...prev, namaPerusahaan: val.target.value }));
}} }}
label={<Text fw={"bold"} fz={"sm"}>Nama Perusahaan</Text>} label={<Text fw={"bold"} fz={"sm"}>Nama Perusahaan</Text>}
placeholder='Masukkan nama perusahaan' placeholder='Masukkan nama perusahaan'
@@ -104,7 +104,7 @@ function EditLowonganKerja() {
<TextInput <TextInput
value={formData.lokasi} value={formData.lokasi}
onChange={(val) => { onChange={(val) => {
formData.lokasi = val.target.value; setFormData(prev => ({ ...prev, lokasi: val.target.value }));
}} }}
label={<Text fw={"bold"} fz={"sm"}>Lokasi</Text>} label={<Text fw={"bold"} fz={"sm"}>Lokasi</Text>}
placeholder='Masukkan lokasi' placeholder='Masukkan lokasi'
@@ -112,7 +112,7 @@ function EditLowonganKerja() {
<TextInput <TextInput
value={formData.tipePekerjaan} value={formData.tipePekerjaan}
onChange={(val) => { onChange={(val) => {
formData.tipePekerjaan = val.target.value; setFormData(prev => ({ ...prev, tipePekerjaan: val.target.value }));
}} }}
label={<Text fw={"bold"} fz={"sm"}>Tipe Pekerjaan</Text>} label={<Text fw={"bold"} fz={"sm"}>Tipe Pekerjaan</Text>}
placeholder='Masukkan tipe pekerjaan' placeholder='Masukkan tipe pekerjaan'
@@ -120,7 +120,7 @@ function EditLowonganKerja() {
<TextInput <TextInput
value={formData.gaji} value={formData.gaji}
onChange={(val) => { onChange={(val) => {
formData.gaji = val.target.value; setFormData(prev => ({ ...prev, gaji: val.target.value }));
}} }}
label={<Text fw={"bold"} fz={"sm"}>Gaji selama 1 bulan</Text>} label={<Text fw={"bold"} fz={"sm"}>Gaji selama 1 bulan</Text>}
placeholder='Masukkan gaji' placeholder='Masukkan gaji'
@@ -130,7 +130,7 @@ function EditLowonganKerja() {
<EditEditor <EditEditor
value={formData.deskripsi} value={formData.deskripsi}
onChange={(val) => { onChange={(val) => {
formData.deskripsi = val; setFormData(prev => ({ ...prev, deskripsi: val }));
}} }}
/> />
</Box> </Box>
@@ -139,7 +139,7 @@ function EditLowonganKerja() {
<EditEditor <EditEditor
value={formData.kualifikasi} value={formData.kualifikasi}
onChange={(val) => { onChange={(val) => {
formData.kualifikasi = val; setFormData(prev => ({ ...prev, kualifikasi: val }));
}} }}
/> />
</Box> </Box>

View File

@@ -1,6 +1,6 @@
'use client' 'use client'
import colors from '@/con/colors'; import colors from '@/con/colors';
import { Box, Button, Paper, Skeleton, Stack, Table, TableTbody, TableTd, TableTh, TableThead, TableTr } from '@mantine/core'; import { Box, Button, Center, Pagination, Paper, Skeleton, Stack, Table, TableTbody, TableTd, TableTh, TableThead, TableTr, Text } from '@mantine/core';
import { IconDeviceImac, IconSearch } from '@tabler/icons-react'; import { IconDeviceImac, IconSearch } from '@tabler/icons-react';
import HeaderSearch from '../../_com/header'; import HeaderSearch from '../../_com/header';
import JudulList from '../../_com/judulList'; import JudulList from '../../_com/judulList';
@@ -30,20 +30,21 @@ function ListLowonganKerjaLokal({ search }: { search: string }) {
const lowonganState = useProxy(lowonganKerjaState) const lowonganState = useProxy(lowonganKerjaState)
const router = useRouter(); const router = useRouter();
const {
data,
page,
totalPages,
loading,
load,
} = lowonganState.findMany
useShallowEffect(() => { useShallowEffect(() => {
lowonganState.findMany.load(); load(page, 10, search)
}, []) }, [page, search])
const filteredData = (lowonganState.findMany.data || []).filter(item => { const filteredData = data || []
const keyword = search.toLowerCase();
return (
item.posisi.toLowerCase().includes(keyword) ||
item.namaPerusahaan.toLowerCase().includes(keyword) ||
item.lokasi.toLowerCase().includes(keyword)
);
});
if (!lowonganState.findMany.data) { if (loading || !data) {
return ( return (
<Stack py={10}> <Stack py={10}>
<Skeleton h={500} /> <Skeleton h={500} />
@@ -60,18 +61,24 @@ function ListLowonganKerjaLokal({ search }: { search: string }) {
<Table striped withTableBorder withRowBorders> <Table striped withTableBorder withRowBorders>
<TableThead> <TableThead>
<TableTr> <TableTr>
<TableTh>Bekerja Sebagai</TableTh> <TableTh>Pekerjaan</TableTh>
<TableTh>Nama Usaha</TableTh> <TableTh>Nama Perusahaan</TableTh>
<TableTh>Alamat Usaha</TableTh> <TableTh>Lokasi</TableTh>
<TableTh>Detail</TableTh> <TableTh>Detail</TableTh>
</TableTr> </TableTr>
</TableThead> </TableThead>
<TableTbody> <TableTbody>
{filteredData.map((item) => ( {filteredData.map((item) => (
<TableTr key={item.id}> <TableTr key={item.id}>
<TableTd>{item.posisi}</TableTd> <TableTd>
<TableTd>{item.namaPerusahaan}</TableTd> <Text fz={"md"}>{item.posisi}</Text>
<TableTd>{item.lokasi}</TableTd> </TableTd>
<TableTd>
<Text fz={"md"}>{item.namaPerusahaan}</Text>
</TableTd>
<TableTd>
<Text fz={"md"}>{item.lokasi}</Text>
</TableTd>
<TableTd> <TableTd>
<Button onClick={() => router.push(`/admin/ekonomi/lowongan-kerja-lokal/${item.id}`)}> <Button onClick={() => router.push(`/admin/ekonomi/lowongan-kerja-lokal/${item.id}`)}>
<IconDeviceImac size={20} /> <IconDeviceImac size={20} />
@@ -82,6 +89,14 @@ function ListLowonganKerjaLokal({ search }: { search: string }) {
</TableTbody> </TableTbody>
</Table> </Table>
</Paper> </Paper>
<Center>
<Pagination
value={page}
onChange={(newPage) => load(newPage)}
total={totalPages}
my="md"
/>
</Center>
</Box> </Box>
); );
} }

View File

@@ -1,6 +1,6 @@
'use client' 'use client'
import colors from '@/con/colors'; import colors from '@/con/colors';
import { Box, Button, Paper, Skeleton, Stack, Table, TableTbody, TableTd, TableTh, TableThead, TableTr } from '@mantine/core'; import { Box, Button, Center, Pagination, Paper, Skeleton, Stack, Table, TableTbody, TableTd, TableTh, TableThead, TableTr } from '@mantine/core';
import { useShallowEffect } from '@mantine/hooks'; import { useShallowEffect } from '@mantine/hooks';
import { IconEdit, IconSearch, IconX } from '@tabler/icons-react'; import { IconEdit, IconSearch, IconX } from '@tabler/icons-react';
import { useRouter } from 'next/navigation'; import { useRouter } from 'next/navigation';
@@ -13,31 +13,39 @@ import pasarDesaState from '../../../_state/ekonomi/pasar-desa/pasar-desa';
function PasarDesa() { function PasarDesa() {
const [search, setSearch] = useState("") const [search2, setSearch2] = useState("")
return ( return (
<Box> <Box>
<HeaderSearch <HeaderSearch
title='Kategori Produk' title='Kategori Produk'
placeholder='pencarian' placeholder='pencarian'
searchIcon={<IconSearch size={20} />} searchIcon={<IconSearch size={20} />}
value={search} value={search2}
onChange={(e) => setSearch(e.currentTarget.value)} onChange={(e) => setSearch2(e.currentTarget.value)}
/> />
<ListPasarDesa search={search} /> <ListPasarDesa search2={search2} />
</Box> </Box>
); );
} }
function ListPasarDesa({ search }: { search: string }) { function ListPasarDesa({ search2 }: { search2: string }) {
const statePasar = useProxy(pasarDesaState.kategoriProduk) const statePasar = useProxy(pasarDesaState.kategoriProduk)
const [modalHapus, setModalHapus] = useState(false) const [modalHapus, setModalHapus] = useState(false)
const [selectedId, setSelectedId] = useState<string | null>(null) const [selectedId, setSelectedId] = useState<string | null>(null)
// const params = useParams() // const params = useParams()
const router = useRouter() const router = useRouter()
const {
data,
page,
totalPages,
loading,
load,
} = statePasar.findMany
useShallowEffect(() => { useShallowEffect(() => {
statePasar.findMany.load() load(page, 10, search2)
}, []) }, [page, search2])
const handleHapus = () => { const handleHapus = () => {
if (selectedId) { if (selectedId) {
@@ -47,14 +55,9 @@ function ListPasarDesa({ search }: { search: string }) {
} }
} }
const filteredData = (statePasar.findMany.data || []).filter(item => { const filteredData = data || []
const keyword = search.toLowerCase();
return (
item.nama.toLowerCase().includes(keyword)
);
});
if (!statePasar.findMany.data) { if (loading || !data) {
return ( return (
<Stack py={10}> <Stack py={10}>
<Skeleton h={500} /> <Skeleton h={500} />
@@ -99,6 +102,14 @@ function ListPasarDesa({ search }: { search: string }) {
</TableTbody> </TableTbody>
</Table> </Table>
</Paper> </Paper>
<Center>
<Pagination
value={page}
onChange={(newPage) => load(newPage)}
total={totalPages}
my={"md"}
/>
</Center>
{/* Modal Konfirmasi Hapus */} {/* Modal Konfirmasi Hapus */}
<ModalKonfirmasiHapus <ModalKonfirmasiHapus
opened={modalHapus} opened={modalHapus}

View File

@@ -1,6 +1,6 @@
'use client' 'use client'
import colors from '@/con/colors'; import colors from '@/con/colors';
import { Box, Button, Paper, Skeleton, Stack, Table, TableTbody, TableTd, TableTh, TableThead, TableTr } from '@mantine/core'; import { Box, Button, Center, Pagination, Paper, Skeleton, Stack, Table, TableTbody, TableTd, TableTh, TableThead, TableTr } from '@mantine/core';
import { IconDeviceImac, IconSearch } from '@tabler/icons-react'; import { IconDeviceImac, IconSearch } from '@tabler/icons-react';
import { useShallowEffect } from '@mantine/hooks'; import { useShallowEffect } from '@mantine/hooks';
import { useRouter } from 'next/navigation'; import { useRouter } from 'next/navigation';
@@ -30,21 +30,21 @@ function ListPasarDesa({ search }: { search: string }) {
const statePasar = useProxy(pasarDesaState.pasarDesa) const statePasar = useProxy(pasarDesaState.pasarDesa)
const router = useRouter(); const router = useRouter();
const {
data,
page,
totalPages,
loading,
load,
} = statePasar.findMany
useShallowEffect(() => { useShallowEffect(() => {
statePasar.findMany.load() load(page, 10, search)
}, []) }, [page, search])
const filteredData = (statePasar.findMany.data || []).filter(item => { const filteredData = data || []
const keyword = search.toLowerCase();
return (
item.nama.toLowerCase().includes(keyword) ||
item.harga.toString().toLowerCase().includes(keyword) ||
item.rating.toString().toLowerCase().includes(keyword) ||
item.alamatUsaha.toLowerCase().includes(keyword)
);
});
if (!statePasar.findMany.data) { if (loading || !data) {
return ( return (
<Stack py={10}> <Stack py={10}>
<Skeleton h={500} /> <Skeleton h={500} />
@@ -86,6 +86,14 @@ function ListPasarDesa({ search }: { search: string }) {
</TableTbody> </TableTbody>
</Table> </Table>
</Paper> </Paper>
<Center>
<Pagination
value={page}
onChange={(newPage) => load(newPage)}
total={totalPages}
my={"md"}
/>
</Center>
</Box> </Box>
); );
} }

View File

@@ -54,6 +54,10 @@ function DetailKeamananLingkungan() {
{keamananState.findUnique.data ? ( {keamananState.findUnique.data ? (
<Paper key={keamananState.findUnique.data.id} bg={colors['BG-trans']} p={'md'}> <Paper key={keamananState.findUnique.data.id} bg={colors['BG-trans']} p={'md'}>
<Stack gap={"xs"}> <Stack gap={"xs"}>
<Box>
<Text fw={"bold"} fz={"lg"}>Gambar</Text>
<Image w={{ base: 150, md: 490}} src={keamananState.findUnique.data?.image?.link} alt="gambar" />
</Box>
<Box> <Box>
<Text fw={"bold"} fz={"lg"}>Judul Keamanan Lingkungan</Text> <Text fw={"bold"} fz={"lg"}>Judul Keamanan Lingkungan</Text>
<Text fz={"lg"}>{keamananState.findUnique.data?.name}</Text> <Text fz={"lg"}>{keamananState.findUnique.data?.name}</Text>
@@ -62,10 +66,6 @@ function DetailKeamananLingkungan() {
<Text fw={"bold"} fz={"lg"}>Deskripsi</Text> <Text fw={"bold"} fz={"lg"}>Deskripsi</Text>
<Text fz={"lg"} dangerouslySetInnerHTML={{ __html: keamananState.findUnique.data?.deskripsi }} /> <Text fz={"lg"} dangerouslySetInnerHTML={{ __html: keamananState.findUnique.data?.deskripsi }} />
</Box> </Box>
<Box>
<Text fw={"bold"} fz={"lg"}>Gambar</Text>
<Image w={{ base: 150, md: 150, lg: 150 }} src={keamananState.findUnique.data?.image?.link} alt="gambar" />
</Box>
<Flex gap={"xs"} mt={10}> <Flex gap={"xs"} mt={10}>
<Button <Button
onClick={() => { onClick={() => {

View File

@@ -1,6 +1,6 @@
'use client' 'use client'
import colors from '@/con/colors'; import colors from '@/con/colors';
import { Box, Button, Paper, Skeleton, Stack, Table, TableTbody, TableTd, TableTh, TableThead, TableTr, Text } from '@mantine/core'; import { Box, Button, Center, Pagination, Paper, Skeleton, Stack, Table, TableTbody, TableTd, TableTh, TableThead, TableTr, Text } from '@mantine/core';
import { IconDeviceImac, IconSearch } from '@tabler/icons-react'; import { IconDeviceImac, IconSearch } from '@tabler/icons-react';
import HeaderSearch from '../../_com/header'; import HeaderSearch from '../../_com/header';
import JudulList from '../../_com/judulList'; import JudulList from '../../_com/judulList';
@@ -30,19 +30,21 @@ function ListKeamananLingkungan({ search }: { search: string }) {
const keamananState = useProxy(keamananLingkunganState) const keamananState = useProxy(keamananLingkunganState)
const router = useRouter(); const router = useRouter();
const {
data,
page,
totalPages,
loading,
load,
} = keamananState.findMany;
useShallowEffect(() => { useShallowEffect(() => {
keamananState.findMany.load() load(page, 10, search)
}, []) }, [page, search])
const filteredData = (keamananState.findMany.data || []).filter(item => { const filteredData = data || []
const keyword = search.toLowerCase();
return (
item.name.toLowerCase().includes(keyword) ||
item.deskripsi.toLowerCase().includes(keyword)
);
});
if (!keamananState.findMany.data) { if (loading || !data) {
return ( return (
<Stack py={10}> <Stack py={10}>
<Skeleton h={500} /> <Skeleton h={500} />
@@ -67,9 +69,15 @@ function ListKeamananLingkungan({ search }: { search: string }) {
<TableTbody> <TableTbody>
{filteredData.map((item) => ( {filteredData.map((item) => (
<TableTr key={item.id}> <TableTr key={item.id}>
<TableTd>{item.name}</TableTd>
<TableTd> <TableTd>
<Text fz={"md"} dangerouslySetInnerHTML={{ __html: item.deskripsi }} /> <Box w={180}>
<Text fz={"md"} truncate={"end"} lineClamp={1}>{item.name}</Text>
</Box>
</TableTd>
<TableTd>
<Box w={250}>
<Text fz={"md"} truncate={"end"} lineClamp={1} dangerouslySetInnerHTML={{ __html: item.deskripsi }} />
</Box>
</TableTd> </TableTd>
<TableTd> <TableTd>
<Button onClick={() => router.push(`/admin/keamanan/keamanan-lingkungan-pecalang-patwal/${item.id}`)}> <Button onClick={() => router.push(`/admin/keamanan/keamanan-lingkungan-pecalang-patwal/${item.id}`)}>
@@ -81,6 +89,14 @@ function ListKeamananLingkungan({ search }: { search: string }) {
</TableTbody> </TableTbody>
</Table> </Table>
</Paper> </Paper>
<Center>
<Pagination
value={page}
onChange={(newPage) => load(newPage)}
total={totalPages}
my={"md"}
/>
</Center>
</Box> </Box>
); );
} }

View File

@@ -1,6 +1,6 @@
'use client' 'use client'
import colors from '@/con/colors'; import colors from '@/con/colors';
import { Box, Button, Paper, Skeleton, Stack, Table, TableTbody, TableTd, TableTh, TableThead, TableTr } from '@mantine/core'; import { Box, Button, Center, Pagination, Paper, Skeleton, Stack, Table, TableTbody, TableTd, TableTh, TableThead, TableTr, Text } from '@mantine/core';
import { useShallowEffect } from '@mantine/hooks'; import { useShallowEffect } from '@mantine/hooks';
import { IconDeviceImac, IconSearch } from '@tabler/icons-react'; import { IconDeviceImac, IconSearch } from '@tabler/icons-react';
import { useRouter } from 'next/navigation'; import { useRouter } from 'next/navigation';
@@ -21,7 +21,7 @@ function PolsekTerdekat() {
value={search} value={search}
onChange={(e) => setSearch(e.currentTarget.value)} onChange={(e) => setSearch(e.currentTarget.value)}
/> />
<ListPolsekTerdekat search={search}/> <ListPolsekTerdekat search={search} />
</Box> </Box>
); );
} }
@@ -30,20 +30,21 @@ function ListPolsekTerdekat({ search }: { search: string }) {
const polsekState = useProxy(polsekTerdekat) const polsekState = useProxy(polsekTerdekat)
const router = useRouter(); const router = useRouter();
const {
data,
page,
totalPages,
loading,
load,
} = polsekState.findMany;
useShallowEffect(() => { useShallowEffect(() => {
polsekState.findMany.load() load(page, 10, search)
}, []) }, [page, search])
const filteredData = (polsekState.findMany.data || []).filter(item => { const filteredData = data || []
const keyword = search.toLowerCase();
return (
item.nama.toLowerCase().includes(keyword) ||
item.jarakKeDesa.toLowerCase().includes(keyword) ||
item.alamat.toLowerCase().includes(keyword)
);
});
if (!polsekState.findMany.data) { if (loading || !data) {
return ( return (
<Stack py={10}> <Stack py={10}>
<Skeleton h={500} /> <Skeleton h={500} />
@@ -64,24 +65,44 @@ function ListPolsekTerdekat({ search }: { search: string }) {
<TableTh>Jarak Polsek</TableTh> <TableTh>Jarak Polsek</TableTh>
<TableTh>Alamat</TableTh> <TableTh>Alamat</TableTh>
<TableTh>Detail</TableTh> <TableTh>Detail</TableTh>
</TableTr> </TableTr>
</TableThead> </TableThead>
<TableTbody> <TableTbody>
{filteredData.map((item) => ( {filteredData.map((item) => (
<TableTr key={item.id}> <TableTr key={item.id}>
<TableTd>{item.nama}</TableTd> <TableTd>
<TableTd>{item.jarakKeDesa}</TableTd> <Box w={180}>
<TableTd>{item.alamat}</TableTd> <Text fz='md' truncate={"end"} lineClamp={1}>{item.nama}</Text>
</Box>
</TableTd>
<TableTd>
<Box w={180}>
<Text fz='md' truncate={"end"} lineClamp={1}>{item.jarakKeDesa}</Text>
</Box>
</TableTd>
<TableTd>
<Box w={250}>
<Text fz='md' truncate={"end"} lineClamp={1}>{item.alamat}</Text>
</Box>
</TableTd>
<TableTd> <TableTd>
<Button onClick={() => router.push(`/admin/keamanan/polsek-terdekat/${item.id}`)}> <Button onClick={() => router.push(`/admin/keamanan/polsek-terdekat/${item.id}`)}>
<IconDeviceImac size={20} /> <IconDeviceImac size={20} />
</Button> </Button>
</TableTd> </TableTd>
</TableTr> </TableTr>
))} ))}
</TableTbody> </TableTbody>
</Table> </Table>
</Paper> </Paper>
<Center>
<Pagination
value={page}
onChange={(newPage) => load(newPage)}
total={totalPages}
my="md"
/>
</Center>
</Box> </Box>
); );
} }

View File

@@ -1,6 +1,6 @@
'use client' 'use client'
import colors from '@/con/colors'; import colors from '@/con/colors';
import { Box, Button, Paper, Skeleton, Stack, Table, TableTbody, TableTd, TableTh, TableThead, TableTr, Text } from '@mantine/core'; import { Box, Button, Center, Pagination, Paper, Skeleton, Stack, Table, TableTbody, TableTd, TableTh, TableThead, TableTr, Text } from '@mantine/core';
import { IconDeviceImac, IconSearch } from '@tabler/icons-react'; import { IconDeviceImac, IconSearch } from '@tabler/icons-react';
import HeaderSearch from '../../_com/header'; import HeaderSearch from '../../_com/header';
import JudulList from '../../_com/judulList'; import JudulList from '../../_com/judulList';
@@ -30,19 +30,21 @@ function ListTipsKeamanan({ search }: { search: string }) {
const stateKeamanan = useProxy(tipsKeamananState) const stateKeamanan = useProxy(tipsKeamananState)
const router = useRouter(); const router = useRouter();
const {
data,
page,
totalPages,
loading,
load,
} = stateKeamanan.findMany
useShallowEffect(() => { useShallowEffect(() => {
stateKeamanan.findMany.load() load(page, 10, search)
}, []) }, [page, search])
const filteredData = (stateKeamanan.findMany.data || []).filter(item => { const filteredData = data || []
const keyword = search.toLowerCase();
return (
item.judul.toLowerCase().includes(keyword) ||
item.deskripsi.toLowerCase().includes(keyword)
);
});
if (!stateKeamanan.findMany.data) { if (loading || !data) {
return ( return (
<Stack py={10}> <Stack py={10}>
<Skeleton h={500} /> <Skeleton h={500} />
@@ -67,9 +69,15 @@ function ListTipsKeamanan({ search }: { search: string }) {
<TableTbody> <TableTbody>
{filteredData.map((item) => ( {filteredData.map((item) => (
<TableTr key={item.id}> <TableTr key={item.id}>
<TableTd>{item.judul}</TableTd>
<TableTd> <TableTd>
<Text fz={"xs"} dangerouslySetInnerHTML={{__html: item.deskripsi}} /> <Box w={150}>
<Text fz={"md"} truncate={"end"} lineClamp={1}>{item.judul}</Text>
</Box>
</TableTd>
<TableTd>
<Box w={250}>
<Text fz={"md"} truncate={"end"} lineClamp={1} dangerouslySetInnerHTML={{__html: item.deskripsi}} />
</Box>
</TableTd> </TableTd>
<TableTd> <TableTd>
<Button onClick={() => router.push(`/admin/keamanan/tips-keamanan/${item.id}`)}> <Button onClick={() => router.push(`/admin/keamanan/tips-keamanan/${item.id}`)}>
@@ -81,6 +89,14 @@ function ListTipsKeamanan({ search }: { search: string }) {
</TableTbody> </TableTbody>
</Table> </Table>
</Paper> </Paper>
<Center>
<Pagination
value={page}
onChange={(newPage) => load(newPage)}
total={totalPages}
my="md"
/>
</Center>
</Box> </Box>
); );
} }

View File

@@ -106,8 +106,16 @@ function ListDaftarInformasi({ search }: { search: string }) {
{filteredData.map((item, index) => ( {filteredData.map((item, index) => (
<TableTr key={item.id}> <TableTr key={item.id}>
<TableTd style={{ textAlign: 'center' }}>{index + 1}</TableTd> <TableTd style={{ textAlign: 'center' }}>{index + 1}</TableTd>
<TableTd style={{ wordWrap: 'break-word' }}>{item.jenisInformasi}</TableTd> <TableTd style={{ wordWrap: 'break-word' }}>
<TableTd style={{ wordWrap: 'break-word' }} dangerouslySetInnerHTML={{ __html: item.deskripsi }}></TableTd> <Box w={200}>
<Text fz={"md"} truncate={"end"} lineClamp={1}>{item.jenisInformasi}</Text>
</Box>
</TableTd>
<TableTd style={{ wordWrap: 'break-word' }}>
<Box w={200}>
<Text fz={"md"} truncate={"end"} lineClamp={1} dangerouslySetInnerHTML={{ __html: item.deskripsi }}></Text>
</Box>
</TableTd>
<TableTd style={{ textAlign: 'center' }}> <TableTd style={{ textAlign: 'center' }}>
<Button bg={"green"} onClick={() => router.push(`/admin/ppid/daftar-informasi-publik-desa-darmasaba/${item.id}`)}> <Button bg={"green"} onClick={() => router.push(`/admin/ppid/daftar-informasi-publik-desa-darmasaba/${item.id}`)}>
<IconDeviceImacCog size={25} /> <IconDeviceImacCog size={25} />

View File

@@ -41,15 +41,11 @@ function EditResponden() {
try { try {
const data = await state.update.load(id); const data = await state.update.load(id);
if (data) { if (data) {
const formattedDate = data.tanggal && !isNaN(new Date(data.tanggal).getTime())
? new Date(data.tanggal).toISOString().split('T')[0]
: '';
// ⬇️ FIX PENTING: tambahkan ini
state.update.id = id; state.update.id = id;
state.update.form = { state.update.form = {
name: data.name, name: data.name,
tanggal: formattedDate, tanggal: data.tanggal,
jenisKelaminId: data.jenisKelaminId, jenisKelaminId: data.jenisKelaminId,
ratingId: data.ratingId, ratingId: data.ratingId,
kelompokUmurId: data.kelompokUmurId, kelompokUmurId: data.kelompokUmurId,
@@ -76,6 +72,7 @@ function EditResponden() {
const handleSubmit = async () => { const handleSubmit = async () => {
state.update.id = id; state.update.id = id;
state.update.form = { ...formData }; // <-- sinkronisasi manual
await state.update.submit(); await state.update.submit();
router.push('/admin/ppid/ikm-desa-darmasaba/responden') router.push('/admin/ppid/ikm-desa-darmasaba/responden')
} }
@@ -103,12 +100,15 @@ function EditResponden() {
}} }}
/> />
<TextInput <TextInput
label="Tanggal"
type="date" type="date"
value={formData.tanggal} placeholder='Pilih tanggal'
value={formData.tanggal ? new Date(formData.tanggal).toISOString().split('T')[0] : ''}
onChange={(e) => { onChange={(e) => {
const selectedDate = e.currentTarget.value;
setFormData({ setFormData({
...formData, ...formData,
tanggal: e.currentTarget.value, // ✅ sudah format YYYY-MM-DD tanggal: selectedDate,
}); });
}} }}
/> />

View File

@@ -1,21 +1,55 @@
/* eslint-disable @typescript-eslint/no-explicit-any */
// /api/berita/findManyPaginated.ts
import prisma from "@/lib/prisma"; import prisma from "@/lib/prisma";
import { Context } from "elysia";
export default async function lowonganKerjaFindMany() { async function lowonganKerjaFindMany(context: Context) {
try { // Ambil parameter dari query
const data = await prisma.lowonganPekerjaan.findMany({ const page = Number(context.query.page) || 1;
where: { isActive: true }, const limit = Number(context.query.limit) || 10;
}); const search = (context.query.search as string) || '';
const skip = (page - 1) * limit;
return { // Buat where clause
success: true, const where: any = { isActive: true };
message: "Success fetch lowongan kerja",
data, // Tambahkan pencarian (jika ada)
}; if (search) {
} catch (e) { where.OR = [
console.error("Find many error:", e); { posisi: { contains: search, mode: 'insensitive' } },
return { { namaPerusahaan: { contains: search, mode: 'insensitive' } },
success: false, { lokasi: { contains: search, mode: 'insensitive' } },
message: "Failed fetch lowongan kerja", ];
}; }
}
} try {
// Ambil data dan total count secara paralel
const [data, total] = await Promise.all([
prisma.lowonganPekerjaan.findMany({
where,
skip,
take: limit,
orderBy: { createdAt: 'desc' },
}),
prisma.lowonganPekerjaan.count({ where }),
]);
return {
success: true,
message: "Berhasil ambil lowongan kerja dengan pagination",
data,
page,
limit,
total,
totalPages: Math.ceil(total / limit),
};
} catch (e) {
console.error("Error di findMany paginated:", e);
return {
success: false,
message: "Gagal mengambil data lowongan kerja",
};
}
}
export default lowonganKerjaFindMany;

View File

@@ -1,26 +1,84 @@
/* eslint-disable @typescript-eslint/no-explicit-any */
// /api/berita/findManyPaginated.ts
import prisma from "@/lib/prisma"; import prisma from "@/lib/prisma";
import { Context } from "elysia";
export default async function pasarDesaFindMany() { async function pasarDesaFindMany(context: Context) {
const data = await prisma.pasarDesa.findMany({ // Ambil parameter dari query
where: { const page = Number(context.query.page) || 1;
isActive: true, // Opsional filter const limit = Number(context.query.limit) || 10;
}, const search = (context.query.search as string) || '';
orderBy: { const categoryId = context.query.categoryId as string | undefined;
createdAt: "desc", const skip = (page - 1) * limit;
},
include: { // Buat where clause
image: true, const where: any = { isActive: true };
KategoriToPasar: {
// Tambahkan filter kategori (jika ada)
if (categoryId) {
where.KategoriToPasar = {
some: {
kategoriId: categoryId
}
};
}
// Tambahkan pencarian (jika ada)
if (search) {
where.AND = where.AND || [];
where.AND.push({
OR: [
{ nama: { contains: search, mode: 'insensitive' } },
{ alamatUsaha: { contains: search, mode: 'insensitive' } },
{
KategoriToPasar: {
some: {
kategori: {
nama: { contains: search, mode: 'insensitive' }
}
}
}
}
]
});
}
try {
// Ambil data dan total count secara paralel
const [data, total] = await Promise.all([
prisma.pasarDesa.findMany({
where,
include: { include: {
kategori: true, image: true,
KategoriToPasar: {
include: {
kategori: true
}
}
}, },
}, skip,
}, take: limit,
}); orderBy: { createdAt: 'desc' },
}),
prisma.pasarDesa.count({ where }),
]);
return { return {
success: true, success: true,
message: "Berhasil mengambil semua data pasar desa", message: "Berhasil ambil pasar desa dengan pagination",
data, data,
}; page,
limit,
total,
totalPages: Math.ceil(total / limit),
};
} catch (e) {
console.error("Error di findMany paginated:", e);
return {
success: false,
message: "Gagal mengambil data pasar desa",
};
}
} }
export default pasarDesaFindMany;

View File

@@ -1,15 +1,60 @@
/* eslint-disable @typescript-eslint/no-explicit-any */ /* eslint-disable @typescript-eslint/no-explicit-any */
// /api/berita/findManyPaginated.ts
import prisma from "@/lib/prisma"; import prisma from "@/lib/prisma";
import { Context } from "elysia";
export default async function kategoriProdukFindMany() { async function kategoriProdukFindMany(context: Context) {
const data = await prisma.kategoriProduk.findMany(); // Ambil parameter dari query
return { const page = Number(context.query.page) || 1;
success: true, const limit = Number(context.query.limit) || 10;
data: data.map((item: any) => { const search2 = (context.query.search as string) || '';
return { const skip = (page - 1) * limit;
id: item.id,
nama: item.nama, // Buat where clause
} const where: any = { isActive: true };
}),
// Tambahkan pencarian (jika ada)
if (search2) {
where.OR = [
{ nama: { contains: search2, mode: 'insensitive' } },
{KategoriToPasar : {
some: {
kategori: {
nama: { contains: search2, mode: 'insensitive' }
}
}
}}
];
}
try {
// Ambil data dan total count secara paralel
const [data, total] = await Promise.all([
prisma.kategoriProduk.findMany({
where,
skip,
take: limit,
orderBy: { createdAt: 'desc' },
}),
prisma.kategoriProduk.count({ where }),
]);
return {
success: true,
message: "Berhasil ambil kategori produk dengan pagination",
data,
page,
limit,
total,
totalPages: Math.ceil(total / limit),
}; };
} } catch (e) {
console.error("Error di findMany paginated:", e);
return {
success: false,
message: "Gagal mengambil data kategori produk",
};
}
}
export default kategoriProdukFindMany;

View File

@@ -1,24 +1,57 @@
/* eslint-disable @typescript-eslint/no-explicit-any */
// /api/berita/findManyPaginated.ts
import prisma from "@/lib/prisma"; import prisma from "@/lib/prisma";
import { Context } from "elysia";
export default async function keamananLingkunganFindMany() { async function keamananLingkunganFindMany(context: Context) {
try { // Ambil parameter dari query
const data = await prisma.keamananLingkungan.findMany({ const page = Number(context.query.page) || 1;
where: { isActive: true }, const limit = Number(context.query.limit) || 10;
include: { const search = (context.query.search as string) || '';
image: true, const skip = (page - 1) * limit;
},
});
return { // Buat where clause
success: true, const where: any = { isActive: true };
message: "Success fetch keamanan lingkungan",
data, // Tambahkan pencarian (jika ada)
}; if (search) {
} catch (e) { where.OR = [
console.error("Find many error:", e); { name: { contains: search, mode: 'insensitive' } },
return { { deskripsi: { contains: search, mode: 'insensitive' } },
success: false, ];
message: "Failed fetch keamanan lingkungan", }
};
} try {
} // Ambil data dan total count secara paralel
const [data, total] = await Promise.all([
prisma.keamananLingkungan.findMany({
where,
include: {
image: true,
},
skip,
take: limit,
orderBy: { createdAt: 'desc' },
}),
prisma.keamananLingkungan.count({ where }),
]);
return {
success: true,
message: "Berhasil ambil keamanan lingkungan dengan pagination",
data,
page,
limit,
total,
totalPages: Math.ceil(total / limit),
};
} catch (e) {
console.error("Error di findMany paginated:", e);
return {
success: false,
message: "Gagal mengambil data keamanan lingkungan",
};
}
}
export default keamananLingkunganFindMany;

View File

@@ -0,0 +1,31 @@
/* eslint-disable @typescript-eslint/no-explicit-any */
// find-first.ts
import prisma from "@/lib/prisma";
async function polsekTerdekatFindFirst() {
const where: any = { isActive: true };
try {
const data = await prisma.polsekTerdekat.findFirst({
where,
include: {
layananPolsek: true,
},
orderBy: { createdAt: 'desc' },
});
return {
success: true,
message: "Berhasil ambil polsek terdekat terbaru",
data,
};
} catch (e) {
console.error("Error di findFirst:", e);
return {
success: false,
message: "Gagal ambil polsek terdekat terbaru",
};
}
}
export default polsekTerdekatFindFirst;

View File

@@ -1,24 +1,57 @@
/* eslint-disable @typescript-eslint/no-explicit-any */
// /api/berita/findManyPaginated.ts
import prisma from "@/lib/prisma"; import prisma from "@/lib/prisma";
import { Context } from "elysia";
export default async function polsekTerdekatFindMany() { async function polsekTerdekatFindMany(context: Context) {
try { // Ambil parameter dari query
const data = await prisma.polsekTerdekat.findMany({ const page = Number(context.query.page) || 1;
where: { isActive: true }, const limit = Number(context.query.limit) || 10;
include: { const search = (context.query.search as string) || '';
layananPolsek: true, const skip = (page - 1) * limit;
},
});
return { // Buat where clause
success: true, const where: any = { isActive: true };
message: "Success fetch polsek terdekat",
data, // Tambahkan pencarian (jika ada)
}; if (search) {
} catch (e) { where.OR = [
console.error("Find many error:", e); { nama: { contains: search, mode: 'insensitive' } },
return { { alamat: { contains: search, mode: 'insensitive' } },
success: false, ];
message: "Failed fetch polsek terdekat", }
};
} try {
} // Ambil data dan total count secara paralel
const [data, total] = await Promise.all([
prisma.polsekTerdekat.findMany({
where,
include: {
layananPolsek: true,
},
skip,
take: limit,
orderBy: { createdAt: 'desc' },
}),
prisma.polsekTerdekat.count({ where }),
]);
return {
success: true,
message: "Berhasil ambil polsek terdekat dengan pagination",
data,
page,
limit,
total,
totalPages: Math.ceil(total / limit),
};
} catch (e) {
console.error("Error di findMany paginated:", e);
return {
success: false,
message: "Gagal mengambil data polsek terdekat",
};
}
}
export default polsekTerdekatFindMany;

View File

@@ -4,6 +4,7 @@ import polsekTerdekatDelete from "./del";
import polsekTerdekatFindMany from "./findMany"; import polsekTerdekatFindMany from "./findMany";
import polsekTerdekatFindUnique from "./findUnique"; import polsekTerdekatFindUnique from "./findUnique";
import polsekTerdekatUpdate from "./updt"; import polsekTerdekatUpdate from "./updt";
import polsekTerdekatFindFirst from "./findFirst";
const PolsekTerdekat = new Elysia({ prefix: "/polsekterdekat", tags: ["Keamanan/Polsek Terdekat"] }) const PolsekTerdekat = new Elysia({ prefix: "/polsekterdekat", tags: ["Keamanan/Polsek Terdekat"] })
@@ -12,6 +13,7 @@ const PolsekTerdekat = new Elysia({ prefix: "/polsekterdekat", tags: ["Keamanan/
const response = await polsekTerdekatFindUnique(new Request(context.request)); const response = await polsekTerdekatFindUnique(new Request(context.request));
return response; return response;
}) })
.get("/find-first", polsekTerdekatFindFirst)
.post("/create", polsekTerdekatCreate, { .post("/create", polsekTerdekatCreate, {
body: t.Object({ body: t.Object({
nama: t.String(), nama: t.String(),

View File

@@ -1,24 +1,57 @@
/* eslint-disable @typescript-eslint/no-explicit-any */
// /api/berita/findManyPaginated.ts
import prisma from "@/lib/prisma"; import prisma from "@/lib/prisma";
import { Context } from "elysia";
export default async function menuTipsKeamananFindMany() { async function tipsKeamananFindMany(context: Context) {
try { // Ambil parameter dari query
const data = await prisma.menuTipsKeamanan.findMany({ const page = Number(context.query.page) || 1;
where: { isActive: true }, const limit = Number(context.query.limit) || 10;
include: { const search = (context.query.search as string) || '';
image: true, const skip = (page - 1) * limit;
},
});
return { // Buat where clause
success: true, const where: any = { isActive: true };
message: "Success fetch menu tips keamanan",
data, // Tambahkan pencarian (jika ada)
}; if (search) {
} catch (e) { where.OR = [
console.error("Find many error:", e); { judul: { contains: search, mode: 'insensitive' } },
return { { deskripsi: { contains: search, mode: 'insensitive' } },
success: false, ];
message: "Failed fetch menu tips keamanan", }
};
} try {
} // Ambil data dan total count secara paralel
const [data, total] = await Promise.all([
prisma.menuTipsKeamanan.findMany({
where,
include: {
image: true,
},
skip,
take: limit,
orderBy: { createdAt: 'desc' },
}),
prisma.menuTipsKeamanan.count({ where }),
]);
return {
success: true,
message: "Berhasil ambil menu tips keamanan dengan pagination",
data,
page,
limit,
total,
totalPages: Math.ceil(total / limit),
};
} catch (e) {
console.error("Error di findMany paginated:", e);
return {
success: false,
message: "Gagal mengambil data tips keamanan",
};
}
}
export default tipsKeamananFindMany;

View File

@@ -15,16 +15,12 @@ export default async function respondenCreate(context: Context) {
try { try {
// Convert the date string to a Date object // Convert the date string to a Date object
const tanggal = new Date(body.tanggal); const tanggal = new Date(body.tanggal);
// Validate the date
if (isNaN(tanggal.getTime())) {
throw new Error('Tanggal tidak valid');
}
const result = await prisma.responden.create({ const result = await prisma.responden.create({
data: { data: {
name: body.name, name: body.name,
tanggal: tanggal, // Use the Date object tanggal: tanggal.toISOString(), // Use the Date object
jenisKelaminId: body.jenisKelaminId, jenisKelaminId: body.jenisKelaminId,
ratingId: body.ratingId, ratingId: body.ratingId,
kelompokUmurId: body.kelompokUmurId, kelompokUmurId: body.kelompokUmurId,

View File

@@ -2,35 +2,40 @@ import prisma from "@/lib/prisma";
import { Context } from "elysia"; import { Context } from "elysia";
type FormUpdate = { type FormUpdate = {
name: string; name: string;
tanggal: string; tanggal: string;
jenisKelaminId: string; jenisKelaminId: string;
ratingId: string; ratingId: string;
kelompokUmurId: string; kelompokUmurId: string;
} };
export default async function respondenUpdate(context: Context) { export default async function respondenUpdate(context: Context) {
const body = (await context.body) as FormUpdate; const body = (await context.body) as FormUpdate;
const id = context.params.id as string; const id = context.params.id as string;
try { try {
const result = await prisma.responden.update({ const result = await prisma.responden.update({
where: { id }, where: { id },
data: { data: {
name: body.name, name: body.name,
tanggal: body.tanggal, tanggal: new Date(body.tanggal).toISOString(),
jenisKelaminId: body.jenisKelaminId, jenisKelaminId: body.jenisKelaminId,
ratingId: body.ratingId, ratingId: body.ratingId,
kelompokUmurId: body.kelompokUmurId, kelompokUmurId: body.kelompokUmurId,
}, },
}); });
return { return {
success: true, success: true,
message: "Berhasil mengupdate responden", message: "Berhasil mengupdate responden",
data: result, data: result,
}; };
} catch (error) { } catch (error) {
console.error("Error updating responden:", error); console.error("Error updating responden:", error);
throw new Error("Gagal mengupdate responden: " + (error as Error).message); // Instead of throwing an error, return a proper JSON response
} return {
success: false,
message: "Gagal mengupdate responden: " + (error as Error).message,
data: null,
};
}
} }

View File

@@ -1,44 +1,54 @@
/* eslint-disable @typescript-eslint/no-explicit-any */
// /api/berita/findManyPaginated.ts
import prisma from "@/lib/prisma"; import prisma from "@/lib/prisma";
import { Context } from "elysia"; import { Context } from "elysia";
// Di findMany.ts async function daftarInformasiPublikFindMany(context: Context) {
export default async function daftarInformasiPublikFindMany(context: Context) { // Ambil parameter dari query
const page = Number(context.query.page) || 1; const page = Number(context.query.page) || 1;
const limit = Number(context.query.limit) || 10; const limit = Number(context.query.limit) || 10;
const search = (context.query.search as string) || '';
const skip = (page - 1) * limit; const skip = (page - 1) * limit;
// Buat where clause
const where: any = { isActive: true };
// Tambahkan pencarian (jika ada)
if (search) {
where.OR = [
{ jenisInformasi: { contains: search, mode: 'insensitive' } },
{ deskripsi: { contains: search, mode: 'insensitive' } },
];
}
try { try {
// Ambil data dan total count secara paralel
const [data, total] = await Promise.all([ const [data, total] = await Promise.all([
prisma.daftarInformasiPublik.findMany({ prisma.daftarInformasiPublik.findMany({
where: { isActive: true }, where,
skip, skip,
take: limit, take: limit,
orderBy: { createdAt: 'desc' }, orderBy: { createdAt: 'desc' },
}), }),
prisma.daftarInformasiPublik.count({ prisma.daftarInformasiPublik.count({ where }),
where: { isActive: true }
})
]); ]);
const totalPages = Math.ceil(total / limit);
return { return {
success: true, success: true,
message: "Success fetch daftar informasi publik with pagination", message: "Berhasil ambil daftar informasi publik dengan pagination",
data, data,
page, page,
totalPages, limit,
total, total,
totalPages: Math.ceil(total / limit),
}; };
} catch (e) { } catch (e) {
console.error("Find many paginated error:", e); console.error("Error di findMany paginated:", e);
return { return {
success: false, success: false,
message: "Failed fetch daftar informasi publik with pagination", message: "Gagal mengambil data daftar informasi publik",
data: [],
page: 1,
totalPages: 1,
total: 0,
}; };
} }
} }
export default daftarInformasiPublikFindMany;

View File

@@ -1,64 +1,52 @@
'use client' 'use client'
import lowonganKerjaState from '@/app/admin/(dashboard)/_state/ekonomi/lowongan-kerja';
import colors from '@/con/colors'; import colors from '@/con/colors';
import { Stack, Box, Text, TextInput, Group, SimpleGrid, Paper, Flex, Button } from '@mantine/core'; import { Box, Button, Center, Flex, Group, Pagination, Paper, SimpleGrid, Skeleton, Stack, Text, TextInput } from '@mantine/core';
import React from 'react'; import { useDebouncedValue } from '@mantine/hooks';
import BackButton from '../../desa/layanan/_com/BackButto';
import { IconBriefcase, IconClock, IconMapPin, IconSearch } from '@tabler/icons-react'; import { IconBriefcase, IconClock, IconMapPin, IconSearch } from '@tabler/icons-react';
import { useRouter } from 'next/navigation'; import { useRouter } from 'next/navigation';
import { useEffect, useState } from 'react';
import { useProxy } from 'valtio/utils';
import BackButton from '../../desa/layanan/_com/BackButto';
const data = [ const formatCurrency = (value: string | number) => {
{ // Convert to string if it's a number
id: 1, const numStr = typeof value === 'number' ? value.toString() : value;
kerja: 'Kasir',
tempat: 'Toko Sumber Rejeki', // Remove all non-digit characters
alamat: 'Desa Munggu , Badung', const digitsOnly = numStr.replace(/\D/g, '');
gaji: 'Rp. 2.500.000 / bulan'
// Format with thousand separators
const formatted = digitsOnly.replace(/\B(?=(\d{3})+(?!\d))/g, '.');
return `Rp.${formatted}`;
};
},
{
id: 2,
kerja: 'Kasir',
tempat: 'Toko Sumber Rejeki',
alamat: 'Desa Munggu , Badung',
gaji: 'Rp. 2.500.000 / bulan'
},
{
id: 3,
kerja: 'Kasir',
tempat: 'Toko Sumber Rejeki',
alamat: 'Desa Munggu , Badung',
gaji: 'Rp. 2.500.000 / bulan'
},
{
id: 4,
kerja: 'Kasir',
tempat: 'Toko Sumber Rejeki',
alamat: 'Desa Munggu , Badung',
gaji: 'Rp. 2.500.000 / bulan'
},
{
id: 5,
kerja: 'Kasir',
tempat: 'Toko Sumber Rejeki',
alamat: 'Desa Munggu , Badung',
gaji: 'Rp. 2.500.000 / bulan'
},
{
id: 6,
kerja: 'Kasir',
tempat: 'Toko Sumber Rejeki',
alamat: 'Desa Munggu , Badung',
gaji: 'Rp. 2.500.000 / bulan'
},
]
function Page() { function Page() {
const state = useProxy(lowonganKerjaState)
const router = useRouter() const router = useRouter()
const [search, setSearch] = useState('')
const [debouncedSearch] = useDebouncedValue(search, 500); // 500ms delay
const {
data,
page,
totalPages,
loading,
load,
} = state.findMany
useEffect(() => {
load(page, 6, debouncedSearch)
}, [page, debouncedSearch, load])
if (loading || !data) {
return (
<Stack py={10}>
<Skeleton h={500} />
</Stack>
)
}
return ( return (
<Stack pos={"relative"} bg={colors.Bg} py={"xl"} gap={"22"}> <Stack pos={"relative"} bg={colors.Bg} py={"xl"} gap={"22"}>
<Box px={{ base: 'md', md: 100 }}> <Box px={{ base: 'md', md: 100 }}>
@@ -74,6 +62,8 @@ function Page() {
w={{ base: 500, md: 700 }} w={{ base: 500, md: 700 }}
placeholder='Cari Pekerjaan' placeholder='Cari Pekerjaan'
leftSection={<IconSearch size={20} />} leftSection={<IconSearch size={20} />}
value={search}
onChange={(e) => setSearch(e.currentTarget.value)}
/> />
</Group> </Group>
</Box> </Box>
@@ -93,15 +83,15 @@ function Page() {
<Flex gap={'xl'} align={'center'}> <Flex gap={'xl'} align={'center'}>
<IconBriefcase color={colors['blue-button']} size={50} /> <IconBriefcase color={colors['blue-button']} size={50} />
<Box> <Box>
<Text fw={'bold'} fz={'h4'} c={colors['blue-button']}>{v.kerja}</Text> <Text fw={'bold'} fz={'h4'} c={colors['blue-button']}>{v.posisi}</Text>
<Text fz={'h4'}>{v.tempat}</Text> <Text fz={'h4'}>{v.namaPerusahaan}</Text>
</Box> </Box>
</Flex> </Flex>
</Box> </Box>
<Box> <Box>
<Flex gap={'xl'} align={'center'}> <Flex gap={'xl'} align={'center'}>
<IconMapPin color={colors['blue-button']} size={50} /> <IconMapPin color={colors['blue-button']} size={50} />
<Text fz={'h4'}>{v.alamat}</Text> <Text fz={'h4'}>{v.lokasi}</Text>
</Flex> </Flex>
</Box> </Box>
<Box> <Box>
@@ -109,7 +99,7 @@ function Page() {
<IconClock color={colors['blue-button']} size={50} /> <IconClock color={colors['blue-button']} size={50} />
<Box> <Box>
<Text fw={'bold'} fz={'h4'} c={colors['blue-button']}>Full Time</Text> <Text fw={'bold'} fz={'h4'} c={colors['blue-button']}>Full Time</Text>
<Text fz={'h4'}>{v.gaji}</Text> <Text fz={'h4'}>{formatCurrency(v.gaji)}</Text>
</Box> </Box>
</Flex> </Flex>
</Box> </Box>
@@ -119,6 +109,14 @@ function Page() {
) )
})} })}
</SimpleGrid> </SimpleGrid>
<Center>
<Pagination
value={page}
onChange={(newPage) => load(newPage)}
total={totalPages}
my="md"
/>
</Center>
</Stack> </Stack>
</Box > </Box >
</Stack > </Stack >

View File

@@ -1,90 +1,76 @@
'use client' 'use client'
import pasarDesaState from '@/app/admin/(dashboard)/_state/ekonomi/pasar-desa/pasar-desa';
import colors from '@/con/colors'; import colors from '@/con/colors';
import { Box, Combobox, Flex, Image, InputBase, InputPlaceholder, Paper, SimpleGrid, Stack, Text, TextInput, useCombobox } from '@mantine/core'; import { Box, Center, Flex, Grid, GridCol, Image, Pagination, Paper, Select, SimpleGrid, Skeleton, Stack, Text, TextInput } from '@mantine/core';
import { useShallowEffect } from '@mantine/hooks';
import { IconMapPinFilled, IconSearch, IconShoppingCartFilled, IconStarFilled } from '@tabler/icons-react'; import { IconMapPinFilled, IconSearch, IconShoppingCartFilled, IconStarFilled } from '@tabler/icons-react';
import { motion } from 'motion/react'; import { motion } from 'motion/react';
import { useRouter } from 'next/navigation'; import { useRouter } from 'next/navigation';
import { useState } from 'react'; import { useState } from 'react';
import { useProxy } from 'valtio/utils';
import BackButton from '../../desa/layanan/_com/BackButto'; import BackButton from '../../desa/layanan/_com/BackButto';
const groceries = [
'Makanan',
'Minuman',
'Pakaian',
'Alat Dapur',
'Alat Mandi',
'Furniture',
];
const dataBarang = [
{
id: 1,
image: '/api/img/semat.png',
judul: 'Semat Bambu / Semat Banten',
harga: 'Rp. 3000 / pcs',
bintang: '4.9',
alamat: 'Jl. Kecubung no.6'
},
{
id: 2,
image: '/api/img/kerupuk.png',
judul: 'Kerupuk Babi',
harga: 'Rp. 12000 / pcs',
bintang: '4.9',
alamat: 'Jl. Kenari no.7'
},
{
id: 3,
image: '/api/img/beras.png',
judul: 'beras Merah Organik',
harga: 'Rp. 40000 / 1 kg',
bintang: '4.9',
alamat: 'Jl. Mawar no.8'
},
{
id: 4,
image: '/api/img/genteng.png',
judul: 'Genteng',
harga: 'Rp. 3600 / pcs',
bintang: '4.9',
alamat: 'Jl. Kecubung no.16'
},
]
function Page() { function Page() {
const [search, setSearch] = useState('');
const combobox = useCombobox({
onDropdownClose: () => {
combobox.resetSelectedOption();
combobox.focusTarget();
setSearch('');
},
onDropdownOpen: () => {
combobox.focusSearchInput();
},
});
const [value, setValue] = useState<string | null>(null);
const options = groceries
.filter((item) => item.toLowerCase().includes(search.toLowerCase().trim()))
.map((item) => (
<Combobox.Option value={item} key={item}>
{item}
</Combobox.Option>
));
const router = useRouter() const router = useRouter()
const state = useProxy(pasarDesaState.pasarDesa)
const [search, setSearch] = useState('');
const [selectedCategory, setSelectedCategory] = useState<string | null>(null);
const {
data,
page,
loading,
totalPages,
load,
} = state.findMany
useShallowEffect(() => {
pasarDesaState.kategoriProduk.findMany.load()
}, [])
// Filter data based on selected category
const filteredData = selectedCategory
? data?.filter(item =>
item.KategoriToPasar?.some(kategori => kategori.kategoriId === selectedCategory)
)
: data;
useShallowEffect(() => {
load(page, 4, search, selectedCategory || undefined)
}, [page, search, selectedCategory])
if (loading || !data) {
return (
<Stack py={10}>
<Skeleton h={500} />
</Stack>
)
}
return ( return (
<Stack pos={"relative"} bg={colors.Bg} py={"xl"} gap={"22"}> <Stack pos={"relative"} bg={colors.Bg} py={"xl"} gap={"22"}>
<Box px={{ base: 'md', md: 100 }}> <Box px={{ base: 'md', md: 100 }}>
<BackButton /> <BackButton />
</Box> </Box>
<Box> <Box>
<Text ta={"center"} fz={{ base: "h1", md: "2.5rem" }} c={colors["blue-button"]} fw={"bold"}> <Grid align='center' px={{ base: 'md', md: 100 }}>
Pasar Desa <GridCol span={{ base: 12, md: 9 }}>
</Text> <Text fz={{ base: "h1", md: "2.5rem" }} c={colors["blue-button"]} fw={"bold"}>
<Text px={{ base: 20, md: 150 }} ta={"center"} fz={{ base: "h4", md: "h3" }} > Pasar Desa
</Text>
</GridCol>
<GridCol span={{ base: 12, md: 3 }}>
<TextInput
radius={"lg"}
placeholder='Cari Produk'
value={search}
onChange={(e) => setSearch(e.target.value)}
leftSection={<IconSearch size={20} />}
w={{ base: "50%", md: "100%" }}
/>
</GridCol>
</Grid>
<Text px={{ base: 'md', md: 100 }} ta={"justify"} fz={{ base: "h4", md: "h3" }} >
Pasar Desa Online merupakan Media Promosi yang bertujuan untuk membantu warga desa dalam memasarkan dan memperkenalkan produknya kepada masyarakat. Pasar Desa Online merupakan Media Promosi yang bertujuan untuk membantu warga desa dalam memasarkan dan memperkenalkan produknya kepada masyarakat.
</Text> </Text>
</Box> </Box>
@@ -98,48 +84,23 @@ function Page() {
}} }}
> >
<Box> <Box>
<Combobox <Select
store={combobox} placeholder="Pilih Kategori"
withinPortal={false} data={pasarDesaState.kategoriProduk.findMany.data?.map((v) => ({
onOptionSubmit={(val) => { value: v.id,
setValue(val); label: v.nama
combobox.closeDropdown(); })) || []}
}} value={selectedCategory}
> onChange={setSelectedCategory}
<Combobox.Target> clearable
<InputBase searchable
component="button" nothingFoundMessage="Tidak ada kategori ditemukan"
type="button" style={{ width: '100%' }}
pointer
rightSection={<Combobox.Chevron />}
onClick={() => combobox.toggleDropdown()}
rightSectionPointerEvents="none"
>
{value || <InputPlaceholder>Kategori</InputPlaceholder>}
</InputBase>
</Combobox.Target>
<Combobox.Dropdown>
<Combobox.Search
value={search}
onChange={(event) => setSearch(event.currentTarget.value)}
placeholder="Search groceries"
/>
<Combobox.Options>
{options.length > 0 ? options : <Combobox.Empty>Nothing found</Combobox.Empty>}
</Combobox.Options>
</Combobox.Dropdown>
</Combobox>
</Box>
<Box>
<TextInput
placeholder='Cari Produk'
leftSection={<IconSearch size={20} />}
/> />
</Box> </Box>
</SimpleGrid> </SimpleGrid>
<SimpleGrid cols={{ base: 1, md: 4 }}> <SimpleGrid cols={{ base: 1, md: 4 }}>
{dataBarang.map((v, k) => { {filteredData?.map((v, k) => {
return ( return (
<Stack key={k}> <Stack key={k}>
<motion.div <motion.div
@@ -148,18 +109,25 @@ function Page() {
whileTap={{ scale: 0.8 }} whileTap={{ scale: 0.8 }}
> >
<Paper p={'lg'}> <Paper p={'lg'}>
<Image radius={'lg'} src={v.image} alt='' /> <Image
<Text py={10} fw={'bold'} fz={'lg'}>{v.judul}</Text> radius={'lg'}
<Text fz={'md'}>{v.harga}</Text> src={v.image?.link || '/placeholder-product.jpg'}
alt={v.nama}
h={200}
w='100%'
style={{ objectFit: 'cover' }}
/>
<Text py={10} fw={'bold'} fz={'lg'}>{v.nama}</Text>
<Text fz={'md'}>Rp {v.harga.toLocaleString('id-ID')}</Text>
<Flex py={10} gap={'md'}> <Flex py={10} gap={'md'}>
<IconStarFilled size={20} color='#EBCB09' /> <IconStarFilled size={20} color='#EBCB09' />
<Text fz={'sm'} ml={2}>{v.bintang}</Text> <Text fz={'sm'} ml={2}>{v.rating}</Text>
</Flex> </Flex>
<Flex justify={'space-between'} align={'center'}> <Flex justify={'space-between'} align={'center'}>
<Box> <Box>
<Flex gap={'md'} align={'center'}> <Flex gap={'md'} align={'center'}>
<IconMapPinFilled size={20} color='red' /> <IconMapPinFilled size={20} color='red' />
<Text fz={'sm'} ml={2}>{v.alamat}</Text> <Text fz={'sm'} ml={2}>{v.alamatUsaha}</Text>
</Flex> </Flex>
</Box> </Box>
<IconShoppingCartFilled size={20} color={colors['blue-button']} /> <IconShoppingCartFilled size={20} color={colors['blue-button']} />
@@ -170,6 +138,14 @@ function Page() {
) )
})} })}
</SimpleGrid> </SimpleGrid>
<Center>
<Pagination
value={page}
onChange={(newPage) => load(newPage)} // ini penting!
total={totalPages}
my="md"
/>
</Center>
</Stack> </Stack>
</Box> </Box>
</Stack> </Stack>

View File

@@ -1,67 +1,61 @@
'use client'
import keamananLingkunganState from '@/app/admin/(dashboard)/_state/keamanan/keamanan-lingkungan';
import colors from '@/con/colors'; import colors from '@/con/colors';
import { Box, Center, Image, List, ListItem, Paper, SimpleGrid, Stack, Text } from '@mantine/core'; import { Box, Center, Grid, GridCol, Image, Pagination, Paper, SimpleGrid, Skeleton, Stack, Text, TextInput } from '@mantine/core';
import { useShallowEffect } from '@mantine/hooks';
import { IconSearch } from '@tabler/icons-react';
import { useState } from 'react';
import { useProxy } from 'valtio/utils';
import BackButton from '../../desa/layanan/_com/BackButto'; import BackButton from '../../desa/layanan/_com/BackButto';
const data1 = [
{
id: 1,
judul: 'Peran Pecalang dalam Keamanan Desa',
image: '/api/img/pecalang.png',
pengertian: 'Pecalang adalah petugas keamanan adat di Bali yang memiliki peran penting dalam menjaga ketertiban dan budaya lokal. Tugas mereka meliputi:',
deskripsi: <List>
<ListItem fz={{ base: 'h4', md: 'lg' }}>Mengamankan upacara adat dan kegiatan keagamaan.</ListItem>
<ListItem fz={{ base: 'h4', md: 'lg' }}>Mengatur lalu lintas saat ada perayaan atau kegiatan besar.</ListItem>
<ListItem fz={{ base: 'h4', md: 'lg' }}>Berpatroli untuk mencegah gangguan keamanan di lingkungan desa.</ListItem>
<ListItem fz={{ base: 'h4', md: 'lg' }}>Berkoordinasi dengan aparat desa dan kepolisian dalam penanganan situasi darurat.</ListItem>
</List>
},
{
id: 2,
judul: 'Patwal (Patroli Pengawal) Desa',
image: '/api/img/patwal-1.png',
pengertian: 'Selain Pecalang, Desa Darmasaba juga memiliki Patwal yang bertugas menjaga keamanan sehari-hari. Peran mereka antara lain:',
deskripsi: <List>
<ListItem fz={{ base: 'h4', md: 'lg' }}>Berpatroli secara rutin untuk memastikan lingkungan tetap aman.</ListItem>
<ListItem fz={{ base: 'h4', md: 'lg' }}>Menjaga ketertiban lalu lintas di area desa.</ListItem>
<ListItem fz={{ base: 'h4', md: 'lg' }}>Melakukan tindakan preventif terhadap potensi gangguan keamanan.</ListItem>
<ListItem fz={{ base: 'h4', md: 'lg' }}>Siap siaga dalam keadaan darurat untuk membantu warga.</ListItem>
</List>
},
{
id: 3,
judul: 'Layanan Keamanan yang Tersedia',
image: '/api/img/pospecalang.png',
pengertian: 'Jika terjadi keadaan darurat atau membutuhkan bantuan keamanan, warga dapat menghubungi:',
deskripsi: <List>
<ListItem fz={{ base: 'h4', md: 'lg' }}>Pos Pecalang Desa: [Masukkan Nomor Kontak].</ListItem>
<ListItem fz={{ base: 'h4', md: 'lg' }}>Patwal Desa Darmasaba: [Masukkan Nomor Kontak].</ListItem>
<ListItem fz={{ base: 'h4', md: 'lg' }}>Polsek Terdekat: 110 (Layanan Kepolisian).</ListItem>
</List>
},
{
id: 4,
judul: 'Program Keamanan Desa',
image: '/api/img/rond.png',
pengertian: 'Untuk meningkatkan keamanan, Desa Darmasaba menjalankan berbagai program, seperti:',
deskripsi: <List>
<ListItem fz={{ base: 'h4', md: 'lg' }}>Ronda Malam Warga: Kegiatan jaga malam secara bergilir oleh warga bersama Pecalang dan Patwal.</ListItem>
<ListItem fz={{ base: 'h4', md: 'lg' }}>Sosialisasi Keamanan: Edukasi bagi warga tentang cara menjaga keamanan lingkungan.</ListItem>
<ListItem fz={{ base: 'h4', md: 'lg' }}> Pengawasan Kamera CCTV: Memantau titik- titik strategis untuk mencegah tindak kejahatan.</ListItem>
</List>
}
]
function Page() { function Page() {
const state = useProxy(keamananLingkunganState)
const [search, setSearch] = useState('')
const {
data,
page,
totalPages,
loading,
load,
} = state.findMany;
useShallowEffect(() => {
load(page, 3, search)
}, [page, search])
if (loading || !data) {
return (
<Box py={10}>
<Skeleton h={500} />
</Box>
)
}
return ( return (
<Stack pos={"relative"} bg={colors.Bg} py={"xl"} gap={"22"}> <Stack pos={"relative"} bg={colors.Bg} py={"xl"} gap={"22"}>
<Box px={{ base: 'md', md: 100 }}> <Box px={{ base: 'md', md: 100 }}>
<BackButton /> <BackButton />
</Box> </Box>
<Box> <Box>
<Text ta={"center"} fz={{ base: "h1", md: "2.5rem" }} c={colors["blue-button"]} fw={"bold"}> <Grid align='center' px={{ base: 'md', md: 100 }}>
Keamanan Lingkungan (Pecalang / Patwal) <GridCol span={{ base: 12, md: 9 }}>
</Text> <Text fz={{ base: "h1", md: "2.5rem" }} c={colors["blue-button"]} fw={"bold"}>
<Text px={{ base: 20, md: 150 }} ta={"center"} fz={{ base: "h4", md: "h3" }} > Keamanan Lingkungan (Pecalang / Patwal)
</Text>
</GridCol>
<GridCol span={{ base: 12, md: 3 }}>
<TextInput
radius={"lg"}
placeholder='Cari Puskesmas'
value={search}
onChange={(e) => setSearch(e.target.value)}
leftSection={<IconSearch size={20} />}
w={{ base: "50%", md: "100%" }}
/>
</GridCol>
</Grid>
<Text px={{ base: 'md', md: 100 }} ta={"justify"} fz={{ base: "h4", md: "h3" }} >
Keamanan dan ketertiban lingkungan di Desa Darmasaba dijaga melalui peran aktif Pecalang dan Patwal (Patroli Pengawal). Mereka bertugas memastikan desa tetap aman, tertib, dan kondusif bagi seluruh warga. Keamanan dan ketertiban lingkungan di Desa Darmasaba dijaga melalui peran aktif Pecalang dan Patwal (Patroli Pengawal). Mereka bertugas memastikan desa tetap aman, tertib, dan kondusif bagi seluruh warga.
</Text> </Text>
</Box> </Box>
@@ -73,24 +67,19 @@ function Page() {
base: 1, base: 1,
md: 3, md: 3,
}}> }}>
{data1.map((v, k) => { {data.map((v, k) => {
return ( return (
<Paper radius={10} key={k} bg={colors["white-trans-1"]}> <Paper radius={10} key={k} bg={colors["white-trans-1"]}>
<Stack gap={'xs'}> <Stack gap={'xs'}>
<Center px={10} py={20}> <Center px={10} py={20}>
<Image src={v.image} alt='' /> <Image src={v.image?.link} alt='' />
</Center> </Center>
<Box px={'lg'}> <Box px={'lg'}>
<Box pb={20}> <Box pb={20}>
<Text pb={10} c={colors["blue-button"]} fw={"bold"} fz={"h3"}> <Text pb={10} c={colors["blue-button"]} fw={"bold"} fz={"h3"}>
{v.judul} {v.name}
</Text> </Text>
<Text pb={10} fz={"h4"} ta={'justify'}> <Text pb={10} fz={"h4"} ta={'justify'} dangerouslySetInnerHTML={{ __html: v.deskripsi }} />
{v.pengertian}
</Text>
<Box px={10}>
{v.deskripsi}
</Box>
</Box> </Box>
</Box> </Box>
</Stack> </Stack>
@@ -99,7 +88,15 @@ function Page() {
})} })}
</SimpleGrid> </SimpleGrid>
</Stack> </Stack>
</Box> </Box>
<Center>
<Pagination
value={page}
onChange={(newPage) => load(newPage)} // ini penting!
total={totalPages}
my="md"
/>
</Center>
</Stack> </Stack>
); );
} }

View File

@@ -1,10 +1,30 @@
'use client'
/* eslint-disable react-hooks/exhaustive-deps */
import polsekTerdekatState from '@/app/admin/(dashboard)/_state/keamanan/polsek-terdekat';
import colors from '@/con/colors'; import colors from '@/con/colors';
import { Badge, Box, Button, Flex, Paper, SimpleGrid, Stack, Text, TextInput } from '@mantine/core'; import { Badge, Box, Button, Center, Flex, Paper, SimpleGrid, Skeleton, Stack, Text } from '@mantine/core';
import { IconArrowDown, IconClock, IconNavigation, IconPhone, IconPin, IconSearch } from '@tabler/icons-react'; import { IconArrowDown, IconClock, IconNavigation, IconPhone, IconPin } from '@tabler/icons-react';
import { useEffect } from 'react';
import { useProxy } from 'valtio/utils';
import BackButton from '../../desa/layanan/_com/BackButto'; import BackButton from '../../desa/layanan/_com/BackButto';
import { useRouter } from 'next/navigation';
function Page() { function Page() {
const state = useProxy(polsekTerdekatState.findFirst);
const router = useRouter()
const {
data,
loading,
load,
} = state;
useEffect(() => {
if (!data && !loading) {
load();
}
}, [data, loading]);
return ( return (
<Stack pos={"relative"} bg={colors.Bg} py={"xl"} gap={"22"}> <Stack pos={"relative"} bg={colors.Bg} py={"xl"} gap={"22"}>
<Box px={{ base: 'md', md: 100 }}> <Box px={{ base: 'md', md: 100 }}>
@@ -17,7 +37,6 @@ function Page() {
<Text pb={15} fz={'h4'} > <Text pb={15} fz={'h4'} >
Desa Darmasaba, Kecamatan Abiansemal, Kabupaten Badung Desa Darmasaba, Kecamatan Abiansemal, Kabupaten Badung
</Text> </Text>
<TextInput radius={10} leftSection={<IconSearch size={20} />} placeholder='Cari Kantor Polisi' />
</Box> </Box>
<Box px={{ base: "md", md: 100 }}> <Box px={{ base: "md", md: 100 }}>
<Stack gap={'lg'}> <Stack gap={'lg'}>
@@ -30,59 +49,56 @@ function Page() {
}} }}
> >
{/* Content Sebelah Kiri */} {/* Content Sebelah Kiri */}
<Box> {loading ? (
<Text c={colors["blue-button"]} fw={'bold'} fz={'h2'}>Polsek Abiansemal</Text> <Center><Skeleton h={400} /></Center>
<Text c={colors["blue-button"]} fz={'sm'}>2,5 Km dari Desa Darmasaba</Text> ) : data ? (
<Flex py={10} gap={9} align={'center'}> <>
<IconPin size={25} color={colors["blue-button"]} /> <Box>
<Text c={colors["blue-button"]} fz={'lg'}>Jl. Gandamayu 1 Blahkiuh</Text> <Text c={colors["blue-button"]} fw={'bold'} fz={'h2'}>{data.nama}</Text>
</Flex> <Text c={colors["blue-button"]} fz={'sm'}>{data.jarakKeDesa}</Text>
<Flex gap={9} align={'center'}> <Flex py={10} gap={9} align={'center'}>
<IconPhone size={25} color={colors["blue-button"]} /> <IconPin size={25} color={colors["blue-button"]} />
<Text c={colors["blue-button"]} fz={'lg'}>08xxxxxxxx</Text> <Text c={colors["blue-button"]} fz={'lg'}>{data.alamat}</Text>
</Flex> </Flex>
<Flex py={10} gap={9} align={'center'}> <Flex gap={9} align={'center'}>
<IconClock size={25} color={colors["blue-button"]} /> <IconPhone size={25} color={colors["blue-button"]} />
<Text c={colors["blue-button"]} fz={'lg'}>24 Jam</Text> <Text c={colors["blue-button"]} fz={'lg'}>{data.nomorTelepon}</Text>
</Flex> </Flex>
<Box> <Flex py={10} gap={9} align={'center'}>
<Text c={colors["blue-button"]} fw={'bold'} fz={'h2'}>Layanan Yang Tersedia :</Text> <IconClock size={25} color={colors["blue-button"]} />
<SimpleGrid <Text c={colors["blue-button"]} fz={'lg'}>{data.jamOperasional}</Text>
py={10} </Flex>
cols={{
base: 1,
md: 2,
}}
>
<Box> <Box>
<Text c={colors["blue-button"]} fz={'lg'}>Laporan Kehilangan</Text> <Text c={colors["blue-button"]} fw={'bold'} fz={'h2'}>Layanan Yang Tersedia :</Text>
<SimpleGrid
py={10}
cols={{
base: 1,
md: 2,
}}
>
<Box>
<Text c={colors["blue-button"]} fz={'lg'}>{data.layananPolsek.nama}</Text>
</Box>
</SimpleGrid>
</Box> </Box>
<Box> <Box>
<Text c={colors["blue-button"]} fz={'lg'}>Laporan Kriminal</Text> <Button bg={colors["blue-button"]} onClick={() => router.push(`/darmasaba/keamanan/polsek-terdekat/semua-polsek`)} rightSection={<IconArrowDown size={20} />}>Lihat Kantor Polisi Lainnya</Button>
</Box> </Box>
<Box> </Box>
<Text c={colors["blue-button"]} fz={'lg'}>Pelayanan SKCK</Text> <Box pos={'relative'}>
<Box style={{ position: 'absolute', top: 0, right: 0 }}>
<Badge size='lg' c={'#287407'} bg={'#A8EDC4'}>Buka</Badge>
</Box> </Box>
<Box> <Box pt={40}>
<Text c={colors["blue-button"]} fz={'lg'}>Pengaduan Masyarakat</Text> <iframe style={{ border: 2, width: "100%" }} src={data.embedMapUrl} width="550" height="300" ></iframe>
</Box> </Box>
</SimpleGrid> <Box pt={20}>
</Box> <Button onClick={() => router.push(data.linkPetunjukArah)} fullWidth bg={colors["blue-button"]} radius={10} leftSection={<IconNavigation size={20} />}>Petunjuk Arah</Button>
<Box> </Box>
<Button bg={colors["blue-button"]} rightSection={<IconArrowDown size={20}/>}>Lihat Kantor Polisi Lainnya</Button> </Box>
</Box> </>
</Box> ) : null}
<Box pos={'relative'}>
<Box style={{ position: 'absolute', top: 0, right: 0 }}>
<Badge size='lg' c={'#287407'} bg={'#A8EDC4'}>Buka</Badge>
</Box>
<Box pt={40}>
<iframe style={{ border: 2, width: "100%" }} src="https://www.google.com/maps/embed?pb=!1m18!1m12!1m3!1d3945.7949871034166!2d115.20778387533218!3d-8.519275686287415!2m3!1f0!2f0!3f0!3m2!1i1024!2i768!4f13.1!3m3!1m2!1s0x2dd23ceb4f6e5363%3A0xa353662f070f47d8!2sAbian%20Semal%20Police%20Station!5e0!3m2!1sid!2sid!4v1742789148825!5m2!1sid!2sid" width="550" height="300" ></iframe>
</Box>
<Box pt={20}>
<Button fullWidth bg={colors["blue-button"]} radius={10} leftSection={<IconNavigation size={20}/>}>Petunjuk Arah</Button>
</Box>
</Box>
</SimpleGrid> </SimpleGrid>
</Stack> </Stack>
</Paper> </Paper>

View File

@@ -0,0 +1,100 @@
'use client'
import polsekTerdekatState from '@/app/admin/(dashboard)/_state/keamanan/polsek-terdekat';
import colors from '@/con/colors';
import { Box, Button, Center, Grid, GridCol, Pagination, Paper, SimpleGrid, Skeleton, Stack, Text, TextInput } from '@mantine/core';
import { useShallowEffect } from '@mantine/hooks';
import { IconNavigation, IconSearch } from '@tabler/icons-react';
import React, { useState } from 'react';
import { useProxy } from 'valtio/utils';
import BackButton from '../../../desa/layanan/_com/BackButto';
import { useRouter } from 'next/navigation';
function Page() {
const state = useProxy(polsekTerdekatState);
const [search, setSearch] = useState('');
const router = useRouter()
const {
data,
page,
totalPages,
loading,
load,
} = state.findMany;
useShallowEffect(() => {
load(page, 3, search)
}, [page, search])
if (loading || !data) {
return (
<Box py={10}>
<Skeleton h={500} />
</Box>
)
}
return (
<Stack pos={"relative"} bg={colors.Bg} py={"xl"} gap={"22"}>
<Box px={{ base: 'md', md: 100 }}>
<BackButton />
</Box>
<Grid align='center' px={{ base: 'md', md: 100 }}>
<GridCol span={{ base: 12, md: 9 }}>
<Text fz={{ base: "h1", md: "2.5rem" }} c={colors["blue-button"]} fw={"bold"}>
Semua Polsek Terdekat
</Text>
</GridCol>
<GridCol span={{ base: 12, md: 3 }}>
<TextInput
radius={"lg"}
placeholder='Cari Polsek'
value={search}
onChange={(e) => setSearch(e.target.value)}
leftSection={<IconSearch size={20} />}
w={{ base: "50%", md: "100%" }}
/>
</GridCol>
</Grid>
<Box px={{ base: "md", md: 100 }}>
<SimpleGrid
cols={{
base: 1,
md: 3,
}}
>
{data.map((v, k) => {
return (
<Paper p={"xl"} bg={colors['white-trans-1']} key={k}>
<Stack gap={"xs"}>
<Text fw={"bold"} fz={"h3"}>{v.nama}</Text>
<Text>Alamat: {v.alamat}</Text>
<Text>Jarak: {v.jarakKeDesa}</Text>
<Text>Telepon: {v.nomorTelepon}</Text>
<Text>Jam Operasional: {v.jamOperasional}</Text>
<Box pt={20}>
<iframe style={{ border: 2, width: "100%" }} src={v.embedMapUrl} width="550" height="300" ></iframe>
</Box>
<Box pt={20}>
<Button onClick={() => router.push(v.linkPetunjukArah)} fullWidth bg={colors["blue-button"]} radius={10} leftSection={<IconNavigation size={20} />}>Petunjuk Arah</Button>
</Box>
</Stack>
</Paper>
)
})}
</SimpleGrid>
</Box>
<Center>
<Pagination
value={page}
onChange={(newPage) => load(newPage)} // ini penting!
total={totalPages}
mt="md"
mb="md"
/>
</Center>
</Stack>
);
}
export default Page;

View File

@@ -1,83 +1,62 @@
'use client'
import colors from '@/con/colors'; import colors from '@/con/colors';
import { Box, Center, Image, List, ListItem, Paper, SimpleGrid, Stack, Text } from '@mantine/core'; import { Box, Center, Grid, GridCol, Image, Pagination, Paper, SimpleGrid, Skeleton, Stack, Text, TextInput } from '@mantine/core';
import BackButton from '../../desa/layanan/_com/BackButto'; import BackButton from '../../desa/layanan/_com/BackButto';
import { useProxy } from 'valtio/utils';
import tipsKeamananState from '@/app/admin/(dashboard)/_state/keamanan/tips-keamanan';
import { useShallowEffect } from '@mantine/hooks';
import { useState } from 'react';
import { IconSearch } from '@tabler/icons-react';
const data1 = [
{
id: 1,
judul: 'Keamanan Rumah',
image: '/api/img/kemanan.png',
deskripsi: <List>
<ListItem ta={'justify'} fz={{ base: 'h4', md: 'lg' }}>Pastikan pintu dan jendela selalu terkunci saat meninggalkan rumah.</ListItem>
<ListItem ta={'justify'} fz={{ base: 'h4', md: 'lg' }}>Pasang lampu penerangan di halaman dan area sekitar rumah untuk mencegah tindak kejahatan.</ListItem>
<ListItem ta={'justify'} fz={{ base: 'h4', md: 'lg' }}>Jangan mudah memberikan akses masuk ke orang yang tidak dikenal.</ListItem>
</List>
},
{
id: 2,
judul: 'Keamanan di Jalan',
image: '/api/img/keamananjalan.png',
deskripsi: <List>
<ListItem ta={'justify'} fz={{ base: 'h4', md: 'lg' }}>Hindari berjalan sendirian di tempat sepi, terutama pada malam hari.</ListItem>
<ListItem ta={'justify'} fz={{ base: 'h4', md: 'lg' }}>Simpan barang berharga di tempat yang aman saat bepergian.</ListItem>
<ListItem ta={'justify'} fz={{ base: 'h4', md: 'lg' }}>Gunakan jalur yang ramai dan terang saat pulang malam.</ListItem>
</List>
},
{
id: 3,
judul: 'Keamanan Kendaraan',
image: '/api/img/keamanankendaraan.png',
deskripsi: <List>
<ListItem ta={'justify'} fz={{ base: 'h4', md: 'lg' }}>Gunakan kunci ganda saat memarkir kendaraan, terutama di tempat umum.</ListItem>
<ListItem ta={'justify'} fz={{ base: 'h4', md: 'lg' }}>Parkir di tempat yang terang dan mudah diawasi.</ListItem>
<ListItem ta={'justify'} fz={{ base: 'h4', md: 'lg' }}>Jangan meninggalkan barang berharga di dalam kendaraan.</ListItem>
</List>
},
{
id: 4,
judul: 'Keamanan Sosial',
image: '/api/img/mencurigakan.png',
deskripsi: <List>
<ListItem ta={'justify'} fz={{ base: 'h4', md: 'lg' }}>Laporkan kejadian mencurigakan kepada Pecalang atau perangkat desa.</ListItem>
<ListItem ta={'justify'} fz={{ base: 'h4', md: 'lg' }}>Jangan mudah percaya terhadap informasi yang belum jelas sumbernya.</ListItem>
<ListItem ta={'justify'} fz={{ base: 'h4', md: 'lg' }}>Ikuti program sosialisasi keamanan yang diadakan oleh desa.</ListItem>
</List>
},
{
id: 5,
judul: 'Sistem Laporan Kejadian',
image: '/api/img/securitydigital.png',
deskripsi: <List>
<ListItem>Jangan mudah membagikan informasi pribadi di media sosial.</ListItem>
<ListItem>Waspada terhadap penipuan online dan telepon yang mengatasnamakan instansi resmi.</ListItem>
<ListItem>Gunakan kata sandi yang kuat untuk akun digital dan ganti secara berkala.</ListItem>
</List>
},
{
id: 6,
judul: 'Nomor Darurat yang Bisa Dihubungi',
image: '/api/img/kontakpecalang.png',
deskripsi: <List>
<ListItem ta={'justify'} fz={{ base: 'h4', md: 'lg' }}>Pecalang: 08125651052</ListItem>
<ListItem ta={'justify'} fz={{ base: 'h4', md: 'lg' }}>Ambulans: 08125651052</ListItem>
<ListItem ta={'justify'} fz={{ base: 'h4', md: 'lg' }}>Pemadam Kebakaran: 113</ListItem>
<ListItem ta={'justify'} fz={{ base: 'h4', md: 'lg' }}>Polisi: 110</ListItem>
</List>
}
]
function Page() { function Page() {
const state = useProxy(tipsKeamananState)
const [search, setSearch] = useState('')
const {
data,
page,
totalPages,
loading,
load,
} = state.findMany;
useShallowEffect(() => {
load(page, 3, search)
}, [page, search])
if (loading || !data) {
return (
<Stack pos={"relative"} bg={colors.Bg} py={"xl"} gap={"22"}>
<Skeleton h={500} />
</Stack>
)
}
return ( return (
<Stack pos={"relative"} bg={colors.Bg} py={"xl"} gap={"22"}> <Stack pos={"relative"} bg={colors.Bg} py={"xl"} gap={"22"}>
<Box px={{ base: 'md', md: 100 }}> <Box px={{ base: 'md', md: 100 }}>
<BackButton /> <BackButton />
</Box> </Box>
<Box> <Box>
<Text ta={"center"} fz={{ base: "h1", md: "2.5rem" }} c={colors["blue-button"]} fw={"bold"}> <Grid align='center' px={{ base: 'md', md: 100 }}>
Tips Keamanan <GridCol span={{ base: 12, md: 9 }}>
</Text> <Text fz={{ base: "h1", md: "2.5rem" }} c={colors["blue-button"]} fw={"bold"}>
<Text px={{ base: 20, md: 150 }} ta={"center"} fz={{ base: "h4", md: "h3" }} > Tips Keamanan
Desa Darmasaba berkomitmen untuk menjaga keamanan dan kenyamanan seluruh warganya. Berikut beberapa tips yang dapat membantu meningkatkan keamanan di lingkungan desa. </Text>
</GridCol>
<GridCol span={{ base: 12, md: 3 }}>
<TextInput
radius={"lg"}
placeholder='Cari Tips'
value={search}
onChange={(e) => setSearch(e.target.value)}
leftSection={<IconSearch size={20} />}
w={{ base: "50%", md: "100%" }}
/>
</GridCol>
</Grid>
<Text px={{ base: 'md', md: 100 }} ta={"justify"} fz={{ base: "h4", md: "h3" }} >
Keamanan dan ketertiban lingkungan di Desa Darmasaba dijaga melalui peran aktif Pecalang dan Patwal (Patroli Pengawal). Mereka bertugas memastikan desa tetap aman, tertib, dan kondusif bagi seluruh warga.
</Text> </Text>
</Box> </Box>
<Box px={{ base: "md", md: 100 }}> <Box px={{ base: "md", md: 100 }}>
@@ -88,21 +67,21 @@ function Page() {
base: 1, base: 1,
md: 3, md: 3,
}}> }}>
{data1.map((v, k) => { {data.map((v, k) => {
return ( return (
<Paper radius={10} key={k} bg={colors["white-trans-1"]}> <Paper radius={10} key={k} bg={colors["white-trans-1"]}>
<Stack gap={'xs'}> <Stack gap={'xs'}>
<Center p={10}> <Center p={10}>
<Image src={v.image} radius={10} <Image src={v.image?.link} radius={10}
alt='' /> alt='' />
</Center> </Center>
<Box px={'xl'}> <Box px={'xl'}>
<Box pb={20}> <Box pb={20}>
<Text pb={10} c={colors["blue-button"]} fw={"bold"} fz={"h3"}> <Text pb={10} c={colors["blue-button"]} fw={"bold"} fz={"h3"}>
{v.judul} {v.judul}
</Text> </Text>
<Box pr={10}> <Box>
{v.deskripsi} <Text pb={10} fz={"md"} dangerouslySetInnerHTML={{ __html: v.deskripsi }} />
</Box> </Box>
</Box> </Box>
</Box> </Box>
@@ -113,6 +92,14 @@ function Page() {
</SimpleGrid> </SimpleGrid>
</Stack> </Stack>
</Box> </Box>
<Center>
<Pagination
value={page}
onChange={(newPage) => load(newPage)} // ini penting!
total={totalPages}
my="md"
/>
</Center>
</Stack> </Stack>
); );
} }

View File

@@ -1,18 +1,32 @@
'use client' 'use client'
import daftarInformasiPublik from '@/app/admin/(dashboard)/_state/ppid/daftar_informasi_publik/daftarInformasiPublik'; import daftarInformasiPublik from '@/app/admin/(dashboard)/_state/ppid/daftar_informasi_publik/daftarInformasiPublik';
import colors from '@/con/colors'; import colors from '@/con/colors';
import { Box, Center, Image, Skeleton, Stack, Table, TableTbody, TableTd, TableTh, TableThead, TableTr, Text, TextInput } from '@mantine/core'; import { Box, Center, Image, Pagination, Skeleton, Stack, Table, TableTbody, TableTd, TableTh, TableThead, TableTr, Text, TextInput } from '@mantine/core';
import { useShallowEffect } from '@mantine/hooks'; import { useShallowEffect } from '@mantine/hooks';
import { IconSearch } from '@tabler/icons-react'; import { IconSearch } from '@tabler/icons-react';
import { useState } from 'react';
import { useProxy } from 'valtio/utils'; import { useProxy } from 'valtio/utils';
import BackButton from '../../desa/layanan/_com/BackButto'; import BackButton from '../../desa/layanan/_com/BackButto';
function Page() { function Page() {
const listData = useProxy(daftarInformasiPublik) const listData = useProxy(daftarInformasiPublik)
const [search, setSearch] = useState('')
const {
data,
page,
totalPages,
loading,
load,
} = listData.findMany
useShallowEffect(() => { useShallowEffect(() => {
listData.findMany.load() load(page, 5, search)
}, []) }, [page, search])
if (!listData.findMany.data) return <Stack pos={"relative"} bg={colors.Bg} py={"xl"} gap={"22"}>
if (loading || !data) return <Stack pos={"relative"} bg={colors.Bg} py={"xl"} gap={"22"}>
<Box px={{ base: 'md', md: 100 }}> <Box px={{ base: 'md', md: 100 }}>
<Skeleton h={40} /> <Skeleton h={40} />
</Box> </Box>
@@ -40,6 +54,9 @@ function Page() {
<TextInput <TextInput
placeholder='Cari Informasi...' placeholder='Cari Informasi...'
leftSection={<IconSearch size={20} />} leftSection={<IconSearch size={20} />}
value={search}
onChange={(e) => setSearch(e.target.value)}
style={{ marginBottom: 16 }}
/> />
<Table withRowBorders withColumnBorders withTableBorder> <Table withRowBorders withColumnBorders withTableBorder>
<TableThead bg={colors['blue-button']}> <TableThead bg={colors['blue-button']}>
@@ -54,8 +71,17 @@ function Page() {
{listData.findMany.data?.map((item, index) => ( {listData.findMany.data?.map((item, index) => (
<TableTr key={item.id}> <TableTr key={item.id}>
<TableTd ta={'center'}>{index + 1}</TableTd> <TableTd ta={'center'}>{index + 1}</TableTd>
<TableTd>{item.jenisInformasi}</TableTd> <TableTd>
<TableTd dangerouslySetInnerHTML={{ __html: item.deskripsi }}></TableTd> <Text fz={'md'}>
{item.jenisInformasi}
</Text>
</TableTd>
<TableTd>
<Text
fz={'md'}
dangerouslySetInnerHTML={{ __html: item.deskripsi }}
/>
</TableTd>
<TableTd style={{ width: '20%', textAlign: 'center' }}>{item.tanggal <TableTd style={{ width: '20%', textAlign: 'center' }}>{item.tanggal
? new Date(item.tanggal).toLocaleDateString('id-ID') ? new Date(item.tanggal).toLocaleDateString('id-ID')
: '-'}</TableTd> : '-'}</TableTd>
@@ -64,6 +90,14 @@ function Page() {
</TableTbody> </TableTbody>
</Table> </Table>
</Stack> </Stack>
<Center>
<Pagination
value={page}
onChange={(newPage) => load(newPage)}
total={totalPages}
my={"md"}
/>
</Center>
<Text pt={20} fz={'h4'} fw={"bold"}>Kontak PPID</Text> <Text pt={20} fz={'h4'} fw={"bold"}>Kontak PPID</Text>
<Text fz={'sm'}>Email: ppid@desadarmasaba.id | WhatsApp: 081-xxx-xxx-xxx</Text> <Text fz={'sm'}>Email: ppid@desadarmasaba.id | WhatsApp: 081-xxx-xxx-xxx</Text>
</Box> </Box>

View File

@@ -4,7 +4,7 @@
<head> <head>
<meta charset="UTF-8" /> <meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" /> <meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>HIPMI Feature Checklist</title> <title>Desa Darmasaba Feature Checklist</title>
<style> <style>
body { body {
font-family: Arial, sans-serif; font-family: Arial, sans-serif;
@@ -267,284 +267,262 @@
</li> </li>
</ul> </ul>
<!-- SubMenu Indeks Kepuasan Masayarakat (IKM) (Landing Page - PPID) --> <!-- SubMenu Indeks Kepuasan Masayarakat (IKM) (Landing Page - PPID) -->
<h4>Indeks Kepuasan Masayarakat (IKM) (Landing Page - PPID)</h4> <h4>Indeks Kepuasan Masayarakat (IKM) (Landing Page - PPID)</h4>
<!-- List Responden --> <!-- List Responden -->
<h4>List Desa Anti Korupsi</h4> <h4>List Responden</h4>
<ul> <ul>
<li> <li>
<input type="checkbox" id="19" /> <label for="19">Search sudah berfungsi</label> <input type="checkbox" id="33" />
</li> <label for="33">Search sudah berfungsi</label>
<li> </li>
<input type="checkbox" id="20" /> <label for="20">Data tampil di halaman list</label> <li>
<input type="checkbox" id="34" />
<label for="34">Data tampil di halaman list</label>
</li>
<li>
<input type="checkbox" id="35" />
<label for="35">Create list responden</label>
</li>
<li>
<input type="checkbox" id="36" />
<label for="36">Detail mau tampil datanya</label>
</li>
<li>
<input type="checkbox" id="37" />
<label for="37">Hapus list responden bekerja</label>
</li>
<li>
<input type="checkbox" id="38" />
<label for="38">Edit / Update list responden bekerja</label>
</li>
<li>
<input type="checkbox" id="39" />
<label for="39">Pagination bekerja</label>
</li> </li>
<li> <li>
<input type="checkbox" id="21" /> <input type="checkbox" id="40" />
<label for="21">Create list desa anti korupsi</label> <label for="40">Sinkronisasi ke UI</label>
</li> </li>
<li>
<input type="checkbox" id="22" />
<label for="22">Detail mau tampil datanya</label>
</li>
<li>
<input type="checkbox" id="23" />
<label for="23">Hapus list desa anti korupsi bekerja</label>
</li>
<li>
<input type="checkbox" id="24" />
<label for="24">Edit / Update list desa anti korupsi bekerja</label>
</li>
<li>
<h4>List Responden</h4>
<ul>
<li>
<input type="checkbox" id="33" />
<label for="33">Search sudah berfungsi</label>
</li>
<li>
<input type="checkbox" id="34" />
<label for="34">Data tampil di halaman list</label>
</li>
<li>
<input type="checkbox" id="35" />
<label for="35">Create list responden</label>
</li>
<li>
<input type="checkbox" id="36" />
<label for="36">Detail mau tampil datanya</label>
</li>
<li>
<input type="checkbox" id="37" />
<label for="37">Hapus list responden bekerja</label>
</li>
<li>
<input type="checkbox" id="38" />
<label for="38">Edit / Update list responden bekerja</label>
</li>
<li>
<input type="checkbox" id="39" />
<label for="39">Pagination bekerja</label>
</li>
<li>
<input type="checkbox" id="40" />
<label for="40">Sinkronisasi ke UI</label>
</li>
</ul>
<!-- SubMenu SDGs Desa --> </ul>
<h4>SDGs Desa</h4>
<!-- List SDGs Desa --> <!-- SubMenu SDGs Desa -->
<h4>List SDGs Desa</h4> <h4>SDGs Desa</h4>
<!-- List SDGs Desa -->
<h4>List SDGs Desa</h4>
<ul>
<li>
<input type="checkbox" id="41" />
<label for="41">Search sudah berfungsi</label>
</li>
<li>
<input type="checkbox" id="42" />
<label for="42">Data tampil di halaman list</label>
</li>
<li>
<input type="checkbox" id="43" />
<label for="43">Create list SDGs Desa</label>
</li>
<li>
<input type="checkbox" id="44" />
<label for="44">Detail mau tampil datanya</label>
</li>
<li>
<input type="checkbox" id="45" />
<label for="45">Hapus list SDGs Desa bekerja</label>
</li>
<li>
<input type="checkbox" id="46" />
<label for="46">Edit / Update list SDGs Desa bekerja</label>
</li>
<li>
<input type="checkbox" id="47" />
<label for="47">Pagination bekerja</label>
</li>
<li>
<input type="checkbox" id="48" />
<label for="48">Sinkronisasi ke UI</label>
</li>
</ul>
<!-- SubMenu APBDes -->
<h4>APBDes</h4>
<!-- List APBDes -->
<h4>List APBDes</h4>
<ul>
<li>
<input type="checkbox" id="49" />
<label for="49">Search sudah berfungsi</label>
</li>
<li>
<input type="checkbox" id="50" />
<label for="50">Data tampil di halaman list</label>
</li>
<li>
<input type="checkbox" id="51" />
<label for="51">Create list APBDes</label>
</li>
<li>
<input type="checkbox" id="52" />
<label for="52">Detail mau tampil datanya</label>
</li>
<li>
<input type="checkbox" id="53" />
<label for="53">Hapus list APBDes bekerja</label>
</li>
<li>
<input type="checkbox" id="54" />
<label for="54">Edit / Update list APBDes bekerja</label>
</li>
<li>
<input type="checkbox" id="55" />
<label for="55">Pagination bekerja</label>
</li>
<li>
<input type="checkbox" id="56" />
<label for="56">Sinkronisasi ke UI</label>
</li>
</ul>
<!-- SubMenu Prestasi Desa -->
<h4>Prestasi Desa</h4>
<!-- List Prestasi Desa -->
<h4>List Prestasi Desa</h4>
<ul> <ul>
<li> <li>
<input type="checkbox" id="41" /> <input type="checkbox" id="57" />
<label for="41">Search sudah berfungsi</label> <label for="57">Search sudah berfungsi</label>
</li> </li>
<li> <li>
<input type="checkbox" id="42" /> <input type="checkbox" id="58" />
<label for="42">Data tampil di halaman list</label> <label for="58">Data tampil di halaman list</label>
</li> </li>
<li> <li>
<input type="checkbox" id="43" /> <input type="checkbox" id="59" />
<label for="43">Create list SDGs Desa</label> <label for="59">Create list Prestasi Desa</label>
</li> </li>
<li> <li>
<input type="checkbox" id="44" /> <input type="checkbox" id="60" />
<label for="44">Detail mau tampil datanya</label> <label for="60">Detail mau tampil datanya</label>
</li> </li>
<li> <li>
<input type="checkbox" id="45" /> <input type="checkbox" id="61" />
<label for="45">Hapus list SDGs Desa bekerja</label> <label for="61">Hapus list Prestasi Desa bekerja</label>
</li> </li>
<li> <li>
<input type="checkbox" id="46" /> <input type="checkbox" id="62" />
<label for="46">Edit / Update list SDGs Desa bekerja</label> <label for="62">Edit / Update list Prestasi Desa bekerja</label>
</li> </li>
<li> <li>
<input type="checkbox" id="47" /> <input type="checkbox" id="63" />
<label for="47">Pagination bekerja</label> <label for="63">Pagination bekerja</label>
</li> </li>
<li> <li>
<input type="checkbox" id="48" /> <input type="checkbox" id="64" />
<label for="48">Sinkronisasi ke UI</label> <label for="64">Sinkronisasi ke UI</label>
</li> </li>
</ul> </ul>
<!-- SubMenu APBDes --> <!-- Menu PPID -->
<h4>APBDes</h4> <h3>Menu PPID</h3>
<!-- List APBDes -->
<h4>List APBDes</h4> <!-- SubMenu Profile PPID -->
<ul> <h4>Profile PPID</h4>
<li> <ul>
<input type="checkbox" id="49" /> <li>
<label for="49">Search sudah berfungsi</label> <input type="checkbox" id="65" />
</li> <label for="65">Data tampil di halaman</label>
<li> </li>
<input type="checkbox" id="50" /> <li>
<label for="50">Data tampil di halaman list</label> <input type="checkbox" id="66" />
</li> <label for="66">Edit / Update Profile PPID</label>
<li> </li>
<input type="checkbox" id="51" /> <li>
<label for="51">Create list APBDes</label> <input type="checkbox" id="67" />
</li> <label for="67">Sinkronisasi ke UI</label>
<li> </li>
<input type="checkbox" id="52" /> </ul>
<label for="52">Detail mau tampil datanya</label>
</li> <!-- SubMenu Struktur PPID -->
<li> <h4>Struktur PPID</h4>
<input type="checkbox" id="53" /> <h4>Pegawai</h4>
<label for="53">Hapus list APBDes bekerja</label> <ul>
</li> <li>
<li> <input type="checkbox" id="68" />
<input type="checkbox" id="54" /> <label for="68">Search sudah berfungsi</label>
<label for="54">Edit / Update list APBDes bekerja</label> </li>
</li> <li>
<li> <input type="checkbox" id="69" />
<input type="checkbox" id="55" /> <label for="69">Data tampil di halaman list</label>
<label for="55">Pagination bekerja</label> </li>
</li> <li>
<li> <input type="checkbox" id="70" />
<input type="checkbox" id="56" /> <label for="70">Create list Pegawai</label>
<label for="56">Sinkronisasi ke UI</label> </li>
<li>
<input type="checkbox" id="71" />
<label for="71">Detail mau tampil datanya</label>
</li>
<li>
<input type="checkbox" id="72" />
<label for="72">Hapus list Pegawai bekerja</label>
</li>
<li>
<input type="checkbox" id="73" />
<label for="73">Edit / Update list Pegawai bekerja</label>
</li>
<li>
<input type="checkbox" id="74" />
<label for="74">Pagination bekerja</label>
</li>
<li>
<input type="checkbox" id="75" />
<label for="75">Sinkronisasi ke UI</label>
</li> </li>
</ul> </ul>
<!-- SubMenu Prestasi Desa -->
<h4>Prestasi Desa</h4>
<!-- List Prestasi Desa -->
<h4>List Prestasi Desa</h4>
<ul>
<li>
<input type="checkbox" id="57" />
<label for="57">Search sudah berfungsi</label>
</li>
<li>
<input type="checkbox" id="58" />
<label for="58">Data tampil di halaman list</label>
</li>
<li>
<input type="checkbox" id="59" />
<label for="59">Create list Prestasi Desa</label>
</li>
<li>
<input type="checkbox" id="60" />
<label for="60">Detail mau tampil datanya</label>
</li>
<li>
<input type="checkbox" id="61" />
<label for="61">Hapus list Prestasi Desa bekerja</label>
</li>
<li>
<input type="checkbox" id="62" />
<label for="62">Edit / Update list Prestasi Desa bekerja</label>
</li>
<li>
<input type="checkbox" id="63" />
<label for="63">Pagination bekerja</label>
</li>
<li>
<input type="checkbox" id="64" />
<label for="64">Sinkronisasi ke UI</label>
</li>
</ul>
<!-- Menu PPID -->
<h3>Menu PPID</h3>
<!-- SubMenu Profile PPID -->
<h4>Profile PPID</h4>
<ul>
<li>
<input type="checkbox" id="65" />
<label for="65">Data tampil di halaman</label>
</li>
<li>
<input type="checkbox" id="66" />
<label for="66">Edit / Update Profile PPID</label>
</li>
<li>
<input type="checkbox" id="67" />
<label for="67">Sinkronisasi ke UI</label>
</li>
</ul>
<!-- SubMenu Struktur PPID -->
<h4>Struktur PPID</h4>
<h4>Pegawai</h4>
<ul>
<li>
<input type="checkbox" id="68" />
<label for="68">Search sudah berfungsi</label>
</li>
<li>
<input type="checkbox" id="69" />
<label for="69">Data tampil di halaman list</label>
</li>
<li>
<input type="checkbox" id="70" />
<label for="70">Create list Pegawai</label>
</li>
<li>
<input type="checkbox" id="71" />
<label for="71">Detail mau tampil datanya</label>
</li>
<li>
<input type="checkbox" id="72" />
<label for="72">Hapus list Pegawai bekerja</label>
</li>
<li>
<input type="checkbox" id="73" />
<label for="73">Edit / Update list Pegawai bekerja</label>
</li>
<li>
<input type="checkbox" id="74" />
<label for="74">Pagination bekerja</label>
</li>
<li>
<input type="checkbox" id="75" />
<label for="75">Sinkronisasi ke UI</label>
</li>
</ul>
<h4>Posisi Organisasi PPID</h4> <h4>Posisi Organisasi PPID</h4>
<ul> <ul>
<li> <li>
<input type="checkbox" id="76" /> <input type="checkbox" id="76" />
<label for="76">Search sudah berfungsi</label> <label for="76">Search sudah berfungsi</label>
</li> </li>
<li> <li>
<input type="checkbox" id="77" /> <input type="checkbox" id="77" />
<label for="77">Data tampil di halaman list</label> <label for="77">Data tampil di halaman list</label>
</li> </li>
<li> <li>
<input type="checkbox" id="78" /> <input type="checkbox" id="78" />
<label for="78">Create list Posisi Organisasi PPID</label> <label for="78">Create list Posisi Organisasi PPID</label>
</li> </li>
<li> <li>
<input type="checkbox" id="79" /> <input type="checkbox" id="79" />
<label for="79">Detail mau tampil datanya</label> <label for="79">Detail mau tampil datanya</label>
</li> </li>
<li> <li>
<input type="checkbox" id="80" /> <input type="checkbox" id="80" />
<label for="80">Hapus list Posisi Organisasi PPID bekerja</label> <label for="80">Hapus list Posisi Organisasi PPID bekerja</label>
</li> </li>
<li> <li>
<input type="checkbox" id="81" /> <input type="checkbox" id="81" />
<label for="81">Edit / Update list Posisi Organisasi PPID bekerja</label> <label for="81">Edit / Update list Posisi Organisasi PPID bekerja</label>
</li> </li>
<li> <li>
<input type="checkbox" id="82" /> <input type="checkbox" id="82" />
<label for="82">Pagination bekerja</label> <label for="82">Pagination bekerja</label>
</li> </li>
<li> <li>
<input type="checkbox" id="83" /> <input type="checkbox" id="83" />
<label for="83">Sinkronisasi ke UI</label> <label for="83">Sinkronisasi ke UI</label>
</li> </li>
</ul> </ul>
<!-- SubMenu Visi Misi PPID --> <!-- SubMenu Visi Misi PPID -->
<h4>Visi Misi PPID</h4> <h4>Visi Misi PPID</h4>
<ul> <ul>
<li> <li>
<input type="checkbox" id="84" /> <input type="checkbox" id="84" />
<label for="84">Data tampil di halaman</label> <label for="84">Data tampil di halaman</label>
</li> </li>
<li> <li>
@@ -558,27 +536,27 @@
</ul> </ul>
<!-- SubMenu Dasar Hukum PPID --> <!-- SubMenu Dasar Hukum PPID -->
<h4>Dasar Hukum PPID</h4> <h4>Dasar Hukum PPID</h4>
<ul> <ul>
<li> <li>
<input type="checkbox" id="87" /> <input type="checkbox" id="87" />
<label for="87">Data tampil di halaman</label> <label for="87">Data tampil di halaman</label>
</li> </li>
<li> <li>
<input type="checkbox" id="88" /> <input type="checkbox" id="88" />
<label for="88">Edit / Update Dasar Hukum PPID</label> <label for="88">Edit / Update Dasar Hukum PPID</label>
</li> </li>
<li> <li>
<input type="checkbox" id="89" /> <input type="checkbox" id="89" />
<label for="89">Sinkronisasi ke UI</label> <label for="89">Sinkronisasi ke UI</label>
</li> </li>
</ul> </ul>
<!-- SubMenu Permohonan Informasi Publik --> <!-- SubMenu Permohonan Informasi Publik -->
<h4>Permohonan Informasi Publik</h4> <h4>Permohonan Informasi Publik</h4>
<ul> <ul>
<li> <li>
<input type="checkbox" id="90" /> <input type="checkbox" id="90" />
<label for="90">Data tampil di halaman</label> <label for="90">Data tampil di halaman</label>
</li> </li>
<li> <li>
@@ -591,22 +569,65 @@
</li> </li>
</ul> </ul>
<!-- SubMenu Permohonan Keberatan Informasi Publik --> <!-- SubMenu Permohonan Keberatan Informasi Publik -->
<h4>Permohonan Keberatan Informasi Publik</h4> <h4>Permohonan Keberatan Informasi Publik</h4>
<ul> <ul>
<li> <li>
<input type="checkbox" id="93" /> <input type="checkbox" id="93" />
<label for="93">Data tampil di halaman</label> <label for="93">Data tampil di halaman</label>
</li> </li>
<li> <li>
<input type="checkbox" id="94" /> <input type="checkbox" id="94" />
<label for="94">Create Permohonan Keberatan Informasi Publik</label> <label for="94">Create Permohonan Keberatan Informasi Publik</label>
</li> </li>
<li> <li>
<input type="checkbox" id="95" /> <input type="checkbox" id="95" />
<label for="95">Sinkronisasi ke UI</label> <label for="95">Sinkronisasi ke UI</label>
</li> </li>
</ul> </ul>
<!-- SubMenu Daftar Informasi Publik -->
<h4>Daftar Informasi Publik</h4>
<ul>
<li>
<input type="checkbox" id="96" />
<label for="96">Search sudah berfungsi</label>
</li>
<li>
<input type="checkbox" id="97" />
<label for="97">Data tampil di halaman list</label>
</li>
<li>
<input type="checkbox" id="98" />
<label for="98">Create list daftar informasi publik</label>
</li>
<li>
<input type="checkbox" id="99" />
<label for="99">Detail mau tampil datanya</label>
</li>
<li>
<input type="checkbox" id="100" />
<label for="100">Hapus list daftar informasi publik bekerja</label>
</li>
<li>
<input type="checkbox" id="101" />
<label for="101">Edit / Update list daftar informasi publik bekerja</label>
</li>
<li>
<input type="checkbox" id="102" />
<label for="102">Pagination bekerja</label>
</li>
<li>
<input type="checkbox" id="103" />
<label for="103">Sinkronisasi ke UI</label>
</li>
</ul>
</div> </div>