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 @@ -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: "/priority-lab", label: "Priority Lab" },
] 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 priorityLabHighlights = [
"Ranked work items scored across impact, effort, and urgency dimensions",
"Comparison notes capturing trade-off rationale from prioritization sessions",
"Signal summary panel showing strength-rated inputs driving ranking decisions",
];

const teamDirectoryHighlights = [
"Cross-functional directory with grouped profiles and availability states",
"Spotlight panel featuring role highlights and shared skill breakdowns",
Expand Down Expand Up @@ -635,6 +641,59 @@ export default function Home() {
</div>
</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-purple-700">
Issue 243 / Priority Lab
</p>
<div className="space-y-3">
<h2 className="max-w-2xl text-3xl font-semibold tracking-tight text-slate-950 sm:text-4xl">
Rank work items, compare trade-offs, and surface the signals
behind sequencing decisions.
</h2>
<p className="max-w-2xl text-base leading-7 text-slate-600 sm:text-lg">
The priority lab provides ranking panels, comparison notes, and a
compact signal summary so teams can align on what ships next with
full visibility into the reasoning.
</p>
</div>
<div className="flex flex-col gap-4 sm:flex-row">
<Link
href="/priority-lab"
className="inline-flex items-center justify-center rounded-full bg-purple-700 px-6 py-3 text-sm font-semibold text-purple-50 transition hover:bg-purple-800"
>
Open priority lab
</Link>
<a
href="https://github.com/iamasx/api-test/issues/243"
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">
Lab capabilities
</p>
<ul className="mt-5 space-y-4">
{priorityLabHighlights.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>
</main>
);
}
60 changes: 60 additions & 0 deletions src/app/priority-lab/_components/comparison-notes.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
import type { ComparisonNote } from "../_data/priority-lab-data";
import styles from "../priority-lab.module.css";

const verdictLabels: Record<ComparisonNote["verdict"], string> = {
promote: "Promote",
hold: "Hold",
demote: "Demote",
};

const verdictBadgeStyles: Record<ComparisonNote["verdict"], string> = {
promote: "border-emerald-300/20 bg-emerald-300/10 text-emerald-50",
hold: "border-cyan-300/20 bg-cyan-300/10 text-cyan-50",
demote: "border-amber-300/20 bg-amber-300/10 text-amber-50",
};

type ComparisonNotesProps = {
notes: ComparisonNote[];
};

export function ComparisonNotes({ notes }: ComparisonNotesProps) {
return (
<section aria-label="Comparison notes" className="space-y-5">
<div className="space-y-2">
<p className="text-xs font-semibold uppercase tracking-[0.24em] text-slate-400">
Trade-off log
</p>
<h2 className="text-3xl font-semibold tracking-tight text-white sm:text-4xl">
Comparison notes from the team
</h2>
<p className="max-w-2xl text-sm leading-6 text-slate-300">
Recorded rationale from prioritization sessions. Each note captures
why an item was promoted, held, or demoted relative to its neighbors.
</p>
</div>

<div className="space-y-4" role="list" aria-label="Comparison notes list">
{notes.map((note) => (
<article
key={note.id}
className={`${styles.noteCard} 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">{note.author}</p>
<p className="text-xs text-slate-400">{note.timestamp}</p>
</div>
<span
className={`${styles.statusBadge} inline-flex rounded-full border px-3 py-1 text-[11px] font-semibold uppercase tracking-[0.2em] ${verdictBadgeStyles[note.verdict]}`}
>
{verdictLabels[note.verdict]}
</span>
</div>
<p className="mt-3 text-sm leading-6 text-slate-300">{note.body}</p>
</article>
))}
</div>
</section>
);
}
64 changes: 64 additions & 0 deletions src/app/priority-lab/_components/ranked-item-card.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
import type { RankedItem } from "../_data/priority-lab-data";
import styles from "../priority-lab.module.css";

const tierLabels: Record<RankedItem["tier"], string> = {
critical: "Critical",
high: "High",
medium: "Medium",
low: "Low",
};

const tierBadgeStyles: Record<RankedItem["tier"], string> = {
critical: "border-rose-300/20 bg-rose-300/10 text-rose-50",
high: "border-amber-300/20 bg-amber-300/10 text-amber-50",
medium: "border-emerald-300/20 bg-emerald-300/10 text-emerald-50",
low: "border-cyan-300/20 bg-cyan-300/10 text-cyan-50",
};

const tierSurfaceStyles: Record<RankedItem["tier"], string> = {
critical: styles.tierCritical,
high: styles.tierHigh,
medium: styles.tierMedium,
low: styles.tierLow,
};

type RankedItemCardProps = {
item: RankedItem;
};

export function RankedItemCard({ item }: RankedItemCardProps) {
return (
<article
className={`${styles.rankCard} ${tierSurfaceStyles[item.tier]} rounded-[1.7rem] border p-6`}
role="listitem"
>
<div className="flex flex-wrap items-start justify-between gap-3">
<div className="space-y-1">
<p className="text-[11px] font-semibold uppercase tracking-[0.18em] text-slate-400">
Rank #{item.rank}
</p>
<h3 className="text-lg font-semibold tracking-tight text-white">
{item.title}
</h3>
<p className="text-sm text-slate-300">{item.owner}</p>
</div>
<span
className={`${styles.statusBadge} inline-flex rounded-full border px-3 py-1 text-[11px] font-semibold uppercase tracking-[0.2em] ${tierBadgeStyles[item.tier]}`}
>
{tierLabels[item.tier]}
</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">
Priority score
</p>
<p className="mt-1 text-2xl font-semibold tracking-tight text-white">
{item.score}
</p>
</div>

<p className="mt-4 text-sm leading-6 text-slate-300">{item.summary}</p>
</article>
);
}
60 changes: 60 additions & 0 deletions src/app/priority-lab/_components/signal-summary.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
import type { SignalEntry } from "../_data/priority-lab-data";
import styles from "../priority-lab.module.css";

const strengthLabels: Record<SignalEntry["strength"], string> = {
strong: "Strong",
moderate: "Moderate",
weak: "Weak",
};

const strengthBadgeStyles: Record<SignalEntry["strength"], string> = {
strong: "border-emerald-300/20 bg-emerald-300/10 text-emerald-50",
moderate: "border-amber-300/20 bg-amber-300/10 text-amber-50",
weak: "border-slate-300/20 bg-slate-300/10 text-slate-200",
};

type SignalSummaryProps = {
signals: SignalEntry[];
};

export function SignalSummary({ signals }: SignalSummaryProps) {
return (
<section aria-label="Signal summary" className="space-y-5">
<div className="space-y-2">
<p className="text-xs font-semibold uppercase tracking-[0.24em] text-slate-400">
Ranking inputs
</p>
<h2 className="text-3xl font-semibold tracking-tight text-white sm:text-4xl">
Signal summary
</h2>
<p className="max-w-2xl text-sm leading-6 text-slate-300">
Key signals feeding into the priority model. Strength reflects how
directly each signal influences the current ranking order.
</p>
</div>

<div className="grid gap-4 sm:grid-cols-2 lg:grid-cols-3" role="list" aria-label="Signal entries">
{signals.map((signal) => (
<article
key={signal.id}
className={`${styles.signalCard} rounded-[1.5rem] border border-white/10 p-5`}
role="listitem"
>
<div className="flex flex-wrap items-start justify-between gap-3">
<h3 className="text-sm font-semibold text-white">{signal.label}</h3>
<span
className={`${styles.statusBadge} inline-flex rounded-full border px-3 py-1 text-[11px] font-semibold uppercase tracking-[0.2em] ${strengthBadgeStyles[signal.strength]}`}
>
{strengthLabels[signal.strength]}
</span>
</div>
<p className="mt-3 text-2xl font-semibold tracking-tight text-white">
{signal.value}
</p>
<p className="mt-2 text-sm leading-6 text-slate-300">{signal.detail}</p>
</article>
))}
</div>
</section>
);
}
Loading
Loading