mirror of
https://github.com/MBombeck/HealthLog.git
synced 2026-11-08 04:32:42 +00:00
merge(reconcile): g3-symmetry audit findings
This commit is contained in:
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(
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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" />
|
||||
|
||||
@@ -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
|
||||
) : (
|
||||
|
||||
Reference in new issue
Block a user