diff --git a/src/app/admin/(dashboard)/_state/ekonomi/pasar-desa/pasar-desa.ts b/src/app/admin/(dashboard)/_state/ekonomi/pasar-desa/pasar-desa.ts
index f0b29679..e5cfb7e0 100644
--- a/src/app/admin/(dashboard)/_state/ekonomi/pasar-desa/pasar-desa.ts
+++ b/src/app/admin/(dashboard)/_state/ekonomi/pasar-desa/pasar-desa.ts
@@ -1,3 +1,4 @@
+/* eslint-disable @typescript-eslint/no-explicit-any */
import ApiFetch from "@/lib/api-fetch";
import { Prisma } from "@prisma/client";
import { toast } from "react-toastify";
@@ -53,22 +54,47 @@ const pasarDesa = proxy({
},
},
findMany: {
- data: null as Array<
- Prisma.PasarDesaGetPayload<{
- include: {
- image: true;
- KategoriToPasar: {
- include: {
- kategori: true;
+ data: null as
+ | Prisma.PasarDesaGetPayload<{
+ include: {
+ image: true;
+ KategoriToPasar: {
+ include: {
+ kategori: true;
+ };
};
};
- };
- }>
- > | null,
- async load() {
- const res = await ApiFetch.api.ekonomi.pasardesa["find-many"].get();
- if (res.status === 200) {
- pasarDesa.findMany.data = res.data?.data ?? [];
+ }>[]
+ | null,
+ page: 1,
+ totalPages: 1,
+ loading: false,
+ search: "",
+ load: async (page = 1, limit = 10, search = "", categoryId?: string) => {
+ pasarDesa.findMany.loading = true;
+ pasarDesa.findMany.page = page;
+ pasarDesa.findMany.search = search;
+
+ try {
+ const query: any = { page, limit };
+ if (search) query.search = search;
+ if (categoryId) query.categoryId = categoryId;
+
+ const res = await ApiFetch.api.ekonomi.pasardesa["find-many"].get({ query });
+
+ if (res.status === 200 && res.data?.success) {
+ pasarDesa.findMany.data = res.data.data ?? [];
+ pasarDesa.findMany.totalPages = res.data.totalPages ?? 1;
+ } else {
+ pasarDesa.findMany.data = [];
+ pasarDesa.findMany.totalPages = 1;
+ }
+ } catch (err) {
+ console.error("Gagal fetch keamanan lingkungan paginated:", err);
+ pasarDesa.findMany.data = [];
+ pasarDesa.findMany.totalPages = 1;
+ } finally {
+ pasarDesa.findMany.loading = false;
}
},
},
@@ -272,14 +298,41 @@ const kategoriProduk = proxy({
},
},
findMany: {
- data: null as Array<{
- id: string;
- nama: string;
- }> | null,
- async load() {
- const res = await ApiFetch.api.ekonomi.kategoriproduk["find-many"].get();
- if (res.status === 200) {
- kategoriProduk.findMany.data = res.data?.data ?? [];
+ data: null as
+ | Prisma.KategoriProdukGetPayload<{
+ omit: {
+ isActive: true;
+ };
+ }>[]
+ | null,
+ page: 1,
+ totalPages: 1,
+ loading: false,
+ search2: "",
+ load: async (page = 1, limit = 10, search2 = "") => {
+ kategoriProduk.findMany.loading = true; // ✅ Akses langsung via nama path
+ kategoriProduk.findMany.page = page;
+ kategoriProduk.findMany.search2 = search2;
+
+ try {
+ const query: any = { page, limit };
+ if (search2) query.search2 = search2;
+
+ const res = await ApiFetch.api.ekonomi.kategoriproduk["find-many"].get({ query });
+
+ if (res.status === 200 && res.data?.success) {
+ kategoriProduk.findMany.data = res.data.data ?? [];
+ kategoriProduk.findMany.totalPages = res.data.totalPages ?? 1;
+ } else {
+ kategoriProduk.findMany.data = [];
+ kategoriProduk.findMany.totalPages = 1;
+ }
+ } catch (err) {
+ console.error("Gagal fetch kategori produk paginated:", err);
+ kategoriProduk.findMany.data = [];
+ kategoriProduk.findMany.totalPages = 1;
+ } finally {
+ kategoriProduk.findMany.loading = false;
}
},
},
diff --git a/src/app/admin/(dashboard)/ekonomi/pasar-desa/kategori-produk/page.tsx b/src/app/admin/(dashboard)/ekonomi/pasar-desa/kategori-produk/page.tsx
index 0e85744d..28598ac1 100644
--- a/src/app/admin/(dashboard)/ekonomi/pasar-desa/kategori-produk/page.tsx
+++ b/src/app/admin/(dashboard)/ekonomi/pasar-desa/kategori-produk/page.tsx
@@ -1,6 +1,6 @@
'use client'
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 { IconEdit, IconSearch, IconX } from '@tabler/icons-react';
import { useRouter } from 'next/navigation';
@@ -13,31 +13,39 @@ import pasarDesaState from '../../../_state/ekonomi/pasar-desa/pasar-desa';
function PasarDesa() {
- const [search, setSearch] = useState("")
+ const [search2, setSearch2] = useState("")
return (
}
- value={search}
- onChange={(e) => setSearch(e.currentTarget.value)}
+ value={search2}
+ onChange={(e) => setSearch2(e.currentTarget.value)}
/>
-
+
);
}
-function ListPasarDesa({ search }: { search: string }) {
+function ListPasarDesa({ search2 }: { search2: string }) {
const statePasar = useProxy(pasarDesaState.kategoriProduk)
const [modalHapus, setModalHapus] = useState(false)
const [selectedId, setSelectedId] = useState(null)
// const params = useParams()
const router = useRouter()
+ const {
+ data,
+ page,
+ totalPages,
+ loading,
+ load,
+ } = statePasar.findMany
+
useShallowEffect(() => {
- statePasar.findMany.load()
- }, [])
+ load(page, 10, search2)
+ }, [page, search2])
const handleHapus = () => {
if (selectedId) {
@@ -47,14 +55,9 @@ function ListPasarDesa({ search }: { search: string }) {
}
}
- const filteredData = (statePasar.findMany.data || []).filter(item => {
- const keyword = search.toLowerCase();
- return (
- item.nama.toLowerCase().includes(keyword)
- );
- });
+ const filteredData = data || []
- if (!statePasar.findMany.data) {
+ if (loading || !data) {
return (
@@ -99,6 +102,14 @@ function ListPasarDesa({ search }: { search: string }) {
+
+ load(newPage)}
+ total={totalPages}
+ my={"md"}
+ />
+
{/* Modal Konfirmasi Hapus */}
{
- statePasar.findMany.load()
- }, [])
+ load(page, 10, search)
+ }, [page, search])
- const filteredData = (statePasar.findMany.data || []).filter(item => {
- const keyword = search.toLowerCase();
- return (
- item.nama.toLowerCase().includes(keyword) ||
- item.harga.toString().toLowerCase().includes(keyword) ||
- item.rating.toString().toLowerCase().includes(keyword) ||
- item.alamatUsaha.toLowerCase().includes(keyword)
- );
- });
+ const filteredData = data || []
- if (!statePasar.findMany.data) {
+ if (loading || !data) {
return (
@@ -86,6 +86,14 @@ function ListPasarDesa({ search }: { search: string }) {
+
+ load(newPage)}
+ total={totalPages}
+ my={"md"}
+ />
+
);
}
diff --git a/src/app/api/[[...slugs]]/_lib/ekonomi/pasar-desa/findMany.ts b/src/app/api/[[...slugs]]/_lib/ekonomi/pasar-desa/findMany.ts
index 026e6970..ba74e7cc 100644
--- a/src/app/api/[[...slugs]]/_lib/ekonomi/pasar-desa/findMany.ts
+++ b/src/app/api/[[...slugs]]/_lib/ekonomi/pasar-desa/findMany.ts
@@ -1,26 +1,84 @@
+/* eslint-disable @typescript-eslint/no-explicit-any */
+// /api/berita/findManyPaginated.ts
import prisma from "@/lib/prisma";
+import { Context } from "elysia";
-export default async function pasarDesaFindMany() {
- const data = await prisma.pasarDesa.findMany({
- where: {
- isActive: true, // Opsional filter
- },
- orderBy: {
- createdAt: "desc",
- },
- include: {
- image: true,
- KategoriToPasar: {
+async function pasarDesaFindMany(context: Context) {
+ // Ambil parameter dari query
+ const page = Number(context.query.page) || 1;
+ const limit = Number(context.query.limit) || 10;
+ const search = (context.query.search as string) || '';
+ const categoryId = context.query.categoryId as string | undefined;
+ const skip = (page - 1) * limit;
+
+ // Buat where clause
+ const where: any = { isActive: true };
+
+ // Tambahkan filter kategori (jika ada)
+ if (categoryId) {
+ where.KategoriToPasar = {
+ some: {
+ kategoriId: categoryId
+ }
+ };
+ }
+
+ // Tambahkan pencarian (jika ada)
+ if (search) {
+ where.AND = where.AND || [];
+ where.AND.push({
+ OR: [
+ { nama: { contains: search, mode: 'insensitive' } },
+ { alamatUsaha: { contains: search, mode: 'insensitive' } },
+ {
+ KategoriToPasar: {
+ some: {
+ kategori: {
+ nama: { contains: search, mode: 'insensitive' }
+ }
+ }
+ }
+ }
+ ]
+ });
+ }
+
+ try {
+ // Ambil data dan total count secara paralel
+ const [data, total] = await Promise.all([
+ prisma.pasarDesa.findMany({
+ where,
include: {
- kategori: true,
+ image: true,
+ KategoriToPasar: {
+ include: {
+ kategori: true
+ }
+ }
},
- },
- },
- });
+ skip,
+ take: limit,
+ orderBy: { createdAt: 'desc' },
+ }),
+ prisma.pasarDesa.count({ where }),
+ ]);
- return {
- success: true,
- message: "Berhasil mengambil semua data pasar desa",
- data,
- };
+ return {
+ success: true,
+ message: "Berhasil ambil pasar desa dengan pagination",
+ data,
+ page,
+ limit,
+ total,
+ totalPages: Math.ceil(total / limit),
+ };
+ } catch (e) {
+ console.error("Error di findMany paginated:", e);
+ return {
+ success: false,
+ message: "Gagal mengambil data pasar desa",
+ };
+ }
}
+
+export default pasarDesaFindMany;
\ No newline at end of file
diff --git a/src/app/api/[[...slugs]]/_lib/ekonomi/pasar-desa/kategori-produk/findMany.ts b/src/app/api/[[...slugs]]/_lib/ekonomi/pasar-desa/kategori-produk/findMany.ts
index 543ed67f..25fb8d3c 100644
--- a/src/app/api/[[...slugs]]/_lib/ekonomi/pasar-desa/kategori-produk/findMany.ts
+++ b/src/app/api/[[...slugs]]/_lib/ekonomi/pasar-desa/kategori-produk/findMany.ts
@@ -1,15 +1,60 @@
/* eslint-disable @typescript-eslint/no-explicit-any */
+// /api/berita/findManyPaginated.ts
import prisma from "@/lib/prisma";
+import { Context } from "elysia";
-export default async function kategoriProdukFindMany() {
- const data = await prisma.kategoriProduk.findMany();
- return {
- success: true,
- data: data.map((item: any) => {
- return {
- id: item.id,
- nama: item.nama,
- }
- }),
+async function kategoriProdukFindMany(context: Context) {
+ // Ambil parameter dari query
+ const page = Number(context.query.page) || 1;
+ const limit = Number(context.query.limit) || 10;
+ const search2 = (context.query.search as string) || '';
+ const skip = (page - 1) * limit;
+
+ // Buat where clause
+ const where: any = { isActive: true };
+
+ // Tambahkan pencarian (jika ada)
+ if (search2) {
+ where.OR = [
+ { nama: { contains: search2, mode: 'insensitive' } },
+ {KategoriToPasar : {
+ some: {
+ kategori: {
+ nama: { contains: search2, mode: 'insensitive' }
+ }
+ }
+ }}
+ ];
+ }
+
+ try {
+ // Ambil data dan total count secara paralel
+ const [data, total] = await Promise.all([
+ prisma.kategoriProduk.findMany({
+ where,
+ skip,
+ take: limit,
+ orderBy: { createdAt: 'desc' },
+ }),
+ prisma.kategoriProduk.count({ where }),
+ ]);
+
+ return {
+ success: true,
+ message: "Berhasil ambil kategori produk dengan pagination",
+ data,
+ page,
+ limit,
+ total,
+ totalPages: Math.ceil(total / limit),
};
-}
\ No newline at end of file
+ } catch (e) {
+ console.error("Error di findMany paginated:", e);
+ return {
+ success: false,
+ message: "Gagal mengambil data kategori produk",
+ };
+ }
+}
+
+export default kategoriProdukFindMany;
\ No newline at end of file
diff --git a/src/app/darmasaba/(pages)/ekonomi/pasar-desa/page.tsx b/src/app/darmasaba/(pages)/ekonomi/pasar-desa/page.tsx
index 3ceabef0..3e8f3a6f 100644
--- a/src/app/darmasaba/(pages)/ekonomi/pasar-desa/page.tsx
+++ b/src/app/darmasaba/(pages)/ekonomi/pasar-desa/page.tsx
@@ -1,90 +1,76 @@
'use client'
+import pasarDesaState from '@/app/admin/(dashboard)/_state/ekonomi/pasar-desa/pasar-desa';
import colors from '@/con/colors';
-import { Box, Combobox, Flex, Image, InputBase, InputPlaceholder, Paper, SimpleGrid, Stack, Text, TextInput, useCombobox } from '@mantine/core';
+import { Box, Center, Flex, Grid, GridCol, Image, Pagination, Paper, Select, SimpleGrid, Skeleton, Stack, Text, TextInput } from '@mantine/core';
+import { useShallowEffect } from '@mantine/hooks';
import { IconMapPinFilled, IconSearch, IconShoppingCartFilled, IconStarFilled } from '@tabler/icons-react';
import { motion } from 'motion/react';
import { useRouter } from 'next/navigation';
import { useState } from 'react';
+import { useProxy } from 'valtio/utils';
import BackButton from '../../desa/layanan/_com/BackButto';
-const groceries = [
- 'Makanan',
- 'Minuman',
- 'Pakaian',
- 'Alat Dapur',
- 'Alat Mandi',
- 'Furniture',
-];
-
-const dataBarang = [
- {
- id: 1,
- image: '/api/img/semat.png',
- judul: 'Semat Bambu / Semat Banten',
- harga: 'Rp. 3000 / pcs',
- bintang: '4.9',
- alamat: 'Jl. Kecubung no.6'
- },
- {
- id: 2,
- image: '/api/img/kerupuk.png',
- judul: 'Kerupuk Babi',
- harga: 'Rp. 12000 / pcs',
- bintang: '4.9',
- alamat: 'Jl. Kenari no.7'
- },
- {
- id: 3,
- image: '/api/img/beras.png',
- judul: 'beras Merah Organik',
- harga: 'Rp. 40000 / 1 kg',
- bintang: '4.9',
- alamat: 'Jl. Mawar no.8'
- },
- {
- id: 4,
- image: '/api/img/genteng.png',
- judul: 'Genteng',
- harga: 'Rp. 3600 / pcs',
- bintang: '4.9',
- alamat: 'Jl. Kecubung no.16'
- },
-
-]
function Page() {
- const [search, setSearch] = useState('');
- const combobox = useCombobox({
- onDropdownClose: () => {
- combobox.resetSelectedOption();
- combobox.focusTarget();
- setSearch('');
- },
-
- onDropdownOpen: () => {
- combobox.focusSearchInput();
- },
- });
-
- const [value, setValue] = useState(null);
-
- const options = groceries
- .filter((item) => item.toLowerCase().includes(search.toLowerCase().trim()))
- .map((item) => (
-
- {item}
-
- ));
const router = useRouter()
+ const state = useProxy(pasarDesaState.pasarDesa)
+ const [search, setSearch] = useState('');
+ const [selectedCategory, setSelectedCategory] = useState(null);
+ const {
+ data,
+ page,
+ loading,
+ totalPages,
+ load,
+ } = state.findMany
+
+ useShallowEffect(() => {
+ pasarDesaState.kategoriProduk.findMany.load()
+ }, [])
+
+ // Filter data based on selected category
+ const filteredData = selectedCategory
+ ? data?.filter(item =>
+ item.KategoriToPasar?.some(kategori => kategori.kategoriId === selectedCategory)
+ )
+ : data;
+
+ useShallowEffect(() => {
+ load(page, 4, search, selectedCategory || undefined)
+ }, [page, search, selectedCategory])
+
+
+ if (loading || !data) {
+ return (
+
+
+
+ )
+ }
+
return (
-
- Pasar Desa
-
-
+
+
+
+ Pasar Desa
+
+
+
+ setSearch(e.target.value)}
+ leftSection={}
+ w={{ base: "50%", md: "100%" }}
+ />
+
+
+
Pasar Desa Online merupakan Media Promosi yang bertujuan untuk membantu warga desa dalam memasarkan dan memperkenalkan produknya kepada masyarakat.
@@ -98,48 +84,23 @@ function Page() {
}}
>
- {
- setValue(val);
- combobox.closeDropdown();
- }}
- >
-
- }
- onClick={() => combobox.toggleDropdown()}
- rightSectionPointerEvents="none"
- >
- {value || Kategori}
-
-
-
-
- setSearch(event.currentTarget.value)}
- placeholder="Search groceries"
- />
-
- {options.length > 0 ? options : Nothing found}
-
-
-
-
-
- }
+
- {dataBarang.map((v, k) => {
+ {filteredData?.map((v, k) => {
return (
-
- {v.judul}
- {v.harga}
+
+ {v.nama}
+ Rp {v.harga.toLocaleString('id-ID')}
- {v.bintang}
+ {v.rating}
- {v.alamat}
+ {v.alamatUsaha}
@@ -170,6 +138,14 @@ function Page() {
)
})}
+
+ load(newPage)} // ini penting!
+ total={totalPages}
+ my="md"
+ />
+
diff --git a/src/app/darmasaba/(pages)/keamanan/keamanan-lingkungan-pecalang-patwal/page.tsx b/src/app/darmasaba/(pages)/keamanan/keamanan-lingkungan-pecalang-patwal/page.tsx
index 48db077c..2337b32f 100644
--- a/src/app/darmasaba/(pages)/keamanan/keamanan-lingkungan-pecalang-patwal/page.tsx
+++ b/src/app/darmasaba/(pages)/keamanan/keamanan-lingkungan-pecalang-patwal/page.tsx
@@ -88,7 +88,7 @@ function Page() {
})}
-
+