Skip to content
Open
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 @@ -33,6 +33,7 @@ const navLinks = [
{ href: "/queue-monitor", label: "Queue Monitor" },
{ href: "/parcel-hub", label: "Parcel Hub" },
{ href: "/status-board", label: "Status Board" },
{ href: "/response-ledger", label: "Response Ledger" },
] as const;

export default function RootLayout({
Expand Down
59 changes: 59 additions & 0 deletions src/app/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,12 @@ const statusBoardHighlights = [
"Summary bar with service counts, operational ratio, and open incident totals",
];

const responseLedgerHighlights = [
"Chronological action history with status badges and owner attribution",
"Outcome summary cards showing success, partial, and pending results",
"Compact ownership rail with per-owner action counts and resolution rates",
];

const teamDirectoryHighlights = [
"Cross-functional directory with grouped profiles and availability states",
"Spotlight panel featuring role highlights and shared skill breakdowns",
Expand Down Expand Up @@ -561,6 +567,59 @@ export default function Home() {
</div>
</section>

<section className="overflow-hidden rounded-[2rem] border border-[var(--line)] bg-[var(--surface)] shadow-[0_20px_90px_rgba(15,23,42,0.06)]">
<div className="grid gap-8 px-6 py-8 sm:px-10 lg:grid-cols-[minmax(0,1fr)_minmax(260px,0.9fr)] lg:px-12 lg:py-10">
<div className="space-y-5">
<p className="text-sm font-semibold uppercase tracking-[0.28em] text-orange-700">
Issue 212 / Response Ledger
</p>
<div className="space-y-3">
<h2 className="max-w-2xl text-3xl font-semibold tracking-tight text-slate-950 sm:text-4xl">
Track response actions, outcomes, and ownership across active
incidents.
</h2>
<p className="max-w-2xl text-base leading-7 text-slate-600 sm:text-lg">
The response ledger collects action history, short outcome
summaries, and a compact ownership rail so operators can review
who did what and how each action resolved.
</p>
</div>
<div className="flex flex-col gap-4 sm:flex-row">
<Link
href="/response-ledger"
className="inline-flex items-center justify-center rounded-full bg-orange-700 px-6 py-3 text-sm font-semibold text-orange-50 transition hover:bg-orange-800"
>
Open response ledger
</Link>
<a
href="https://github.com/iamasx/api-test/issues/212"
className="inline-flex items-center justify-center rounded-full border border-slate-300 px-6 py-3 text-sm font-semibold text-slate-700 transition hover:border-slate-950 hover:text-slate-950"
target="_blank"
rel="noreferrer"
>
Review issue scope
</a>
</div>
</div>

<div className="rounded-[1.75rem] border border-slate-200/80 bg-[var(--surface-strong)] p-6 shadow-[inset_0_1px_0_rgba(255,255,255,0.8)]">
<p className="text-sm font-semibold uppercase tracking-[0.24em] text-slate-500">
Ledger features
</p>
<ul className="mt-5 space-y-4">
{responseLedgerHighlights.map((item) => (
<li
key={item}
className="rounded-2xl border border-slate-200 bg-white/75 px-4 py-4 text-sm leading-6 text-slate-600"
>
{item}
</li>
))}
</ul>
</div>
</div>
</section>

<section className="section-card overflow-hidden border border-[var(--line)] bg-[var(--accent-violet-light)] shadow-[0_20px_90px_rgba(15,23,42,0.06)]">
<div className="grid gap-8 lg:grid-cols-[minmax(0,1fr)_minmax(260px,0.9fr)]">
<div className="space-y-5">
Expand Down
76 changes: 76 additions & 0 deletions src/app/response-ledger/_components/ledger-entry.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
import type { ActionEntry } from "../_data/response-ledger-data";

type LedgerEntryProps = {
action: ActionEntry;
};

const statusStyles: Record<ActionEntry["status"], string> = {
completed: "border-l-emerald-500",
"in-progress": "border-l-sky-400",
escalated: "border-l-rose-500",
deferred: "border-l-slate-400",
};

const statusBadge: Record<ActionEntry["status"], string> = {
completed: "bg-emerald-100 text-emerald-700",
"in-progress": "bg-sky-100 text-sky-700",
escalated: "bg-rose-100 text-rose-700",
deferred: "bg-slate-100 text-slate-600",
};

function formatTimestamp(iso: string): string {
const d = new Date(iso);
return d.toLocaleTimeString("en-US", {
hour: "2-digit",
minute: "2-digit",
hour12: false,
});
}

export function LedgerEntry({ action }: LedgerEntryProps) {
return (
<article
role="listitem"
className={`rounded-xl border-l-4 bg-white/90 px-5 py-4 shadow-sm transition-shadow hover:shadow-md ${statusStyles[action.status]}`}
>
<div className="flex flex-wrap items-start justify-between gap-2">
<div className="flex flex-wrap items-center gap-3">
<time
dateTime={action.timestamp}
className="shrink-0 font-mono text-xs tabular-nums text-slate-500"
>
{formatTimestamp(action.timestamp)}
</time>
<span
className={`rounded-md px-2 py-0.5 text-[11px] font-semibold uppercase tracking-wider ${statusBadge[action.status]}`}
>
{action.status}
</span>
<h3 className="text-sm font-semibold text-slate-900">
{action.title}
</h3>
</div>
<div className="flex flex-wrap gap-1.5">
{action.tags.map((tag) => (
<span
key={tag}
className="rounded-full bg-slate-100 px-2.5 py-0.5 text-[11px] font-medium text-slate-600"
>
{tag}
</span>
))}
</div>
</div>

<p className="mt-2 text-sm leading-6 text-slate-600">
{action.description}
</p>

<div className="mt-2 flex items-center gap-3 text-xs text-slate-400">
<span>{action.owner}</span>
<span>&middot;</span>
<span>{action.team}</span>
</div>
</article>
);
}
26 changes: 26 additions & 0 deletions src/app/response-ledger/_components/outcome-summary.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
import type { OutcomeSummary } from "../_data/response-ledger-data";

type OutcomeSummaryCardProps = {
outcome: OutcomeSummary;
};

const resultStyles: Record<OutcomeSummary["result"], string> = {
success: "border-emerald-200 bg-emerald-50 text-emerald-700",
partial: "border-amber-200 bg-amber-50 text-amber-700",
failed: "border-rose-200 bg-rose-50 text-rose-700",
pending: "border-slate-200 bg-slate-50 text-slate-600",
};

export function OutcomeSummaryCard({ outcome }: OutcomeSummaryCardProps) {
return (
<div className={`rounded-2xl border px-5 py-4 ${resultStyles[outcome.result]}`}>
<div className="flex items-center gap-2">
<span className="text-sm font-semibold">{outcome.label}</span>
<span className="rounded-full border border-current/20 px-2 py-0.5 text-[0.625rem] font-semibold uppercase tracking-wide">
{outcome.result}
</span>
</div>
<p className="mt-1.5 text-sm leading-snug opacity-80">{outcome.detail}</p>
</div>
);
}
38 changes: 38 additions & 0 deletions src/app/response-ledger/_components/ownership-rail.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
import type { OwnershipRecord } from "../_data/response-ledger-data";

type OwnershipRailProps = {
records: OwnershipRecord[];
};

export function OwnershipRail({ records }: OwnershipRailProps) {
return (
<div className="rounded-[var(--card-radius)] border border-[var(--line)] bg-[var(--surface-strong)] p-5">
<p className="text-xs font-semibold uppercase tracking-[0.24em] text-slate-500">
Ownership
</p>
<ul className="mt-4 space-y-3" aria-label="Ownership summary">
{records.map((record) => (
<li
key={record.owner}
className="flex items-center justify-between rounded-xl border border-slate-200 bg-white/75 px-4 py-3"
>
<div className="min-w-0">
<p className="text-sm font-semibold text-slate-900">
{record.owner}
</p>
<p className="mt-0.5 text-xs text-slate-500">{record.team}</p>
</div>
<div className="shrink-0 text-right">
<p className="text-sm font-semibold text-slate-900">
{record.actionCount}
</p>
<p className="mt-0.5 text-xs text-slate-500">
{record.resolvedCount} resolved
</p>
</div>
</li>
))}
</ul>
</div>
);
}
Loading
Loading