From 2bc9b2f3c6153914e768e8a16984c897b99e92f9 Mon Sep 17 00:00:00 2001 From: nico Date: Thu, 10 Jul 2025 10:46:58 +0800 Subject: [PATCH] Menerapkan pagination di submenu pegawai & berita --- .../admin/(dashboard)/_state/desa/berita.ts | 28 +- .../struktur-organisasi.ts | 589 ++++++++++-------- .../admin/(dashboard)/desa/berita/page.tsx | 78 ++- .../pegawai/page.tsx | 178 +++--- .../_lib/desa/berita/find-many.ts | 41 +- .../struktur-organisasi/pegawai/findMany.ts | 52 +- 6 files changed, 566 insertions(+), 400 deletions(-) diff --git a/src/app/admin/(dashboard)/_state/desa/berita.ts b/src/app/admin/(dashboard)/_state/desa/berita.ts index 56b4fb01..b314fcd4 100644 --- a/src/app/admin/(dashboard)/_state/desa/berita.ts +++ b/src/app/admin/(dashboard)/_state/desa/berita.ts @@ -80,13 +80,33 @@ const berita = proxy({ }; }>[] | null, - async load() { - const res = await ApiFetch.api.desa.berita["find-many"].get(); - if (res.status === 200) { - berita.findMany.data = (res.data?.data ) ?? []; + page: 1, + totalPages: 1, + loading: false, + + async load(page = 1, limit = 10) { + berita.findMany.loading = true; + berita.findMany.page = page; + try { + const res = await ApiFetch.api.desa.berita["find-many"].get({ + query: { + page, + limit, + }, + }); + + if (res.status === 200 && res.data?.success) { + berita.findMany.data = res.data.data ?? []; + berita.findMany.totalPages = res.data.totalPages ?? 1; + } + } catch (err) { + console.error("Gagal fetch berita paginated:", err); + } finally { + berita.findMany.loading = false; } }, }, + findUnique: { data: null as | Prisma.BeritaGetPayload<{ 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 cacc801e..7819facb 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 @@ -30,9 +30,9 @@ 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(); @@ -62,12 +62,15 @@ const posisiOrganisasi = proxy({ return null; } try { - const response = await fetch(`/api/ekonomi/struktur-organisasi/posisi-organisasi/${id}`, { - method: "GET", - headers: { - "Content-Type": "application/json", - }, - }); + const response = await fetch( + `/api/ekonomi/struktur-organisasi/posisi-organisasi/${id}`, + { + method: "GET", + headers: { + "Content-Type": "application/json", + }, + } + ); if (!response.ok) { throw new Error(`HTTP error! status: ${response.status}`); } @@ -160,7 +163,9 @@ const posisiOrganisasi = proxy({ }>, async load() { try { - const res = await ApiFetch.api.ekonomi["struktur-organisasi"]["posisi-organisasi"]["find-many"].get(); + const res = await ApiFetch.api.ekonomi["struktur-organisasi"][ + "posisi-organisasi" + ]["find-many"].get(); if (res.status === 200) { // The API now returns the id field, so we can use it directly this.data = res.data?.data ?? []; @@ -209,238 +214,278 @@ const posisiOrganisasi = proxy({ }); const templatePegawai = z.object({ - namaLengkap: z.string().min(1, "Nama wajib diisi"), - gelarAkademik: z.string().optional(), - imageId: z.string().nullable().optional(), - tanggalMasuk: z.string().optional(), // ISO format - email: z.string().email("Email tidak valid").optional(), - telepon: z.string().optional(), - alamat: z.string().optional(), - posisiId: z.string().min(1, "Posisi wajib diisi"), - isActive: z.boolean().default(true), - }); - - const pegawaiDefaultForm = { - namaLengkap: "", - gelarAkademik: "", - imageId: "", - tanggalMasuk: "", - email: "", - telepon: "", - alamat: "", - posisiId: "", - isActive: true, - }; - - const pegawai = proxy({ - create: { - form: { ...pegawaiDefaultForm }, - loading: false, - async submit() { - const cek = templatePegawai.safeParse(pegawai.create.form); - if (!cek.success) { - const err = cek.error.issues.map(i => i.message).join("\n"); - toast.error(err); - return; - } - - try { - pegawai.create.loading = true; - const res = await ApiFetch.api.ekonomi["struktur-organisasi"]["pegawai"]["create"].post( - pegawai.create.form - ); - if (res.status === 200) { - toast.success("Pegawai berhasil ditambahkan"); - await pegawai.findMany.load(); - } else { - toast.error(res.data?.message ?? "Gagal tambah pegawai"); - } - } catch (error) { - console.error("Gagal create:", error); - toast.error("Terjadi kesalahan saat menambahkan pegawai"); - } finally { - pegawai.create.loading = false; - } - }, - }, - findMany: { - data: null as (Prisma.PegawaiGetPayload<{ include: { posisi: true, image: true } }> & { isActive: boolean })[] | null, - async load() { - try { - const res = await ApiFetch.api.ekonomi["struktur-organisasi"]["pegawai"]["find-many"].get(); - if (res.status === 200) { - pegawai.findMany.data = (res.data?.data ?? []).map((item: any) => ({ - ...item, - posisi: item.posisi || { id: '', nama: '' }, // Ensure posisi exists with required fields - isActive: item.isActive ?? true // Default to true if not provided - })); - } else { - console.error('Failed to load pegawai:', res.data?.message); - } - } catch (error) { - console.error('Error loading pegawai:', error); - pegawai.findMany.data = []; - } - }, - }, - findUnique: { - data: null as (Prisma.PegawaiGetPayload<{ include: { posisi: true, image: true } }> & { isActive: boolean }) | null, - async load(id: string) { - const res = await fetch(`/api/ekonomi/struktur-organisasi/pegawai/${id}`); - if (res.ok) { - const json = await res.json(); - pegawai.findUnique.data = json.data ? { - ...json.data, - isActive: json.data.isActive ?? json.data.aktif ?? true // Fallback ke aktif:true jika tidak ada data - } : null; - } else { - pegawai.findUnique.data = null; - } - }, - }, - - delete: { - loading: false, - async byId(id: string) { - if (!id) return toast.warn("ID tidak valid"); - try { - pegawai.delete.loading = true; - const res = await fetch(`/api/ekonomi/struktur-organisasi/pegawai/del/${id}`, { - method: "DELETE", - }); - const json = await res.json(); - if (res.ok) { - toast.success(json.message ?? "Berhasil hapus pegawai"); - await pegawai.findMany.load(); - } else { - toast.error(json.message ?? "Gagal hapus pegawai"); - } - } catch (error) { - console.error("Gagal delete:", error); - toast.error("Terjadi kesalahan saat menghapus"); - } finally { - pegawai.delete.loading = false; - } - }, - }, - - edit: { - id: "", - form: { ...pegawaiDefaultForm }, - loading: false, - - async load(id: string) { - if (!id) { - toast.warn("ID tidak valid"); - return null; - } - - try { - const response = await fetch(`/api/ekonomi/struktur-organisasi/pegawai/${id}`, { - method: 'GET', - headers: { - 'Content-Type': 'application/json', - }, - }); - - if (!response.ok) { - throw new Error(`HTTP error! status: ${response.status}`); - } - - const result = await response.json(); - - if (result?.success) { - const data = result.data; - this.id = data.id; - this.form = { - namaLengkap: data.namaLengkap, - gelarAkademik: data.gelarAkademik, - imageId: data.imageId, - tanggalMasuk: data.tanggalMasuk, - email: data.email, - telepon: data.telepon, - alamat: data.alamat, - posisiId: data.posisiId, - isActive: data.isActive, - }; - return data; // Return the loaded data - } else { - throw new Error(result?.message || "Gagal memuat data"); - } - } catch (error) { - console.error("Error loading berita:", error); - toast.error(error instanceof Error ? error.message : "Gagal memuat data"); - return null; - } - }, - - async submit() { - const cek = templatePegawai.safeParse(pegawai.edit.form); - if (!cek.success) { - const err = `[${cek.error.issues - .map((v) => `${v.path.join(".")}`) - .join("\n")}] required`; - toast.error(err); - return false; - } - - try { - pegawai.edit.loading = true; - - // Format tanggalMasuk to ISO string if it exists - const formattedTanggalMasuk = this.form.tanggalMasuk - ? new Date(this.form.tanggalMasuk).toISOString() - : undefined; - - const response = await fetch(`/api/ekonomi/struktur-organisasi/pegawai/${this.id}`, { - method: 'PUT', - headers: { - 'Content-Type': 'application/json', - }, - body: JSON.stringify({ - id: this.id, - namaLengkap: this.form.namaLengkap, - gelarAkademik: this.form.gelarAkademik, - imageId: this.form.imageId || null, - tanggalMasuk: formattedTanggalMasuk, - email: this.form.email, - telepon: this.form.telepon, - alamat: this.form.alamat, - posisiId: this.form.posisiId, - isActive: this.form.isActive, - }), - }); - - if (!response.ok) { - const errorData = await response.json().catch(() => ({})); - throw new Error(errorData.message || `HTTP error! status: ${response.status}`); - } - - const result = await response.json(); - - if (result.success) { - toast.success("Berhasil update pegawai"); - await pegawai.findMany.load(); // refresh list - return true; - } else { - throw new Error(result.message || "Gagal update pegawai"); - } - } catch (error) { - console.error("Error updating pegawai:", error); - toast.error(error instanceof Error ? error.message : "Terjadi kesalahan saat update pegawai"); - return false; - } finally { - pegawai.edit.loading = false; - } - }, - - reset() { - pegawai.edit.id = ""; - pegawai.edit.form = { ...pegawaiDefaultForm }; - }, - }, - }); + namaLengkap: z.string().min(1, "Nama wajib diisi"), + gelarAkademik: z.string().optional(), + imageId: z.string().nullable().optional(), + tanggalMasuk: z.string().optional(), // ISO format + email: z.string().email("Email tidak valid").optional(), + telepon: z.string().optional(), + alamat: z.string().optional(), + posisiId: z.string().min(1, "Posisi wajib diisi"), + isActive: z.boolean().default(true), +}); +const pegawaiDefaultForm = { + namaLengkap: "", + gelarAkademik: "", + imageId: "", + tanggalMasuk: "", + email: "", + telepon: "", + alamat: "", + posisiId: "", + isActive: true, +}; + +const pegawai = proxy({ + create: { + form: { ...pegawaiDefaultForm }, + loading: false, + async submit() { + const cek = templatePegawai.safeParse(pegawai.create.form); + if (!cek.success) { + const err = cek.error.issues.map((i) => i.message).join("\n"); + toast.error(err); + return; + } + + try { + pegawai.create.loading = true; + const res = await ApiFetch.api.ekonomi["struktur-organisasi"][ + "pegawai" + ]["create"].post(pegawai.create.form); + if (res.status === 200) { + toast.success("Pegawai berhasil ditambahkan"); + await pegawai.findMany.load(); + } else { + toast.error(res.data?.message ?? "Gagal tambah pegawai"); + } + } catch (error) { + console.error("Gagal create:", error); + toast.error("Terjadi kesalahan saat menambahkan pegawai"); + } finally { + pegawai.create.loading = false; + } + }, + }, + + // In struktur-organisasi.ts +findMany: { + data: null as any[] | null, + page: 1, + totalPages: 1, + total: 0, + loading: false, + load: async (page = 1, limit = 10) => { // Change to arrow function + pegawai.findMany.loading = true; // Use the full path to access the property + pegawai.findMany.page = page; + try { + const res = await ApiFetch.api.ekonomi["struktur-organisasi"][ + "pegawai" + ]["find-many"].get({ + query: { page, limit }, + }); + + if (res.status === 200 && res.data?.success) { + pegawai.findMany.data = res.data.data || []; + pegawai.findMany.total = res.data.total || 0; + pegawai.findMany.totalPages = res.data.totalPages || 1; + } else { + console.error("Failed to load pegawai:", res.data?.message); + pegawai.findMany.data = []; + pegawai.findMany.total = 0; + pegawai.findMany.totalPages = 1; + } + } catch (error) { + console.error("Error loading pegawai:", error); + pegawai.findMany.data = []; + pegawai.findMany.total = 0; + pegawai.findMany.totalPages = 1; + } finally { + pegawai.findMany.loading = false; + } + }, +}, + findUnique: { + data: null as + | (Prisma.PegawaiGetPayload<{ + include: { posisi: true; image: true }; + }> & { isActive: boolean }) + | null, + async load(id: string) { + const res = await fetch(`/api/ekonomi/struktur-organisasi/pegawai/${id}`); + if (res.ok) { + const json = await res.json(); + pegawai.findUnique.data = json.data + ? { + ...json.data, + isActive: json.data.isActive ?? json.data.aktif ?? true, // Fallback ke aktif:true jika tidak ada data + } + : null; + } else { + pegawai.findUnique.data = null; + } + }, + }, + + delete: { + loading: false, + async byId(id: string) { + if (!id) return toast.warn("ID tidak valid"); + try { + pegawai.delete.loading = true; + const res = await fetch( + `/api/ekonomi/struktur-organisasi/pegawai/del/${id}`, + { + method: "DELETE", + } + ); + const json = await res.json(); + if (res.ok) { + toast.success(json.message ?? "Berhasil hapus pegawai"); + await pegawai.findMany.load(); + } else { + toast.error(json.message ?? "Gagal hapus pegawai"); + } + } catch (error) { + console.error("Gagal delete:", error); + toast.error("Terjadi kesalahan saat menghapus"); + } finally { + pegawai.delete.loading = false; + } + }, + }, + + edit: { + id: "", + form: { ...pegawaiDefaultForm }, + loading: false, + + async load(id: string) { + if (!id) { + toast.warn("ID tidak valid"); + return null; + } + + try { + const response = await fetch( + `/api/ekonomi/struktur-organisasi/pegawai/${id}`, + { + method: "GET", + headers: { + "Content-Type": "application/json", + }, + } + ); + + if (!response.ok) { + throw new Error(`HTTP error! status: ${response.status}`); + } + + const result = await response.json(); + + if (result?.success) { + const data = result.data; + this.id = data.id; + this.form = { + namaLengkap: data.namaLengkap, + gelarAkademik: data.gelarAkademik, + imageId: data.imageId, + tanggalMasuk: data.tanggalMasuk, + email: data.email, + telepon: data.telepon, + alamat: data.alamat, + posisiId: data.posisiId, + isActive: data.isActive, + }; + return data; // Return the loaded data + } else { + throw new Error(result?.message || "Gagal memuat data"); + } + } catch (error) { + console.error("Error loading berita:", error); + toast.error( + error instanceof Error ? error.message : "Gagal memuat data" + ); + return null; + } + }, + + async submit() { + const cek = templatePegawai.safeParse(pegawai.edit.form); + if (!cek.success) { + const err = `[${cek.error.issues + .map((v) => `${v.path.join(".")}`) + .join("\n")}] required`; + toast.error(err); + return false; + } + + try { + pegawai.edit.loading = true; + + // Format tanggalMasuk to ISO string if it exists + const formattedTanggalMasuk = this.form.tanggalMasuk + ? new Date(this.form.tanggalMasuk).toISOString() + : undefined; + + const response = await fetch( + `/api/ekonomi/struktur-organisasi/pegawai/${this.id}`, + { + method: "PUT", + headers: { + "Content-Type": "application/json", + }, + body: JSON.stringify({ + id: this.id, + namaLengkap: this.form.namaLengkap, + gelarAkademik: this.form.gelarAkademik, + imageId: this.form.imageId || null, + tanggalMasuk: formattedTanggalMasuk, + email: this.form.email, + telepon: this.form.telepon, + alamat: this.form.alamat, + posisiId: this.form.posisiId, + isActive: this.form.isActive, + }), + } + ); + + if (!response.ok) { + const errorData = await response.json().catch(() => ({})); + throw new Error( + errorData.message || `HTTP error! status: ${response.status}` + ); + } + + const result = await response.json(); + + if (result.success) { + toast.success("Berhasil update pegawai"); + await pegawai.findMany.load(); // refresh list + return true; + } else { + throw new Error(result.message || "Gagal update pegawai"); + } + } catch (error) { + console.error("Error updating pegawai:", error); + toast.error( + error instanceof Error + ? error.message + : "Terjadi kesalahan saat update pegawai" + ); + return false; + } finally { + pegawai.edit.loading = false; + } + }, + + reset() { + pegawai.edit.id = ""; + pegawai.edit.form = { ...pegawaiDefaultForm }; + }, + }, +}); // Schema Zod untuk form validasi const templateHubunganOrganisasiForm = z.object({ @@ -474,7 +519,9 @@ const hubunganOrganisasi = proxy({ try { hubunganOrganisasi.create.loading = true; - const res = await ApiFetch.api.ekonomi["struktur-organisasi"]["hubungan-organisasi"]["create"].post(hubunganOrganisasi.create.form); + const res = await ApiFetch.api.ekonomi["struktur-organisasi"][ + "hubungan-organisasi" + ]["create"].post(hubunganOrganisasi.create.form); if (res.status === 200 && res.data?.success) { hubunganOrganisasi.findMany.load(); @@ -482,7 +529,7 @@ const hubunganOrganisasi = proxy({ } else { return toast.error(res.data?.message || "Gagal menambahkan data"); } - } catch (error) { + } catch (error) { console.error("Gagal create:", error); toast.error("Terjadi kesalahan saat menambahkan"); } finally { @@ -490,7 +537,7 @@ const hubunganOrganisasi = proxy({ } }, }, - findMany: { + findMany: { data: null as Array<{ id: string; atasanId: string; @@ -528,20 +575,29 @@ const hubunganOrganisasi = proxy({ async load() { try { - const res = await ApiFetch.api.ekonomi["struktur-organisasi"]["hubungan-organisasi"]["find-many"].get(); + const res = await ApiFetch.api.ekonomi["struktur-organisasi"][ + "hubungan-organisasi" + ]["find-many"].get(); if (res.status === 200) { - hubunganOrganisasi.findMany.data = (res.data?.data ?? []).map((item: any) => ({ - ...item, - atasan: item.atasan ? { - ...item.atasan, - isActive: item.atasan.isActive ?? item.atasan.aktif ?? true - } : null, - bawahan: item.bawahan ? { - ...item.bawahan, - isActive: item.bawahan.isActive ?? item.bawahan.aktif ?? true - } : null - })); + hubunganOrganisasi.findMany.data = (res.data?.data ?? []).map( + (item: any) => ({ + ...item, + atasan: item.atasan + ? { + ...item.atasan, + isActive: item.atasan.isActive ?? item.atasan.aktif ?? true, + } + : null, + bawahan: item.bawahan + ? { + ...item.bawahan, + isActive: + item.bawahan.isActive ?? item.bawahan.aktif ?? true, + } + : null, + }) + ); } else { hubunganOrganisasi.findMany.data = []; } @@ -591,7 +647,9 @@ const hubunganOrganisasi = proxy({ async load(id: string) { try { - const res = await fetch(`/api/ekonomi/struktur-organisasi/hubungan-organisasi/${id}`); + const res = await fetch( + `/api/ekonomi/struktur-organisasi/hubungan-organisasi/${id}` + ); const result = await res.json(); if (res.ok && result?.success) { @@ -616,7 +674,9 @@ const hubunganOrganisasi = proxy({ if (!id) return toast.warn("ID tidak valid"); try { - const res = await fetch(`/api/ekonomi/struktur-organisasi/hubungan-organisasi/${id}`); + const res = await fetch( + `/api/ekonomi/struktur-organisasi/hubungan-organisasi/${id}` + ); const result = await res.json(); if (res.ok && result?.success) { @@ -633,7 +693,9 @@ const hubunganOrganisasi = proxy({ } } catch (error) { console.error("Error loading:", error); - toast.error(error instanceof Error ? error.message : "Gagal memuat data"); + toast.error( + error instanceof Error ? error.message : "Gagal memuat data" + ); return null; } }, @@ -690,9 +752,12 @@ const hubunganOrganisasi = proxy({ try { hubunganOrganisasi.delete.loading = true; - const res = await fetch(`/api/ekonomi/struktur-organisasi/hubungan-organisasi/del/${id}`, { - method: "DELETE", - }); + const res = await fetch( + `/api/ekonomi/struktur-organisasi/hubungan-organisasi/del/${id}`, + { + method: "DELETE", + } + ); const result = await res.json(); if (res.ok && result?.success) { diff --git a/src/app/admin/(dashboard)/desa/berita/page.tsx b/src/app/admin/(dashboard)/desa/berita/page.tsx index e0cff5d4..7ba706f9 100644 --- a/src/app/admin/(dashboard)/desa/berita/page.tsx +++ b/src/app/admin/(dashboard)/desa/berita/page.tsx @@ -1,6 +1,6 @@ 'use client' import colors from '@/con/colors'; -import { Box, Button, Grid, GridCol, Image, Paper, Skeleton, Stack, Table, TableTbody, TableTd, TableTh, TableThead, TableTr, Text } from '@mantine/core'; +import { Box, Button, Grid, GridCol, Image, Pagination, Paper, Skeleton, Stack, Table, TableTbody, TableTd, TableTh, TableThead, TableTr, Text } from '@mantine/core'; import { useShallowEffect } from '@mantine/hooks'; import { IconCircleDashedPlus, IconDeviceImacCog, IconSearch } from '@tabler/icons-react'; import { useRouter } from 'next/navigation'; @@ -29,12 +29,21 @@ function Berita() { function ListBerita({ search }: { search: string }) { const beritaState = useProxy(stateDashboardBerita) const router = useRouter() + const { + data, + page, + totalPages, + loading, + load, + } = beritaState.berita.findMany; + + // Fetch pertama kali useShallowEffect(() => { - beritaState.berita.findMany.load() - }, []) + load(page); // awal page = 1 + }, []); - const filteredData = (beritaState.berita.findMany.data || []).filter(item => { + const filteredData = (data || []).filter((item) => { const keyword = search.toLowerCase(); return ( item.judul.toLowerCase().includes(keyword) || @@ -42,67 +51,84 @@ function ListBerita({ search }: { search: string }) { ); }); - - if (!beritaState.berita.findMany.data) { - return ( - - - - ) + if (loading || !data) { + return ; } return ( - + load(newPage)} // ini penting! + total={totalPages} + mt="md" + mb="md" + /> + + - List Berita + + List Berita + - - +
Judul Kategori Image Detail - - + {filteredData.map((item) => ( - + - {item.judul} + + {item.judul} + - {item.kategoriBerita?.name} + {item.kategoriBerita?.name} - - ))} -
+ +
- - ) + ); } - export default Berita; diff --git a/src/app/admin/(dashboard)/ekonomi/struktur-organisasi-dan-sk-pengurus-bumdesa/pegawai/page.tsx b/src/app/admin/(dashboard)/ekonomi/struktur-organisasi-dan-sk-pengurus-bumdesa/pegawai/page.tsx index fe7f3864..f1d69d6e 100644 --- a/src/app/admin/(dashboard)/ekonomi/struktur-organisasi-dan-sk-pengurus-bumdesa/pegawai/page.tsx +++ b/src/app/admin/(dashboard)/ekonomi/struktur-organisasi-dan-sk-pengurus-bumdesa/pegawai/page.tsx @@ -1,10 +1,10 @@ /* eslint-disable react-hooks/exhaustive-deps */ 'use client' import colors from '@/con/colors'; -import { Badge, Box, Button, Paper, Skeleton, Stack, Table, TableTbody, TableTd, TableTh, TableThead, TableTr } from '@mantine/core'; -import { IconDeviceImacCog, IconSearch } from '@tabler/icons-react'; +import { Badge, Box, Button, Center, Group, Pagination, Paper, Skeleton, Stack, Table, TableTbody, TableTd, TableTh, TableThead, TableTr, ThemeIcon } from '@mantine/core'; +import { IconCheck, IconDeviceImacCog, IconSearch, IconX } from '@tabler/icons-react'; import { useRouter } from 'next/navigation'; -import { useEffect, useState } from 'react'; +import { useEffect, useMemo, useState } from 'react'; import { useProxy } from 'valtio/utils'; import HeaderSearch from '../../../_com/header'; import JudulList from '../../../_com/judulList'; @@ -30,46 +30,33 @@ function ListPegawai({ search }: { search: string }) { const stateOrganisasi = useProxy(strukturorganisasiState.pegawai); const router = useRouter(); + const { + data, + page, + totalPages, + loading, + load, + } = stateOrganisasi.findMany; + useEffect(() => { - const loadData = async () => { - try { - // Clear existing data to ensure we see the loading state - stateOrganisasi.findMany.data = []; + load(page, 10); + }, [page]); - // Load new data - await stateOrganisasi.findMany.load(); - - // Type guard to ensure data is an array - const data = stateOrganisasi.findMany.data || []; - if (data.length > 0) { - console.log('4. First record sample:', data[0]); - } - } catch (error) { - console.error('Error loading pegawai data:', error); - stateOrganisasi.findMany.data = []; - } - }; - - loadData(); - - // Cleanup function - return () => { - console.log('Cleanup: Unmounting component'); - }; - }, []); - - const filteredData = (stateOrganisasi.findMany.data || []).filter(item => { - const keyword = search.toLowerCase(); - return ( - item.namaLengkap?.toLowerCase().includes(keyword) || - item.gelarAkademik?.toLowerCase().includes(keyword) || - item.telepon?.toLowerCase().includes(keyword) || - item.posisi?.nama?.toLowerCase().includes(keyword) - ); - }); + const filteredData = useMemo(() => { + if (!data) return []; + return data.filter(item => { + const keyword = search.toLowerCase(); + return ( + item.namaLengkap?.toLowerCase().includes(keyword) || + item.gelarAkademik?.toLowerCase().includes(keyword) || + item.telepon?.toLowerCase().includes(keyword) || + item.posisi?.nama?.toLowerCase().includes(keyword) + ); + }); + }, [data, search]); // Handle loading state - if (stateOrganisasi.findMany.data === null) { + if (loading || !data) { return ( @@ -77,8 +64,6 @@ function ListPegawai({ search }: { search: string }) { ); } - // Check if data is an empty array - const data = stateOrganisasi.findMany.data || []; if (data.length === 0) { return ( @@ -95,48 +80,79 @@ function ListPegawai({ search }: { search: string }) { title='List Pegawai' href='/admin/ekonomi/struktur-organisasi-dan-sk-pengurus-bumdesa/pegawai/create' /> - - - - Nama - Gelar Akademik - Telepon - Posisi - Aktif - Detail - - - - {(() => { - console.log('Rendering table with items:', stateOrganisasi.findMany.data); - return null; - })()} - {([...filteredData] - .sort((a, b) => { - if (a.isActive === b.isActive) { - return a.namaLengkap.localeCompare(b.namaLengkap); // kalau status sama, urut nama - } - return Number(b.isActive) - Number(a.isActive); // aktif duluan - }) // Aktif di atas - ).map((item) => ( - - {item.namaLengkap} - {item.gelarAkademik} - {item.telepon} - {item.posisi?.nama} - - {item.isActive ? "Aktif" : "Tidak Aktif"} - - - - + +
+ + + Nama + Gelar Akademik + Telepon + Posisi + Aktif + Detail - ))} - -
+ + + {(() => { + console.log('Rendering table with items:', stateOrganisasi.findMany.data); + return null; + })()} + {([...filteredData] + .sort((a, b) => { + if (a.isActive === b.isActive) { + return a.namaLengkap.localeCompare(b.namaLengkap); // kalau status sama, urut nama + } + return Number(b.isActive) - Number(a.isActive); // aktif duluan + }) // Aktif di atas + ).map((item) => ( + + {item.namaLengkap} + {item.gelarAkademik} + {item.telepon} + {item.posisi?.nama} + + + + + {item.isActive ? "Aktif" : "Tidak Aktif"} + + + + {item.isActive ? ( + + + + ) : ( + + + + )} + + + + + + + + ))} + + +
+
+ { + load(newPage, 10); + window.scrollTo(0, 0); + }} + total={totalPages} + mt="md" + mb="md" + /> +
); } diff --git a/src/app/api/[[...slugs]]/_lib/desa/berita/find-many.ts b/src/app/api/[[...slugs]]/_lib/desa/berita/find-many.ts index 5d8fb601..0d582ab7 100644 --- a/src/app/api/[[...slugs]]/_lib/desa/berita/find-many.ts +++ b/src/app/api/[[...slugs]]/_lib/desa/berita/find-many.ts @@ -1,27 +1,44 @@ +// /api/berita/findManyPaginated.ts import prisma from "@/lib/prisma"; +import { Context } from "elysia"; + +async function beritaFindManyPaginated(context: Context) { + const page = Number(context.query.page) || 1; + const limit = Number(context.query.limit) || 10; + const skip = (page - 1) * limit; -async function beritaFindMany() { try { - const data = await prisma.berita.findMany({ - where: { isActive: true }, - include: { - image: true, - kategoriBerita: true, - }, - }); + const [data, total] = await Promise.all([ + prisma.berita.findMany({ + where: { isActive: true }, + include: { + image: true, + kategoriBerita: true, + }, + skip, + take: limit, + orderBy: { createdAt: 'desc' }, // opsional, kalau mau urut berdasarkan waktu + }), + prisma.berita.count({ + where: { isActive: true } + }) + ]); return { success: true, - message: "Success fetch berita", + message: "Success fetch berita with pagination", data, + page, + totalPages: Math.ceil(total / limit), + total, }; } catch (e) { - console.error("Find many error:", e); + console.error("Find many paginated error:", e); return { success: false, - message: "Failed fetch berita", + message: "Failed fetch berita with pagination", }; } } -export default beritaFindMany; +export default beritaFindManyPaginated; diff --git a/src/app/api/[[...slugs]]/_lib/ekonomi/struktur-organisasi/pegawai/findMany.ts b/src/app/api/[[...slugs]]/_lib/ekonomi/struktur-organisasi/pegawai/findMany.ts index e15dd235..b5bf9570 100644 --- a/src/app/api/[[...slugs]]/_lib/ekonomi/struktur-organisasi/pegawai/findMany.ts +++ b/src/app/api/[[...slugs]]/_lib/ekonomi/struktur-organisasi/pegawai/findMany.ts @@ -1,26 +1,48 @@ -/* eslint-disable @typescript-eslint/no-explicit-any */ import prisma from "@/lib/prisma"; +import { Context } from "elysia"; + +// Di findMany.ts +export default async function pegawaiFindMany(context: Context) { + const page = Number(context.query.page) || 1; + const limit = Number(context.query.limit) || 10; + const skip = (page - 1) * limit; -export default async function pegawaiFindMany() { try { - const pegawaiList = await prisma.pegawai.findMany({ - orderBy: { createdAt: "desc" }, - include: { - posisi: true, - image: true, - }, - }); + const [data, total] = await Promise.all([ + prisma.pegawai.findMany({ + where: { isActive: true }, + include: { + posisi: true, + image: true, + }, + skip, + take: limit, + orderBy: { createdAt: 'desc' }, + }), + prisma.pegawai.count({ + where: { isActive: true } + }) + ]); + + const totalPages = Math.ceil(total / limit); return { success: true, - data: pegawaiList, + message: "Success fetch pegawai with pagination", + data, + page, + totalPages, + total, }; - } catch (error: any) { - console.error("Error findMany pegawai:", error); + } catch (e) { + console.error("Find many paginated error:", e); return { success: false, - message: "Gagal mengambil data pegawai", - error: error.message, + message: "Failed fetch pegawai with pagination", + data: [], + page: 1, + totalPages: 1, + total: 0, }; } -} +} \ No newline at end of file