Fix admin menu Landing page

This commit is contained in:
2025-08-21 10:16:05 +08:00
parent b580978f8e
commit 01aa0da5cc
22 changed files with 412 additions and 196 deletions

View File

@@ -1,3 +1,4 @@
/* eslint-disable @typescript-eslint/no-explicit-any */
import ApiFetch from "@/lib/api-fetch"; import ApiFetch from "@/lib/api-fetch";
import { Prisma } from "@prisma/client"; import { Prisma } from "@prisma/client";
import { toast } from "react-toastify"; import { toast } from "react-toastify";
@@ -50,18 +51,50 @@ const apbdes = proxy({
}, },
}, },
findMany: { findMany: {
data: null as Array< data: null as
Prisma.APBDesGetPayload<{ | Prisma.APBDesGetPayload<{
include: { include: {
image: true; image: true;
file: true; file: true;
}; };
}> }>[]
> | null, | null,
async load() { page: 1,
const res = await ApiFetch.api.landingpage.apbdes["find-many"].get(); totalPages: 1,
if (res.status === 200) { total: 0,
apbdes.findMany.data = res.data?.data ?? []; loading: false,
search: "",
load: async (page = 1, limit = 10, search = "") => { // Change to arrow function
apbdes.findMany.loading = true; // Use the full path to access the property
apbdes.findMany.page = page;
apbdes.findMany.search = search;
try {
const query: any = { page, limit };
if (search) query.search = search;
const res = await ApiFetch.api.landingpage.apbdes[
"findMany"
].get({
query
});
if (res.status === 200 && res.data?.success) {
apbdes.findMany.data = res.data.data || [];
apbdes.findMany.total = res.data.total || 0;
apbdes.findMany.totalPages = res.data.totalPages || 1;
} else {
console.error("Failed to load pegawai:", res.data?.message);
apbdes.findMany.data = [];
apbdes.findMany.total = 0;
apbdes.findMany.totalPages = 1;
}
} catch (error) {
console.error("Error loading pegawai:", error);
apbdes.findMany.data = [];
apbdes.findMany.total = 0;
apbdes.findMany.totalPages = 1;
} finally {
apbdes.findMany.loading = false;
} }
}, },
}, },

View File

@@ -60,14 +60,20 @@ const desaAntikorupsi = proxy({
totalPages: 1, totalPages: 1,
total: 0, total: 0,
loading: false, loading: false,
load: async (page = 1, limit = 10) => { // Change to arrow function search: "",
desaAntikorupsi.findMany.loading = true; // Use the full path to access the property load: async (page = 1, limit = 10, search = "") => {
// Change to arrow function
desaAntikorupsi.findMany.loading = true; // Use the full path to access the property
desaAntikorupsi.findMany.page = page; desaAntikorupsi.findMany.page = page;
desaAntikorupsi.findMany.search = search;
try { try {
const query: any = { page, limit };
if (search) query.search = search;
const res = await ApiFetch.api.landingpage.desaantikorupsi[ const res = await ApiFetch.api.landingpage.desaantikorupsi[
"findMany" "findMany"
].get({ ].get({
query: { page, limit }, query,
}); });
if (res.status === 200 && res.data?.success) { if (res.status === 200 && res.data?.success) {
@@ -305,20 +311,25 @@ const kategoriDesaAntiKorupsi = proxy({
totalPages: 1, totalPages: 1,
total: 0, total: 0,
loading: false, loading: false,
load: async (page = 1, limit = 10) => { // Change to arrow function search: "",
kategoriDesaAntiKorupsi.findMany.loading = true; // Use the full path to access the property load: async (page = 1, limit = 10, search = "") => {
// Change to arrow function
kategoriDesaAntiKorupsi.findMany.loading = true; // Use the full path to access the property
kategoriDesaAntiKorupsi.findMany.page = page; kategoriDesaAntiKorupsi.findMany.page = page;
kategoriDesaAntiKorupsi.findMany.search = search;
try { try {
const res = await ApiFetch.api.landingpage.kategoridak[ const query: any = { page, limit };
"findMany" if (search) query.search = search;
].get({
query: { page, limit }, const res = await ApiFetch.api.landingpage.kategoridak["findMany"].get({
query,
}); });
if (res.status === 200 && res.data?.success) { if (res.status === 200 && res.data?.success) {
kategoriDesaAntiKorupsi.findMany.data = res.data.data || []; kategoriDesaAntiKorupsi.findMany.data = res.data.data || [];
kategoriDesaAntiKorupsi.findMany.total = res.data.total || 0; kategoriDesaAntiKorupsi.findMany.total = res.data.total || 0;
kategoriDesaAntiKorupsi.findMany.totalPages = res.data.totalPages || 1; kategoriDesaAntiKorupsi.findMany.totalPages =
res.data.totalPages || 1;
} else { } else {
console.error("Failed to load media sosial:", res.data?.message); console.error("Failed to load media sosial:", res.data?.message);
kategoriDesaAntiKorupsi.findMany.data = []; kategoriDesaAntiKorupsi.findMany.data = [];
@@ -363,27 +374,30 @@ const kategoriDesaAntiKorupsi = proxy({
try { try {
kategoriDesaAntiKorupsi.delete.loading = true; kategoriDesaAntiKorupsi.delete.loading = true;
const response = await fetch( const response = await fetch(`/api/landingpage/kategoridak/del/${id}`, {
`/api/landingpage/kategoridak/del/${id}`, method: "DELETE",
{ headers: {
method: "DELETE", "Content-Type": "application/json",
headers: { },
"Content-Type": "application/json", });
},
}
);
const result = await response.json(); const result = await response.json();
if (response.ok && result?.success) { if (response.ok && result?.success) {
toast.success(result.message || "Kategori desa anti korupsi berhasil dihapus"); toast.success(
result.message || "Kategori desa anti korupsi berhasil dihapus"
);
await kategoriDesaAntiKorupsi.findMany.load(); // refresh list await kategoriDesaAntiKorupsi.findMany.load(); // refresh list
} else { } else {
toast.error(result?.message || "Gagal menghapus kategori desa anti korupsi"); toast.error(
result?.message || "Gagal menghapus kategori desa anti korupsi"
);
} }
} catch (error) { } catch (error) {
console.error("Gagal delete:", error); console.error("Gagal delete:", error);
toast.error("Terjadi kesalahan saat menghapus kategori desa anti korupsi"); toast.error(
"Terjadi kesalahan saat menghapus kategori desa anti korupsi"
);
} finally { } finally {
kategoriDesaAntiKorupsi.delete.loading = false; kategoriDesaAntiKorupsi.delete.loading = false;
} }

View File

@@ -1,3 +1,4 @@
/* eslint-disable @typescript-eslint/no-explicit-any */
import ApiFetch from "@/lib/api-fetch"; import ApiFetch from "@/lib/api-fetch";
import { Prisma } from "@prisma/client"; import { Prisma } from "@prisma/client";
import { toast } from "react-toastify"; import { toast } from "react-toastify";
@@ -62,12 +63,34 @@ const prestasiDesa = proxy({
}; };
}> }>
> | null, > | null,
async load() { page: 1,
const res = await ApiFetch.api.landingpage.prestasidesa[ totalPages: 1,
"find-many" loading: false,
].get(); search: "",
if (res.status === 200) { load: async (page = 1, limit = 10, search = "") => {
prestasiDesa.findMany.data = res.data?.data ?? []; prestasiDesa.findMany.loading = true; // ✅ Akses langsung via nama path
prestasiDesa.findMany.page = page;
prestasiDesa.findMany.search = search;
try {
const query: any = { page, limit };
if (search) query.search = search;
const res = await ApiFetch.api.landingpage.prestasidesa["find-many"].get({ query });
if (res.status === 200 && res.data?.success) {
prestasiDesa.findMany.data = res.data.data ?? [];
prestasiDesa.findMany.totalPages = res.data.totalPages ?? 1;
} else {
prestasiDesa.findMany.data = [];
prestasiDesa.findMany.totalPages = 1;
}
} catch (err) {
console.error("Gagal fetch prestasi desa paginated:", err);
prestasiDesa.findMany.data = [];
prestasiDesa.findMany.totalPages = 1;
} finally {
prestasiDesa.findMany.loading = false;
} }
}, },
}, },
@@ -283,12 +306,34 @@ const kategoriPrestasi = proxy({
id: string; id: string;
name: string; name: string;
}> | null, }> | null,
async load() { page: 1,
const res = await ApiFetch.api.landingpage.kategoriprestasi[ totalPages: 1,
"find-many" loading: false,
].get(); search: "",
if (res.status === 200) { load: async (page = 1, limit = 10, search = "") => {
kategoriPrestasi.findMany.data = res.data?.data ?? []; kategoriPrestasi.findMany.loading = true; // ✅ Akses langsung via nama path
kategoriPrestasi.findMany.page = page;
kategoriPrestasi.findMany.search = search;
try {
const query: any = { page, limit };
if (search) query.search = search;
const res = await ApiFetch.api.landingpage.kategoriprestasi["find-many"].get({ query });
if (res.status === 200 && res.data?.success) {
kategoriPrestasi.findMany.data = res.data.data ?? [];
kategoriPrestasi.findMany.totalPages = res.data.totalPages ?? 1;
} else {
kategoriPrestasi.findMany.data = [];
kategoriPrestasi.findMany.totalPages = 1;
}
} catch (err) {
console.error("Gagal fetch kategori prestasi paginated:", err);
kategoriPrestasi.findMany.data = [];
kategoriPrestasi.findMany.totalPages = 1;
} finally {
kategoriPrestasi.findMany.loading = false;
} }
}, },
}, },

View File

@@ -65,14 +65,19 @@ const programInovasi = proxy({
totalPages: 1, totalPages: 1,
total: 0, total: 0,
loading: false, loading: false,
load: async (page = 1, limit = 10) => { // Change to arrow function search: "",
load: async (page = 1, limit = 10, search = "") => { // Change to arrow function
programInovasi.findMany.loading = true; // Use the full path to access the property programInovasi.findMany.loading = true; // Use the full path to access the property
programInovasi.findMany.page = page; programInovasi.findMany.page = page;
programInovasi.findMany.search = search;
try { try {
const query: any = { page, limit };
if (search) query.search = search;
const res = await ApiFetch.api.landingpage.programinovasi[ const res = await ApiFetch.api.landingpage.programinovasi[
"findMany" "findMany"
].get({ ].get({
query: { page, limit }, query
}); });
if (res.status === 200 && res.data?.success) { if (res.status === 200 && res.data?.success) {
@@ -482,14 +487,19 @@ const mediaSosial = proxy({
totalPages: 1, totalPages: 1,
total: 0, total: 0,
loading: false, loading: false,
load: async (page = 1, limit = 10) => { // Change to arrow function search: "",
load: async (page = 1, limit = 10, search = "") => { // Change to arrow function
mediaSosial.findMany.loading = true; // Use the full path to access the property mediaSosial.findMany.loading = true; // Use the full path to access the property
mediaSosial.findMany.page = page; mediaSosial.findMany.page = page;
mediaSosial.findMany.search = search;
try { try {
const query: any = { page, limit };
if (search) query.search = search;
const res = await ApiFetch.api.landingpage.mediasosial[ const res = await ApiFetch.api.landingpage.mediasosial[
"findMany" "findMany"
].get({ ].get({
query: { page, limit }, query,
}); });
if (res.status === 200 && res.data?.success) { if (res.status === 200 && res.data?.success) {

View File

@@ -58,14 +58,19 @@ const sdgsDesa = proxy({
totalPages: 1, totalPages: 1,
total: 0, total: 0,
loading: false, loading: false,
load: async (page = 1, limit = 10) => { // Change to arrow function search: "",
load: async (page = 1, limit = 10, search = "") => { // Change to arrow function
sdgsDesa.findMany.loading = true; // Use the full path to access the property sdgsDesa.findMany.loading = true; // Use the full path to access the property
sdgsDesa.findMany.page = page; sdgsDesa.findMany.page = page;
sdgsDesa.findMany.search = search;
try { try {
const query: any = { page, limit };
if (search) query.search = search;
const res = await ApiFetch.api.landingpage.sdgsdesa[ const res = await ApiFetch.api.landingpage.sdgsdesa[
"findMany" "findMany"
].get({ ].get({
query: { page, limit }, query,
}); });
if (res.status === 200 && res.data?.success) { if (res.status === 200 && res.data?.success) {

View File

@@ -1,10 +1,10 @@
/* eslint-disable react-hooks/exhaustive-deps */
'use client' 'use client'
import colors from '@/con/colors'; import colors from '@/con/colors';
import { ActionIcon, Box, Button, Paper, Skeleton, Stack, Table, TableTbody, TableTd, TableTh, TableThead, TableTr, Text } from '@mantine/core'; import { ActionIcon, Box, Button, Center, Pagination, Paper, Skeleton, Stack, Table, TableTbody, TableTd, TableTh, TableThead, TableTr, Text } from '@mantine/core';
import { useShallowEffect } from '@mantine/hooks';
import { IconDeviceImacCog, IconFile, IconSearch } from '@tabler/icons-react'; import { IconDeviceImacCog, IconFile, IconSearch } from '@tabler/icons-react';
import { useRouter } from 'next/navigation'; import { useRouter } from 'next/navigation';
import { useEffect, useState } from 'react'; import { 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,19 +30,22 @@ function APBDes() {
function ListAPBDes({ search }: { search: string }) { function ListAPBDes({ search }: { search: string }) {
const listState = useProxy(apbdes) const listState = useProxy(apbdes)
const router = useRouter(); const router = useRouter();
useEffect(() => {
listState.findMany.load()
}, [])
const filteredData = (listState.findMany.data || []).filter(item => { const {
const keyword = search.toLowerCase(); data,
return ( page,
item.name.toLowerCase().includes(keyword) || totalPages,
item.jumlah.toLowerCase().includes(keyword) loading,
) load,
}); } = listState.findMany
if (!listState.findMany.data) { useShallowEffect(() => {
load(page, 10, search)
}, [page, search])
const filteredData = data || []
if (loading || !data) {
return ( return (
<Stack py={10}> <Stack py={10}>
<Skeleton h={500} /> <Skeleton h={500} />
@@ -88,7 +91,7 @@ function ListAPBDes({ search }: { search: string }) {
rel="noopener noreferrer" rel="noopener noreferrer"
variant='transparent' variant='transparent'
> >
<IconFile size={25} color={colors['blue-button']}/> <IconFile size={25} color={colors['blue-button']} />
</ActionIcon> </ActionIcon>
) : ( ) : (
<Text>Tidak ada dokumen tersedia</Text> <Text>Tidak ada dokumen tersedia</Text>
@@ -106,6 +109,14 @@ function ListAPBDes({ search }: { search: string }) {
</Box> </Box>
</Stack> </Stack>
</Paper> </Paper>
<Center>
<Pagination
value={page}
onChange={(newPage) => load(newPage)}
total={totalPages}
my={"md"}
/>
</Center>
</Box> </Box>
) )
} }

View File

@@ -1,10 +1,10 @@
/* eslint-disable react-hooks/exhaustive-deps */
'use client' 'use client'
import colors from '@/con/colors'; import colors from '@/con/colors';
import { Box, Button, Center, Pagination, Paper, Skeleton, Stack, Table, TableTbody, TableTd, TableTh, TableThead, TableTr } from '@mantine/core'; import { Box, Button, Center, Pagination, Paper, Skeleton, Stack, Table, TableTbody, TableTd, TableTh, TableThead, TableTr } from '@mantine/core';
import { useShallowEffect } from '@mantine/hooks';
import { IconEdit, IconSearch, IconX } from '@tabler/icons-react'; import { IconEdit, IconSearch, IconX } from '@tabler/icons-react';
import { useRouter } from 'next/navigation'; import { useRouter } from 'next/navigation';
import { useEffect, useMemo, useState } from 'react'; import { 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';
@@ -50,19 +50,11 @@ function ListKategoriKegiatan({ search }: { search: string }) {
} }
} }
useEffect(() => { useShallowEffect(() => {
load(page, 10) load(page, 10, search)
}, [page]) }, [page, search])
const filteredData = useMemo(() => { const filteredData = data || []
if (!data) return [];
return data.filter(item => {
const keyword = search.toLowerCase();
return (
item.name?.toLowerCase().includes(keyword)
);
})
}, [data, search]);
// Handle loading state // Handle loading state
if (loading || !data) { if (loading || !data) {

View File

@@ -1,10 +1,10 @@
/* eslint-disable react-hooks/exhaustive-deps */
'use client' 'use client'
import colors from '@/con/colors'; import colors from '@/con/colors';
import { Box, Button, Center, Pagination, Paper, Skeleton, Stack, Table, TableTbody, TableTd, TableTh, TableThead, TableTr, Text } from '@mantine/core'; import { Box, Button, Center, Pagination, Paper, Skeleton, Stack, Table, TableTbody, TableTd, TableTh, TableThead, TableTr, Text } from '@mantine/core';
import { useShallowEffect } from '@mantine/hooks';
import { IconDeviceImacCog, IconSearch } from '@tabler/icons-react'; import { IconDeviceImacCog, IconSearch } from '@tabler/icons-react';
import { useRouter } from 'next/navigation'; import { useRouter } from 'next/navigation';
import { useEffect, useMemo, useState } from 'react'; import { 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';
@@ -38,22 +38,11 @@ function ListDesaAntiKorupsi({ search }: { search: string }) {
load, load,
} = listState.findMany; } = listState.findMany;
useEffect(() => { useShallowEffect(() => {
load(page, 10); load(page, 10, search);
}, [page]); }, [page, search]);
const filteredData = useMemo(() => { const filteredData = data || []
if (!data) return [];
return data.filter(item => {
const keyword = search.toLowerCase();
return (
item.name?.toLowerCase().includes(keyword) ||
item.deskripsi?.toLowerCase().includes(keyword) ||
item.kategori?.name?.toLowerCase().includes(keyword)
);
})
.sort((a, b) => b.createdAt - a.createdAt);
}, [data, search]);
// Handle loading state // Handle loading state
if (loading || !data) { if (loading || !data) {

View File

@@ -1,6 +1,6 @@
'use client' 'use client'
import colors from '@/con/colors'; import colors from '@/con/colors';
import { Box, Button, Paper, Skeleton, Stack, Table, TableTbody, TableTd, TableTh, TableThead, TableTr } from '@mantine/core'; import { Box, Button, Center, Pagination, Paper, Skeleton, Stack, Table, TableTbody, TableTd, TableTh, TableThead, TableTr } from '@mantine/core';
import { useShallowEffect } from '@mantine/hooks'; import { useShallowEffect } from '@mantine/hooks';
import { IconEdit, IconSearch, IconX } from '@tabler/icons-react'; import { IconEdit, IconSearch, IconX } from '@tabler/icons-react';
import { useRouter } from 'next/navigation'; import { useRouter } from 'next/navigation';
@@ -42,18 +42,21 @@ function ListKategoriPrestasi({ search }: { search: string }) {
} }
} }
const {
data,
page,
totalPages,
loading,
load,
} = stateKategori.findMany
useShallowEffect(() => { useShallowEffect(() => {
stateKategori.findMany.load() load(page, 10, search)
}, []) }, [page, search])
const filteredData = (stateKategori.findMany.data || []).filter(item => { const filteredData = data || []
const keyword = search.toLowerCase();
return (
item.name.toLowerCase().includes(keyword)
);
});
if (!stateKategori.findMany.data) { if (loading || !data) {
return ( return (
<Stack py={10}> <Stack py={10}>
<Skeleton h={500} /> <Skeleton h={500} />
@@ -100,6 +103,14 @@ function ListKategoriPrestasi({ search }: { search: string }) {
</Table> </Table>
</Box> </Box>
</Paper> </Paper>
<Center>
<Pagination
value={page}
onChange={(newPage) => load(newPage)}
total={totalPages}
my={"md"}
/>
</Center>
{/* Modal Konfirmasi Hapus */} {/* Modal Konfirmasi Hapus */}
<ModalKonfirmasiHapus <ModalKonfirmasiHapus
opened={modalHapus} opened={modalHapus}

View File

@@ -1,7 +1,7 @@
/* 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 { Box, Button, Paper, Skeleton, Stack, Table, TableTbody, TableTd, TableTh, TableThead, TableTr, Text } from '@mantine/core'; import { Box, Button, Center, Pagination, Paper, Skeleton, Stack, Table, TableTbody, TableTd, TableTh, TableThead, TableTr, Text } from '@mantine/core';
import { IconDeviceImacCog, IconSearch } from '@tabler/icons-react'; import { IconDeviceImacCog, IconSearch } from '@tabler/icons-react';
import { useRouter } from 'next/navigation'; import { useRouter } from 'next/navigation';
import { useEffect, useState } from 'react'; import { useEffect, useState } from 'react';
@@ -30,20 +30,22 @@ function ListPrestasiDesa() {
function ListPrestasi({ search }: { search: string }) { function ListPrestasi({ search }: { search: string }) {
const listState = useProxy(prestasiState.prestasiDesa) const listState = useProxy(prestasiState.prestasiDesa)
const router = useRouter(); const router = useRouter();
const{
data,
page,
totalPages,
loading,
load,
} = listState.findMany
useEffect(() => { useEffect(() => {
listState.findMany.load() load(page, 10, search)
}, []) }, [page, search])
const filteredData = (listState.findMany.data || []).filter(item => { const filteredData = data || []
const keyword = search.toLowerCase();
return (
item.name.toLowerCase().includes(keyword) ||
item.deskripsi.toLowerCase().includes(keyword) ||
item.kategori?.name?.toLowerCase().includes(keyword)
);
});
if (!listState.findMany.data) { if (loading || !data) {
return ( return (
<Stack py={10}> <Stack py={10}>
<Skeleton h={500} /> <Skeleton h={500} />
@@ -95,6 +97,14 @@ function ListPrestasi({ search }: { search: string }) {
</Box> </Box>
</Stack> </Stack>
</Paper> </Paper>
<Center>
<Pagination
value={page}
onChange={(newPage) => load(newPage)}
total={totalPages}
my={"md"}
/>
</Center>
</Box> </Box>
) )
} }

View File

@@ -1,10 +1,10 @@
/* eslint-disable react-hooks/exhaustive-deps */
'use client' 'use client'
import colors from '@/con/colors'; import colors from '@/con/colors';
import { Box, Button, Center, Image, Pagination, Paper, Skeleton, Stack, Table, TableTbody, TableTd, TableTh, TableThead, TableTr, Text } from '@mantine/core'; import { Box, Button, Center, Image, Pagination, Paper, Skeleton, Stack, Table, TableTbody, TableTd, TableTh, TableThead, TableTr, Text } from '@mantine/core';
import { useShallowEffect } from '@mantine/hooks';
import { IconDeviceImac, IconSearch } from '@tabler/icons-react'; import { IconDeviceImac, IconSearch } from '@tabler/icons-react';
import { useRouter } from 'next/navigation'; import { useRouter } from 'next/navigation';
import { useEffect, useMemo, useState } from 'react'; import { 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';
@@ -38,20 +38,11 @@ function ListMediaSosial({ search }: { search: string }) {
load, load,
} = stateMediaSosial.findMany; } = stateMediaSosial.findMany;
useEffect(() => { useShallowEffect(() => {
load(page, 10) load(page, 10, search)
}, [page]) }, [page, search])
const filteredData = useMemo(() => { const filteredData = data || []
if (!data) return [];
return data.filter(item => {
const keyword = search.toLowerCase();
return (
item.name?.toLowerCase().includes(keyword) ||
item.iconUrl?.toLowerCase().includes(keyword)
);
})
}, [data, search]);
// Handle loading state // Handle loading state
if (loading || !data) { if (loading || !data) {

View File

@@ -1,10 +1,10 @@
/* eslint-disable react-hooks/exhaustive-deps */
'use client' 'use client'
import colors from '@/con/colors'; import colors from '@/con/colors';
import { Box, Button, Center, Pagination, Paper, Skeleton, Stack, Table, TableTbody, TableTd, TableTh, TableThead, TableTr, Text } from '@mantine/core'; import { Box, Button, Center, Pagination, Paper, Skeleton, Stack, Table, TableTbody, TableTd, TableTh, TableThead, TableTr, Text } from '@mantine/core';
import { useShallowEffect } from '@mantine/hooks';
import { IconDeviceImac, IconSearch } from '@tabler/icons-react'; import { IconDeviceImac, IconSearch } from '@tabler/icons-react';
import { useRouter } from 'next/navigation'; import { useRouter } from 'next/navigation';
import { useEffect, useMemo, useState } from 'react'; import { 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';
@@ -38,21 +38,11 @@ function ListProgramInovasi({ search }: { search: string }) {
load, load,
} = stateProgramInovasi.findMany; } = stateProgramInovasi.findMany;
useEffect(() => { useShallowEffect(() => {
load(page, 10); load(page, 10, search);
}, [page]); }, [page, search]);
const filteredData = useMemo(() => { const filteredData = data || []
if (!data) return [];
return data.filter(item => {
const keyword = search.toLowerCase();
return (
item.name?.toLowerCase().includes(keyword) ||
item.description?.toLowerCase().includes(keyword) ||
item.link?.toLowerCase().includes(keyword)
);
})
}, [data, search]);
if (loading || !data) { if (loading || !data) {
return ( return (

View File

@@ -1,14 +1,14 @@
/* eslint-disable react-hooks/exhaustive-deps */
'use client' 'use client'
import colors from '@/con/colors'; import colors from '@/con/colors';
import { Box, Button, Center, Pagination, Paper, Skeleton, Stack, Table, TableTbody, TableTd, TableTh, TableThead, TableTr, Text } from '@mantine/core'; import { Box, Button, Center, Pagination, Paper, Skeleton, Stack, Table, TableTbody, TableTd, TableTh, TableThead, TableTr, Text } from '@mantine/core';
import { useShallowEffect } from '@mantine/hooks';
import { IconDeviceImacCog, IconSearch } from '@tabler/icons-react'; import { IconDeviceImacCog, IconSearch } from '@tabler/icons-react';
import { useRouter } from 'next/navigation'; import { useRouter } from 'next/navigation';
import { useEffect, useMemo, useState } from 'react'; import { useState } from 'react';
import { useProxy } from 'valtio/utils'; import { useProxy } from 'valtio/utils';
import HeaderSearch from '../../_com/header'; import HeaderSearch from '../../_com/header';
import sdgsDesa from '../../_state/landing-page/sdgs-desa';
import JudulList from '../../_com/judulList'; import JudulList from '../../_com/judulList';
import sdgsDesa from '../../_state/landing-page/sdgs-desa';
function SdgsDesa() { function SdgsDesa() {
@@ -39,20 +39,11 @@ function ListSdgsDesa({ search }: { search: string }) {
load, load,
} = listState.findMany; } = listState.findMany;
useEffect(() => { useShallowEffect(() => {
load(page, 10) load(page, 10, search)
}, []) }, [page, search])
const filteredData = useMemo(() => { const filteredData = data || []
if (!data) return [];
return data.filter(item => {
const keyword = search.toLowerCase();
return (
item.name?.toLowerCase().includes(keyword) ||
item.jumlah?.toLowerCase().includes(keyword)
);
})
}, [data, search]);
// Handle loading state // Handle loading state
if (loading || !data) { if (loading || !data) {

View File

@@ -1,3 +1,4 @@
/* eslint-disable @typescript-eslint/no-explicit-any */
// /api/berita/findManyPaginated.ts // /api/berita/findManyPaginated.ts
import prisma from "@/lib/prisma"; import prisma from "@/lib/prisma";
import { Context } from "elysia"; import { Context } from "elysia";
@@ -5,12 +6,23 @@ import { Context } from "elysia";
async function apbdesFindMany(context: Context) { async function apbdesFindMany(context: Context) {
const page = Number(context.query.page) || 1; const page = Number(context.query.page) || 1;
const limit = Number(context.query.limit) || 10; const limit = Number(context.query.limit) || 10;
const search = (context.query.search as string) || '';
const skip = (page - 1) * limit; const skip = (page - 1) * limit;
// Buat where clause
const where: any = { isActive: true };
// Tambahkan pencarian (jika ada)
if (search) {
where.OR = [
{ name: { contains: search, mode: 'insensitive' } },
];
}
try { try {
const [data, total] = await Promise.all([ const [data, total] = await Promise.all([
prisma.aPBDes.findMany({ prisma.aPBDes.findMany({
where: { isActive: true }, where,
include: { include: {
image: true, image: true,
file: true, file: true,
@@ -20,7 +32,7 @@ async function apbdesFindMany(context: Context) {
orderBy: { name: "asc" }, // opsional, kalau mau urut berdasarkan waktu orderBy: { name: "asc" }, // opsional, kalau mau urut berdasarkan waktu
}), }),
prisma.aPBDes.count({ prisma.aPBDes.count({
where: { isActive: true }, where,
}), }),
]); ]);

View File

@@ -11,7 +11,7 @@ const APBDes = new Elysia({
}) })
// ✅ Find all // ✅ Find all
.get("/find-many", apbdesFindMany) .get("/findMany", apbdesFindMany)
// ✅ Find by ID // ✅ Find by ID
.get("/:id", apbdesFindUnique) .get("/:id", apbdesFindUnique)

View File

@@ -1,3 +1,4 @@
/* eslint-disable @typescript-eslint/no-explicit-any */
// /api/berita/findManyPaginated.ts // /api/berita/findManyPaginated.ts
import prisma from "@/lib/prisma"; import prisma from "@/lib/prisma";
import { Context } from "elysia"; import { Context } from "elysia";
@@ -5,23 +6,35 @@ import { Context } from "elysia";
async function desaAntiKorupsiFindMany(context: Context) { async function desaAntiKorupsiFindMany(context: Context) {
const page = Number(context.query.page) || 1; const page = Number(context.query.page) || 1;
const limit = Number(context.query.limit) || 10; const limit = Number(context.query.limit) || 10;
const search = (context.query.search as string) || "";
const skip = (page - 1) * limit; const skip = (page - 1) * limit;
// Buat where clause
const where: any = { isActive: true };
// Tambahkan pencarian (jika ada)
if (search) {
where.OR = [
{ name: { contains: search, mode: "insensitive" } },
{ deskripsi: { contains: search, mode: "insensitive" } },
{ kategori: { name: { contains: search, mode: "insensitive" } } },
];
}
try { try {
const [data, total] = await Promise.all([ const [data, total] = await Promise.all([
prisma.desaAntiKorupsi.findMany({ prisma.desaAntiKorupsi.findMany({
where: { isActive: true }, where,
include: { include: {
kategori: true, kategori: true,
file: true, file: true,
}, },
skip, skip,
take: limit, take: limit,
orderBy: { name: 'asc' }, // opsional, kalau mau urut berdasarkan waktu orderBy: { name: "asc" }, // opsional, kalau mau urut berdasarkan waktu
}), }),
prisma.desaAntiKorupsi.count({ prisma.desaAntiKorupsi.count({
where: { isActive: true } where,
}) }),
]); ]);
return { return {

View File

@@ -1,3 +1,4 @@
/* eslint-disable @typescript-eslint/no-explicit-any */
// /api/berita/findManyPaginated.ts // /api/berita/findManyPaginated.ts
import prisma from "@/lib/prisma"; import prisma from "@/lib/prisma";
import { Context } from "elysia"; import { Context } from "elysia";
@@ -5,18 +6,29 @@ import { Context } from "elysia";
async function kategoriDesaAntiKorupsiFindMany(context: Context) { async function kategoriDesaAntiKorupsiFindMany(context: Context) {
const page = Number(context.query.page) || 1; const page = Number(context.query.page) || 1;
const limit = Number(context.query.limit) || 10; const limit = Number(context.query.limit) || 10;
const search = (context.query.search as string) || '';
const skip = (page - 1) * limit; const skip = (page - 1) * limit;
// Buat where clause
const where: any = { isActive: true };
// Tambahkan pencarian (jika ada)
if (search) {
where.OR = [
{ name: { contains: search, mode: 'insensitive' } },
];
}
try { try {
const [data, total] = await Promise.all([ const [data, total] = await Promise.all([
prisma.kategoriDesaAntiKorupsi.findMany({ prisma.kategoriDesaAntiKorupsi.findMany({
where: { isActive: true }, where,
skip, skip,
take: limit, take: limit,
orderBy: { name: 'asc' }, // opsional, kalau mau urut berdasarkan waktu orderBy: { name: 'asc' }, // opsional, kalau mau urut berdasarkan waktu
}), }),
prisma.kategoriDesaAntiKorupsi.count({ prisma.kategoriDesaAntiKorupsi.count({
where: { isActive: true } where,
}) })
]); ]);

View File

@@ -1,3 +1,4 @@
/* eslint-disable @typescript-eslint/no-explicit-any */
// /api/berita/findManyPaginated.ts // /api/berita/findManyPaginated.ts
import prisma from "@/lib/prisma"; import prisma from "@/lib/prisma";
import { Context } from "elysia"; import { Context } from "elysia";
@@ -5,12 +6,25 @@ import { Context } from "elysia";
async function prestasiDesaFindMany(context: Context) { async function prestasiDesaFindMany(context: Context) {
const page = Number(context.query.page) || 1; const page = Number(context.query.page) || 1;
const limit = Number(context.query.limit) || 10; const limit = Number(context.query.limit) || 10;
const search = (context.query.search as string) || '';
const skip = (page - 1) * limit; const skip = (page - 1) * limit;
// Buat where clause
const where: any = { isActive: true };
// Tambahkan pencarian (jika ada)
if (search) {
where.OR = [
{ name: { contains: search, mode: 'insensitive' } },
{ deskripsi: { contains: search, mode: 'insensitive' } },
{ kategori: { name: { contains: search, mode: 'insensitive' } } },
];
}
try { try {
const [data, total] = await Promise.all([ const [data, total] = await Promise.all([
prisma.prestasiDesa.findMany({ prisma.prestasiDesa.findMany({
where: { isActive: true }, where,
include: { include: {
image: true, image: true,
kategori: true, kategori: true,
@@ -20,7 +34,7 @@ async function prestasiDesaFindMany(context: Context) {
orderBy: { createdAt: "desc" }, // opsional, kalau mau urut berdasarkan waktu orderBy: { createdAt: "desc" }, // opsional, kalau mau urut berdasarkan waktu
}), }),
prisma.prestasiDesa.count({ prisma.prestasiDesa.count({
where: { isActive: true }, where,
}), }),
]); ]);

View File

@@ -1,15 +1,52 @@
/* eslint-disable @typescript-eslint/no-explicit-any */ /* eslint-disable @typescript-eslint/no-explicit-any */
// /api/berita/findManyPaginated.ts
import prisma from "@/lib/prisma"; import prisma from "@/lib/prisma";
import { Context } from "elysia";
async function kategoriPrestasiFindMany(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;
// Buat where clause
const where: any = { isActive: true };
// Tambahkan pencarian (jika ada)
if (search) {
where.OR = [
{ name: { contains: search, mode: 'insensitive' } },
];
}
try {
const [data, total] = await Promise.all([
prisma.kategoriPrestasiDesa.findMany({
where,
skip,
take: limit,
orderBy: { createdAt: "desc" }, // opsional, kalau mau urut berdasarkan waktu
}),
prisma.kategoriPrestasiDesa.count({
where,
}),
]);
export default async function kategoriPrestasiFindMany() {
const data = await prisma.kategoriPrestasiDesa.findMany();
return { return {
success: true, success: true,
data: data.map((item: any) => { message: "Success fetch Kategori Prestasi Desa with pagination",
return { data,
id: item.id, page,
name: item.name, totalPages: Math.ceil(total / limit),
} total,
}),
}; };
} catch (e) {
console.error("Find many paginated error:", e);
return {
success: false,
message: "Failed fetch Kategori Prestasi Desa with pagination",
};
}
} }
export default kategoriPrestasiFindMany;

View File

@@ -1,3 +1,4 @@
/* eslint-disable @typescript-eslint/no-explicit-any */
// /api/berita/findManyPaginated.ts // /api/berita/findManyPaginated.ts
import prisma from "@/lib/prisma"; import prisma from "@/lib/prisma";
import { Context } from "elysia"; import { Context } from "elysia";
@@ -5,12 +6,23 @@ import { Context } from "elysia";
async function mediaSosialFindMany(context: Context) { async function mediaSosialFindMany(context: Context) {
const page = Number(context.query.page) || 1; const page = Number(context.query.page) || 1;
const limit = Number(context.query.limit) || 10; const limit = Number(context.query.limit) || 10;
const search = (context.query.search as string) || '';
const skip = (page - 1) * limit; const skip = (page - 1) * limit;
// Buat where clause
const where: any = { isActive: true };
// Tambahkan pencarian (jika ada)
if (search) {
where.OR = [
{ name: { contains: search, mode: 'insensitive' } },
];
}
try { try {
const [data, total] = await Promise.all([ const [data, total] = await Promise.all([
prisma.mediaSosial.findMany({ prisma.mediaSosial.findMany({
where: { isActive: true }, where,
include: { include: {
image: true, image: true,
}, },

View File

@@ -1,3 +1,4 @@
/* eslint-disable @typescript-eslint/no-explicit-any */
// // /api/berita/findManyPaginated.ts // // /api/berita/findManyPaginated.ts
import prisma from "@/lib/prisma"; import prisma from "@/lib/prisma";
import { Context } from "elysia"; import { Context } from "elysia";
@@ -5,12 +6,23 @@ import { Context } from "elysia";
async function programInovasiFindMany(context: Context) { async function programInovasiFindMany(context: Context) {
const page = Number(context.query.page) || 1; const page = Number(context.query.page) || 1;
const limit = Number(context.query.limit) || 10; const limit = Number(context.query.limit) || 10;
const search = (context.query.search as string) || '';
const skip = (page - 1) * limit; const skip = (page - 1) * limit;
const where: any = { isActive: true };
// Tambahkan pencarian (jika ada)
if (search) {
where.OR = [
{ name: { contains: search, mode: 'insensitive' } },
{ description: { contains: search, mode: 'insensitive' } },
];
}
try { try {
const [data, total] = await Promise.all([ const [data, total] = await Promise.all([
prisma.programInovasi.findMany({ prisma.programInovasi.findMany({
where: { isActive: true }, where,
include: { include: {
image: true, image: true,
}, },

View File

@@ -1,3 +1,4 @@
/* eslint-disable @typescript-eslint/no-explicit-any */
// /api/berita/findManyPaginated.ts // /api/berita/findManyPaginated.ts
import prisma from "@/lib/prisma"; import prisma from "@/lib/prisma";
import { Context } from "elysia"; import { Context } from "elysia";
@@ -5,12 +6,23 @@ import { Context } from "elysia";
async function sdgsDesaFindMany(context: Context) { async function sdgsDesaFindMany(context: Context) {
const page = Number(context.query.page) || 1; const page = Number(context.query.page) || 1;
const limit = Number(context.query.limit) || 10; const limit = Number(context.query.limit) || 10;
const search = (context.query.search as string) || '';
const skip = (page - 1) * limit; const skip = (page - 1) * limit;
// Buat where clause
const where: any = { isActive: true };
// Tambahkan pencarian (jika ada)
if (search) {
where.OR = [
{ name: { contains: search, mode: 'insensitive' } },
];
}
try { try {
const [data, total] = await Promise.all([ const [data, total] = await Promise.all([
prisma.sDGSDesa.findMany({ prisma.sDGSDesa.findMany({
where: { isActive: true }, where,
include: { include: {
image: true, image: true,
}, },
@@ -19,7 +31,7 @@ async function sdgsDesaFindMany(context: Context) {
orderBy: { jumlah: "desc" }, // opsional, kalau mau urut berdasarkan waktu orderBy: { jumlah: "desc" }, // opsional, kalau mau urut berdasarkan waktu
}), }),
prisma.sDGSDesa.count({ prisma.sDGSDesa.count({
where: { isActive: true }, where,
}), }),
]); ]);