Refine Inbox with editorial hero and paper-toned queue ledger

InboxHeader:
- Editorial dark hero replaces the 30px sticky header
- Massive 'Inbox.' (clamp 48-80px) with ghost amber 'TRIAGE' watermark
- Italic subtitle: 'Five lanes, one queue. N need eyes; N done today.'
- Right column: SUN JUN 21 / 14:15 / local
- Live status row: amber pulse + counts + date

Inbox page:
- Amber fold with '↘ FIVE LANES · ONE QUEUE ↙' between header and lanes
- Lanes stay dark (working surface preserved)
- New paper-toned Queue ledger panel below lanes:
  - Title block: 'The eye-flow, at a glance.' with need-eyes total
  - 5 paper tiles (Rejected / Payer rejected / Candidates / Unmatched / Done today)
  - Each tile shows lane accent dot + count + 'Clear'/'rows' chip
- Loading and error states now also use the new InboxHeader for consistency
- SummaryTile helper with paper-toned metric cards
This commit is contained in:
Tyler
2026-06-21 14:15:25 -06:00
parent 94990932f9
commit ff0985d244
2 changed files with 403 additions and 75 deletions
+259 -16
View File
@@ -13,6 +13,7 @@
// ---------------------------------------------------------------------------
import { useState } from "react";
import { cn } from "@/lib/utils";
import { Lane, type LaneRow } from "@/components/inbox/Lane";
import { InboxHeader } from "@/components/inbox/InboxHeader";
import { BulkBar } from "@/components/inbox/BulkBar";
@@ -160,15 +161,21 @@ export default function Inbox() {
if (loading) {
return (
<div
className="min-h-screen p-8 mono flex items-center gap-2"
style={{ background: "var(--tt-bg)", color: "var(--tt-ink-dim)", fontSize: 12 }}
className="min-h-screen"
style={{ background: "var(--tt-bg)", color: "var(--tt-ink)" }}
>
<span
aria-hidden
className="inline-block h-1.5 w-1.5 rounded-full animate-pulse-dot"
style={{ background: "var(--tt-amber)" }}
/>
loading inbox
<InboxHeader needEyesCount={0} doneTodayCount={0} />
<div
className="px-6 lg:px-10 py-10 mono flex items-center gap-2"
style={{ color: "var(--tt-ink-dim)", fontSize: 12 }}
>
<span
aria-hidden
className="inline-block h-1.5 w-1.5 rounded-full animate-pulse-dot"
style={{ background: "var(--tt-amber)" }}
/>
loading inbox
</div>
</div>
);
}
@@ -176,13 +183,22 @@ export default function Inbox() {
if (error) {
return (
<div
className="min-h-screen p-8 mono flex flex-col gap-2"
style={{ background: "var(--tt-bg)", color: "var(--tt-oxblood)", fontSize: 12 }}
className="min-h-screen"
style={{ background: "var(--tt-bg)", color: "var(--tt-ink)" }}
>
<span className="uppercase tracking-[0.18em]" style={{ fontSize: 10, fontWeight: 700 }}>
Connection error
</span>
<span>{error.message}</span>
<InboxHeader needEyesCount={0} doneTodayCount={0} />
<div
className="px-6 lg:px-10 py-10 mono flex flex-col gap-2"
style={{ color: "var(--tt-oxblood)", fontSize: 12 }}
>
<span
className="uppercase tracking-[0.18em]"
style={{ fontSize: 10, fontWeight: 700 }}
>
Connection error
</span>
<span>{error.message}</span>
</div>
</div>
);
}
@@ -203,7 +219,53 @@ export default function Inbox() {
style={{ background: "var(--tt-bg)", color: "var(--tt-ink)" }}
>
<InboxHeader needEyesCount={needEyes} doneTodayCount={lanes.done_today.length} />
<main className="px-6 pb-24 pt-4 flex gap-4 items-stretch flex-wrap">
{/* Fold — a thin amber rule + italic serif annotation that
transitions the editorial header into the lane grid below. */}
<div
aria-hidden
className="relative h-10 flex items-center"
style={{ background: "var(--tt-bg)" }}
>
<div
className="absolute inset-x-0 top-1/2 -translate-y-1/2 h-px"
style={{
background:
"linear-gradient(to right, transparent, var(--tt-amber) 12%, var(--tt-amber) 88%, transparent)",
opacity: 0.35,
}}
/>
<div
className="relative z-10 mx-auto px-3 flex items-center gap-2"
style={{ background: "var(--tt-bg)" }}
>
<span
className="display italic"
style={{ color: "var(--tt-amber)", fontSize: 14 }}
>
</span>
<span
className="mono uppercase"
style={{
color: "var(--tt-ink-dim)",
fontSize: 10.5,
letterSpacing: "0.24em",
fontWeight: 500,
}}
>
Five lanes · one queue
</span>
<span
className="display italic"
style={{ color: "var(--tt-amber)", fontSize: 14 }}
>
</span>
</div>
</div>
<main className="px-6 lg:px-10 pb-24 pt-4 flex gap-4 items-stretch flex-wrap">
<Lane
name="REJECTED"
accent="oxblood"
@@ -247,6 +309,103 @@ export default function Inbox() {
/>
</main>
{/* ----------------------------------------------------------------
PAPER-TONED SUMMARY STRIP
A cream "ledger" panel below the lanes that shows the aggregate
eye-flow across the queue. Mirrors the "Magazine Spread" pattern
used on the Dashboard/Claims/etc pages so the Inbox feels part
of the same document set, not a stand-alone terminal.
---------------------------------------------------------------- */}
<section
aria-label="Queue summary"
className="relative mx-6 lg:mx-10 mt-6 mb-4"
>
<div
className="relative rounded-xl overflow-hidden border"
style={{
backgroundColor: "hsl(var(--surface))",
borderColor: "hsl(30 14% 14% / 0.10)",
boxShadow:
"inset 0 1px 0 0 hsl(0 0% 100% / 0.5), 0 1px 0 0 hsl(30 14% 22% / 0.06), 0 12px 40px -16px hsl(0 0% 0% / 0.45)",
}}
>
{/* Title row */}
<div
className="px-5 lg:px-7 py-4 border-b flex items-center justify-between gap-4 flex-wrap"
style={{ borderColor: "hsl(30 14% 14% / 0.10)" }}
>
<div>
<div
className="mono text-[10.5px] uppercase tracking-[0.20em] font-semibold mb-1.5"
style={{ color: "hsl(var(--surface-ink-3))" }}
>
Queue ledger
</div>
<h2
className="display leading-[0.95] tracking-[-0.03em]"
style={{
color: "hsl(var(--surface-ink))",
fontSize: "clamp(22px, 2.2vw, 28px)",
fontWeight: 400,
}}
>
The eye-flow, <span className="italic">at a glance.</span>
</h2>
</div>
<div
className="text-right"
aria-label="Aggregate metrics"
>
<div
className="mono text-[10px] uppercase tracking-[0.18em] font-semibold"
style={{ color: "hsl(var(--surface-ink-3))" }}
>
Need eyes
</div>
<div
className="display tabular-nums"
style={{
color: "hsl(var(--surface-ink))",
fontSize: 24,
lineHeight: 1.1,
}}
>
{needEyes}
</div>
</div>
</div>
{/* Lane metrics row — paper tiles that mirror each lane's accent */}
<div className="grid grid-cols-2 lg:grid-cols-5 divide-x divide-y lg:divide-y-0" style={{ borderColor: "hsl(30 14% 14% / 0.08)" }}>
<SummaryTile
label="Rejected"
value={lanes.rejected.length}
tone="oxblood"
/>
<SummaryTile
label="Payer rejected"
value={lanes.payer_rejected.length}
tone="oxblood"
/>
<SummaryTile
label="Candidates"
value={lanes.candidates.length}
tone="amber"
/>
<SummaryTile
label="Unmatched"
value={lanes.unmatched.length}
tone="ink"
/>
<SummaryTile
label="Done today"
value={lanes.done_today.length}
tone="muted"
/>
</div>
</div>
</section>
{/* Per-lane bulk bars. Each shows when its lane has a selection. */}
<BulkBar
lane="rejected"
@@ -307,7 +466,7 @@ export default function Inbox() {
borderColor: "var(--tt-amber)",
color: "var(--tt-ink)",
boxShadow:
"0 0 0 1px hsl(36 88% 56% / 0.18), 0 24px 60px -12px hsl(0 0% 0% / 0.7), inset 0 1px 0 0 hsl(0 0% 100% / 0.04)",
"0 0 0 1px hsl(36 75% 58% / 0.18), 0 24px 60px -12px hsl(0 0% 0% / 0.5), inset 0 1px 0 0 hsl(0 0% 100% / 0.03)",
}}
>
<div className="flex items-center gap-2 mb-3">
@@ -404,3 +563,87 @@ export default function Inbox() {
</div>
);
}
// ---------------------------------------------------------------------------
// SummaryTile — paper-toned metric for the queue ledger strip. Mirrors
// the lane accent (oxblood / amber / ink-blue / muted) so the eye-flow
// reads at a glance.
// ---------------------------------------------------------------------------
const SUMMARY_ACCENTS: Record<
"oxblood" | "amber" | "ink" | "muted",
{ dot: string; tint: string; text: string }
> = {
oxblood: {
dot: "hsl(358 70% 42%)",
tint: "hsl(358 70% 92%)",
text: "hsl(358 70% 30%)",
},
amber: {
dot: "hsl(36 92% 50%)",
tint: "hsl(36 82% 88%)",
text: "hsl(36 92% 30%)",
},
ink: {
dot: "hsl(212 80% 45%)",
tint: "hsl(212 85% 92%)",
text: "hsl(212 80% 30%)",
},
muted: {
dot: "hsl(30 8% 50%)",
tint: "hsl(36 22% 90%)",
text: "hsl(30 8% 30%)",
},
};
function SummaryTile({
label,
value,
tone,
}: {
label: string;
value: number;
tone: "oxblood" | "amber" | "ink" | "muted";
}) {
const a = SUMMARY_ACCENTS[tone];
return (
<div
className={cn(
"relative px-4 py-4 first:pl-5 lg:first:pl-7 last:pr-5 lg:last:pr-7"
)}
>
<div className="flex items-center gap-2 mb-2">
<span
aria-hidden
className="block h-1.5 w-1.5 rounded-full"
style={{ backgroundColor: a.dot }}
/>
<span
className="mono text-[10px] uppercase tracking-[0.20em] font-semibold"
style={{ color: "hsl(var(--surface-ink-3))" }}
>
{label}
</span>
</div>
<div
className="display tabular-nums tracking-[-0.03em]"
style={{
color: "hsl(var(--surface-ink))",
fontSize: "clamp(24px, 2vw, 30px)",
lineHeight: 1,
fontWeight: 400,
}}
>
{value}
</div>
<div
className="mono text-[10px] uppercase tracking-[0.16em] mt-1.5 inline-flex items-center gap-1 px-1.5 py-0.5 rounded-sm"
style={{
color: a.text,
backgroundColor: a.tint,
}}
>
{value === 0 ? "Clear" : value === 1 ? "row" : "rows"}
</div>
</div>
);
}