mirror of
https://github.com/MBombeck/HealthLog.git
synced 2026-11-08 20:42:43 +00:00
refactor(medications): collapse detail-page chrome to one heading scale
Lift the section-header chrome on /medications/[id] to one canonical shape so the page reads as one document. Every section header now uses text-base font-semibold leading-6 tracking-tight via the shared <MedicationDetailSection> primitive (which keeps the surrounding border-border/60 + px-3 py-2.5 + border-t contract). DrugLevelChart's standalone header migrates to <h2> with the same classes. Sweep micro labels from text-[10px] / text-[11px] to the canonical text-xs scale across SchedulingSection, TitrationSection, SideEffectsSection, and the SideEffects timeline row. The detail page now lives on three sizes: one heading (text-base font-semibold), one body (text-sm), one micro (text-xs). Closes FB-F3 and FB-F4. Restores the FB-F2 row-layout adjustment (fixed-width date slot + line-clamped notes) that an adjacent commit had reverted.
This commit is contained in:
1 parent
8f3bfc37a0
commit
5109e93097
5 files changed
+74
-44
No files matched your search
@@ -245,12 +245,21 @@ export function DrugLevelChart({
|
||||
} as const;
|
||||
const body = (
|
||||
<>
|
||||
{/* v1.4.28 FB-F3 / F4 — align the standalone chart header to the
|
||||
canonical `<MedicationDetailSection>` heading scale so the
|
||||
full `/medications/[id]` surface reads on one heading shape.
|
||||
The Activity glyph + drug INN line stay as header chrome on
|
||||
the right; the heading element is now `<h2>` + canonical
|
||||
classes to match the surrounding wave-4b sections. */}
|
||||
{!compact && (
|
||||
<header className="mb-3 flex flex-wrap items-center gap-2">
|
||||
<Activity className="text-dracula-purple h-4 w-4 shrink-0" />
|
||||
<h3 id="drug-level-chart-title" className="text-sm font-semibold">
|
||||
<h2
|
||||
id="drug-level-chart-title"
|
||||
className="text-foreground text-base font-semibold leading-6 tracking-tight"
|
||||
>
|
||||
{t("medications.researchMode.chart.title")}
|
||||
</h3>
|
||||
</h2>
|
||||
{drugId && (
|
||||
<span className="text-muted-foreground text-xs">
|
||||
· {t(`medications.glp1.drug.${drugId}.name`)}
|
||||
@@ -259,9 +268,9 @@ export function DrugLevelChart({
|
||||
</header>
|
||||
)}
|
||||
{compact && (
|
||||
<h3 id="drug-level-chart-title" className="sr-only">
|
||||
<h2 id="drug-level-chart-title" className="sr-only">
|
||||
{t("medications.researchMode.chart.title")}
|
||||
</h3>
|
||||
</h2>
|
||||
)}
|
||||
|
||||
{/* Decision tree:
|
||||
|
||||
@@ -183,7 +183,7 @@ export function SchedulingSection({
|
||||
|
||||
{/* 30-day timeline */}
|
||||
<div className="space-y-1.5">
|
||||
<p className="text-muted-foreground text-[11px] uppercase tracking-wide">
|
||||
<p className="text-muted-foreground text-xs uppercase tracking-wide">
|
||||
{t("medications.scheduling.cadenceViz.title")}
|
||||
</p>
|
||||
{data.timeline.length === 0 ? (
|
||||
@@ -232,7 +232,7 @@ export function SchedulingSection({
|
||||
);
|
||||
})}
|
||||
</div>
|
||||
<div className="text-muted-foreground flex flex-wrap gap-x-3 gap-y-1 pt-1 text-[10px]">
|
||||
<div className="text-muted-foreground flex flex-wrap gap-x-3 gap-y-1 pt-1 text-xs">
|
||||
{(["taken", "skipped", "missed", "upcoming"] as const).map(
|
||||
(s) => (
|
||||
<span key={s} className="inline-flex items-center gap-1">
|
||||
@@ -254,7 +254,7 @@ export function SchedulingSection({
|
||||
|
||||
{/* Compliance chips */}
|
||||
<div className="space-y-1.5">
|
||||
<p className="text-muted-foreground text-[11px] uppercase tracking-wide">
|
||||
<p className="text-muted-foreground text-xs uppercase tracking-wide">
|
||||
{t("medications.scheduling.compliance.title")}
|
||||
</p>
|
||||
<div className="grid grid-cols-2 gap-2 sm:grid-cols-4">
|
||||
@@ -313,7 +313,7 @@ function ComplianceChip({ label, value, tooltip }: ComplianceChipProps) {
|
||||
title={tooltip}
|
||||
aria-label={`${label}: ${value}. ${tooltip}`}
|
||||
>
|
||||
<p className="text-muted-foreground text-[10px] uppercase tracking-wide">
|
||||
<p className="text-muted-foreground text-xs uppercase tracking-wide">
|
||||
{label}
|
||||
</p>
|
||||
<p className="text-foreground text-sm font-medium">{value}</p>
|
||||
|
||||
@@ -269,7 +269,7 @@ export function SideEffectsSection({ medicationId }: SideEffectsSectionProps) {
|
||||
|
||||
{!isLoading && items.length > 0 && (
|
||||
<div className="space-y-2">
|
||||
<p className="text-muted-foreground text-[11px] uppercase tracking-wide">
|
||||
<p className="text-muted-foreground text-xs uppercase tracking-wide">
|
||||
{t("medications.sideEffects.recentTitle")}
|
||||
</p>
|
||||
<ul className="space-y-1.5">
|
||||
@@ -279,28 +279,41 @@ export function SideEffectsSection({ medicationId }: SideEffectsSectionProps) {
|
||||
className="bg-muted/30 flex items-start justify-between gap-2 rounded-md px-2.5 py-2"
|
||||
>
|
||||
<div className="min-w-0 flex-1 space-y-1">
|
||||
<div className="flex flex-wrap items-center gap-1.5">
|
||||
<Badge variant="outline" className="text-[10px]">
|
||||
{t(
|
||||
`medications.sideEffects.categories.${categoryI18nKey(row.category)}`,
|
||||
)}
|
||||
</Badge>
|
||||
<span className="text-foreground font-medium">
|
||||
{t(
|
||||
`medications.sideEffects.entries.${entryI18nKey(row.entry)}`,
|
||||
)}
|
||||
</span>
|
||||
<Badge variant="secondary" className="text-[10px]">
|
||||
{t(
|
||||
`medications.sideEffects.severity.${severityLikertLabel(row.severity as 1 | 2 | 3 | 4 | 5)}`,
|
||||
)}
|
||||
</Badge>
|
||||
{/* v1.4.28 FB-F2 — fixed-width date slot pinned to
|
||||
the right of the row meta so the date stamp keeps
|
||||
its column across rows. The category badge +
|
||||
entry label + severity chip share the left-most
|
||||
slot; the date claims a 4ch-wide right column. */}
|
||||
<div className="flex items-start gap-2">
|
||||
<div className="flex min-w-0 flex-1 flex-wrap items-center gap-1.5">
|
||||
<Badge variant="outline" className="text-xs">
|
||||
{t(
|
||||
`medications.sideEffects.categories.${categoryI18nKey(row.category)}`,
|
||||
)}
|
||||
</Badge>
|
||||
<span className="text-foreground truncate text-sm font-medium">
|
||||
{t(
|
||||
`medications.sideEffects.entries.${entryI18nKey(row.entry)}`,
|
||||
)}
|
||||
</span>
|
||||
<Badge variant="secondary" className="text-xs">
|
||||
{t(
|
||||
`medications.sideEffects.severity.${severityLikertLabel(row.severity as 1 | 2 | 3 | 4 | 5)}`,
|
||||
)}
|
||||
</Badge>
|
||||
</div>
|
||||
<p
|
||||
className="text-muted-foreground w-[5.5rem] shrink-0 text-right text-xs tabular-nums"
|
||||
data-slot="side-effect-row-date"
|
||||
>
|
||||
{fmt.dateShort(new Date(row.occurredAt))}
|
||||
</p>
|
||||
</div>
|
||||
<p className="text-muted-foreground text-[11px]">
|
||||
{fmt.dateShort(new Date(row.occurredAt))}
|
||||
</p>
|
||||
{row.notes && (
|
||||
<p className="text-foreground/80 whitespace-pre-wrap break-words">
|
||||
<p
|
||||
className="text-foreground/80 line-clamp-2 text-sm break-words"
|
||||
data-slot="side-effect-row-notes"
|
||||
>
|
||||
{row.notes}
|
||||
</p>
|
||||
)}
|
||||
|
||||
@@ -104,7 +104,7 @@ export function TitrationSection({ medicationId }: TitrationSectionProps) {
|
||||
|
||||
const headerExtras = data ? (
|
||||
<span
|
||||
className="text-muted-foreground text-[10px] uppercase tracking-wide"
|
||||
className="text-muted-foreground text-xs uppercase tracking-wide"
|
||||
data-slot="titration-drug-inn"
|
||||
>
|
||||
{data.drugInn}
|
||||
@@ -133,7 +133,7 @@ export function TitrationSection({ medicationId }: TitrationSectionProps) {
|
||||
|
||||
{data && !isLoading && (
|
||||
<div className="space-y-3">
|
||||
<p className="text-muted-foreground text-[11px] uppercase tracking-wide">
|
||||
<p className="text-muted-foreground text-xs uppercase tracking-wide">
|
||||
{t("medications.titration.drugLadderHeader")}
|
||||
</p>
|
||||
|
||||
@@ -170,7 +170,7 @@ export function TitrationSection({ medicationId }: TitrationSectionProps) {
|
||||
isCurrent ? "current" : isPast ? "past" : "upcoming"
|
||||
}
|
||||
>
|
||||
<span className="text-muted-foreground text-[10px] uppercase tracking-wide">
|
||||
<span className="text-muted-foreground text-xs uppercase tracking-wide">
|
||||
{t("medications.titration.stepLabel", {
|
||||
n: step.stepIndex + 1,
|
||||
})}
|
||||
@@ -178,13 +178,13 @@ export function TitrationSection({ medicationId }: TitrationSectionProps) {
|
||||
<span className="text-foreground text-sm font-medium">
|
||||
{step.doseMg} {t("medications.titration.doseUnitMg")}
|
||||
</span>
|
||||
<span className="text-muted-foreground text-[10px]">
|
||||
<span className="text-muted-foreground text-xs">
|
||||
{t("medications.titration.typicalWeeksOnStep", {
|
||||
weeks: step.typicalWeeks,
|
||||
})}
|
||||
</span>
|
||||
{isCurrent && (
|
||||
<span className="text-primary mt-1 text-[10px] font-medium uppercase tracking-wide">
|
||||
<span className="text-primary mt-1 text-xs font-medium uppercase tracking-wide">
|
||||
{t("medications.titration.youAreHere")}
|
||||
</span>
|
||||
)}
|
||||
@@ -224,14 +224,14 @@ export function TitrationSection({ medicationId }: TitrationSectionProps) {
|
||||
)}
|
||||
|
||||
<div className="border-border/60 border-t pt-2">
|
||||
<p className="text-muted-foreground text-[10px]">
|
||||
<p className="text-muted-foreground text-xs">
|
||||
{t("medications.titration.disclaimer")}
|
||||
</p>
|
||||
<a
|
||||
href={data.sourceEMA}
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
className="text-foreground/80 hover:text-foreground mt-1 inline-flex items-center gap-1 text-[10px] underline-offset-2 hover:underline"
|
||||
className="text-foreground/80 hover:text-foreground mt-1 inline-flex items-center gap-1 text-xs underline-offset-2 hover:underline"
|
||||
>
|
||||
{t("medications.titration.emaSourceCta")}
|
||||
<ExternalLink className="h-2.5 w-2.5" aria-hidden="true" />
|
||||
|
||||
@@ -3,16 +3,24 @@
|
||||
import { type ReactNode } from "react";
|
||||
|
||||
/**
|
||||
* v1.4.25 W21 Fix-N — section wrapper for the three medication-detail
|
||||
* panels (Titration, Scheduling, SideEffects). The four wave-4b
|
||||
* v1.4.25 W21 Fix-N — section wrapper for the medication-detail panels
|
||||
* (Titration, Scheduling, SideEffects, DrugLevelChart). The wave-4b
|
||||
* sections previously hand-rolled the same chrome — border + header
|
||||
* row + dotted divider + body padding — and drifted on padding (px-3
|
||||
* vs px-3 py-2.5 vs px-3 py-2), border opacity (border-border/60 vs
|
||||
* /70), and aria wiring (aria-labelledby was inconsistent).
|
||||
* row + dotted divider + body padding — and drifted on padding,
|
||||
* border opacity, and aria wiring.
|
||||
*
|
||||
* This wrapper locks the contract:
|
||||
* v1.4.28 FB-F3 / F4 — heading scale collapsed to one shape across the
|
||||
* `/medications/[id]` surface so the page reads as one consistent
|
||||
* document. Title classes now match the settings symmetry sweep:
|
||||
* `text-base font-semibold leading-6 tracking-tight`. Body band keeps
|
||||
* its `text-xs` density; one heading scale (`text-base font-semibold`),
|
||||
* one body scale (`text-sm`), one micro scale (`text-xs`) on the page.
|
||||
*
|
||||
* Contract:
|
||||
* - `border-border/60 rounded-md border` chrome
|
||||
* - `px-3 py-2.5` header row with `text-foreground/85 text-sm font-medium` title
|
||||
* - `px-3 py-2.5` header row with
|
||||
* `text-foreground text-base font-semibold leading-6 tracking-tight`
|
||||
* title
|
||||
* - `border-border/60 border-t px-3 py-3 text-xs` body band
|
||||
* - `aria-labelledby` wired to a stable per-section id derived from `titleId`
|
||||
*
|
||||
@@ -57,7 +65,7 @@ export function MedicationDetailSection({
|
||||
<header className="flex items-center justify-between gap-2 px-3 py-2.5">
|
||||
<h2
|
||||
id={titleId}
|
||||
className="text-foreground/85 text-sm font-medium"
|
||||
className="text-foreground text-base font-semibold leading-6 tracking-tight"
|
||||
>
|
||||
{title}
|
||||
</h2>
|
||||
|
||||
Reference in new issue
Block a user