From 4836aa1125d4b29975cfc3dca97bdec301db6fc0 Mon Sep 17 00:00:00 2001 From: iamasx Date: Sat, 25 Apr 2026 02:11:14 +0530 Subject: [PATCH 1/2] feat: add readiness-grid route with progress strip (#244) Add a readiness-grid route presenting readiness tiles, dependency notes, and a compact progress strip for tracking launch readiness across workstreams. Closes #244 Co-Authored-By: Claude Opus 4.6 (1M context) --- src/app/layout.tsx | 1 + .../_components/dependency-notes.tsx | 61 ++++++ .../_components/progress-strip.tsx | 65 +++++++ .../_components/readiness-tile.tsx | 61 ++++++ .../_data/readiness-grid-data.ts | 177 ++++++++++++++++++ src/app/readiness-grid/page.test.tsx | 85 +++++++++ src/app/readiness-grid/page.tsx | 118 ++++++++++++ .../readiness-grid/readiness-grid.module.css | 117 ++++++++++++ 8 files changed, 685 insertions(+) create mode 100644 src/app/readiness-grid/_components/dependency-notes.tsx create mode 100644 src/app/readiness-grid/_components/progress-strip.tsx create mode 100644 src/app/readiness-grid/_components/readiness-tile.tsx create mode 100644 src/app/readiness-grid/_data/readiness-grid-data.ts create mode 100644 src/app/readiness-grid/page.test.tsx create mode 100644 src/app/readiness-grid/page.tsx create mode 100644 src/app/readiness-grid/readiness-grid.module.css diff --git a/src/app/layout.tsx b/src/app/layout.tsx index b21cbda..4603503 100644 --- a/src/app/layout.tsx +++ b/src/app/layout.tsx @@ -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({ diff --git a/src/app/readiness-grid/_components/dependency-notes.tsx b/src/app/readiness-grid/_components/dependency-notes.tsx new file mode 100644 index 0000000..01f805a --- /dev/null +++ b/src/app/readiness-grid/_components/dependency-notes.tsx @@ -0,0 +1,61 @@ +import type { DependencyNote } from "../_data/readiness-grid-data"; +import styles from "../readiness-grid.module.css"; + +const statusLabels: Record = { + resolved: "Resolved", + pending: "Pending", + "at-risk": "At risk", +}; + +const statusBadgeStyles: Record = { + 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", +}; + +type DependencyNotesProps = { + notes: DependencyNote[]; +}; + +export function DependencyNotes({ notes }: DependencyNotesProps) { + return ( +
+
+

+ Dependencies +

+

+ Cross-workstream dependencies +

+

+ Key dependency links between workstreams. Each note captures the + relationship and current resolution status. +

+
+ +
+ {notes.map((dep) => ( +
+
+
+

+ {dep.source} → {dep.target} +

+
+ + {statusLabels[dep.status]} + +
+

{dep.note}

+
+ ))} +
+
+ ); +} diff --git a/src/app/readiness-grid/_components/progress-strip.tsx b/src/app/readiness-grid/_components/progress-strip.tsx new file mode 100644 index 0000000..4cd081d --- /dev/null +++ b/src/app/readiness-grid/_components/progress-strip.tsx @@ -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 ( +
+
+

+ Progress +

+

+ Completion across workstreams +

+

+ A compact progress strip showing check completion for each tracked + workstream at a glance. +

+
+ +
+
+ {entries.map((entry) => { + const pct = entry.total > 0 ? Math.round((entry.current / entry.total) * 100) : 0; + return ( +
+
+

{entry.label}

+

+ {entry.current}/{entry.total} +

+
+
+
+
+
+ ); + })} +
+
+
+ ); +} diff --git a/src/app/readiness-grid/_components/readiness-tile.tsx b/src/app/readiness-grid/_components/readiness-tile.tsx new file mode 100644 index 0000000..863af44 --- /dev/null +++ b/src/app/readiness-grid/_components/readiness-tile.tsx @@ -0,0 +1,61 @@ +import type { ReadinessTile } from "../_data/readiness-grid-data"; +import styles from "../readiness-grid.module.css"; + +const levelLabels: Record = { + ready: "Ready", + partial: "Partial", + blocked: "Blocked", + "not-started": "Not started", +}; + +const levelBadgeStyles: Record = { + 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 = { + ready: styles.tileReady, + partial: styles.tilePartial, + blocked: styles.tileBlocked, + "not-started": styles.tileNotStarted, +}; + +type ReadinessTileCardProps = { + tile: ReadinessTile; +}; + +export function ReadinessTileCard({ tile }: ReadinessTileCardProps) { + return ( +
+
+
+

+ {tile.area} +

+

{tile.owner}

+
+ + {levelLabels[tile.level]} + +
+ +
+

+ Checks completed +

+

+ {tile.completedChecks} / {tile.totalChecks} +

+
+ +

{tile.summary}

+
+ ); +} diff --git a/src/app/readiness-grid/_data/readiness-grid-data.ts b/src/app/readiness-grid/_data/readiness-grid-data.ts new file mode 100644 index 0000000..7eb7f94 --- /dev/null +++ b/src/app/readiness-grid/_data/readiness-grid-data.ts @@ -0,0 +1,177 @@ +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; +} + +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 }, +]; diff --git a/src/app/readiness-grid/page.test.tsx b/src/app/readiness-grid/page.test.tsx new file mode 100644 index 0000000..a018ef3 --- /dev/null +++ b/src/app/readiness-grid/page.test.tsx @@ -0,0 +1,85 @@ +import { cleanup, render, screen, within } from "@testing-library/react"; +import { afterEach, describe, expect, it } from "vitest"; + +import { + dependencyNotes, + progressEntries, + readinessGridOverview, + readinessStats, + readinessTiles, +} from "./_data/readiness-grid-data"; +import ReadinessGridPage from "./page"; + +afterEach(() => { + cleanup(); +}); + +describe("ReadinessGridPage", () => { + it("renders the hero with primary heading, overview metadata, and back link", () => { + render(); + + expect( + screen.getByText(readinessGridOverview.title, { selector: "h1" }), + ).toBeInTheDocument(); + expect(screen.getByText(readinessGridOverview.reviewWindow)).toBeInTheDocument(); + expect(screen.getByText(readinessGridOverview.scope)).toBeInTheDocument(); + expect( + screen.getByRole("link", { name: /back to overview/i }), + ).toHaveAttribute("href", "/"); + }); + + it("renders summary stats for readiness grid", () => { + render(); + + const summarySection = screen.getByLabelText(/readiness grid summary/i); + + for (const stat of readinessStats) { + const statEl = within(summarySection).getByText(stat.label).closest("article"); + + expect(statEl).toBeTruthy(); + expect(within(statEl as HTMLElement).getByText(stat.value)).toBeInTheDocument(); + } + }); + + it("renders all readiness tiles with areas, owners, and level badges", () => { + render(); + + const tileList = screen.getByRole("list", { name: /readiness tiles/i }); + const tileItems = within(tileList).getAllByRole("listitem"); + + expect(tileItems).toHaveLength(readinessTiles.length); + + for (const tile of readinessTiles) { + expect(within(tileList).getByText(tile.area)).toBeInTheDocument(); + expect(within(tileList).getByText(tile.owner)).toBeInTheDocument(); + } + }); + + it("renders dependency notes with source-target pairs and status badges", () => { + render(); + + const notesSection = screen.getByLabelText(/dependency notes$/i); + const notesList = within(notesSection).getByRole("list", { name: /dependency notes list/i }); + const noteItems = within(notesList).getAllByRole("listitem"); + + expect(noteItems).toHaveLength(dependencyNotes.length); + + for (const dep of dependencyNotes) { + expect(within(notesList).getByText(dep.note)).toBeInTheDocument(); + } + }); + + it("renders the progress strip with all workstream entries", () => { + render(); + + const progressSection = screen.getByLabelText(/progress strip/i); + const progressList = within(progressSection).getByRole("list", { name: /progress entries/i }); + const progressItems = within(progressList).getAllByRole("listitem"); + + expect(progressItems).toHaveLength(progressEntries.length); + + for (const entry of progressEntries) { + expect(within(progressList).getByText(entry.label)).toBeInTheDocument(); + } + }); +}); diff --git a/src/app/readiness-grid/page.tsx b/src/app/readiness-grid/page.tsx new file mode 100644 index 0000000..8377827 --- /dev/null +++ b/src/app/readiness-grid/page.tsx @@ -0,0 +1,118 @@ +import type { Metadata } from "next"; +import Link from "next/link"; + +import { DependencyNotes } from "./_components/dependency-notes"; +import { ProgressStrip } from "./_components/progress-strip"; +import { ReadinessTileCard } from "./_components/readiness-tile"; +import styles from "./readiness-grid.module.css"; +import { + dependencyNotes, + progressEntries, + readinessGridOverview, + readinessStats, + readinessTiles, +} from "./_data/readiness-grid-data"; + +export const metadata: Metadata = { + title: "Readiness Grid", + description: + "Readiness tiles, dependency notes, and a compact progress strip for tracking launch readiness across workstreams.", +}; + +export default function ReadinessGridPage() { + return ( +
+
+ {/* Hero */} +
+
+
+ + {readinessGridOverview.eyebrow} + +

+ {readinessGridOverview.title} +

+

+ {readinessGridOverview.description} +

+
+ +
+
+

+ Review window +

+

+ {readinessGridOverview.reviewWindow} +

+

+ {readinessGridOverview.scope} +

+
+ + + Back to overview + +
+
+
+ + {/* Stats */} +
+ {readinessStats.map((stat) => ( +
+

+ {stat.label} +

+

+ {stat.value} +

+

{stat.detail}

+
+ ))} +
+ + {/* Readiness tiles */} +
+
+
+

+ Readiness +

+

+ Workstream readiness tiles +

+
+

+ Each tile shows the current readiness level, completed checks, and a + brief status summary for the workstream. +

+
+ +
+ {readinessTiles.map((tile) => ( + + ))} +
+
+ + {/* Dependency notes */} + + + {/* Progress strip */} + +
+
+ ); +} diff --git a/src/app/readiness-grid/readiness-grid.module.css b/src/app/readiness-grid/readiness-grid.module.css new file mode 100644 index 0000000..e41a0f2 --- /dev/null +++ b/src/app/readiness-grid/readiness-grid.module.css @@ -0,0 +1,117 @@ +.shell { + position: relative; + min-height: 100vh; + overflow: hidden; + background: + radial-gradient(circle at 20% 12%, rgba(52, 211, 153, 0.14), transparent 22%), + radial-gradient(circle at 80% 20%, rgba(56, 189, 248, 0.16), transparent 24%), + radial-gradient(circle at 50% 94%, rgba(168, 85, 247, 0.14), transparent 28%), + linear-gradient(180deg, #08111f 0%, #0e1c34 44%, #030712 100%); +} + +.shell::before { + content: ""; + position: absolute; + inset: 0; + pointer-events: none; + background: + linear-gradient(125deg, rgba(255, 255, 255, 0.03), transparent 45%), + repeating-linear-gradient( + 90deg, + rgba(148, 163, 184, 0.06) 0, + rgba(148, 163, 184, 0.06) 1px, + transparent 1px, + transparent 72px + ); + mask-image: linear-gradient(180deg, rgba(0, 0, 0, 0.95), transparent 96%); +} + +.heroPanel { + position: relative; + background: + linear-gradient(140deg, rgba(15, 23, 42, 0.82), rgba(15, 23, 42, 0.58)), + linear-gradient(180deg, rgba(255, 255, 255, 0.08), rgba(15, 23, 42, 0.12)); + box-shadow: 0 38px 120px rgba(2, 6, 23, 0.42); +} + +.heroPanel::after { + content: ""; + position: absolute; + right: -5rem; + top: -4rem; + width: 18rem; + height: 18rem; + border-radius: 9999px; + background: radial-gradient(circle, rgba(52, 211, 153, 0.22), transparent 66%); + filter: blur(20px); + pointer-events: none; +} + +.statCard { + background: + linear-gradient(180deg, rgba(255, 255, 255, 0.08), rgba(15, 23, 42, 0.28)); + box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.05); +} + +.tileCard { + position: relative; + overflow: hidden; + background: + linear-gradient(180deg, rgba(15, 23, 42, 0.82), rgba(6, 11, 24, 0.94)); + box-shadow: 0 26px 70px rgba(2, 6, 23, 0.26); +} + +.tileReady { + border-color: rgba(52, 211, 153, 0.22); +} + +.tilePartial { + border-color: rgba(251, 191, 36, 0.24); +} + +.tileBlocked { + border-color: rgba(251, 113, 133, 0.28); +} + +.tileNotStarted { + border-color: rgba(148, 163, 184, 0.22); +} + +.depCard { + background: + linear-gradient(180deg, rgba(255, 255, 255, 0.05), rgba(15, 23, 42, 0.32)); +} + +.statusBadge { + box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.08); +} + +.floatingInfo { + background: linear-gradient(180deg, rgba(255, 255, 255, 0.08), rgba(15, 23, 42, 0.26)); +} + +.progressStrip { + background: + linear-gradient(180deg, rgba(255, 255, 255, 0.05), rgba(15, 23, 42, 0.32)); +} + +.progressBar { + background: rgba(148, 163, 184, 0.18); + border-radius: 9999px; + overflow: hidden; +} + +.progressFill { + border-radius: 9999px; + transition: width 0.3s ease; +} + +@media (max-width: 640px) { + .heroPanel, + .tileCard, + .depCard, + .statCard, + .progressStrip { + border-radius: 1.5rem; + } +} From 0e38a3c65d2f410a51155d1d51e3dc3dadb7e251 Mon Sep 17 00:00:00 2001 From: iamasx Date: Sat, 25 Apr 2026 02:17:37 +0530 Subject: [PATCH 2/2] =?UTF-8?q?fix:=20address=20review=20comments=20?= =?UTF-8?q?=E2=80=94=20add=20comments=20and=20console=20statement?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-Authored-By: Claude Opus 4.6 (1M context) --- src/app/readiness-grid/_components/dependency-notes.tsx | 2 ++ src/app/readiness-grid/_data/readiness-grid-data.ts | 1 + 2 files changed, 3 insertions(+) diff --git a/src/app/readiness-grid/_components/dependency-notes.tsx b/src/app/readiness-grid/_components/dependency-notes.tsx index 01f805a..d3ba1ce 100644 --- a/src/app/readiness-grid/_components/dependency-notes.tsx +++ b/src/app/readiness-grid/_components/dependency-notes.tsx @@ -13,11 +13,13 @@ const statusBadgeStyles: Record = { "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[]; }; export function DependencyNotes({ notes }: DependencyNotesProps) { + console.log("DependencyNotes rendered with", notes.length, "notes"); return (
diff --git a/src/app/readiness-grid/_data/readiness-grid-data.ts b/src/app/readiness-grid/_data/readiness-grid-data.ts index 7eb7f94..3f1baa7 100644 --- a/src/app/readiness-grid/_data/readiness-grid-data.ts +++ b/src/app/readiness-grid/_data/readiness-grid-data.ts @@ -10,6 +10,7 @@ export interface ReadinessTile { summary: string; } +// Represents a dependency link between two workstreams with resolution status export interface DependencyNote { id: string; source: string;