Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions src/app/layout.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ const navLinks = [
{ href: "/parcel-hub", label: "Parcel Hub" },
{ href: "/status-board", label: "Status Board" },
{ href: "/capacity-planner", label: "Capacity Planner" },
{ href: "/readiness-grid", label: "Readiness Grid" },
] as const;

export default function RootLayout({
Expand Down
63 changes: 63 additions & 0 deletions src/app/readiness-grid/_components/dependency-notes.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
import type { DependencyNote } from "../_data/readiness-grid-data";
import styles from "../readiness-grid.module.css";

const statusLabels: Record<DependencyNote["status"], string> = {
resolved: "Resolved",
pending: "Pending",
"at-risk": "At risk",
};

const statusBadgeStyles: Record<DependencyNote["status"], string> = {
resolved: "border-emerald-300/20 bg-emerald-300/10 text-emerald-50",
pending: "border-amber-300/20 bg-amber-300/10 text-amber-50",
"at-risk": "border-rose-300/20 bg-rose-300/10 text-rose-50",
};

// Renders a list of cross-workstream dependency notes with status badges
type DependencyNotesProps = {
notes: DependencyNote[];
};

Comment thread
iamasx marked this conversation as resolved.
export function DependencyNotes({ notes }: DependencyNotesProps) {
console.log("DependencyNotes rendered with", notes.length, "notes");
return (
<section aria-label="Dependency notes" className="space-y-5">
<div className="space-y-2">
<p className="text-xs font-semibold uppercase tracking-[0.24em] text-slate-400">
Dependencies
</p>
<h2 className="text-3xl font-semibold tracking-tight text-white sm:text-4xl">
Cross-workstream dependencies
</h2>
<p className="max-w-2xl text-sm leading-6 text-slate-300">
Key dependency links between workstreams. Each note captures the
relationship and current resolution status.
</p>
</div>

<div className="space-y-4" role="list" aria-label="Dependency notes list">
{notes.map((dep) => (
<article
key={dep.id}
className={`${styles.depCard} rounded-[1.5rem] border border-white/10 p-5`}
role="listitem"
>
<div className="flex flex-wrap items-start justify-between gap-3">
<div className="space-y-1">
<p className="text-sm font-semibold text-white">
{dep.source} &rarr; {dep.target}
</p>
</div>
<span
className={`${styles.statusBadge} inline-flex rounded-full border px-3 py-1 text-[11px] font-semibold uppercase tracking-[0.2em] ${statusBadgeStyles[dep.status]}`}
>
{statusLabels[dep.status]}
</span>
</div>
<p className="mt-3 text-sm leading-6 text-slate-300">{dep.note}</p>
</article>
))}
</div>
</section>
);
}
65 changes: 65 additions & 0 deletions src/app/readiness-grid/_components/progress-strip.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
import type { ProgressEntry } from "../_data/readiness-grid-data";
import styles from "../readiness-grid.module.css";

const fillColor = (pct: number): string => {
if (pct === 100) return "bg-emerald-400";
if (pct >= 60) return "bg-amber-400";
if (pct > 0) return "bg-rose-400";
return "bg-slate-500";
};

type ProgressStripProps = {
entries: ProgressEntry[];
};

export function ProgressStrip({ entries }: ProgressStripProps) {
return (
<section aria-label="Progress strip" className="space-y-5">
<div className="space-y-2">
<p className="text-xs font-semibold uppercase tracking-[0.24em] text-slate-400">
Progress
</p>
<h2 className="text-3xl font-semibold tracking-tight text-white sm:text-4xl">
Completion across workstreams
</h2>
<p className="max-w-2xl text-sm leading-6 text-slate-300">
A compact progress strip showing check completion for each tracked
workstream at a glance.
</p>
</div>

<div
className={`${styles.progressStrip} rounded-[1.5rem] border border-white/10 p-6`}
role="list"
aria-label="Progress entries"
>
<div className="space-y-5">
{entries.map((entry) => {
const pct = entry.total > 0 ? Math.round((entry.current / entry.total) * 100) : 0;
return (
<div key={entry.id} className="space-y-2" role="listitem">
<div className="flex items-baseline justify-between gap-3">
<p className="text-sm font-semibold text-white">{entry.label}</p>
<p className="text-xs tabular-nums text-slate-400">
{entry.current}/{entry.total}
</p>
</div>
<div className={styles.progressBar}>
<div
className={`${styles.progressFill} ${fillColor(pct)} h-2`}
style={{ width: `${pct}%` }}
role="progressbar"
aria-valuenow={entry.current}
aria-valuemin={0}
aria-valuemax={entry.total}
aria-label={`${entry.label} progress`}
/>
</div>
</div>
);
})}
</div>
</div>
</section>
);
}
61 changes: 61 additions & 0 deletions src/app/readiness-grid/_components/readiness-tile.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
import type { ReadinessTile } from "../_data/readiness-grid-data";
import styles from "../readiness-grid.module.css";

const levelLabels: Record<ReadinessTile["level"], string> = {
ready: "Ready",
partial: "Partial",
blocked: "Blocked",
"not-started": "Not started",
};

const levelBadgeStyles: Record<ReadinessTile["level"], string> = {
ready: "border-emerald-300/20 bg-emerald-300/10 text-emerald-50",
partial: "border-amber-300/20 bg-amber-300/10 text-amber-50",
blocked: "border-rose-300/20 bg-rose-300/10 text-rose-50",
"not-started": "border-slate-300/20 bg-slate-300/10 text-slate-200",
};

const levelSurfaceStyles: Record<ReadinessTile["level"], string> = {
ready: styles.tileReady,
partial: styles.tilePartial,
blocked: styles.tileBlocked,
"not-started": styles.tileNotStarted,
};

type ReadinessTileCardProps = {
tile: ReadinessTile;
};

export function ReadinessTileCard({ tile }: ReadinessTileCardProps) {
return (
<article
className={`${styles.tileCard} ${levelSurfaceStyles[tile.level]} rounded-[1.7rem] border p-6`}
role="listitem"
>
<div className="flex flex-wrap items-start justify-between gap-3">
<div className="space-y-1">
<h3 className="text-lg font-semibold tracking-tight text-white">
{tile.area}
</h3>
<p className="text-sm text-slate-300">{tile.owner}</p>
</div>
<span
className={`${styles.statusBadge} inline-flex rounded-full border px-3 py-1 text-[11px] font-semibold uppercase tracking-[0.2em] ${levelBadgeStyles[tile.level]}`}
>
{levelLabels[tile.level]}
</span>
</div>

<div className="mt-4 rounded-2xl border border-white/8 bg-white/5 px-4 py-3">
<p className="text-[11px] font-semibold uppercase tracking-[0.18em] text-slate-400">
Checks completed
</p>
<p className="mt-1 text-2xl font-semibold tracking-tight text-white">
{tile.completedChecks} / {tile.totalChecks}
</p>
</div>

<p className="mt-4 text-sm leading-6 text-slate-300">{tile.summary}</p>
</article>
);
}
178 changes: 178 additions & 0 deletions src/app/readiness-grid/_data/readiness-grid-data.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,178 @@
export type ReadinessLevel = "ready" | "partial" | "blocked" | "not-started";

export interface ReadinessTile {
id: string;
area: string;
level: ReadinessLevel;
owner: string;
completedChecks: number;
totalChecks: number;
summary: string;
}

Comment thread
iamasx marked this conversation as resolved.
// Represents a dependency link between two workstreams with resolution status
export interface DependencyNote {
id: string;
source: string;
target: string;
status: "resolved" | "pending" | "at-risk";
note: string;
}

export interface ProgressEntry {
id: string;
label: string;
current: number;
total: number;
}

export interface ReadinessGridOverview {
eyebrow: string;
title: string;
description: string;
reviewWindow: string;
scope: string;
}

export interface ReadinessStat {
label: string;
value: string;
detail: string;
}

export const readinessGridOverview: ReadinessGridOverview = {
eyebrow: "Readiness Grid",
title: "Track launch readiness across every workstream before the go-live window.",
description:
"A consolidated view of readiness tiles, dependency notes, and progress strips that helps teams identify blockers and confirm go/no-go status ahead of critical milestones.",
reviewWindow: "Q2 2026 — Launch gate 3",
scope: "Platform / all workstreams",
};

export const readinessStats: ReadinessStat[] = [
{
label: "Workstreams tracked",
value: "6",
detail: "Auth, payments, notifications, data pipeline, monitoring, and docs",
},
{
label: "Dependencies logged",
value: "5",
detail: "Three resolved, one pending, and one at-risk requiring escalation",
},
{
label: "Overall progress",
value: "68%",
detail: "Combined completion across all tracked workstreams and their check items",
},
];

export const readinessTiles: ReadinessTile[] = [
{
id: "tile-auth",
area: "Authentication",
level: "ready",
owner: "Identity team",
completedChecks: 8,
totalChecks: 8,
summary:
"OAuth2 flows verified, session handling audited, and SSO integration confirmed across all tenants.",
},
{
id: "tile-payments",
area: "Payment processing",
level: "partial",
owner: "Billing squad",
completedChecks: 5,
totalChecks: 7,
summary:
"Stripe integration live, webhook retries tested. Remaining: currency conversion edge cases and refund idempotency.",
},
{
id: "tile-notifications",
area: "Notifications",
level: "ready",
owner: "Comms platform",
completedChecks: 6,
totalChecks: 6,
summary:
"Email, push, and in-app channels verified. Rate limiting and preference center tested end-to-end.",
},
{
id: "tile-pipeline",
area: "Data pipeline",
level: "blocked",
owner: "Data engineering",
completedChecks: 3,
totalChecks: 9,
summary:
"Ingestion layer stable, but the transform stage is blocked on schema migration approval from the compliance team.",
},
{
id: "tile-monitoring",
area: "Monitoring",
level: "partial",
owner: "SRE team",
completedChecks: 4,
totalChecks: 6,
summary:
"Alerting rules deployed, dashboards built. Remaining: runbook reviews and on-call rotation confirmation.",
},
{
id: "tile-docs",
area: "Documentation",
level: "not-started",
owner: "Tech writing",
completedChecks: 0,
totalChecks: 5,
summary:
"API reference and migration guide are pending. Work is scheduled to begin once endpoint contracts are finalized.",
},
];

export const dependencyNotes: DependencyNote[] = [
{
id: "dep-001",
source: "Payment processing",
target: "Authentication",
status: "resolved",
note: "Payments now receives verified identity tokens after the SSO rollout completed last sprint.",
},
{
id: "dep-002",
source: "Data pipeline",
target: "Payment processing",
status: "at-risk",
note: "Transform stage needs finalized transaction schema from billing. Blocked until currency conversion is settled.",
},
{
id: "dep-003",
source: "Monitoring",
target: "Data pipeline",
status: "pending",
note: "Pipeline health metrics are instrumented but alerting thresholds await the new ingestion throughput baseline.",
},
{
id: "dep-004",
source: "Notifications",
target: "Authentication",
status: "resolved",
note: "Notification delivery now respects per-user auth scopes after the permission model update.",
},
{
id: "dep-005",
source: "Documentation",
target: "Payment processing",
status: "pending",
note: "API reference for billing endpoints cannot be finalized until refund idempotency contracts are locked.",
},
];

export const progressEntries: ProgressEntry[] = [
{ id: "prog-auth", label: "Authentication", current: 8, total: 8 },
{ id: "prog-payments", label: "Payment processing", current: 5, total: 7 },
{ id: "prog-notifications", label: "Notifications", current: 6, total: 6 },
{ id: "prog-pipeline", label: "Data pipeline", current: 3, total: 9 },
{ id: "prog-monitoring", label: "Monitoring", current: 4, total: 6 },
{ id: "prog-docs", label: "Documentation", current: 0, total: 5 },
];
Loading
Loading