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

+22 -5
View File
@@ -1,7 +1,7 @@
"use client";
import { useState } from "react";
import { Info } from "lucide-react";
import { Info, type LucideIcon } from "lucide-react";
import {
Popover,
@@ -43,6 +43,18 @@ export interface InfoPopoverProps {
*/
label?: string;
className?: string;
/**
* Trigger glyph. Defaults to `Info` (h-3.5 w-3.5); the compliance-tip
* wrapper passes `HelpCircle` at `h-3 w-3` to keep its pre-merge look.
*/
icon?: LucideIcon;
iconClassName?: string;
/** Popover alignment relative to the trigger. Defaults to `"end"`. */
align?: "start" | "center" | "end";
/** `data-slot` override for the trigger button. */
triggerDataSlot?: string;
/** `data-slot` override for the popover body. */
bodyDataSlot?: string;
}
export function InfoPopover({
@@ -50,6 +62,11 @@ export function InfoPopover({
link,
label,
className,
icon: Icon = Info,
iconClassName = "h-3.5 w-3.5",
align = "end",
triggerDataSlot = "info-popover-trigger",
bodyDataSlot = "info-popover-body",
}: InfoPopoverProps) {
const { t } = useTranslations();
const [open, setOpen] = useState(false);
@@ -59,7 +76,7 @@ export function InfoPopover({
<PopoverTrigger asChild>
<button
type="button"
data-slot="info-popover-trigger"
data-slot={triggerDataSlot}
aria-label={label ?? t("common.moreInfo")}
aria-expanded={open}
className={cn(
@@ -70,12 +87,12 @@ export function InfoPopover({
className,
)}
>
<Info className="h-3.5 w-3.5" aria-hidden="true" />
<Icon className={iconClassName} aria-hidden="true" />
</button>
</PopoverTrigger>
<PopoverContent
data-slot="info-popover-body"
align="end"
data-slot={bodyDataSlot}
align={align}
sideOffset={6}
className="max-w-xs space-y-1.5"
>
+5 -4
View File
@@ -25,9 +25,10 @@ export interface ViewToggleProps<T extends string> {
* two-segment control: the active segment carries the filled surface, the
* inactive one stays ghost. `aria-pressed` announces the state per segment;
* the group is named for screen readers. Segments keep the 44-px mobile tap
* floor (`size-11` via `size-10` + the `sm:size-8` desktop step), shrinking
* to the standard 36-px control height on desktop like a neighbouring Add
* button.
* floor (`size-11`), shrinking to the 36-px control height on desktop
* (`sm:size-9`) like a neighbouring Add button — the same floor the intake
* kebab and history-rail delete controls raised in this release window
* (L3, `.planning/audits/2026-07-18-qa-ui.md`).
*
* Previously forked between the medications module
* (`medications/medication-view-toggle.tsx`) and the Vorsorge/Illness/Labs
@@ -62,7 +63,7 @@ export function ViewToggle<T extends string>({
title={label}
data-slot={`${dataSlotPrefix}-${value}`}
className={cn(
"focus-visible:ring-ring inline-flex size-10 items-center justify-center rounded-[5px] transition-colors focus-visible:ring-2 focus-visible:outline-none motion-reduce:transition-none sm:size-8",
"focus-visible:ring-ring inline-flex size-11 items-center justify-center rounded-[5px] transition-colors focus-visible:ring-2 focus-visible:outline-none motion-reduce:transition-none sm:size-9",
active
? "bg-secondary text-secondary-foreground shadow-sm"
: "text-muted-foreground hover:text-foreground",