diff --git a/frontend/components/chat/chat-input.tsx b/frontend/components/chat/chat-input.tsx
index 46a39b5..e0a43da 100644
--- a/frontend/components/chat/chat-input.tsx
+++ b/frontend/components/chat/chat-input.tsx
@@ -192,10 +192,10 @@ export function ChatInput({ onSubmit, status, onStop }: ChatInputProps) {
event.preventDefault();
submit();
}}
- className="w-full overflow-hidden rounded-[28px] border border-border/60 bg-[oklch(0.172_0.006_277)] shadow-sm"
+ className="w-full overflow-hidden rounded-[28px] border border-border/60 bg-[oklch(0.195_0.006_277)] shadow-sm"
>
{/* Top (lighter) card: textarea + toolbar, with a slightly smaller bottom radius */}
-
+
-
- Primary care:
- {patient.pcp}
+
+
+
+
+
+
+
+ {patient.alerts.length > 0 && (
+
+ {patient.alerts.map((alert) => (
+
+ {alert}
+
+ ))}
+
+ )}
+
+
+ );
+}
+
+function VitalsCard({ patient }: { patient: Patient }) {
+ const { vitals } = patient;
+ const vitalItems = [
+ { label: "BP", value: vitals.bp },
+ { label: "HR", value: vitals.hr },
+ { label: "Temp", value: vitals.temp },
+ { label: "SpO₂", value: vitals.spo2 },
+ ];
+
+ return (
+
+
+ Vitals
+ Taken {vitals.takenAt}
+
+
+
+ {vitalItems.map((item) => (
+
+ ))}
+
+
+
+
+
+ );
+}
+
+function LabsCard({ patient }: { patient: Patient }) {
+ return (
+
+
+ Labs
+ As of {patient.labs[0]?.takenAt ?? "—"}
+
+
+
+ {patient.labs.map((lab) => (
+
+ {lab.value}
+
+ {lab.flag}
+
+
+ }
+ />
+ ))}
+
+
+
+
+
+ );
+}
+
+function MedicationsCard({ patient }: { patient: Patient }) {
+ return (
+
+
+ Medications
+ {patient.medications.length} active
+
+
+ {patient.medications.map((med) => (
+
+ ))}
+
+
+ );
+}
+
+function ProblemsCard({ patient }: { patient: Patient }) {
+ return (
+
+
+ Problems
+ {patient.problems.length} active
+
+
+ {patient.problems.map((problem) => (
+
+ ))}
);
@@ -102,24 +251,26 @@ function AllergiesCard({ patient }: { patient: Patient }) {
No known allergies.
) : (
patient.allergies.map((allergy) => (
-
-
- {allergy.substance}
-
- {" "}
- — {allergy.reaction}
-
-
-
- {allergy.severity}
-
-
+ label={
+ <>
+ {allergy.substance}
+
+ {" "}
+ — {allergy.reaction}
+
+ >
+ }
+ value={
+
+ {allergy.severity}
+
+ }
+ />
))
)}
@@ -128,95 +279,30 @@ function AllergiesCard({ patient }: { patient: Patient }) {
);
}
-function MedicationsCard({ patient }: { patient: Patient }) {
+function VisitsCard({ patient }: { patient: Patient }) {
return (
- Medications & problems
+ Recent visits
-
-
-
Active medications
- {patient.medications.map((med) => (
-
-
{med.name}
-
- {med.dose} · {med.frequency}
+
+ {patient.encounters.map((encounter) => (
+
+
+ {encounter.type}
+
+ {encounter.date}
- ))}
-
-
-
-
Problem list
- {patient.problems.map((problem) => (
-
- {problem.label}
- since {problem.since}
-
- ))}
-
-
-
- );
-}
-
-function VitalsCard({ patient }: { patient: Patient }) {
- const { vitals } = patient;
- const vitalItems = [
- { label: "BP", value: vitals.bp },
- { label: "HR", value: vitals.hr },
- { label: "Temp", value: vitals.temp },
- { label: "SpO₂", value: vitals.spo2 },
- ];
-
- return (
-
-
- Vitals, labs & visits
- Vitals taken {vitals.takenAt}
-
-
-
- {vitalItems.map((item) => (
-
- {item.label}
- {item.value}
-
- ))}
-
-
-
-
Recent labs
- {patient.labs.map((lab) => (
-
-
{lab.name}
-
- {lab.value}
-
- {lab.flag}
-
-
-
- ))}
-
-
-
-
Recent visits
- {patient.encounters.map((encounter) => (
-
-
- {encounter.type}
- {encounter.date}
-
-
{encounter.summary}
-
{encounter.provider}
-
- ))}
-
+ {encounter.summary}
+
+ {encounter.provider}
+
+
+ ))}
);
@@ -231,20 +317,26 @@ function LoadingCards() {
-
+
+
+ {[0, 1, 2, 3].map((cell) => (
+
+ ))}
+
- {[0, 1, 2].map((card) => (
+ {[0, 1, 2, 3, 4, 5].map((card) => (
-
+
{[0, 1, 2].map((row) => (
))}
+ {card % 2 === 1 && }
))}
@@ -272,9 +364,12 @@ export function PatientResult({ status, fileNumber, patient }: PatientResultProp
) : (
<>
-
-
+
+
+
+
+
>
)}
diff --git a/frontend/components/chat/sparkline.tsx b/frontend/components/chat/sparkline.tsx
new file mode 100644
index 0000000..f91fa2a
--- /dev/null
+++ b/frontend/components/chat/sparkline.tsx
@@ -0,0 +1,68 @@
+"use client";
+
+import { useId } from "react";
+
+import { cn } from "@/lib/utils";
+
+// A tiny dependency-free trend chart: a line with a soft shaded fill beneath it.
+// Stretches to its container width; colored via `currentColor` (default text-primary).
+export function Sparkline({
+ points,
+ className,
+}: {
+ points: number[];
+ className?: string;
+}) {
+ const gradientId = useId();
+
+ if (points.length === 0) {
+ return null;
+ }
+
+ const width = 100;
+ const top = 3;
+ const bottom = 29;
+ const baseline = 32;
+ const min = Math.min(...points);
+ const max = Math.max(...points);
+ const range = max - min;
+
+ const coords = points.map((value, index) => {
+ const x =
+ points.length === 1 ? width / 2 : (index / (points.length - 1)) * width;
+ const y =
+ range === 0
+ ? (top + bottom) / 2
+ : bottom - ((value - min) / range) * (bottom - top);
+ return `${x.toFixed(2)},${y.toFixed(2)}`;
+ });
+
+ const line = coords.map((c, i) => `${i === 0 ? "M" : "L"}${c}`).join(" ");
+ const area = `${line} L${width},${baseline} L0,${baseline} Z`;
+
+ return (
+
+ );
+}
diff --git a/frontend/lib/patients.ts b/frontend/lib/patients.ts
index a2fcc08..dad7553 100644
--- a/frontend/lib/patients.ts
+++ b/frontend/lib/patients.ts
@@ -45,6 +45,14 @@ export type Encounter = {
summary: string;
};
+// A short series for a sparkline. `points` are most-recent-last, so the
+// latest reading is points.at(-1).
+export type Trend = {
+ label: string;
+ unit: string;
+ points: number[];
+};
+
export type Patient = {
fileNumber: string; // MRN / file number, e.g. "10293"
name: string;
@@ -58,7 +66,9 @@ export type Patient = {
medications: Medication[];
problems: Problem[];
vitals: Vitals;
+ vitalsTrend: Trend; // headline vital plotted as a sparkline
labs: Lab[];
+ labTrend: Trend; // headline lab plotted as a sparkline
encounters: Encounter[];
};
@@ -95,12 +105,22 @@ const PATIENTS: Record = {
spo2: "97%",
takenAt: "May 28, 2026",
},
+ vitalsTrend: {
+ label: "Heart rate",
+ unit: "bpm",
+ points: [72, 75, 74, 79, 83, 80, 78],
+ },
labs: [
{ name: "HbA1c", value: "8.2 %", flag: "high", takenAt: "May 20, 2026" },
{ name: "eGFR", value: "52 mL/min", flag: "low", takenAt: "May 20, 2026" },
{ name: "Potassium", value: "4.4 mmol/L", flag: "normal", takenAt: "May 20, 2026" },
{ name: "INR", value: "2.6", flag: "normal", takenAt: "May 20, 2026" },
],
+ labTrend: {
+ label: "HbA1c",
+ unit: "%",
+ points: [9.4, 9.1, 8.8, 8.6, 8.2],
+ },
encounters: [
{
date: "May 28, 2026",
@@ -151,12 +171,22 @@ const PATIENTS: Record = {
spo2: "92%",
takenAt: "May 31, 2026",
},
+ vitalsTrend: {
+ label: "Heart rate",
+ unit: "bpm",
+ points: [88, 91, 96, 99, 97, 95, 94],
+ },
labs: [
{ name: "WBC", value: "14.8 ×10⁹/L", flag: "high", takenAt: "May 31, 2026" },
{ name: "CRP", value: "112 mg/L", flag: "critical", takenAt: "May 31, 2026" },
{ name: "Lactate", value: "1.8 mmol/L", flag: "normal", takenAt: "May 31, 2026" },
{ name: "Hemoglobin", value: "11.9 g/dL", flag: "low", takenAt: "May 31, 2026" },
],
+ labTrend: {
+ label: "CRP",
+ unit: "mg/L",
+ points: [38, 64, 98, 126, 112],
+ },
encounters: [
{
date: "May 30, 2026",
@@ -197,11 +227,21 @@ const PATIENTS: Record = {
spo2: "99%",
takenAt: "May 26, 2026",
},
+ vitalsTrend: {
+ label: "Heart rate",
+ unit: "bpm",
+ points: [78, 80, 79, 83, 85, 84, 82],
+ },
labs: [
{ name: "TSH", value: "2.1 mIU/L", flag: "normal", takenAt: "May 19, 2026" },
{ name: "Hemoglobin", value: "11.2 g/dL", flag: "low", takenAt: "May 19, 2026" },
{ name: "Fasting glucose", value: "84 mg/dL", flag: "normal", takenAt: "May 19, 2026" },
],
+ labTrend: {
+ label: "Hemoglobin",
+ unit: "g/dL",
+ points: [12.4, 12.0, 11.7, 11.4, 11.2],
+ },
encounters: [
{
date: "May 26, 2026",