feat(sp6): InboxHeader + CandidateBreakdown
This commit is contained in:
@@ -0,0 +1,23 @@
|
||||
// @vitest-environment happy-dom
|
||||
import { afterEach, describe, expect, it } from "vitest";
|
||||
import { cleanup, render } from "@testing-library/react";
|
||||
import { CandidateBreakdown } from "./CandidateBreakdown";
|
||||
|
||||
afterEach(() => cleanup());
|
||||
|
||||
describe("CandidateBreakdown", () => {
|
||||
it("renders per-field scores", () => {
|
||||
const { container } = render(
|
||||
<CandidateBreakdown
|
||||
score={92}
|
||||
breakdown={{ patient: 1, date: 1, amount: 1, provider: 1 }}
|
||||
tier="strong"
|
||||
/>,
|
||||
);
|
||||
expect(container.textContent).toMatch(/patient/i);
|
||||
expect(container.textContent).toMatch(/date/i);
|
||||
expect(container.textContent).toMatch(/amount/i);
|
||||
expect(container.textContent).toMatch(/provider/i);
|
||||
expect(container.textContent).toContain("92");
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,56 @@
|
||||
import type { ScoreBreakdown, ScoreTier } from "@/lib/inbox-api";
|
||||
|
||||
const FIELDS: Array<{ key: keyof ScoreBreakdown; label: string; weight: number }> = [
|
||||
{ key: "patient", label: "Patient", weight: 40 },
|
||||
{ key: "date", label: "Date", weight: 25 },
|
||||
{ key: "amount", label: "Amount", weight: 20 },
|
||||
{ key: "provider", label: "Provider", weight: 15 },
|
||||
];
|
||||
|
||||
export function CandidateBreakdown({
|
||||
score,
|
||||
breakdown,
|
||||
tier,
|
||||
}: {
|
||||
score: number;
|
||||
breakdown: ScoreBreakdown;
|
||||
tier: ScoreTier;
|
||||
}) {
|
||||
return (
|
||||
<div className="font-mono" style={{ fontSize: 11 }}>
|
||||
<div className="flex items-baseline justify-between mb-2">
|
||||
<span
|
||||
className="uppercase"
|
||||
style={{ color: "var(--tt-ink-dim)", letterSpacing: "0.12em" }}
|
||||
>
|
||||
Score
|
||||
</span>
|
||||
<span
|
||||
className="tabular-nums"
|
||||
style={{ color: "var(--tt-amber)", fontSize: 24 }}
|
||||
>
|
||||
{score}
|
||||
</span>
|
||||
</div>
|
||||
<ul className="space-y-1">
|
||||
{FIELDS.map((f) => (
|
||||
<li key={f.key} className="flex justify-between">
|
||||
<span style={{ color: "var(--tt-ink)" }}>{f.label}</span>
|
||||
<span
|
||||
className="tabular-nums"
|
||||
style={{ color: "var(--tt-ink-dim)" }}
|
||||
>
|
||||
{Math.round(breakdown[f.key] * f.weight)} / {f.weight}
|
||||
</span>
|
||||
</li>
|
||||
))}
|
||||
</ul>
|
||||
<p
|
||||
className="mt-3 uppercase"
|
||||
style={{ color: "var(--tt-ink-dim)", letterSpacing: "0.12em", fontSize: 10 }}
|
||||
>
|
||||
{tier}
|
||||
</p>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,14 @@
|
||||
// @vitest-environment happy-dom
|
||||
import { afterEach, describe, expect, it } from "vitest";
|
||||
import { cleanup, render } from "@testing-library/react";
|
||||
import { InboxHeader } from "./InboxHeader";
|
||||
|
||||
afterEach(() => cleanup());
|
||||
|
||||
describe("InboxHeader", () => {
|
||||
it("renders the date and counts", () => {
|
||||
const { container } = render(<InboxHeader needEyesCount={6} doneTodayCount={23} />);
|
||||
expect(container.textContent).toMatch(/6.*items need eyes/);
|
||||
expect(container.textContent).toMatch(/23.*done today/);
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,50 @@
|
||||
export function InboxHeader({
|
||||
needEyesCount,
|
||||
doneTodayCount,
|
||||
}: {
|
||||
needEyesCount: number;
|
||||
doneTodayCount: number;
|
||||
}) {
|
||||
const now = new Date();
|
||||
const day = now.toLocaleDateString("en-US", { weekday: "short" }).toUpperCase();
|
||||
const time = now.toLocaleTimeString("en-US", {
|
||||
hour: "2-digit",
|
||||
minute: "2-digit",
|
||||
hour12: false,
|
||||
});
|
||||
|
||||
return (
|
||||
<header
|
||||
className="sticky top-0 z-10 px-6 py-4"
|
||||
style={{
|
||||
background: "var(--tt-bg)",
|
||||
borderBottom: "1px solid var(--tt-bg-elev)",
|
||||
}}
|
||||
>
|
||||
<h1
|
||||
className="tracking-tight"
|
||||
style={{
|
||||
color: "var(--tt-ink)",
|
||||
fontFamily: "'Inter Tight', system-ui, sans-serif",
|
||||
fontWeight: 700,
|
||||
fontSize: 22,
|
||||
}}
|
||||
>
|
||||
INBOX{" "}
|
||||
<span
|
||||
className="font-mono"
|
||||
style={{ color: "var(--tt-amber)", fontSize: 16 }}
|
||||
>
|
||||
· {day} {time}
|
||||
</span>
|
||||
</h1>
|
||||
<p
|
||||
className="font-mono mt-1"
|
||||
style={{ color: "var(--tt-ink-dim)", fontSize: 11, letterSpacing: "0.05em" }}
|
||||
>
|
||||
<span style={{ color: "var(--tt-amber)" }}>{needEyesCount}</span> items need eyes ·{" "}
|
||||
<span style={{ color: "var(--tt-ink)" }}>{doneTodayCount}</span> done today
|
||||
</p>
|
||||
</header>
|
||||
);
|
||||
}
|
||||
Reference in New Issue
Block a user