fix(ui): catalog loading/error truth, info-popover dedupe, touch sweep

Metric catalog page painted every metric as "Not tracked yet" (with
Connect CTAs) while its four availability probes were still in flight,
and stayed there forever on a failed batch — a load failure indistinguishable
from an honest absence. Gate rows on the probes' aggregate loading/error
state: row skeletons while loading, a QueryErrorCard with retry on failure.

Repoint ComplianceInfoTip onto InfoPopover instead of carrying a verbatim
structural copy (adds icon/align/data-slot override props to the shared
primitive); migrate the cycle log-sheet's FieldInfo off a hover Tooltip
onto the same click-opened InfoPopover, since a hover trigger never opens
reliably on the sheet's thumb-first touch surface.

Lift ViewToggle/SegmentedToggle off their stale 40px tap floor onto the
44px/36px floor the sibling intake and history-rail controls already
carry, and correct the docstrings to match. Restore the dropped
safe-area-inset-top term in /coach/conversations' height calc so it
matches /coach on notched devices. Bring the coach history rail's
off-scale spacing and sub-floor timestamp text onto the standard scale.
This commit is contained in:
Marc-André Bombeck committed 2026-07-18 06:47:32 +02:00
1 parent 1d559d7ed0
commit a977df24a0
14 files changed
+152 -93

No files matched your search

+17 -35
View File
@@ -1,14 +1,6 @@
"use client";
import { Info } from "lucide-react";
import {
Tooltip,
TooltipContent,
TooltipProvider,
TooltipTrigger,
} from "@/components/ui/tooltip";
import { cn } from "@/lib/utils";
import { InfoPopover } from "@/components/ui/info-popover";
/**
* v1.18.1 — the cycle log-sheet field-explainer affordance.
@@ -16,10 +8,14 @@ import { cn } from "@/lib/utils";
* A small "?" / info icon that puts a factual, descriptive-only one-liner
* (what a clinical field means — BBT, cervical mucus, an LH surge) right at
* the point of capture, so a first-time user never has to leave the sheet to
* understand a term. Mirrors the mood surface's `MoodExplainerIcon` precedent
* (self-contained `TooltipProvider`, a real focusable `<button>` trigger with
* an `aria-label`, keyboard- and SR-reachable via Radix) but bumps the hit
* area to ≥44 px for touch, since the cycle sheet is a thumb-first surface.
* understand a term.
*
* L2 (`.planning/audits/2026-07-18-qa-ui.md`) — this used to be a
* self-contained hover `Tooltip`, which never opens reliably on tap; the
* cycle sheet is a thumb-first surface, so it now wraps the click-opened
* `InfoPopover` (the app's one (i) info-affordance since the v1.28.17
* merge) instead, keeping the `cycle-field-info` data-slot the sheet
* already keys off of.
*
* Copy stays descriptive-only — never diagnosis or medical advice — matching
* the phase-education honesty gate.
@@ -31,31 +27,17 @@ export function FieldInfo({
}: {
/** Accessible name for the trigger (what the icon explains). */
label: string;
/** The explanation shown in the tooltip. */
/** The explanation shown in the popover. */
detail: string;
className?: string;
}) {
return (
<TooltipProvider delayDuration={150}>
<Tooltip>
<TooltipTrigger
type="button"
aria-label={label}
data-slot="cycle-field-info"
className={cn(
// size-6 icon inside an 11-unit (44 px) hit box so touch targets
// clear the WCAG 2.5.8 / Apple HIG minimum without enlarging the
// visible glyph.
"text-muted-foreground hover:text-foreground focus-visible:ring-ring -m-2.5 inline-flex size-11 shrink-0 items-center justify-center rounded-full p-2.5 transition-colors focus-visible:ring-2 focus-visible:outline-none",
className,
)}
>
<Info className="size-4" aria-hidden="true" />
</TooltipTrigger>
<TooltipContent className="max-w-[16rem] text-xs leading-snug">
{detail}
</TooltipContent>
</Tooltip>
</TooltipProvider>
<InfoPopover
content={detail}
label={label}
align="start"
triggerDataSlot="cycle-field-info"
className={className}
/>
);
}