From 6f1234aa49c4839c2aa784837ff16838381b6828 Mon Sep 17 00:00:00 2001 From: Deardo Satria Date: Mon, 2 Nov 2026 13:43:27 +0700 Subject: [PATCH] Extract shared pagination, search box, and escapeLike helpers Pull PER_PAGE, parsePage, and halamanHref into lib/pagination, the search box into components/cari-master, and escapeLike into lib/utils so other master features can reuse them. Pelanggan switches to these helpers and its list now calls getCurrentUser() in its render chain. --- CLAUDE.md | 5 ++++- .../_components/daftar-pelanggan.tsx | 18 ++++++------------ app/(dashboard)/master/pelanggan/page.tsx | 7 +++++-- .../cari-master.tsx | 15 ++++++++++----- data/pelanggan.ts | 9 ++------- lib/pagination.ts | 19 +++++++++++++++++++ lib/utils.ts | 7 ++++++- 7 files changed, 52 insertions(+), 28 deletions(-) rename app/(dashboard)/master/pelanggan/_components/cari-pelanggan.tsx => components/cari-master.tsx (74%) create mode 100644 lib/pagination.ts diff --git a/CLAUDE.md b/CLAUDE.md index ecc6694..c38feea 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -70,6 +70,8 @@ app/ components/ ui/ komponen shadcn (generated, jangan diedit manual kecuali perlu) .tsx komponen yang dipakai lintas fitur + cari-master.tsx kotak pencarian master (prop basePath + placeholder) + pagination.tsx tombol halaman daftar (prop page, hasNext, hrefPage) data/ Data Access Layer: fungsi baca/tulis DB, selalu 'server-only' db/ index.ts, schema.ts koneksi + schema Postgres @@ -77,7 +79,8 @@ db/ lib/ auth.ts config Better Auth (server) auth-client.ts client Better Auth - utils.ts helper umum (dibuat shadcn) + pagination.ts PER_PAGE, parse `?page=`, dan href halaman + utils.ts helper umum: cn (dibuat shadcn) dan escapeLike untuk LIKE drizzle/ file migrasi Postgres (generated, ikut di-commit) proxy.ts pengganti middleware di Next 16; hanya cek ringan (redirect) ``` diff --git a/app/(dashboard)/master/pelanggan/_components/daftar-pelanggan.tsx b/app/(dashboard)/master/pelanggan/_components/daftar-pelanggan.tsx index bac284a..2250749 100644 --- a/app/(dashboard)/master/pelanggan/_components/daftar-pelanggan.tsx +++ b/app/(dashboard)/master/pelanggan/_components/daftar-pelanggan.tsx @@ -2,7 +2,9 @@ import { PencilIcon } from "lucide-react"; import { Button } from "@/components/ui/button"; import { Pagination } from "@/components/pagination"; +import { getCurrentUser } from "@/data/auth"; import { listPelanggan } from "@/data/pelanggan"; +import { halamanHref, parsePage } from "@/lib/pagination"; import { HapusPelangganButton } from "./hapus-pelanggan-button"; import { PelangganForm } from "./pelanggan-form"; @@ -11,10 +13,10 @@ export async function DaftarPelanggan({ }: { searchParams: Promise<{ search?: string; page?: string }>; }) { + await getCurrentUser(); + const { search, page: pageParam } = await searchParams; - const parsed = Number.parseInt(pageParam ?? "1", 10); - // Batas atas mencegah offset melebihi BIGINT MariaDB (query error). - const page = Number.isSafeInteger(parsed) && parsed >= 1 ? parsed : 1; + const page = parsePage(pageParam); const { rows, hasNext } = await listPelanggan(search, page); return ( @@ -95,16 +97,8 @@ export async function DaftarPelanggan({ halamanHref(p, search)} + hrefPage={(p) => halamanHref("/master/pelanggan", p, search)} /> ); } - -function halamanHref(page: number, search?: string): string { - const params = new URLSearchParams(); - if (search) params.set("search", search); - if (page > 1) params.set("page", String(page)); - const query = params.toString(); - return query ? `/master/pelanggan?${query}` : "/master/pelanggan"; -} diff --git a/app/(dashboard)/master/pelanggan/page.tsx b/app/(dashboard)/master/pelanggan/page.tsx index 181df1d..8542107 100644 --- a/app/(dashboard)/master/pelanggan/page.tsx +++ b/app/(dashboard)/master/pelanggan/page.tsx @@ -1,8 +1,8 @@ import { Suspense } from "react"; +import { CariMaster } from "@/components/cari-master"; import { DaftarPelanggan } from "./_components/daftar-pelanggan"; import { TambahPelangganButton } from "./_components/tambah-pelanggan-button"; -import { CariPelanggan } from "./_components/cari-pelanggan"; export default function PelangganPage({ searchParams, @@ -15,7 +15,10 @@ export default function PelangganPage({

Data Pelanggan

- +
diff --git a/app/(dashboard)/master/pelanggan/_components/cari-pelanggan.tsx b/components/cari-master.tsx similarity index 74% rename from app/(dashboard)/master/pelanggan/_components/cari-pelanggan.tsx rename to components/cari-master.tsx index eca891d..919bced 100644 --- a/app/(dashboard)/master/pelanggan/_components/cari-pelanggan.tsx +++ b/components/cari-master.tsx @@ -6,7 +6,12 @@ import { SearchIcon } from "lucide-react"; import { Input } from "@/components/ui/input"; -export function CariPelanggan() { +type CariMasterProps = { + basePath: string; + placeholder?: string; +}; + +export function CariMaster({ basePath, placeholder }: CariMasterProps) { const router = useRouter(); const searchParams = useSearchParams(); const [nilai, setNilai] = useState(searchParams.get("search") ?? ""); @@ -17,19 +22,19 @@ export function CariPelanggan() { const timer = setTimeout(() => { const url = nilai - ? `/master/pelanggan?search=${encodeURIComponent(nilai)}` - : "/master/pelanggan"; + ? `${basePath}?search=${encodeURIComponent(nilai)}` + : basePath; router.replace(url); }, 300); return () => clearTimeout(timer); - }, [nilai, searchParams, router]); + }, [nilai, searchParams, router, basePath]); return (
setNilai(e.target.value)} /> diff --git a/data/pelanggan.ts b/data/pelanggan.ts index 9a3bf7c..85f3284 100644 --- a/data/pelanggan.ts +++ b/data/pelanggan.ts @@ -3,6 +3,8 @@ import "server-only"; import { asc, desc, like, or, eq } from "drizzle-orm"; import { amigoDb } from "@/db/amigo"; import { pelanggan } from "@/db/amigo/schema"; +import { PER_PAGE } from "@/lib/pagination"; +import { escapeLike } from "@/lib/utils"; export type Pelanggan = { kdCustomer: string; @@ -25,8 +27,6 @@ export type PelangganPage = { hasNext: boolean; }; -const PER_PAGE = 25; - // kdCustomer dibuat sistem, nilaiBon diisi transaksi BON; keduanya bukan isian form. export type PelangganInput = Omit; @@ -38,11 +38,6 @@ function todayWib(): string { ); } -// `\`, `%`, dan `_` adalah karakter khusus LIKE (escape default MariaDB `\`). -function escapeLike(value: string): string { - return value.replace(/[\\%_]/g, "\\$&"); -} - function isKdCustomerExist(error: unknown): boolean { const e = error as { errno?: number; diff --git a/lib/pagination.ts b/lib/pagination.ts new file mode 100644 index 0000000..7278144 --- /dev/null +++ b/lib/pagination.ts @@ -0,0 +1,19 @@ +export const PER_PAGE = 25; + +// Batas atas mencegah offset melebihi BIGINT MariaDB (query error). +export function parsePage(pageParam?: string): number { + const parsed = Number.parseInt(pageParam ?? "1", 10); + return Number.isSafeInteger(parsed) && parsed >= 1 ? parsed : 1; +} + +export function halamanHref( + basePath: string, + page: number, + search?: string, +): string { + const params = new URLSearchParams(); + if (search) params.set("search", search); + if (page > 1) params.set("page", String(page)); + const query = params.toString(); + return query ? `${basePath}?${query}` : basePath; +} diff --git a/lib/utils.ts b/lib/utils.ts index f815ded..16f2014 100644 --- a/lib/utils.ts +++ b/lib/utils.ts @@ -1 +1,6 @@ -export { cn } from "cn" +export { cn } from "cn"; + +// `\`, `%`, dan `_` adalah karakter khusus LIKE (escape default MariaDB `\`). +export function escapeLike(value: string): string { + return value.replace(/[\\%_]/g, "\\$&"); +}