Menerapkan pagination di submenu pegawai & berita
This commit is contained in:
@@ -80,13 +80,33 @@ const berita = proxy({
|
|||||||
};
|
};
|
||||||
}>[]
|
}>[]
|
||||||
| null,
|
| null,
|
||||||
async load() {
|
page: 1,
|
||||||
const res = await ApiFetch.api.desa.berita["find-many"].get();
|
totalPages: 1,
|
||||||
if (res.status === 200) {
|
loading: false,
|
||||||
berita.findMany.data = (res.data?.data ) ?? [];
|
|
||||||
|
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: {
|
findUnique: {
|
||||||
data: null as
|
data: null as
|
||||||
| Prisma.BeritaGetPayload<{
|
| Prisma.BeritaGetPayload<{
|
||||||
|
|||||||
@@ -30,9 +30,9 @@ const posisiOrganisasi = proxy({
|
|||||||
|
|
||||||
try {
|
try {
|
||||||
this.loading = true;
|
this.loading = true;
|
||||||
const res = await ApiFetch.api.ekonomi["struktur-organisasi"]["posisi-organisasi"]["create"].post(
|
const res = await ApiFetch.api.ekonomi["struktur-organisasi"][
|
||||||
this.form
|
"posisi-organisasi"
|
||||||
);
|
]["create"].post(this.form);
|
||||||
if (res.status === 200) {
|
if (res.status === 200) {
|
||||||
toast.success("Berhasil menambahkan posisi organisasi");
|
toast.success("Berhasil menambahkan posisi organisasi");
|
||||||
posisiOrganisasi.findMany.load();
|
posisiOrganisasi.findMany.load();
|
||||||
@@ -62,12 +62,15 @@ const posisiOrganisasi = proxy({
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
try {
|
try {
|
||||||
const response = await fetch(`/api/ekonomi/struktur-organisasi/posisi-organisasi/${id}`, {
|
const response = await fetch(
|
||||||
|
`/api/ekonomi/struktur-organisasi/posisi-organisasi/${id}`,
|
||||||
|
{
|
||||||
method: "GET",
|
method: "GET",
|
||||||
headers: {
|
headers: {
|
||||||
"Content-Type": "application/json",
|
"Content-Type": "application/json",
|
||||||
},
|
},
|
||||||
});
|
}
|
||||||
|
);
|
||||||
if (!response.ok) {
|
if (!response.ok) {
|
||||||
throw new Error(`HTTP error! status: ${response.status}`);
|
throw new Error(`HTTP error! status: ${response.status}`);
|
||||||
}
|
}
|
||||||
@@ -160,7 +163,9 @@ const posisiOrganisasi = proxy({
|
|||||||
}>,
|
}>,
|
||||||
async load() {
|
async load() {
|
||||||
try {
|
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) {
|
if (res.status === 200) {
|
||||||
// The API now returns the id field, so we can use it directly
|
// The API now returns the id field, so we can use it directly
|
||||||
this.data = res.data?.data ?? [];
|
this.data = res.data?.data ?? [];
|
||||||
@@ -239,16 +244,16 @@ const templatePegawai = z.object({
|
|||||||
async submit() {
|
async submit() {
|
||||||
const cek = templatePegawai.safeParse(pegawai.create.form);
|
const cek = templatePegawai.safeParse(pegawai.create.form);
|
||||||
if (!cek.success) {
|
if (!cek.success) {
|
||||||
const err = cek.error.issues.map(i => i.message).join("\n");
|
const err = cek.error.issues.map((i) => i.message).join("\n");
|
||||||
toast.error(err);
|
toast.error(err);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
pegawai.create.loading = true;
|
pegawai.create.loading = true;
|
||||||
const res = await ApiFetch.api.ekonomi["struktur-organisasi"]["pegawai"]["create"].post(
|
const res = await ApiFetch.api.ekonomi["struktur-organisasi"][
|
||||||
pegawai.create.form
|
"pegawai"
|
||||||
);
|
]["create"].post(pegawai.create.form);
|
||||||
if (res.status === 200) {
|
if (res.status === 200) {
|
||||||
toast.success("Pegawai berhasil ditambahkan");
|
toast.success("Pegawai berhasil ditambahkan");
|
||||||
await pegawai.findMany.load();
|
await pegawai.findMany.load();
|
||||||
@@ -263,36 +268,60 @@ const templatePegawai = z.object({
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|
||||||
|
// In struktur-organisasi.ts
|
||||||
findMany: {
|
findMany: {
|
||||||
data: null as (Prisma.PegawaiGetPayload<{ include: { posisi: true, image: true } }> & { isActive: boolean })[] | null,
|
data: null as any[] | null,
|
||||||
async load() {
|
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 {
|
try {
|
||||||
const res = await ApiFetch.api.ekonomi["struktur-organisasi"]["pegawai"]["find-many"].get();
|
const res = await ApiFetch.api.ekonomi["struktur-organisasi"][
|
||||||
if (res.status === 200) {
|
"pegawai"
|
||||||
pegawai.findMany.data = (res.data?.data ?? []).map((item: any) => ({
|
]["find-many"].get({
|
||||||
...item,
|
query: { page, limit },
|
||||||
posisi: item.posisi || { id: '', nama: '' }, // Ensure posisi exists with required fields
|
});
|
||||||
isActive: item.isActive ?? true // Default to true if not provided
|
|
||||||
}));
|
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 {
|
} else {
|
||||||
console.error('Failed to load pegawai:', res.data?.message);
|
console.error("Failed to load pegawai:", res.data?.message);
|
||||||
|
pegawai.findMany.data = [];
|
||||||
|
pegawai.findMany.total = 0;
|
||||||
|
pegawai.findMany.totalPages = 1;
|
||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('Error loading pegawai:', error);
|
console.error("Error loading pegawai:", error);
|
||||||
pegawai.findMany.data = [];
|
pegawai.findMany.data = [];
|
||||||
|
pegawai.findMany.total = 0;
|
||||||
|
pegawai.findMany.totalPages = 1;
|
||||||
|
} finally {
|
||||||
|
pegawai.findMany.loading = false;
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
findUnique: {
|
findUnique: {
|
||||||
data: null as (Prisma.PegawaiGetPayload<{ include: { posisi: true, image: true } }> & { isActive: boolean }) | null,
|
data: null as
|
||||||
|
| (Prisma.PegawaiGetPayload<{
|
||||||
|
include: { posisi: true; image: true };
|
||||||
|
}> & { isActive: boolean })
|
||||||
|
| null,
|
||||||
async load(id: string) {
|
async load(id: string) {
|
||||||
const res = await fetch(`/api/ekonomi/struktur-organisasi/pegawai/${id}`);
|
const res = await fetch(`/api/ekonomi/struktur-organisasi/pegawai/${id}`);
|
||||||
if (res.ok) {
|
if (res.ok) {
|
||||||
const json = await res.json();
|
const json = await res.json();
|
||||||
pegawai.findUnique.data = json.data ? {
|
pegawai.findUnique.data = json.data
|
||||||
|
? {
|
||||||
...json.data,
|
...json.data,
|
||||||
isActive: json.data.isActive ?? json.data.aktif ?? true // Fallback ke aktif:true jika tidak ada data
|
isActive: json.data.isActive ?? json.data.aktif ?? true, // Fallback ke aktif:true jika tidak ada data
|
||||||
} : null;
|
}
|
||||||
|
: null;
|
||||||
} else {
|
} else {
|
||||||
pegawai.findUnique.data = null;
|
pegawai.findUnique.data = null;
|
||||||
}
|
}
|
||||||
@@ -305,9 +334,12 @@ const templatePegawai = z.object({
|
|||||||
if (!id) return toast.warn("ID tidak valid");
|
if (!id) return toast.warn("ID tidak valid");
|
||||||
try {
|
try {
|
||||||
pegawai.delete.loading = true;
|
pegawai.delete.loading = true;
|
||||||
const res = await fetch(`/api/ekonomi/struktur-organisasi/pegawai/del/${id}`, {
|
const res = await fetch(
|
||||||
|
`/api/ekonomi/struktur-organisasi/pegawai/del/${id}`,
|
||||||
|
{
|
||||||
method: "DELETE",
|
method: "DELETE",
|
||||||
});
|
}
|
||||||
|
);
|
||||||
const json = await res.json();
|
const json = await res.json();
|
||||||
if (res.ok) {
|
if (res.ok) {
|
||||||
toast.success(json.message ?? "Berhasil hapus pegawai");
|
toast.success(json.message ?? "Berhasil hapus pegawai");
|
||||||
@@ -336,12 +368,15 @@ const templatePegawai = z.object({
|
|||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const response = await fetch(`/api/ekonomi/struktur-organisasi/pegawai/${id}`, {
|
const response = await fetch(
|
||||||
method: 'GET',
|
`/api/ekonomi/struktur-organisasi/pegawai/${id}`,
|
||||||
|
{
|
||||||
|
method: "GET",
|
||||||
headers: {
|
headers: {
|
||||||
'Content-Type': 'application/json',
|
"Content-Type": "application/json",
|
||||||
},
|
},
|
||||||
});
|
}
|
||||||
|
);
|
||||||
|
|
||||||
if (!response.ok) {
|
if (!response.ok) {
|
||||||
throw new Error(`HTTP error! status: ${response.status}`);
|
throw new Error(`HTTP error! status: ${response.status}`);
|
||||||
@@ -369,7 +404,9 @@ const templatePegawai = z.object({
|
|||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error("Error loading berita:", error);
|
console.error("Error loading berita:", error);
|
||||||
toast.error(error instanceof Error ? error.message : "Gagal memuat data");
|
toast.error(
|
||||||
|
error instanceof Error ? error.message : "Gagal memuat data"
|
||||||
|
);
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@@ -392,10 +429,12 @@ const templatePegawai = z.object({
|
|||||||
? new Date(this.form.tanggalMasuk).toISOString()
|
? new Date(this.form.tanggalMasuk).toISOString()
|
||||||
: undefined;
|
: undefined;
|
||||||
|
|
||||||
const response = await fetch(`/api/ekonomi/struktur-organisasi/pegawai/${this.id}`, {
|
const response = await fetch(
|
||||||
method: 'PUT',
|
`/api/ekonomi/struktur-organisasi/pegawai/${this.id}`,
|
||||||
|
{
|
||||||
|
method: "PUT",
|
||||||
headers: {
|
headers: {
|
||||||
'Content-Type': 'application/json',
|
"Content-Type": "application/json",
|
||||||
},
|
},
|
||||||
body: JSON.stringify({
|
body: JSON.stringify({
|
||||||
id: this.id,
|
id: this.id,
|
||||||
@@ -409,11 +448,14 @@ const templatePegawai = z.object({
|
|||||||
posisiId: this.form.posisiId,
|
posisiId: this.form.posisiId,
|
||||||
isActive: this.form.isActive,
|
isActive: this.form.isActive,
|
||||||
}),
|
}),
|
||||||
});
|
}
|
||||||
|
);
|
||||||
|
|
||||||
if (!response.ok) {
|
if (!response.ok) {
|
||||||
const errorData = await response.json().catch(() => ({}));
|
const errorData = await response.json().catch(() => ({}));
|
||||||
throw new Error(errorData.message || `HTTP error! status: ${response.status}`);
|
throw new Error(
|
||||||
|
errorData.message || `HTTP error! status: ${response.status}`
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
const result = await response.json();
|
const result = await response.json();
|
||||||
@@ -427,7 +469,11 @@ const templatePegawai = z.object({
|
|||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error("Error updating pegawai:", error);
|
console.error("Error updating pegawai:", error);
|
||||||
toast.error(error instanceof Error ? error.message : "Terjadi kesalahan saat update pegawai");
|
toast.error(
|
||||||
|
error instanceof Error
|
||||||
|
? error.message
|
||||||
|
: "Terjadi kesalahan saat update pegawai"
|
||||||
|
);
|
||||||
return false;
|
return false;
|
||||||
} finally {
|
} finally {
|
||||||
pegawai.edit.loading = false;
|
pegawai.edit.loading = false;
|
||||||
@@ -441,7 +487,6 @@ const templatePegawai = z.object({
|
|||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
// Schema Zod untuk form validasi
|
// Schema Zod untuk form validasi
|
||||||
const templateHubunganOrganisasiForm = z.object({
|
const templateHubunganOrganisasiForm = z.object({
|
||||||
atasanId: z.string().min(1, "Atasan wajib dipilih"),
|
atasanId: z.string().min(1, "Atasan wajib dipilih"),
|
||||||
@@ -474,7 +519,9 @@ const hubunganOrganisasi = proxy({
|
|||||||
|
|
||||||
try {
|
try {
|
||||||
hubunganOrganisasi.create.loading = true;
|
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) {
|
if (res.status === 200 && res.data?.success) {
|
||||||
hubunganOrganisasi.findMany.load();
|
hubunganOrganisasi.findMany.load();
|
||||||
@@ -528,20 +575,29 @@ const hubunganOrganisasi = proxy({
|
|||||||
|
|
||||||
async load() {
|
async load() {
|
||||||
try {
|
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) {
|
if (res.status === 200) {
|
||||||
hubunganOrganisasi.findMany.data = (res.data?.data ?? []).map((item: any) => ({
|
hubunganOrganisasi.findMany.data = (res.data?.data ?? []).map(
|
||||||
|
(item: any) => ({
|
||||||
...item,
|
...item,
|
||||||
atasan: item.atasan ? {
|
atasan: item.atasan
|
||||||
|
? {
|
||||||
...item.atasan,
|
...item.atasan,
|
||||||
isActive: item.atasan.isActive ?? item.atasan.aktif ?? true
|
isActive: item.atasan.isActive ?? item.atasan.aktif ?? true,
|
||||||
} : null,
|
}
|
||||||
bawahan: item.bawahan ? {
|
: null,
|
||||||
|
bawahan: item.bawahan
|
||||||
|
? {
|
||||||
...item.bawahan,
|
...item.bawahan,
|
||||||
isActive: item.bawahan.isActive ?? item.bawahan.aktif ?? true
|
isActive:
|
||||||
} : null
|
item.bawahan.isActive ?? item.bawahan.aktif ?? true,
|
||||||
}));
|
}
|
||||||
|
: null,
|
||||||
|
})
|
||||||
|
);
|
||||||
} else {
|
} else {
|
||||||
hubunganOrganisasi.findMany.data = [];
|
hubunganOrganisasi.findMany.data = [];
|
||||||
}
|
}
|
||||||
@@ -591,7 +647,9 @@ const hubunganOrganisasi = proxy({
|
|||||||
|
|
||||||
async load(id: string) {
|
async load(id: string) {
|
||||||
try {
|
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();
|
const result = await res.json();
|
||||||
|
|
||||||
if (res.ok && result?.success) {
|
if (res.ok && result?.success) {
|
||||||
@@ -616,7 +674,9 @@ const hubunganOrganisasi = proxy({
|
|||||||
if (!id) return toast.warn("ID tidak valid");
|
if (!id) return toast.warn("ID tidak valid");
|
||||||
|
|
||||||
try {
|
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();
|
const result = await res.json();
|
||||||
|
|
||||||
if (res.ok && result?.success) {
|
if (res.ok && result?.success) {
|
||||||
@@ -633,7 +693,9 @@ const hubunganOrganisasi = proxy({
|
|||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error("Error loading:", 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;
|
return null;
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@@ -690,9 +752,12 @@ const hubunganOrganisasi = proxy({
|
|||||||
|
|
||||||
try {
|
try {
|
||||||
hubunganOrganisasi.delete.loading = true;
|
hubunganOrganisasi.delete.loading = true;
|
||||||
const res = await fetch(`/api/ekonomi/struktur-organisasi/hubungan-organisasi/del/${id}`, {
|
const res = await fetch(
|
||||||
|
`/api/ekonomi/struktur-organisasi/hubungan-organisasi/del/${id}`,
|
||||||
|
{
|
||||||
method: "DELETE",
|
method: "DELETE",
|
||||||
});
|
}
|
||||||
|
);
|
||||||
|
|
||||||
const result = await res.json();
|
const result = await res.json();
|
||||||
if (res.ok && result?.success) {
|
if (res.ok && result?.success) {
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
'use client'
|
'use client'
|
||||||
import colors from '@/con/colors';
|
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 { useShallowEffect } from '@mantine/hooks';
|
||||||
import { IconCircleDashedPlus, IconDeviceImacCog, IconSearch } from '@tabler/icons-react';
|
import { IconCircleDashedPlus, IconDeviceImacCog, IconSearch } from '@tabler/icons-react';
|
||||||
import { useRouter } from 'next/navigation';
|
import { useRouter } from 'next/navigation';
|
||||||
@@ -29,12 +29,21 @@ function Berita() {
|
|||||||
function ListBerita({ search }: { search: string }) {
|
function ListBerita({ search }: { search: string }) {
|
||||||
const beritaState = useProxy(stateDashboardBerita)
|
const beritaState = useProxy(stateDashboardBerita)
|
||||||
const router = useRouter()
|
const router = useRouter()
|
||||||
|
const {
|
||||||
|
data,
|
||||||
|
page,
|
||||||
|
totalPages,
|
||||||
|
loading,
|
||||||
|
load,
|
||||||
|
} = beritaState.berita.findMany;
|
||||||
|
|
||||||
|
|
||||||
|
// Fetch pertama kali
|
||||||
useShallowEffect(() => {
|
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();
|
const keyword = search.toLowerCase();
|
||||||
return (
|
return (
|
||||||
item.judul.toLowerCase().includes(keyword) ||
|
item.judul.toLowerCase().includes(keyword) ||
|
||||||
@@ -42,38 +51,50 @@ function ListBerita({ search }: { search: string }) {
|
|||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
if (loading || !data) {
|
||||||
if (!beritaState.berita.findMany.data) {
|
return <Skeleton h={500} />;
|
||||||
return (
|
|
||||||
<Stack py={10}>
|
|
||||||
<Skeleton h={500} />
|
|
||||||
</Stack>
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Box py={10}>
|
<Box py={10}>
|
||||||
<Paper bg={colors['white-1']} p={'md'}>
|
<Pagination
|
||||||
|
value={page}
|
||||||
|
onChange={(newPage) => load(newPage)} // ini penting!
|
||||||
|
total={totalPages}
|
||||||
|
mt="md"
|
||||||
|
mb="md"
|
||||||
|
/>
|
||||||
|
|
||||||
|
<Paper bg={colors["white-1"]} p={"md"}>
|
||||||
<Stack>
|
<Stack>
|
||||||
<Grid>
|
<Grid>
|
||||||
<GridCol span={{ base: 12, md: 11 }}>
|
<GridCol span={{ base: 12, md: 11 }}>
|
||||||
<Text fz={"xl"} fw={"bold"}>List Berita</Text>
|
<Text fz={"xl"} fw={"bold"}>
|
||||||
|
List Berita
|
||||||
|
</Text>
|
||||||
</GridCol>
|
</GridCol>
|
||||||
<GridCol span={{ base: 12, md: 1 }}>
|
<GridCol span={{ base: 12, md: 1 }}>
|
||||||
<Button onClick={() => router.push("/admin/desa/berita/create")} bg={colors['blue-button']}>
|
<Button
|
||||||
|
onClick={() => router.push("/admin/desa/berita/create")}
|
||||||
|
bg={colors["blue-button"]}
|
||||||
|
>
|
||||||
<IconCircleDashedPlus size={25} />
|
<IconCircleDashedPlus size={25} />
|
||||||
</Button>
|
</Button>
|
||||||
</GridCol>
|
</GridCol>
|
||||||
</Grid>
|
</Grid>
|
||||||
<Box style={{ overflowX: "auto" }}>
|
<Box style={{ overflowX: "auto" }}>
|
||||||
<Table striped withRowBorders withTableBorder style={{ minWidth: '700px' }}>
|
<Table
|
||||||
|
striped
|
||||||
|
withRowBorders
|
||||||
|
withTableBorder
|
||||||
|
style={{ minWidth: "700px" }}
|
||||||
|
>
|
||||||
<TableThead>
|
<TableThead>
|
||||||
<TableTr>
|
<TableTr>
|
||||||
<TableTh w={250}>Judul</TableTh>
|
<TableTh w={250}>Judul</TableTh>
|
||||||
<TableTh w={250}>Kategori</TableTh>
|
<TableTh w={250}>Kategori</TableTh>
|
||||||
<TableTh w={250}>Image</TableTh>
|
<TableTh w={250}>Image</TableTh>
|
||||||
<TableTh w={200}>Detail</TableTh>
|
<TableTh w={200}>Detail</TableTh>
|
||||||
|
|
||||||
</TableTr>
|
</TableTr>
|
||||||
</TableThead>
|
</TableThead>
|
||||||
<TableTbody>
|
<TableTbody>
|
||||||
@@ -81,7 +102,9 @@ function ListBerita({ search }: { search: string }) {
|
|||||||
<TableTr key={item.id}>
|
<TableTr key={item.id}>
|
||||||
<TableTd>
|
<TableTd>
|
||||||
<Box w={100}>
|
<Box w={100}>
|
||||||
<Text truncate="end" fz={"sm"}>{item.judul}</Text>
|
<Text truncate="end" fz={"sm"}>
|
||||||
|
{item.judul}
|
||||||
|
</Text>
|
||||||
</Box>
|
</Box>
|
||||||
</TableTd>
|
</TableTd>
|
||||||
<TableTd>{item.kategoriBerita?.name}</TableTd>
|
<TableTd>{item.kategoriBerita?.name}</TableTd>
|
||||||
@@ -89,20 +112,23 @@ function ListBerita({ search }: { search: string }) {
|
|||||||
<Image w={100} src={item.image?.link} alt="gambar" />
|
<Image w={100} src={item.image?.link} alt="gambar" />
|
||||||
</TableTd>
|
</TableTd>
|
||||||
<TableTd>
|
<TableTd>
|
||||||
<Button bg={"green"} onClick={() => router.push(`/admin/desa/berita/${item.id}`)}>
|
<Button
|
||||||
|
bg={"green"}
|
||||||
|
onClick={() =>
|
||||||
|
router.push(`/admin/desa/berita/${item.id}`)
|
||||||
|
}
|
||||||
|
>
|
||||||
<IconDeviceImacCog size={25} />
|
<IconDeviceImacCog size={25} />
|
||||||
</Button>
|
</Button>
|
||||||
</TableTd>
|
</TableTd>
|
||||||
|
|
||||||
</TableTr>
|
</TableTr>
|
||||||
))}
|
))}
|
||||||
</TableTbody>
|
</TableTbody>
|
||||||
</Table> </Box>
|
</Table>
|
||||||
|
</Box>
|
||||||
</Stack>
|
</Stack>
|
||||||
</Paper>
|
</Paper>
|
||||||
|
|
||||||
</Box>
|
</Box>
|
||||||
)
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
export default Berita;
|
export default Berita;
|
||||||
|
|||||||
@@ -1,10 +1,10 @@
|
|||||||
/* eslint-disable react-hooks/exhaustive-deps */
|
/* eslint-disable react-hooks/exhaustive-deps */
|
||||||
'use client'
|
'use client'
|
||||||
import colors from '@/con/colors';
|
import colors from '@/con/colors';
|
||||||
import { Badge, Box, Button, Paper, Skeleton, Stack, Table, TableTbody, TableTd, TableTh, TableThead, TableTr } from '@mantine/core';
|
import { Badge, Box, Button, Center, Group, Pagination, Paper, Skeleton, Stack, Table, TableTbody, TableTd, TableTh, TableThead, TableTr, ThemeIcon } from '@mantine/core';
|
||||||
import { IconDeviceImacCog, IconSearch } from '@tabler/icons-react';
|
import { IconCheck, IconDeviceImacCog, IconSearch, IconX } from '@tabler/icons-react';
|
||||||
import { useRouter } from 'next/navigation';
|
import { useRouter } from 'next/navigation';
|
||||||
import { useEffect, useState } from 'react';
|
import { useEffect, useMemo, useState } from 'react';
|
||||||
import { useProxy } from 'valtio/utils';
|
import { useProxy } from 'valtio/utils';
|
||||||
import HeaderSearch from '../../../_com/header';
|
import HeaderSearch from '../../../_com/header';
|
||||||
import JudulList from '../../../_com/judulList';
|
import JudulList from '../../../_com/judulList';
|
||||||
@@ -30,35 +30,21 @@ function ListPegawai({ search }: { search: string }) {
|
|||||||
const stateOrganisasi = useProxy(strukturorganisasiState.pegawai);
|
const stateOrganisasi = useProxy(strukturorganisasiState.pegawai);
|
||||||
const router = useRouter();
|
const router = useRouter();
|
||||||
|
|
||||||
|
const {
|
||||||
|
data,
|
||||||
|
page,
|
||||||
|
totalPages,
|
||||||
|
loading,
|
||||||
|
load,
|
||||||
|
} = stateOrganisasi.findMany;
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
const loadData = async () => {
|
load(page, 10);
|
||||||
try {
|
}, [page]);
|
||||||
// Clear existing data to ensure we see the loading state
|
|
||||||
stateOrganisasi.findMany.data = [];
|
|
||||||
|
|
||||||
// Load new data
|
const filteredData = useMemo(() => {
|
||||||
await stateOrganisasi.findMany.load();
|
if (!data) return [];
|
||||||
|
return data.filter(item => {
|
||||||
// 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();
|
const keyword = search.toLowerCase();
|
||||||
return (
|
return (
|
||||||
item.namaLengkap?.toLowerCase().includes(keyword) ||
|
item.namaLengkap?.toLowerCase().includes(keyword) ||
|
||||||
@@ -67,9 +53,10 @@ function ListPegawai({ search }: { search: string }) {
|
|||||||
item.posisi?.nama?.toLowerCase().includes(keyword)
|
item.posisi?.nama?.toLowerCase().includes(keyword)
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
}, [data, search]);
|
||||||
|
|
||||||
// Handle loading state
|
// Handle loading state
|
||||||
if (stateOrganisasi.findMany.data === null) {
|
if (loading || !data) {
|
||||||
return (
|
return (
|
||||||
<Stack py={10}>
|
<Stack py={10}>
|
||||||
<Skeleton height={300} />
|
<Skeleton height={300} />
|
||||||
@@ -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) {
|
if (data.length === 0) {
|
||||||
return (
|
return (
|
||||||
<Box py={10}>
|
<Box py={10}>
|
||||||
@@ -95,6 +80,7 @@ function ListPegawai({ search }: { search: string }) {
|
|||||||
title='List Pegawai'
|
title='List Pegawai'
|
||||||
href='/admin/ekonomi/struktur-organisasi-dan-sk-pengurus-bumdesa/pegawai/create'
|
href='/admin/ekonomi/struktur-organisasi-dan-sk-pengurus-bumdesa/pegawai/create'
|
||||||
/>
|
/>
|
||||||
|
<Box style={{ overflowX: "auto" }}>
|
||||||
<Table striped withTableBorder withRowBorders>
|
<Table striped withTableBorder withRowBorders>
|
||||||
<TableThead>
|
<TableThead>
|
||||||
<TableTr>
|
<TableTr>
|
||||||
@@ -125,7 +111,24 @@ function ListPegawai({ search }: { search: string }) {
|
|||||||
<TableTd>{item.telepon}</TableTd>
|
<TableTd>{item.telepon}</TableTd>
|
||||||
<TableTd>{item.posisi?.nama}</TableTd>
|
<TableTd>{item.posisi?.nama}</TableTd>
|
||||||
<TableTd>
|
<TableTd>
|
||||||
<Badge color={item.isActive ? "green" : "red"}>{item.isActive ? "Aktif" : "Tidak Aktif"}</Badge>
|
<Group gap="xs" wrap="nowrap">
|
||||||
|
<Box visibleFrom="sm">
|
||||||
|
<Badge color={item.isActive ? "green" : "red"}>
|
||||||
|
{item.isActive ? "Aktif" : "Tidak Aktif"}
|
||||||
|
</Badge>
|
||||||
|
</Box>
|
||||||
|
<Box hiddenFrom="sm">
|
||||||
|
{item.isActive ? (
|
||||||
|
<ThemeIcon color="green" variant="light" size="sm">
|
||||||
|
<IconCheck size={16} />
|
||||||
|
</ThemeIcon>
|
||||||
|
) : (
|
||||||
|
<ThemeIcon color="red" variant="light" size="sm">
|
||||||
|
<IconX size={16} />
|
||||||
|
</ThemeIcon>
|
||||||
|
)}
|
||||||
|
</Box>
|
||||||
|
</Group>
|
||||||
</TableTd>
|
</TableTd>
|
||||||
<TableTd>
|
<TableTd>
|
||||||
<Button bg={"green"} onClick={() => router.push(`/admin/ekonomi/struktur-organisasi-dan-sk-pengurus-bumdesa/pegawai/${item.id}`)}>
|
<Button bg={"green"} onClick={() => router.push(`/admin/ekonomi/struktur-organisasi-dan-sk-pengurus-bumdesa/pegawai/${item.id}`)}>
|
||||||
@@ -136,7 +139,20 @@ function ListPegawai({ search }: { search: string }) {
|
|||||||
))}
|
))}
|
||||||
</TableTbody>
|
</TableTbody>
|
||||||
</Table>
|
</Table>
|
||||||
|
</Box>
|
||||||
</Paper>
|
</Paper>
|
||||||
|
<Center>
|
||||||
|
<Pagination
|
||||||
|
value={page}
|
||||||
|
onChange={(newPage) => {
|
||||||
|
load(newPage, 10);
|
||||||
|
window.scrollTo(0, 0);
|
||||||
|
}}
|
||||||
|
total={totalPages}
|
||||||
|
mt="md"
|
||||||
|
mb="md"
|
||||||
|
/>
|
||||||
|
</Center>
|
||||||
</Box>
|
</Box>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,27 +1,44 @@
|
|||||||
|
// /api/berita/findManyPaginated.ts
|
||||||
import prisma from "@/lib/prisma";
|
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 {
|
try {
|
||||||
const data = await prisma.berita.findMany({
|
const [data, total] = await Promise.all([
|
||||||
|
prisma.berita.findMany({
|
||||||
where: { isActive: true },
|
where: { isActive: true },
|
||||||
include: {
|
include: {
|
||||||
image: true,
|
image: true,
|
||||||
kategoriBerita: true,
|
kategoriBerita: true,
|
||||||
},
|
},
|
||||||
});
|
skip,
|
||||||
|
take: limit,
|
||||||
|
orderBy: { createdAt: 'desc' }, // opsional, kalau mau urut berdasarkan waktu
|
||||||
|
}),
|
||||||
|
prisma.berita.count({
|
||||||
|
where: { isActive: true }
|
||||||
|
})
|
||||||
|
]);
|
||||||
|
|
||||||
return {
|
return {
|
||||||
success: true,
|
success: true,
|
||||||
message: "Success fetch berita",
|
message: "Success fetch berita with pagination",
|
||||||
data,
|
data,
|
||||||
|
page,
|
||||||
|
totalPages: Math.ceil(total / limit),
|
||||||
|
total,
|
||||||
};
|
};
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
console.error("Find many error:", e);
|
console.error("Find many paginated error:", e);
|
||||||
return {
|
return {
|
||||||
success: false,
|
success: false,
|
||||||
message: "Failed fetch berita",
|
message: "Failed fetch berita with pagination",
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export default beritaFindMany;
|
export default beritaFindManyPaginated;
|
||||||
|
|||||||
@@ -1,26 +1,48 @@
|
|||||||
/* eslint-disable @typescript-eslint/no-explicit-any */
|
|
||||||
import prisma from "@/lib/prisma";
|
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 {
|
try {
|
||||||
const pegawaiList = await prisma.pegawai.findMany({
|
const [data, total] = await Promise.all([
|
||||||
orderBy: { createdAt: "desc" },
|
prisma.pegawai.findMany({
|
||||||
|
where: { isActive: true },
|
||||||
include: {
|
include: {
|
||||||
posisi: true,
|
posisi: true,
|
||||||
image: true,
|
image: true,
|
||||||
},
|
},
|
||||||
});
|
skip,
|
||||||
|
take: limit,
|
||||||
|
orderBy: { createdAt: 'desc' },
|
||||||
|
}),
|
||||||
|
prisma.pegawai.count({
|
||||||
|
where: { isActive: true }
|
||||||
|
})
|
||||||
|
]);
|
||||||
|
|
||||||
|
const totalPages = Math.ceil(total / limit);
|
||||||
|
|
||||||
return {
|
return {
|
||||||
success: true,
|
success: true,
|
||||||
data: pegawaiList,
|
message: "Success fetch pegawai with pagination",
|
||||||
|
data,
|
||||||
|
page,
|
||||||
|
totalPages,
|
||||||
|
total,
|
||||||
};
|
};
|
||||||
} catch (error: any) {
|
} catch (e) {
|
||||||
console.error("Error findMany pegawai:", error);
|
console.error("Find many paginated error:", e);
|
||||||
return {
|
return {
|
||||||
success: false,
|
success: false,
|
||||||
message: "Gagal mengambil data pegawai",
|
message: "Failed fetch pegawai with pagination",
|
||||||
error: error.message,
|
data: [],
|
||||||
|
page: 1,
|
||||||
|
totalPages: 1,
|
||||||
|
total: 0,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Reference in New Issue
Block a user