mirror of
https://github.com/MBombeck/HealthLog.git
synced 2026-11-14 22:50:51 +00:00
refactor(ui): give Settings and Admin one card rhythm and one page header
The two consoles used the same primitives and still read as two systems, because everything without a primitive had drifted: the header-to-body step existed in three competing techniques at once (`mt-4` on the first body child, `space-y-4` on the card, `mb-4` on the header), the body started either at the card edge or 28 px in, and both shells hand-rolled the page title instead of calling `PageHeader`. Move the decisions into the primitives so a call site cannot re-open them: - `SettingsCard` is gap-based like `Card` (`flex flex-col gap-4`). The header-to-body step is the container's, not a margin somebody remembers. A dense card still overrides the gap on the card itself. - The body starts at the card edge. `pl-7` is gone — off the spacing scale, and it mis-aligned action rows against their own forms wherever two neighbouring cards disagreed about it. - The card header's description slot loses its `space-y-1`. That was the licence call sites used to stack explainer paragraphs into a muted `text-xs` slot; the slot holds one sentence. - New `SettingsCardActions`: one action row, trailing edge, last thing in the card. `align="start"` spells the one exception, a card whose only actions are links out. - New `StatTile` replaces four hand-rolled label/value boxes in Admin. - `SectionHeading` moves from `insights/` to `ui/`. It is the heading above a group of cards, which is what Settings needs; Settings had been using a card header with no card, which reads as a card that failed to paint. Both shells now render their heading through `PageHeader`, which gained `headingAs="div"` for the second copy each shell paints (mobile above the nav strip, desktop inside the grid) so only one is a real `<h1>`. The page explainer is muted everywhere: the primitive said foreground, the standards and every hand-rolled copy said muted, so pages disagreed with each other. Medications, Vorsorge, the labs / custom-metric / illness detail pages and the clinician view drop their hand-rolled title blocks too, including the mid-word truncation on the medications subtitle. Tests pin the parts that can be checked exactly: the card's gap, the description slot's missing rhythm, the action row's two alignments, the `<h1>`-free shells, and a sweep asserting `pl-7` stays gone.
This commit is contained in:
1 parent
00e8d05235
commit
5445bfc1f1
121 files changed
+832
-620
No files matched your search
@@ -493,13 +493,8 @@ export function BackupsSection() {
|
||||
|
||||
return (
|
||||
<SettingsCard>
|
||||
{/* v1.18.1 E3 — the snapshot count leads (numbers first), the
|
||||
"Run now" button follows, and the explainer + docs link move
|
||||
into the header's description slot. Previously the description
|
||||
sat in a separate `mt-1 pl-7` paragraph that crowded the button
|
||||
directly above it; routing it through the header's `space-y-1`
|
||||
stack restores breathing room and left-aligns it under the
|
||||
title. */}
|
||||
{/* v1.18.1 E3 — the snapshot count leads (numbers first) and the
|
||||
"Run now" button follows. */}
|
||||
<SettingsCardHeader
|
||||
icon={Database}
|
||||
title={t("admin.section.backups.title")}
|
||||
@@ -547,7 +542,7 @@ export function BackupsSection() {
|
||||
backup file independently of any existing rows. The visible
|
||||
button proxies a hidden file input so the layout stays clean
|
||||
while keyboard / screen-reader users keep a labelled control. */}
|
||||
<div className="bg-muted/30 border-border mt-4 flex flex-col gap-2 rounded-md border p-3 sm:flex-row sm:items-center sm:justify-between">
|
||||
<div className="bg-muted/30 border-border flex flex-col gap-2 rounded-md border p-3 sm:flex-row sm:items-center sm:justify-between">
|
||||
<div>
|
||||
<div className="text-sm font-medium">
|
||||
{t("admin.section.backups.uploadTitle")}
|
||||
@@ -596,7 +591,7 @@ export function BackupsSection() {
|
||||
) : null}
|
||||
|
||||
{isLoading ? (
|
||||
<div className="mt-4 flex items-center gap-2">
|
||||
<div className="flex items-center gap-2">
|
||||
<Loader2 className="text-muted-foreground h-4 w-4 animate-spin motion-reduce:animate-none" />
|
||||
<span className="text-muted-foreground text-sm">
|
||||
{t("admin.section.backups.loading")}
|
||||
@@ -605,7 +600,7 @@ export function BackupsSection() {
|
||||
) : isError ? (
|
||||
<div
|
||||
role="alert"
|
||||
className="text-destructive bg-destructive/10 border-destructive/30 mt-4 rounded-md border px-3 py-2 text-sm"
|
||||
className="text-destructive bg-destructive/10 border-destructive/30 rounded-md border px-3 py-2 text-sm"
|
||||
>
|
||||
{t("admin.section.backups.loadError")}
|
||||
</div>
|
||||
@@ -614,7 +609,7 @@ export function BackupsSection() {
|
||||
// primitive. The header already exposes "Backup now" but a
|
||||
// brand-new admin lands inside the card and benefits from a
|
||||
// duplicate CTA right next to the explanation.
|
||||
<div className="mt-4">
|
||||
<div>
|
||||
<EmptyState
|
||||
icon={<Database className="size-6" />}
|
||||
title={t("admin.section.backups.emptyTitle")}
|
||||
@@ -637,7 +632,7 @@ export function BackupsSection() {
|
||||
/>
|
||||
</div>
|
||||
) : (
|
||||
<div className="mt-4">
|
||||
<div>
|
||||
{/* Wide layout: the canonical table. Hidden below `md`, where the
|
||||
five columns push Download / Restore off-screen behind a
|
||||
horizontal scroll; the card list below takes over there. */}
|
||||
|
||||
Reference in new issue
Block a user