merge(reconcile): g3-symmetry audit findings

This commit is contained in:
Marc-André Bombeck committed 2026-06-16 01:49:20 +02:00
commit 69fe67e2de
8 files changed
+70 -89

No files matched your search

@@ -109,14 +109,14 @@ export function RecentWorkoutsTile() {
>
<div className="flex items-center justify-between gap-3">
<div className="flex items-center gap-2">
<Activity className="text-primary size-4" aria-hidden="true" />
<Activity className="text-muted-foreground size-4" aria-hidden="true" />
<h2 className="text-sm font-semibold">
{t("dashboard.recentWorkouts.title")}
</h2>
</div>
<Link
href="/insights/workouts"
className="text-muted-foreground hover:text-foreground text-xs"
className="text-muted-foreground hover:text-foreground inline-flex min-h-11 items-center text-xs"
>
{t("dashboard.recentWorkouts.viewAll")}
</Link>
@@ -115,14 +115,14 @@ export function RecentAchievementsCard() {
>
<div className="flex items-center justify-between gap-3">
<div className="flex items-center gap-2">
<Trophy className="text-primary h-4 w-4" aria-hidden="true" />
<Trophy className="text-muted-foreground h-4 w-4" aria-hidden="true" />
<h2 className="text-sm font-semibold">
{t("achievements.dashboardCard.title")}
</h2>
</div>
<Link
href="/achievements"
className="text-primary hover:text-primary/80 inline-flex min-h-11 items-center text-xs font-medium underline-offset-4 hover:underline"
className="text-muted-foreground hover:text-foreground inline-flex min-h-11 items-center text-xs"
>
{t("achievements.dashboardCard.viewAll")}
</Link>
@@ -13,9 +13,10 @@
* relative to "now" but never recomputes the cadence.
*/
import { useState } from "react";
import { CheckCircle2, Plus } from "lucide-react";
import { CalendarClock, CheckCircle2, Plus } from "lucide-react";
import { useTranslations } from "@/lib/i18n/context";
import { SettingsCardHeader } from "@/components/settings/_card-header";
import { DeleteButton } from "@/components/data-list";
import { Badge } from "@/components/ui/badge";
import { Button } from "@/components/ui/button";
@@ -120,28 +121,23 @@ export function VorsorgeSection({ enabled = true }: { enabled?: boolean }) {
aria-labelledby="vorsorge-section-title"
className="space-y-4"
>
<header className="flex items-center justify-between gap-2">
<div className="space-y-1">
<h2
id="vorsorge-section-title"
className="text-lg font-semibold"
<SettingsCardHeader
icon={CalendarClock}
titleId="vorsorge-section-title"
title={t("measurementReminders.sectionTitle")}
description={t("measurementReminders.sectionDescription")}
status={
<Button
type="button"
variant={showForm ? "outline" : "default"}
className="min-h-11 shrink-0 sm:min-h-9"
onClick={() => setShowForm((v) => !v)}
>
{t("measurementReminders.sectionTitle")}
</h2>
<p className="text-muted-foreground text-sm">
{t("measurementReminders.sectionDescription")}
</p>
</div>
<Button
type="button"
size="sm"
variant={showForm ? "outline" : "default"}
onClick={() => setShowForm((v) => !v)}
>
<Plus className="mr-1 h-4 w-4" />
{t("measurementReminders.addButton")}
</Button>
</header>
<Plus className="h-4 w-4" />
{t("measurementReminders.addButton")}
</Button>
}
/>
{showForm && (
<Card>
@@ -221,9 +221,12 @@ export function MedicationCard({
const rate7 = display?.short.rate ?? compliance?.compliance7?.rate ?? 0;
const rate30 = display?.long.rate ?? compliance?.compliance30?.rate ?? 0;
const streak = display?.short.streak ?? compliance?.compliance7?.streak ?? 0;
// v1.15.9 — the open dose's server-derived status drives the card's green
// take-window highlight + the overdue / heavily-overdue top line. Defaults
// to "upcoming" (calm) when the display block is absent (older mocks).
// v1.15.9 — the open dose's server-derived status drives a discreet status
// line/glyph (text + icon only) and the overdue / heavily-overdue top line.
// Per the neutral med-card rule there is NO green take-window highlight and
// no grade-tinted card chrome — status is conveyed by a calm line, never a
// background/border tint. Defaults to "upcoming" (calm) when the display
// block is absent (older mocks).
const doseStatus = display?.currentDose.status ?? "upcoming";
const categoryLabel = getMedicationCategoryLabel(medication.category, t);
const sortedSchedules = [...medication.schedules].sort(
+14 -12
View File
@@ -32,12 +32,14 @@ import {
Loader2,
Pill,
Waves,
type LucideIcon,
} from "lucide-react";
import { Button } from "@/components/ui/button";
import { DateInput } from "@/components/ui/date-input";
import { Label } from "@/components/ui/label";
import { Switch } from "@/components/ui/switch";
import { SettingsCardHeader } from "@/components/settings/_card-header";
import { ImportPanel } from "@/components/settings/import-panel";
import { queryKeys } from "@/lib/query-keys";
import { useTranslations } from "@/lib/i18n/context";
@@ -120,7 +122,7 @@ export function ExportSection() {
interface ExportCardShellProps {
testId: string;
icon: React.ComponentType<{ className?: string }>;
icon: LucideIcon;
title: string;
description: string;
format: ExportFormat;
@@ -146,16 +148,16 @@ function ExportCardShell({
data-testid={testId}
className={`bg-card border-border flex h-full flex-col rounded-xl border p-4 sm:p-6${outerClassName ? ` ${outerClassName}` : ""}`}
>
<div className="flex items-start justify-between gap-3">
<div className="flex items-center gap-2">
<Icon className="text-muted-foreground h-5 w-5 shrink-0" />
<h2 className="text-base font-semibold">{title}</h2>
</div>
<span className="border-border text-muted-foreground rounded-full border px-2 py-0.5 text-[11px] font-medium tracking-wide uppercase">
{format}
</span>
</div>
<p className="text-muted-foreground mt-1 text-xs">{description}</p>
<SettingsCardHeader
icon={Icon}
title={title}
description={description}
status={
<span className="border-border text-muted-foreground rounded-full border px-2 py-0.5 text-[11px] font-medium tracking-wide uppercase">
{format}
</span>
}
/>
{children && <div className="mt-3 space-y-3">{children}</div>}
<div className="mt-4 flex flex-wrap items-center gap-3">{footer}</div>
</div>
@@ -220,7 +222,7 @@ function buildQueryString(base: Record<string, string | undefined>): string {
interface CsvCardProps {
testId: string;
actionTestId: string;
icon: React.ComponentType<{ className?: string }>;
icon: LucideIcon;
titleKey: string;
descriptionKey: string;
endpoint: string;
@@ -24,6 +24,7 @@
import { useId, useState } from "react";
import { ChevronDown, Download, FileText, Loader2 } from "lucide-react";
import { SettingsCardHeader } from "@/components/settings/_card-header";
import { Button } from "@/components/ui/button";
import { Label } from "@/components/ui/label";
import { Input } from "@/components/ui/input";
@@ -202,21 +203,13 @@ export function HealthRecordExportPanel() {
"relative isolate overflow-hidden rounded-xl p-4 sm:p-5",
)}
>
<div className="mb-1 flex items-center gap-2">
<FileText
className="text-muted-foreground h-5 w-5 shrink-0"
aria-hidden="true"
/>
<h2
id="health-record-export-title"
className="text-lg font-semibold tracking-tight sm:text-xl"
>
{t("settings.healthRecord.title")}
</h2>
</div>
<p className="text-muted-foreground mb-4 max-w-2xl text-sm leading-snug">
{t("settings.healthRecord.description")}
</p>
<SettingsCardHeader
className="mb-4"
icon={FileText}
titleId="health-record-export-title"
title={t("settings.healthRecord.title")}
description={t("settings.healthRecord.description")}
/>
<div className="space-y-4">
{/* Format + range share a row on desktop so the panel opens
+10 -15
View File
@@ -8,6 +8,7 @@ import {
type ReorderMedication,
} from "@/components/medications/medication-order-editor";
import { MedicationViewToggle } from "@/components/medications/medication-view-toggle";
import { SettingsCardHeader } from "@/components/settings/_card-header";
import { InjectionSitesCard } from "@/components/settings/injection-sites-card";
import { useAuth } from "@/hooks/use-auth";
import { apiGet } from "@/lib/api/api-fetch";
@@ -90,12 +91,10 @@ export function MedicationsSection() {
id="medications-view"
className="bg-card border-border scroll-mt-28 space-y-4 rounded-xl border p-4 sm:p-6"
>
<div className="flex items-center gap-2">
<LayoutGrid className="text-muted-foreground h-5 w-5" />
<h2 className="text-lg font-semibold">
{t("medications.viewToggleLabel")}
</h2>
</div>
<SettingsCardHeader
icon={LayoutGrid}
title={t("medications.viewToggleLabel")}
/>
<div className="border-border bg-background/30 flex min-h-12 items-center justify-between gap-3 rounded-md border px-3 py-2">
<p className="text-muted-foreground min-w-0 text-xs">
{t("medications.viewToggleHint")}
@@ -113,15 +112,11 @@ export function MedicationsSection() {
id="medications-order"
className="bg-card border-border scroll-mt-28 space-y-4 rounded-xl border p-4 sm:p-6"
>
<div className="flex items-center gap-2">
<Pill className="text-muted-foreground h-5 w-5" />
<h2 className="text-lg font-semibold">
{t("medications.reorderTitle")}
</h2>
</div>
<p className="text-muted-foreground text-xs">
{t("medications.reorderDescription")}
</p>
<SettingsCardHeader
icon={Pill}
title={t("medications.reorderTitle")}
description={t("medications.reorderDescription")}
/>
{isLoading || isLayoutLoading ? (
<div className="text-muted-foreground flex items-center gap-2 text-sm">
<Loader2 className="h-4 w-4 animate-spin motion-reduce:animate-none" />
+10 -18
View File
@@ -2,6 +2,7 @@
import { Archive, Folders, Loader2, Tags } from "lucide-react";
import { SettingsCardHeader } from "@/components/settings/_card-header";
import { ArchivedTagsCard } from "@/components/mood/manage/archived-tags-card";
import { TagGroupsCard } from "@/components/mood/manage/tag-groups-card";
import { TagManagerCard } from "@/components/mood/manage/tag-manager-card";
@@ -58,12 +59,10 @@ export function MoodSection() {
id="mood-groups"
className="bg-card border-border scroll-mt-28 space-y-4 rounded-xl border p-4 sm:p-6"
>
<div className="flex items-center gap-2">
<Folders className="text-muted-foreground h-5 w-5" />
<h2 className="text-lg font-semibold">
{t("mood.manage.groupsTitle")}
</h2>
</div>
<SettingsCardHeader
icon={Folders}
title={t("mood.manage.groupsTitle")}
/>
{isLoading || !catalog ? loading : <TagGroupsCard catalog={catalog} />}
</div>
@@ -72,12 +71,7 @@ export function MoodSection() {
id="mood-tags"
className="bg-card border-border scroll-mt-28 space-y-4 rounded-xl border p-4 sm:p-6"
>
<div className="flex items-center gap-2">
<Tags className="text-muted-foreground h-5 w-5" />
<h2 className="text-lg font-semibold">
{t("mood.manage.tagsTitle")}
</h2>
</div>
<SettingsCardHeader icon={Tags} title={t("mood.manage.tagsTitle")} />
{isLoading || !catalog ? loading : <TagManagerCard catalog={catalog} />}
</div>
@@ -86,12 +80,10 @@ export function MoodSection() {
id="mood-archived"
className="bg-card border-border scroll-mt-28 space-y-4 rounded-xl border p-4 sm:p-6"
>
<div className="flex items-center gap-2">
<Archive className="text-muted-foreground h-5 w-5" />
<h2 className="text-lg font-semibold">
{t("mood.manage.archivedTitle")}
</h2>
</div>
<SettingsCardHeader
icon={Archive}
title={t("mood.manage.archivedTitle")}
/>
{isLoading || !catalog ? (
loading
) : (