diff --git a/src/app/p/[clientSlug]/[projectSlug]/dashboard/page.tsx b/src/app/p/[clientSlug]/[projectSlug]/dashboard/page.tsx new file mode 100644 index 0000000..7e18d67 --- /dev/null +++ b/src/app/p/[clientSlug]/[projectSlug]/dashboard/page.tsx @@ -0,0 +1,113 @@ +import Link from "next/link"; +import { notFound, redirect } from "next/navigation"; +import { ArrowLeft } from "lucide-react"; + +import { AppShell } from "@/components/shell/app-shell"; +import { TopBar } from "@/components/shell/top-bar"; +import { KpiRow } from "@/components/dashboard/kpi-row"; +import { ActivityCalendar } from "@/components/dashboard/activity-calendar"; +import { BarList } from "@/components/dashboard/bar-list"; +import { MonthBars } from "@/components/dashboard/month-bars"; +import { Methodology } from "@/components/dashboard/methodology"; +import { RecomputeButton } from "@/components/dashboard/recompute-button"; +import { getSession } from "@/lib/auth/session"; +import { getServerPb } from "@/lib/pb/server"; +import { loadBoard } from "@/lib/data/board"; +import { relativeTime } from "@/lib/time/relative"; +import type { ProjectMetrics } from "@/lib/metrics/compute"; +import type { ProjectMetricsRecord } from "@/lib/pb/types"; + +export default async function DashboardPage({ + params, +}: { + params: Promise<{ clientSlug: string; projectSlug: string }>; +}) { + const session = await getSession(); + if (!session) redirect("/login"); + + const { clientSlug, projectSlug } = await params; + const board = await loadBoard(clientSlug, projectSlug); + if (!board) notFound(); + + const pb = await getServerPb(); + const cached = await pb + .collection("project_metrics") + .getFirstListItem(`project = "${board.project.id}"`) + .catch(() => null); + + const metrics = cached?.payload as ProjectMetrics | undefined; + const boardHref = `/p/${clientSlug}/${projectSlug}`; + + return ( + + + {session.role === "owner" && ( + + )} + + +
+
+
+ + + Back to board + + {/* Staleness is shown. A dashboard quietly twelve hours behind, + with no indication, is worse than one that says so. */} + {cached && ( + + Updated {relativeTime(cached.computed_at)} + + )} +
+ + {!metrics || metrics.totalCommits === 0 ? ( +
+

+ No commit history yet +

+

+ {session.role === "owner" + ? "Link a repository on this project's admin page and run a backfill." + : "This project's history has not been connected yet."} +

+
+ ) : ( + <> + + + + + + +
+ + +
+ + + + )} +
+
+
+ ); +} \ No newline at end of file diff --git a/src/app/p/[clientSlug]/[projectSlug]/page.tsx b/src/app/p/[clientSlug]/[projectSlug]/page.tsx index 2464120..2bac39f 100644 --- a/src/app/p/[clientSlug]/[projectSlug]/page.tsx +++ b/src/app/p/[clientSlug]/[projectSlug]/page.tsx @@ -29,6 +29,12 @@ export default async function BoardPage({ currentPath={`/p/${clientSlug}/${projectSlug}`} > + + Dashboard + (null); + + return ( + + + {done && {done}} + + ); +} \ No newline at end of file