diff --git a/src/app/admin/(dashboard)/_state/ekonomi/pasar-desa/pasar-desa.ts b/src/app/admin/(dashboard)/_state/ekonomi/pasar-desa/pasar-desa.ts index 21d8e7b4..c25a99ff 100644 --- a/src/app/admin/(dashboard)/_state/ekonomi/pasar-desa/pasar-desa.ts +++ b/src/app/admin/(dashboard)/_state/ekonomi/pasar-desa/pasar-desa.ts @@ -312,15 +312,15 @@ const kategoriProduk = proxy({ page: 1, totalPages: 1, loading: false, - search2: "", - load: async (page = 1, limit = 10, search2 = "") => { + search: "", + load: async (page = 1, limit = 10, search = "") => { kategoriProduk.findMany.loading = true; // ✅ Akses langsung via nama path kategoriProduk.findMany.page = page; - kategoriProduk.findMany.search2 = search2; + kategoriProduk.findMany.search = search; try { const query: any = { page, limit }; - if (search2) query.search2 = search2; + if (search) query.search = search; const res = await ApiFetch.api.ekonomi.kategoriproduk["find-many"].get({ query }); diff --git a/src/app/admin/(dashboard)/_state/ekonomi/struktur-organisasi/struktur-organisasi.ts b/src/app/admin/(dashboard)/_state/ekonomi/struktur-organisasi/struktur-organisasi.ts index cef5c3fa..e347f63c 100644 --- a/src/app/admin/(dashboard)/_state/ekonomi/struktur-organisasi/struktur-organisasi.ts +++ b/src/app/admin/(dashboard)/_state/ekonomi/struktur-organisasi/struktur-organisasi.ts @@ -194,7 +194,7 @@ const posisiOrganisasi = proxy({ try { this.loading = true; - const res = await ApiFetch.api.ekonomi['struktur-organisasi']['posisi-organisasi']['create'].post(this.form); + const res = await ApiFetch.api.ekonomi["struktur-organisasi"]["posisi-organisasi"]["create"].post(this.form); if (res.status === 200) { toast.success("Berhasil menambahkan posisi organisasi"); posisiOrganisasi.findMany.load(); diff --git a/src/app/admin/(dashboard)/_state/landing-page/indeks-kepuasan.ts b/src/app/admin/(dashboard)/_state/landing-page/indeks-kepuasan.ts index 48d707b7..9caeb019 100644 --- a/src/app/admin/(dashboard)/_state/landing-page/indeks-kepuasan.ts +++ b/src/app/admin/(dashboard)/_state/landing-page/indeks-kepuasan.ts @@ -60,13 +60,18 @@ const responden = proxy({ totalPages: 1, total: 0, loading: false, - load: async (page = 1, limit = 10) => { + search: "", + load: async (page = 1, limit = 10, search = "") => { // Change to arrow function responden.findMany.loading = true; // Use the full path to access the property responden.findMany.page = page; + responden.findMany.search = search; try { + const query: any = { page, limit }; + if (search) query.search = search; + const res = await ApiFetch.api.landingpage.responden["findMany"].get({ - query: { page, limit }, + query, }); if (res.status === 200 && res.data?.success) { diff --git a/src/app/admin/(dashboard)/_state/pendidikan/data-pendidikan.ts b/src/app/admin/(dashboard)/_state/pendidikan/data-pendidikan.ts index f8ffa416..5e6d4cb1 100644 --- a/src/app/admin/(dashboard)/_state/pendidikan/data-pendidikan.ts +++ b/src/app/admin/(dashboard)/_state/pendidikan/data-pendidikan.ts @@ -1,3 +1,4 @@ +/* eslint-disable @typescript-eslint/no-explicit-any */ import ApiFetch from "@/lib/api-fetch"; import { Prisma } from "@prisma/client"; import { toast } from "react-toastify"; @@ -65,13 +66,46 @@ const dataPendidikan = proxy({ select: { id: true; name: true; jumlah: true }; }>[] | null, + page: 1, + totalPages: 1, + total: 0, loading: false, - async load() { - const res = await ApiFetch.api.pendidikan.datapendidikan[ - "findMany" - ].get(); - if (res.status === 200) { - dataPendidikan.findMany.data = res.data?.data ?? []; + search: "", + load: async (page = 1, limit = 10, search = "") => { + // Change to arrow function + dataPendidikan.findMany.loading = true; // Use the full path to access the property + dataPendidikan.findMany.page = page; + dataPendidikan.findMany.search = search; + try { + const query: any = { page, limit }; + if (search) query.search = search; + + const res = await ApiFetch.api.pendidikan.datapendidikan[ + "findMany" + ].get({ + query, + }); + + if (res.status === 200 && res.data?.success) { + dataPendidikan.findMany.data = res.data.data || []; + dataPendidikan.findMany.total = res.data.total || 0; + dataPendidikan.findMany.totalPages = res.data.totalPages || 1; + } else { + console.error( + "Failed to load data pendidikan:", + res.data?.message + ); + dataPendidikan.findMany.data = []; + dataPendidikan.findMany.total = 0; + dataPendidikan.findMany.totalPages = 1; + } + } catch (error) { + console.error("Error loading data pendidikan:", error); + dataPendidikan.findMany.data = []; + dataPendidikan.findMany.total = 0; + dataPendidikan.findMany.totalPages = 1; + } finally { + dataPendidikan.findMany.loading = false; } }, }, diff --git a/src/app/admin/(dashboard)/_state/user/user-state.ts b/src/app/admin/(dashboard)/_state/user/user-state.ts index e3864904..c049d703 100644 --- a/src/app/admin/(dashboard)/_state/user/user-state.ts +++ b/src/app/admin/(dashboard)/_state/user/user-state.ts @@ -220,11 +220,34 @@ const roleState = proxy({ isActive: true; }; }>[], + page: 1, + totalPages: 1, loading: false, - async load() { - const res = await ApiFetch.api.role["findMany"].get(); - if (res.status === 200) { - roleState.findMany.data = res.data?.data ?? []; + search: "", + load: async (page = 1, limit = 10, search = "") => { + roleState.findMany.loading = true; // ✅ Akses langsung via nama path + roleState.findMany.page = page; + roleState.findMany.search = search; + + try { + const query: any = { page, limit }; + if (search) query.search = search; + + const res = await ApiFetch.api.role["findMany"].get({ query }); + + if (res.status === 200 && res.data?.success) { + roleState.findMany.data = res.data.data ?? []; + roleState.findMany.totalPages = res.data.totalPages ?? 1; + } else { + roleState.findMany.data = []; + roleState.findMany.totalPages = 1; + } + } catch (err) { + console.error("Gagal fetch role paginated:", err); + roleState.findMany.data = []; + roleState.findMany.totalPages = 1; + } finally { + roleState.findMany.loading = false; } }, }, diff --git a/src/app/admin/(dashboard)/desa/_com/layoutTabLayanan.tsx b/src/app/admin/(dashboard)/desa/_com/layoutTabLayanan.tsx index 56029f20..c0cf829b 100644 --- a/src/app/admin/(dashboard)/desa/_com/layoutTabLayanan.tsx +++ b/src/app/admin/(dashboard)/desa/_com/layoutTabLayanan.tsx @@ -73,17 +73,17 @@ function LayoutTabsLayanan({ children }: { children: React.ReactNode }) { > {/* ✅ Scroll horizontal wrapper */} - + {tabs.map((tab, i) => ( diff --git a/src/app/admin/(dashboard)/desa/gallery/video/page.tsx b/src/app/admin/(dashboard)/desa/gallery/video/page.tsx index f642873e..df15c04d 100644 --- a/src/app/admin/(dashboard)/desa/gallery/video/page.tsx +++ b/src/app/admin/(dashboard)/desa/gallery/video/page.tsx @@ -88,63 +88,65 @@ function ListVideo({ search }: { search: string }) { {/* Desktop Table */} - - - - Judul Video - Tanggal - Deskripsi - Aksi - - - - {filteredData.length > 0 ? ( - filteredData.map((item) => ( - - - - {item.name} - - - - - {new Date(item.createdAt).toLocaleDateString('id-ID', { - day: 'numeric', - month: 'long', - year: 'numeric', - })} - - - - - - - + +
+ + + Judul Video + Tanggal + Deskripsi + Aksi + + + + {filteredData.length > 0 ? ( + filteredData.map((item) => ( + + + + {item.name} + + + + + {new Date(item.createdAt).toLocaleDateString('id-ID', { + day: 'numeric', + month: 'long', + year: 'numeric', + })} + + + + + + + + + + )) + ) : ( + + +
+ + Tidak ada video yang cocok + +
- )) - ) : ( - - -
- - Tidak ada video yang cocok - -
-
-
- )} -
-
+ )} + + +
{/* Mobile Cards */} diff --git a/src/app/admin/(dashboard)/desa/layanan/pelayanan_penduduk_non_permanent/page.tsx b/src/app/admin/(dashboard)/desa/layanan/pelayanan_penduduk_non_permanent/page.tsx index f6b93484..48a3e4dd 100644 --- a/src/app/admin/(dashboard)/desa/layanan/pelayanan_penduduk_non_permanent/page.tsx +++ b/src/app/admin/(dashboard)/desa/layanan/pelayanan_penduduk_non_permanent/page.tsx @@ -5,8 +5,7 @@ import { Button, Center, Divider, - Grid, - GridCol, + Group, Paper, Skeleton, Stack, @@ -43,32 +42,29 @@ function PelayananPendudukNonPermanent() { {/* Header */} - - - - Preview Pelayanan Penduduk Non Permanen - - - - - - + + + + Preview Pelayanan Penduduk Non Permanen + + + {/* Content */} diff --git a/src/app/admin/(dashboard)/desa/layanan/pelayanan_perizinan_berusaha/page.tsx b/src/app/admin/(dashboard)/desa/layanan/pelayanan_perizinan_berusaha/page.tsx index 43854e4c..712eadc1 100644 --- a/src/app/admin/(dashboard)/desa/layanan/pelayanan_perizinan_berusaha/page.tsx +++ b/src/app/admin/(dashboard)/desa/layanan/pelayanan_perizinan_berusaha/page.tsx @@ -6,8 +6,6 @@ import { Button, Center, Divider, - Grid, - GridCol, Group, Paper, Skeleton, @@ -76,28 +74,24 @@ function PerizinanBerusaha() { {/* Header */} - - - - Preview Pelayanan Perizinan Berusaha - - - - - - + + + Preview Pelayanan Perizinan Berusaha + + + {/* Content */} @@ -136,7 +130,7 @@ function PerizinanBerusaha() { umum: - + - + - + - + - + - + - + - + diff --git a/src/app/admin/(dashboard)/ekonomi/PADesa-pendapatan-asli-desa/apbdesa/page.tsx b/src/app/admin/(dashboard)/ekonomi/PADesa-pendapatan-asli-desa/apbdesa/page.tsx index cc7cf319..5015c2a4 100644 --- a/src/app/admin/(dashboard)/ekonomi/PADesa-pendapatan-asli-desa/apbdesa/page.tsx +++ b/src/app/admin/(dashboard)/ekonomi/PADesa-pendapatan-asli-desa/apbdesa/page.tsx @@ -166,7 +166,7 @@ function ListAPBDesa({ search }: { search: string }) { @@ -176,7 +175,6 @@ function ListPendapatan({ search }: { search: string }) { px="xs" > - Hapus diff --git a/src/app/admin/(dashboard)/ekonomi/Struktur-Organisasi-Dan-Sk-Pengurus-BumDes/posisi-organisasi/page.tsx b/src/app/admin/(dashboard)/ekonomi/Struktur-Organisasi-Dan-Sk-Pengurus-BumDes/posisi-organisasi/page.tsx index 26d9dba1..a86edc28 100644 --- a/src/app/admin/(dashboard)/ekonomi/Struktur-Organisasi-Dan-Sk-Pengurus-BumDes/posisi-organisasi/page.tsx +++ b/src/app/admin/(dashboard)/ekonomi/Struktur-Organisasi-Dan-Sk-Pengurus-BumDes/posisi-organisasi/page.tsx @@ -153,9 +153,14 @@ function ListPosisiOrganisasiBumDes({ search }: { search: string }) { - - {item.deskripsi || '-'} - + {item.hierarki || '-'} @@ -223,9 +228,14 @@ function ListPosisiOrganisasiBumDes({ search }: { search: string }) { Deskripsi - - {item.deskripsi || '-'} - + diff --git a/src/app/admin/(dashboard)/ekonomi/demografi-pekerjaan/page.tsx b/src/app/admin/(dashboard)/ekonomi/demografi-pekerjaan/page.tsx index b65b6172..b3b14f74 100644 --- a/src/app/admin/(dashboard)/ekonomi/demografi-pekerjaan/page.tsx +++ b/src/app/admin/(dashboard)/ekonomi/demografi-pekerjaan/page.tsx @@ -21,7 +21,7 @@ import { Text, Title } from '@mantine/core'; -import { useShallowEffect } from '@mantine/hooks'; +import { useDebouncedValue, useShallowEffect } from '@mantine/hooks'; import { IconEdit, IconPlus, IconSearch, IconTrash } from '@tabler/icons-react'; import { useRouter } from 'next/navigation'; import { useEffect, useState } from 'react'; @@ -59,6 +59,8 @@ function ListDemografiPekerjaan({ search }: { search: string }) { const [chartData, setChartData] = useState([]); const [mounted, setMounted] = useState(false); const [modalHapus, setModalHapus] = useState(false); + const [debouncedSearch] = useDebouncedValue(search, 1000); // 500ms delay + const [selectedId, setSelectedId] = useState(null); const { @@ -79,8 +81,8 @@ function ListDemografiPekerjaan({ search }: { search: string }) { useShallowEffect(() => { setMounted(true); - load(page, 10, search); - }, [page, search]); + load(page, 10, debouncedSearch); + }, [page, debouncedSearch]); useEffect(() => { if (data) { diff --git a/src/app/admin/(dashboard)/ekonomi/pasar-desa/kategori-produk/page.tsx b/src/app/admin/(dashboard)/ekonomi/pasar-desa/kategori-produk/page.tsx index 7fd3749c..048d4099 100644 --- a/src/app/admin/(dashboard)/ekonomi/pasar-desa/kategori-produk/page.tsx +++ b/src/app/admin/(dashboard)/ekonomi/pasar-desa/kategori-produk/page.tsx @@ -28,27 +28,27 @@ import { ModalKonfirmasiHapus } from '../../../_com/modalKonfirmasiHapus'; import pasarDesaState from '../../../_state/ekonomi/pasar-desa/pasar-desa'; function KategoriProduk() { - const [search2, setSearch2] = useState(''); + const [search, setSearch] = useState(''); return ( } - value={search2} - onChange={(e) => setSearch2(e.currentTarget.value)} + value={search} + onChange={(e) => setSearch(e.currentTarget.value)} /> - + ); } -function ListKategoriProduk({ search2 }: { search2: string }) { +function ListKategoriProduk({ search }: { search: string }) { const statePasar = useProxy(pasarDesaState.kategoriProduk); const [modalHapus, setModalHapus] = useState(false); const [selectedId, setSelectedId] = useState(null); const router = useRouter(); - const [debouncedSearch] = useDebouncedValue(search2, 1000); + const [debouncedSearch] = useDebouncedValue(search, 1000); const { data, page, totalPages, loading, load } = statePasar.findMany; diff --git a/src/app/admin/(dashboard)/inovasi/kolaborasi-inovasi/list-kolaborasi-inovasi/page.tsx b/src/app/admin/(dashboard)/inovasi/kolaborasi-inovasi/list-kolaborasi-inovasi/page.tsx index ea69ed22..77ef345e 100644 --- a/src/app/admin/(dashboard)/inovasi/kolaborasi-inovasi/list-kolaborasi-inovasi/page.tsx +++ b/src/app/admin/(dashboard)/inovasi/kolaborasi-inovasi/list-kolaborasi-inovasi/page.tsx @@ -142,9 +142,7 @@ function ListKolaborasiInovasi({ search }: { search: string }) { - - {item.slug} - + + diff --git a/src/app/admin/(dashboard)/ppid/indeks-kepuasan-masyarakat/responden/[id]/page.tsx b/src/app/admin/(dashboard)/ppid/indeks-kepuasan-masyarakat/responden/[id]/page.tsx index dc74a8b0..d288b980 100644 --- a/src/app/admin/(dashboard)/ppid/indeks-kepuasan-masyarakat/responden/[id]/page.tsx +++ b/src/app/admin/(dashboard)/ppid/indeks-kepuasan-masyarakat/responden/[id]/page.tsx @@ -26,7 +26,7 @@ export default function DetailResponden() { stateDetail.delete.byId(selectedId) setModalHapus(false) setSelectedId(null) - router.push("/admin/ppid/ikm-desa-darmasaba/responden") + router.push("/admin/ppid/indeks-kepuasan-masyarakat/responden") } } @@ -108,7 +108,7 @@ export default function DetailResponden() { variant="light" onClick={() => { if (stateDetail.findUnique.data) { - router.push(`/admin/ppid/ikm-desa-darmasaba/responden/${stateDetail.findUnique.data.id}/edit`); + router.push(`/admin/ppid/indeks-kepuasan-masyarakat/responden/${stateDetail.findUnique.data.id}/edit`); } }} disabled={!stateDetail.findUnique.data} diff --git a/src/app/admin/(dashboard)/ppid/indeks-kepuasan-masyarakat/responden/create/page.tsx b/src/app/admin/(dashboard)/ppid/indeks-kepuasan-masyarakat/responden/create/page.tsx index 166001f9..37d33418 100644 --- a/src/app/admin/(dashboard)/ppid/indeks-kepuasan-masyarakat/responden/create/page.tsx +++ b/src/app/admin/(dashboard)/ppid/indeks-kepuasan-masyarakat/responden/create/page.tsx @@ -1,21 +1,20 @@ 'use client' /* eslint-disable @typescript-eslint/no-unused-vars */ /* eslint-disable @typescript-eslint/no-explicit-any */ -import React from 'react'; -import { useRouter } from 'next/navigation'; -import grafikBerdasarkanJenisKelamin from '@/app/admin/(dashboard)/_state/ppid/indeks_kepuasan_masyarakat/grafikBerdasarkanJenisKelamin'; -import { useProxy } from 'valtio/utils'; -import { useState } from 'react'; -import colors from '@/con/colors'; -import { Box, Button, Paper, Stack, Title, TextInput, Select, Text } from '@mantine/core'; -import { IconArrowBack } from '@tabler/icons-react'; import indeksKepuasanState from '@/app/admin/(dashboard)/_state/landing-page/indeks-kepuasan'; +import colors from '@/con/colors'; +import { Box, Button, Group, Loader, Paper, Select, Stack, TextInput, Title } from '@mantine/core'; import { useShallowEffect } from '@mantine/hooks'; +import { IconArrowBack } from '@tabler/icons-react'; +import { useRouter } from 'next/navigation'; +import { useState } from 'react'; +import { useProxy } from 'valtio/utils'; function RespondenCreate() { const router = useRouter(); const stategrafikBerdasarkanResponden = useProxy(indeksKepuasanState.responden) const [donutData, setDonutData] = useState([]); + const [isSubmitting, setIsSubmitting] = useState(false); const resetForm = () => { stategrafikBerdasarkanResponden.create.form = { @@ -35,6 +34,7 @@ function RespondenCreate() { }) const handleSubmit = async () => { + setIsSubmitting(true); try { const id = await stategrafikBerdasarkanResponden.create.create(); if (typeof id !== 'undefined') { @@ -45,13 +45,15 @@ function RespondenCreate() { } } resetForm(); - router.push("/admin/ppid/ikm-desa-darmasaba/responden"); + router.push("/admin/ppid/indeks-kepuasan-masyarakat/responden"); } catch (error) { console.error('Error submitting form:', error); + } finally { + setIsSubmitting(false); } } return ( - + + + {/* Tombol Batal */} + + + {/* Tombol Simpan */} + + diff --git a/src/app/admin/(dashboard)/ppid/indeks-kepuasan-masyarakat/responden/page.tsx b/src/app/admin/(dashboard)/ppid/indeks-kepuasan-masyarakat/responden/page.tsx index 5631fe9d..c9e16300 100644 --- a/src/app/admin/(dashboard)/ppid/indeks-kepuasan-masyarakat/responden/page.tsx +++ b/src/app/admin/(dashboard)/ppid/indeks-kepuasan-masyarakat/responden/page.tsx @@ -1,4 +1,8 @@ 'use client'; +import { useState } from 'react'; +import { useRouter } from 'next/navigation'; +import { useProxy } from 'valtio/utils'; +import { useDebouncedValue, useShallowEffect } from '@mantine/hooks'; import { Box, Button, @@ -16,10 +20,7 @@ import { Text, Title, } from '@mantine/core'; -import { IconDeviceImacCog, IconSearch } from '@tabler/icons-react'; -import { useRouter } from 'next/navigation'; -import { useState } from 'react'; -import { useProxy } from 'valtio/utils'; +import { IconDeviceImac, IconSearch } from '@tabler/icons-react'; import HeaderSearch from '../../../_com/header'; import indeksKepuasanState from '../../../_state/landing-page/indeks-kepuasan'; @@ -28,7 +29,7 @@ function Responden() { return ( } value={search} @@ -46,193 +47,182 @@ interface ListRespondenProps { function ListResponden({ search }: ListRespondenProps) { const state = useProxy(indeksKepuasanState.responden); const router = useRouter(); + const [debouncedSearch] = useDebouncedValue(search, 1000); // 500ms delay const { data, page, totalPages, loading, load } = state.findMany; - const filteredData = (data || []).filter((item) => { - const keyword = search.toLowerCase(); - return item.name.toLowerCase().includes(keyword); - }); + useShallowEffect(() => { + load(page, 10, debouncedSearch); + }, [page, debouncedSearch]); + + const filteredData = data || []; if (loading || !data) { return ( - - + + ); } if (data.length === 0) { return ( - - - - Data Responden - - - - - - No - Nama - Tanggal - Jenis Kelamin - Aksi - - -
-
- - Belum ada data responden yang tersedia - -
-
+ + + + + Data Responden + + + Belum ada data responden yang tersedia + + + + ); } return ( - - - - Data Responden - - + + {/* Desktop Table */} - - - - - No - - Nama - Tanggal - Jenis Kelamin - - Aksi - - - - - {filteredData.length === 0 ? ( + + + Daftar Responden + +
+ - - - Tidak ada data yang cocok dengan pencarian - - + No + Nama + Tanggal + Jenis Kelamin + Aksi - ) : ( - filteredData.map((item, index) => ( - - {index + 1} - {item.name} - - {item.tanggal ? new Date(item.tanggal).toLocaleDateString('id-ID') : '-'} - - {item.jenisKelamin.name} - - + + + {filteredData.length === 0 ? ( + + + + Tidak ditemukan data dengan kata kunci pencarian + - )) - )} - -
+ ) : ( + filteredData.map((item, index) => ( + + {index + 1} + {item.name} + + {item.tanggal + ? new Date(item.tanggal).toLocaleDateString('id-ID', { + day: '2-digit', + month: 'long', + year: 'numeric', + }) + : '-'} + + {item.jenisKelamin.name} + + + + + )) + )} + + +
- {/* Mobile Card View */} + {/* Mobile Cards */} - {filteredData.length === 0 ? ( - - Tidak ada data yang cocok dengan pencarian - - ) : ( - - {filteredData.map((item, index) => ( - + + + Daftar Responden + + {filteredData.length === 0 ? ( + + + Tidak ditemukan data dengan kata kunci pencarian + + + ) : ( + filteredData.map((item) => ( + - - - No - - - {index + 1} - - - - - Nama - - - {item.name} - - - - - Tanggal - - - {item.tanggal - ? new Date(item.tanggal).toLocaleDateString('id-ID') - : '-'} - - - - - Jenis Kelamin - - - {item.jenisKelamin.name} - - - - - + Nama + {item.name} + + Tanggal + + {item.tanggal + ? new Date(item.tanggal).toLocaleDateString('id-ID', { + day: '2-digit', + month: 'long', + year: 'numeric', + }) + : '-'} + + + Jenis Kelamin + {item.jenisKelamin.name} + + - ))} - - )} + )) + )} + - {filteredData.length > 0 && ( -
- { - load(newPage, 10); - window.scrollTo({ top: 0, behavior: 'smooth' }); - }} - size="md" - radius="md" - /> -
- )} +
+ { + load(newPage, 10); + window.scrollTo({ top: 0, behavior: 'smooth' }); + }} + size="md" + radius="md" + mt={{ base: 'md', md: 'lg' }} + /> +
- + ); } diff --git a/src/app/admin/(dashboard)/user&role/layout.tsx b/src/app/admin/(dashboard)/user&role/layout.tsx deleted file mode 100644 index 05048967..00000000 --- a/src/app/admin/(dashboard)/user&role/layout.tsx +++ /dev/null @@ -1,155 +0,0 @@ -/* eslint-disable react-hooks/exhaustive-deps */ -'use client' -import colors from '@/con/colors'; -import { Box, ScrollArea, Stack, Tabs, TabsList, TabsPanel, TabsTab, Title } from '@mantine/core'; -import { IconBrush, IconForms, IconUser } from '@tabler/icons-react'; -import { usePathname, useRouter } from 'next/navigation'; -import React, { useEffect, useState } from 'react'; - -function LayoutTabs({ children }: { children: React.ReactNode }) { - const router = useRouter(); - const pathname = usePathname(); - - const tabs = [ - { - label: "User", - value: "user", - href: "/admin/user&role/user", - icon: , - }, - { - label: "Role", - value: "role", - href: "/admin/user&role/role", - icon: , - }, - { - label: "Menu Access", - value: "menu-access", - href: "/admin/user&role/menu-access", - icon: , - } - ]; - - const currentTab = tabs.find(tab => tab.href === pathname); - const [activeTab, setActiveTab] = useState(currentTab?.value || tabs[0].value); - - const handleTabChange = (value: string | null) => { - const tab = tabs.find(t => t.value === value); - if (tab) { - router.push(tab.href); - } - setActiveTab(value); - }; - - useEffect(() => { - const match = tabs.find(tab => tab.href === pathname); - if (match) { - setActiveTab(match.value); - } - }, [pathname]); - - return ( - - - User & Role - - - - - - {tabs.map((tab, i) => ( - - {tab.label} - - ))} - - - - - - - - - {tabs.map((tab, i) => ( - - {tab.label} - - ))} - - - - - {tabs.map((tab, i) => ( - - {children} - - ))} - - - ); -} - -export default LayoutTabs; diff --git a/src/app/admin/(dashboard)/user&role/role/page.tsx b/src/app/admin/(dashboard)/user&role/role/page.tsx index cc8a174d..b4439700 100644 --- a/src/app/admin/(dashboard)/user&role/role/page.tsx +++ b/src/app/admin/(dashboard)/user&role/role/page.tsx @@ -1,10 +1,11 @@ -/* eslint-disable react-hooks/exhaustive-deps */ 'use client' import colors from '@/con/colors'; import { Box, Button, + Center, Group, + Pagination, Paper, Skeleton, Stack, @@ -17,9 +18,10 @@ import { Text, Title } from '@mantine/core'; +import { useDebouncedValue, useShallowEffect } from '@mantine/hooks'; import { IconEdit, IconPlus, IconSearch, IconTrash } from '@tabler/icons-react'; import { useRouter } from 'next/navigation'; -import { useEffect, useState } from 'react'; +import { useState } from 'react'; import { useProxy } from 'valtio/utils'; import HeaderSearch from '../../_com/header'; import { ModalKonfirmasiHapus } from '../../_com/modalKonfirmasiHapus'; @@ -46,10 +48,12 @@ function ListRole({ search }: { search: string }) { const router = useRouter(); const [modalHapus, setModalHapus] = useState(false); const [selectedId, setSelectedId] = useState(null); + const [debouncedSearch] = useDebouncedValue(search, 1000); // 500ms delay + const { data, page, totalPages, loading, load } = listDataState.findMany; - useEffect(() => { - listDataState.findMany.load(); - }, []); + useShallowEffect(() => { + load(page, 10, debouncedSearch); + }, [page, debouncedSearch]); const handleDelete = () => { if (selectedId) { @@ -65,13 +69,14 @@ function ListRole({ search }: { search: string }) { return item.name.toLowerCase().includes(keyword); }); - if (!listDataState.findMany.data) { - return ( - - - - ); - } + if (loading || !data) { + return ( + + + + ); + } + return ( @@ -199,6 +204,18 @@ function ListRole({ search }: { search: string }) { +
+ { + load(newPage, 10); + window.scrollTo({ top: 0, behavior: 'smooth' }); + }} + total={totalPages} + color="blue" + /> +
+ {/* Modal Konfirmasi Hapus */} { + // Sort berdasarkan hierarki terlebih dahulu + if (a.posisi.hierarki !== b.posisi.hierarki) { + return a.posisi.hierarki - b.posisi.hierarki; + } + // Jika hierarki sama, sort berdasarkan nama posisi + return a.posisi.nama.localeCompare(b.posisi.nama); + }); + + // Lakukan pagination manual setelah sorting + const paginatedData = sortedData.slice(skip, skip + limit); const totalPages = Math.ceil(total / limit); return { success: true, - message: "Success fetch pegawai with pagination", - data, + message: "Success fetch pegawai with hierarchy order", + data: paginatedData, page, totalPages, total, }; - } catch (e) { - console.error("Find many paginated error:", e); + } catch (error) { + console.error("Find many pegawai error:", error); return { success: false, - message: "Failed fetch pegawai with pagination", + message: "Failed fetch pegawai", data: [], page: 1, totalPages: 1, total: 0, }; } -} \ No newline at end of file +} diff --git a/src/app/api/[[...slugs]]/_lib/ekonomi/struktur-bumdes/pegawai/findManyAll.ts b/src/app/api/[[...slugs]]/_lib/ekonomi/struktur-bumdes/pegawai/findManyAll.ts new file mode 100644 index 00000000..fbef28a0 --- /dev/null +++ b/src/app/api/[[...slugs]]/_lib/ekonomi/struktur-bumdes/pegawai/findManyAll.ts @@ -0,0 +1,48 @@ +/* eslint-disable @typescript-eslint/no-explicit-any */ +import prisma from "@/lib/prisma"; +import { Context } from "elysia"; + +export default async function pegawaiFindManyAll(context: Context) { + const search = (context.query.search as string) || ""; + const isActiveParam = context.query.isActive; + + // Buat where clause dinamis + const where: any = {}; + + if (isActiveParam !== undefined) { + where.isActive = isActiveParam === "true"; + } + + if (search) { + where.OR = [ + { namaLengkap: { contains: search, mode: "insensitive" } }, + { alamat: { contains: search, mode: "insensitive" } }, + ]; + } + + try { + const data = await prisma.pegawaiBumDes.findMany({ + where, + include: { + posisi: true, + image: true, + }, + orderBy: { posisi: { hierarki: "asc" } }, + }); + + return { + success: true, + message: "Success fetch all pegawai (non-paginated)", + total: data.length, + data, + }; + } catch (error) { + console.error("Find many all error:", error); + return { + success: false, + message: "Failed fetch all pegawai", + total: 0, + data: [], + }; + } +} diff --git a/src/app/api/[[...slugs]]/_lib/pendidikan/data-pendidikan/findMany.ts b/src/app/api/[[...slugs]]/_lib/pendidikan/data-pendidikan/findMany.ts index 395beef9..addb6970 100644 --- a/src/app/api/[[...slugs]]/_lib/pendidikan/data-pendidikan/findMany.ts +++ b/src/app/api/[[...slugs]]/_lib/pendidikan/data-pendidikan/findMany.ts @@ -1,8 +1,49 @@ +/* eslint-disable @typescript-eslint/no-explicit-any */ import prisma from "@/lib/prisma"; +import { Context } from "elysia"; -export default async function dataPendidikanFindMany() { - const res = await prisma.dataPendidikan.findMany(); - return { - data: res, - }; -} \ No newline at end of file +export default async function dataPendidikanFindMany(context: Context) { + const page = Number(context.query.page) || 1; + const limit = Number(context.query.limit) || 10; + const search = (context.query.search as string) || ''; + const skip = (page - 1) * limit; + + const where: any = { isActive: true }; + + // Tambahkan pencarian (jika ada) + if (search) { + where.OR = [ + { name: { contains: search, mode: 'insensitive' } }, + { jumlah: { contains: search, mode: 'insensitive' } }, + ]; + } + + try { + const [data, total] = await Promise.all([ + prisma.dataPendidikan.findMany({ + where, + skip, + take: limit, + orderBy: { name: "asc" }, // opsional, kalau mau urut berdasarkan waktu + }), + prisma.dataPendidikan.count({ + where: { isActive: true }, + }), + ]); + + return { + success: true, + message: "Success fetch program inovasi with pagination", + data, + page, + totalPages: Math.ceil(total / limit), + total, + }; + } catch (e) { + console.error("Find many paginated error:", e); + return { + success: false, + message: "Failed fetch program inovasi with pagination", + }; + } + } \ No newline at end of file diff --git a/src/app/api/[[...slugs]]/_lib/user/role/findMany.ts b/src/app/api/[[...slugs]]/_lib/user/role/findMany.ts index f4a3a343..cf8bc346 100644 --- a/src/app/api/[[...slugs]]/_lib/user/role/findMany.ts +++ b/src/app/api/[[...slugs]]/_lib/user/role/findMany.ts @@ -1,11 +1,49 @@ +/* eslint-disable @typescript-eslint/no-explicit-any */ import prisma from "@/lib/prisma"; +import { Context } from "elysia"; -export default async function roleFindMany() { - const data = await prisma.role.findMany(); +export default async function roleFindMany(context: Context) { + const page = Number(context.query.page) || 1; + const limit = Number(context.query.limit) || 10; + const search = (context.query.search as string) || ''; + const skip = (page - 1) * limit; - return { - success: true, - message: "Success get all role", - data, - }; + const where: any = { isActive: true }; + + // Tambahkan pencarian (jika ada) + if (search) { + where.OR = [ + { name: { contains: search, mode: 'insensitive' } }, + { description: { contains: search, mode: 'insensitive' } }, + ]; + } + + try { + const [data, total] = await Promise.all([ + prisma.role.findMany({ + where, + skip, + take: limit, + orderBy: { name: "asc" }, // opsional, kalau mau urut berdasarkan waktu + }), + prisma.role.count({ + where: { isActive: true }, + }), + ]); + + return { + success: true, + message: "Success fetch role with pagination", + data, + page, + totalPages: Math.ceil(total / limit), + total, + }; + } catch (e) { + console.error("Find many paginated error:", e); + return { + success: false, + message: "Failed fetch role with pagination", + }; + } }