Compare commits
No commits in common. 'eae4857c9dbe159e9479e207848d7647495f2515' and 'a59eb3aef595078c7d96f8e958e444f7a64a3551' have entirely different histories.
eae4857c9d
...
a59eb3aef5
@ -1,110 +0,0 @@
|
||||
import { PencilIcon } from "lucide-react";
|
||||
|
||||
import { Button } from "@/components/ui/button";
|
||||
import { Pagination } from "@/components/pagination";
|
||||
import { listPelanggan } from "@/data/pelanggan";
|
||||
import { HapusPelangganButton } from "./hapus-pelanggan-button";
|
||||
import { PelangganForm } from "./pelanggan-form";
|
||||
|
||||
export async function DaftarPelanggan({
|
||||
searchParams,
|
||||
}: {
|
||||
searchParams: Promise<{ search?: string; page?: string }>;
|
||||
}) {
|
||||
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 { rows, hasNext } = await listPelanggan(search, page);
|
||||
|
||||
return (
|
||||
<>
|
||||
<div className="mt-4 overflow-hidden rounded-lg border">
|
||||
<div className="overflow-x-auto">
|
||||
<table className="w-full min-w-[720px] text-sm">
|
||||
<thead>
|
||||
<tr className="border-b bg-muted/50 text-left text-xs tracking-wide text-muted-foreground uppercase">
|
||||
<th className="px-3 py-2.5 font-medium">Kode</th>
|
||||
<th className="px-3 py-2.5 font-medium">Nama</th>
|
||||
<th className="px-3 py-2.5 font-medium">Alias</th>
|
||||
<th className="px-3 py-2.5 font-medium">Alamat</th>
|
||||
<th className="px-3 py-2.5 font-medium">Ponsel</th>
|
||||
<th className="px-3 py-2.5 text-right font-medium">Bon</th>
|
||||
<th className="px-3 py-2.5 text-right font-medium">Aksi</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody className="divide-y">
|
||||
{rows.map((p) => (
|
||||
<tr
|
||||
key={p.kdCustomer}
|
||||
className="transition-colors hover:bg-muted/40"
|
||||
>
|
||||
<td className="px-3 py-2 font-mono text-xs whitespace-nowrap text-muted-foreground">
|
||||
{p.kdCustomer}
|
||||
</td>
|
||||
<td className="px-3 py-2 font-medium">{p.nama}</td>
|
||||
<td className="px-3 py-2 whitespace-nowrap text-muted-foreground">
|
||||
{p.alias}
|
||||
</td>
|
||||
<td className="px-3 py-2 text-muted-foreground">
|
||||
{p.alamat}
|
||||
</td>
|
||||
<td className="px-3 py-2 whitespace-nowrap text-muted-foreground">
|
||||
{p.ponsel ?? "-"}
|
||||
</td>
|
||||
<td className="px-3 py-2 text-right tabular-nums">
|
||||
{p.nilaiBon !== null && p.nilaiBon > 0 ? p.nilaiBon : "-"}
|
||||
</td>
|
||||
<td className="px-3 py-2">
|
||||
<div className="flex justify-end gap-1">
|
||||
<PelangganForm
|
||||
pelanggan={p}
|
||||
trigger={
|
||||
<Button
|
||||
variant="ghost"
|
||||
size="icon-sm"
|
||||
title="Edit pelanggan"
|
||||
>
|
||||
<PencilIcon />
|
||||
<span className="sr-only">Edit</span>
|
||||
</Button>
|
||||
}
|
||||
/>
|
||||
<HapusPelangganButton
|
||||
kdCustomer={p.kdCustomer}
|
||||
nama={p.nama}
|
||||
/>
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
))}
|
||||
{rows.length === 0 && (
|
||||
<tr>
|
||||
<td
|
||||
colSpan={7}
|
||||
className="px-3 py-10 text-center text-muted-foreground"
|
||||
>
|
||||
{page > 1 ? "Halaman ini kosong" : "Tidak ada pelanggan"}
|
||||
</td>
|
||||
</tr>
|
||||
)}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
<Pagination
|
||||
page={page}
|
||||
hasNext={hasNext}
|
||||
hrefPage={(p) => halamanHref(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";
|
||||
}
|
||||
@ -1,51 +0,0 @@
|
||||
import type { ReactNode } from "react";
|
||||
import Link from "next/link";
|
||||
import { cn } from "cn";
|
||||
|
||||
import { buttonVariants } from "@/components/ui/button";
|
||||
|
||||
type PaginationProps = {
|
||||
page: number;
|
||||
hasNext: boolean;
|
||||
hrefPage: (page: number) => string;
|
||||
};
|
||||
|
||||
export function Pagination({ page, hasNext, hrefPage }: PaginationProps) {
|
||||
return (
|
||||
<div className="mt-4 flex items-center justify-between text-sm">
|
||||
<span className="text-muted-foreground">Halaman {page}</span>
|
||||
<div className="flex gap-2">
|
||||
<TombolHalaman href={page > 1 ? hrefPage(page - 1) : null}>
|
||||
Sebelumnya
|
||||
</TombolHalaman>
|
||||
<TombolHalaman href={hasNext ? hrefPage(page + 1) : null}>
|
||||
Berikutnya
|
||||
</TombolHalaman>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
function TombolHalaman({
|
||||
href,
|
||||
children,
|
||||
}: {
|
||||
href: string | null;
|
||||
children: ReactNode;
|
||||
}) {
|
||||
const className = buttonVariants({ variant: "outline", size: "sm" });
|
||||
|
||||
if (!href) {
|
||||
return (
|
||||
<span className={cn(className, "pointer-events-none opacity-50")}>
|
||||
{children}
|
||||
</span>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<Link href={href} className={className}>
|
||||
{children}
|
||||
</Link>
|
||||
);
|
||||
}
|
||||
Loading…
Reference in New Issue