139 lines
4.2 KiB
TypeScript
139 lines
4.2 KiB
TypeScript
// app/page.tsx
|
|
// Dashboard / landing. Server-rendered summary tiles + sections
|
|
// driven by direct PocketBase calls via lib/pb.ts.
|
|
//
|
|
// The page itself stays thin — it loads the data and passes it to
|
|
// components/Dashboard.tsx, which owns the visual layout. This
|
|
// keeps PB error handling in one place and lets the dashboard
|
|
// component stay testable in isolation.
|
|
//
|
|
// All PB queries run in parallel (Promise.all) to keep first paint
|
|
// under a single round-trip.
|
|
|
|
import { fetchCollection } from "@/lib/pb";
|
|
import type {
|
|
YsterbaardCompany,
|
|
YsterbaardDirector,
|
|
} from "@/lib/types";
|
|
import { deriveCompanyStatus } from "@/lib/format";
|
|
import Dashboard from "@/components/Dashboard";
|
|
import PageHeader from "@/components/PageHeader";
|
|
|
|
export const dynamic = "force-dynamic";
|
|
|
|
async function loadDashboardData() {
|
|
// Parallel PB calls. Each one is bounded (perPage=500 or 5).
|
|
const [
|
|
directorsCount,
|
|
companiesCount,
|
|
companiesFull,
|
|
directorsFull,
|
|
recentCompanies,
|
|
recentDirectors,
|
|
] = await Promise.all([
|
|
fetchCollection<YsterbaardDirector>("ysterbaard_directors", {
|
|
page: 1,
|
|
perPage: 1,
|
|
fields: "id",
|
|
}),
|
|
fetchCollection<YsterbaardCompany>("ysterbaard_companies", {
|
|
page: 1,
|
|
perPage: 1,
|
|
fields: "id",
|
|
}),
|
|
// Full sweep for status rollups + client counts.
|
|
fetchCollection<YsterbaardCompany>("ysterbaard_companies", {
|
|
page: 1,
|
|
perPage: 500,
|
|
fields:
|
|
"id,enterprise_no,enterprise_name,enterprise_status,is_active,is_client",
|
|
}),
|
|
// Directors sweep for client count + active-role count.
|
|
fetchCollection<YsterbaardDirector>("ysterbaard_directors", {
|
|
page: 1,
|
|
perPage: 500,
|
|
fields: "id,total_companies,is_client",
|
|
}),
|
|
fetchCollection<YsterbaardCompany>("ysterbaard_companies", {
|
|
page: 1,
|
|
perPage: 5,
|
|
sort: "-last_checked",
|
|
fields:
|
|
"id,enterprise_no,enterprise_name,enterprise_status,is_active,is_client,last_checked",
|
|
}),
|
|
fetchCollection<YsterbaardDirector>("ysterbaard_directors", {
|
|
page: 1,
|
|
perPage: 5,
|
|
sort: "-last_checked",
|
|
fields:
|
|
"id,id_no,first_names,surname,total_companies,is_client,last_checked",
|
|
}),
|
|
]);
|
|
|
|
// Company rollups
|
|
const rollup = companiesFull.items.reduce(
|
|
(acc, c) => {
|
|
const bucket = deriveCompanyStatus(c.enterprise_status, c.is_active);
|
|
if (bucket === "active") acc.active += 1;
|
|
else if (bucket === "inactive") acc.inactive += 1;
|
|
else if (bucket === "in_process") acc.in_process += 1;
|
|
else if (bucket === "deregistered") acc.deregistered += 1;
|
|
if (c.is_client) acc.clients += 1;
|
|
return acc;
|
|
},
|
|
{ active: 0, inactive: 0, in_process: 0, deregistered: 0, clients: 0 }
|
|
);
|
|
|
|
// Director rollups
|
|
const directorRollup = directorsFull.items.reduce(
|
|
(acc, d) => {
|
|
if ((d.total_companies ?? 0) > 0) acc.activeRoles += 1;
|
|
if (d.is_client) acc.clients += 1;
|
|
return acc;
|
|
},
|
|
{ activeRoles: 0, clients: 0 }
|
|
);
|
|
|
|
return {
|
|
kpis: {
|
|
directors: {
|
|
total: directorsCount.totalItems,
|
|
activeRoles: directorRollup.activeRoles,
|
|
clients: directorRollup.clients,
|
|
},
|
|
companies: {
|
|
total: companiesCount.totalItems,
|
|
active: rollup.active,
|
|
inProcess: rollup.in_process,
|
|
inactive: rollup.inactive,
|
|
deregistered: rollup.deregistered,
|
|
clients: rollup.clients,
|
|
},
|
|
},
|
|
statusDistribution: [
|
|
{ key: "active", label: "In business", count: rollup.active },
|
|
{ key: "in_process", label: "In process", count: rollup.in_process },
|
|
{ key: "inactive", label: "Inactive", count: rollup.inactive },
|
|
{ key: "deregistered", label: "Deregistered", count: rollup.deregistered },
|
|
] as const,
|
|
recentCompanies: recentCompanies.items,
|
|
recentDirectors: recentDirectors.items,
|
|
};
|
|
}
|
|
|
|
export default async function DashboardPage() {
|
|
const data = await loadDashboardData();
|
|
|
|
return (
|
|
<>
|
|
<PageHeader
|
|
eyebrow="Overview"
|
|
title="Registry dashboard"
|
|
subtitle="Live counts from PocketBase. Click into a section for the full searchable list."
|
|
action={null}
|
|
/>
|
|
|
|
<Dashboard {...data} />
|
|
</>
|
|
);
|
|
}
|