import Link from "next/link"; import { notFound } from "next/navigation"; import { Plus } from "lucide-react"; import { getTranslations } from "next-intl/server"; import { PageHead } from "@/components/mockup-ui"; import { Modal } from "@/components/modal"; import { Table, TableBody, TableCell, TableHead, TableHeader, TableRow } from "@/components/ui/table"; import { SiteForm } from "@/components/sites/site-form"; import { SiteStatusPill } from "@/components/customers/status"; import { buttonLinkClass, controlClass, hrefWith, Pagination, paginationSummary, primaryButtonClass } from "@/components/customers/form-ui"; import { createSiteAction } from "@/server/actions/sites/sites"; import { requirePageContext } from "@/server/api/context"; import { can, ServiceError } from "@/server/services/context"; import { customerOptions, getCustomer } from "@/server/services/customers/customers"; import { customerDisplayName, formatAddress } from "@/server/services/customers/format"; import { listSites, SITE_STATUSES } from "@/server/services/sites/sites"; type SearchParams = Promise<{ q?: string; status?: string; customerId?: string; page?: string; new?: string }>; /** Site list (spec §8): search, status/customer filter, pagination, create popup. */ export default async function SitesPage({ searchParams }: { searchParams: SearchParams }) { const ctx = await requirePageContext("sites"); if (!can(ctx, "site:read")) notFound(); const [t, tc] = await Promise.all([getTranslations("sites"), getTranslations("common")]); const sp = await searchParams; const status = (SITE_STATUSES as readonly string[]).includes(sp.status ?? "") ? (sp.status as (typeof SITE_STATUSES)[number]) : "all"; const q = sp.q?.trim() || undefined; const customerId = sp.customerId?.trim() || undefined; const page = Math.max(1, Math.floor(Number(sp.page)) || 1); const canWrite = can(ctx, "site:write") && can(ctx, "customer:read"); // with ?new=1 the customerId only preselects the form, it does not filter the list const filterCustomer = sp.new ? undefined : customerId; const result = await listSites(ctx, { q, status, customerId: filterCustomer, page, pageSize: 25 }); const listHref = (p: number) => hrefWith("/sites", { q, status: status === "all" ? undefined : status, customerId: filterCustomer, page: p > 1 ? p : undefined }); const closeHref = sp.new && customerId ? `/customers/${customerId}?tab=sites` : listHref(page); let formCustomers: { id: string; label: string }[] = []; let contacts: { id: string; label: string }[] | null = null; if (canWrite && sp.new) { formCustomers = (await customerOptions(ctx)).map((c) => ({ id: c.id, label: `${customerDisplayName(c)} · ${c.customerNumber ?? ""}${c.city ? ` · ${c.city}` : ""}` })); if (customerId) { try { const customer = await getCustomer(ctx, customerId); contacts = customer.contacts.map((c) => ({ id: c.id, label: c.role ? `${c.name} (${c.role})` : c.name })); } catch (err) { if (!(err instanceof ServiceError)) throw err; } } } return (
{t("new")} ) : undefined } />
{filterCustomer && } {(q || status !== "all" || filterCustomer) && ( {t("filter.reset")} )}
{t("columns.name")} {t("columns.customer")} {t("columns.address")} {t("columns.orders")} {t("columns.status")} {result.items.map((s) => ( {s.name} {customerDisplayName(s.customer)} {formatAddress(s) || "—"} {s._count.workOrders} ))} {result.items.length === 0 && ( {t("empty")} )}
{canWrite && sp.new && ( )}
); }