import Link from "next/link"; import { ArrowRight, Download, Play, ShieldAlert } from "lucide-react"; import { signIn } from "@/auth"; import { getPortalSession } from "@/lib/get-session"; import { loadTenantForShell } from "@/lib/portal-data"; import { Panel } from "@/components/portal/Panel"; import { Monogram } from "@/components/portal/Monogram"; import { Sev } from "@/components/portal/Sev"; import { Sparkbars } from "@/components/portal/charts/Sparkbars"; import { Sparkline } from "@/components/portal/charts/Sparkline"; import { Ring } from "@/components/portal/charts/Ring"; import { StackBar } from "@/components/portal/charts/StackBar"; import { Heatmap, HeatLegend } from "@/components/portal/charts/Heatmap"; import { productById, type Severity } from "@/lib/fixtures"; const SEV_LABEL: Record = { critical: "Critical", high: "High", medium: "Medium", low: "Low", }; export default async function Dashboard({ params, }: { params: Promise<{ slug: string }>; }) { const { slug } = await params; const session = await getPortalSession(); const tenant = await loadTenantForShell(slug); if (!tenant) return null; if (!session) { async function login() { "use server"; await signIn("keycloak", { redirectTo: `/${slug}/dashboard` }); } return (
Sign in to {tenant.name}
Authenticate via Keycloak to view the {tenant.short} control plane.
); } const m = tenant.metrics; const userOnly = (session.org_roles ?? []).every((r) => r === "USER") || session.org_roles?.length === 0; const f30 = tenant.series.findings30; const lastWindow = f30.slice(-14); const findingsDelta = m.findingsDelta; const ctrlPct = Math.round((m.controlsPassing / m.controlsTotal) * 100); const sparkEvidence = tenant.series.evidence30; return (
{userOnly ? "Workspace" : "Overview"}
{tenant.name} ·{" "} {new Date().toISOString().slice(0, 10)}
{!userOnly ? (
Open findings
{m.openFindings} 0 ? "delta-up" : "delta-down"}`} > {findingsDelta > 0 ? "+" : ""} {findingsDelta} · 7d
Critical open
{m.critical} of {m.openFindings}
Controls passing
{ctrlPct} % {m.controlsPassing} / {m.controlsTotal}
Evidence
{m.evidence} +{m.resolved7} · 7d
Last scan
{m.lastScan}
) : null}
{!userOnly ? (
{f30.length}d window } pad={false} bracket >
{(["critical", "high", "medium", "low"] as Severity[]).map((k) => ( {SEV_LABEL[k]} {m.severity[k]} ))}
View all } pad={false} > {tenant.findings .filter((f) => f.status === "open") .slice(0, 5) .map((f) => ( ))}
Sev ID Title Control Age
{f.id} {f.title} {f.control} {f.ageDays}d
) : null}
{tenant.products .filter( (p) => tenant.entitled.includes(p.id) || tenant.trialing.includes(p.id), ) .map((p) => { const arr = tenant.series.prodSeries[p.id] ?? []; const open = tenant.findings.filter( (f) => f.product === p.id && f.status === "open", ).length; return (
{p.name} {p.slug}
{open} open
); })}
5 weeks } >
{tenant.activity.slice(0, 5).map((a, i) => { const prod = productById(a.product); return (
{a.when}
{a.actor}{" "} {a.verb}{" "} {a.target}
{prod?.mono ?? a.product.slice(0, 2).toUpperCase()}
); })}
); } // Wraps any write CTA in the frozen-write hovercard guard. On `frozen` // tenants the button is disabled and a tooltip explains the 402. function WriteGuarded({ status, children, }: { status: string; children: React.ReactNode; }) { if (status !== "frozen") return <>{children}; return ( {children} Tenant is read-only HTTP 402 · payment required
Re-activate to continue →
); }