mirror of
https://github.com/MBombeck/HealthLog.git
synced 2026-11-13 06:32:03 +00:00
feat(comparison): comparison toggle persists baseline preference
Add a "Compare to" pulldown to the Settings → Dashboard section that lets the user pick None / Last month / Last year. The preference rides on the existing User.dashboardWidgetsJson blob — research §7 Q3 settled on the JSON-pivot path so v1.4.16 ships migration-free. The resolver defaults to "none" for legacy layouts and clamps unknown values back to "none", so a stale client cannot poison the dashboard with a baseline the renderer doesn't know how to draw. The PUT endpoint accepts the new optional comparisonBaseline field; pre-B8 clients keep PUTing without it and see no behavioural change. EN+DE i18n keys land for the toggle label, the three options, the "unavailable" fallback, and the chart caption — Vormonat / Vorjahr per Marc's brief. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
1 parent
43204cbb86
commit
775df8cfe9
6 files changed
+223
No files matched your search
@@ -16,8 +16,17 @@ import { useTranslations } from "@/lib/i18n/context";
|
||||
import {
|
||||
type DashboardLayout,
|
||||
type DashboardWidgetId,
|
||||
type ComparisonBaseline,
|
||||
COMPARISON_BASELINES,
|
||||
DEFAULT_DASHBOARD_LAYOUT,
|
||||
} from "@/lib/dashboard-layout";
|
||||
import {
|
||||
Select,
|
||||
SelectContent,
|
||||
SelectItem,
|
||||
SelectTrigger,
|
||||
SelectValue,
|
||||
} from "@/components/ui/select";
|
||||
|
||||
const WIDGET_LABEL_KEYS: Record<DashboardWidgetId, string> = {
|
||||
weight: "dashboard.weight",
|
||||
@@ -115,6 +124,16 @@ export function DashboardLayoutSection({ id }: { id: string }) {
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* v1.4.16 phase B8 — comparison baseline picker. The toggle persists
|
||||
* via the same `/api/dashboard/widgets` PUT the existing layout
|
||||
* controls already use; saving rides through the same `Save` button.
|
||||
*/
|
||||
function setComparisonBaseline(value: ComparisonBaseline) {
|
||||
if (!layout) return;
|
||||
setDraft({ ...layout, comparisonBaseline: value });
|
||||
}
|
||||
|
||||
function move(widgetId: DashboardWidgetId, delta: -1 | 1) {
|
||||
if (!layout) return;
|
||||
const sorted = [...layout.widgets].sort((a, b) => a.order - b.order);
|
||||
@@ -157,6 +176,50 @@ export function DashboardLayoutSection({ id }: { id: string }) {
|
||||
{t("dashboard.customizeSubtitle")}
|
||||
</p>
|
||||
|
||||
{/* v1.4.16 phase B8 — comparison baseline picker. Lives at the top
|
||||
of the section because it changes how every chart + tile below
|
||||
renders, not just one. Mobile-friendly: full-width Select with
|
||||
a 44px tap target on touch viewports per the Wave-C polish. */}
|
||||
{layout && (
|
||||
<div className="space-y-2">
|
||||
<label
|
||||
htmlFor="comparison-baseline"
|
||||
className="text-foreground text-sm font-medium"
|
||||
>
|
||||
{t("comparison.toggleLabel")}
|
||||
</label>
|
||||
<Select
|
||||
value={layout.comparisonBaseline ?? "none"}
|
||||
onValueChange={(value) =>
|
||||
setComparisonBaseline(value as ComparisonBaseline)
|
||||
}
|
||||
disabled={saveMutation.isPending}
|
||||
>
|
||||
<SelectTrigger
|
||||
id="comparison-baseline"
|
||||
className="min-h-11 w-full sm:w-72"
|
||||
data-slot="comparison-baseline-trigger"
|
||||
>
|
||||
<SelectValue />
|
||||
</SelectTrigger>
|
||||
<SelectContent>
|
||||
{COMPARISON_BASELINES.map((value) => (
|
||||
<SelectItem
|
||||
key={value}
|
||||
value={value}
|
||||
data-slot={`comparison-baseline-option-${value}`}
|
||||
>
|
||||
{t(`comparison.baseline.${value}`)}
|
||||
</SelectItem>
|
||||
))}
|
||||
</SelectContent>
|
||||
</Select>
|
||||
<p className="text-muted-foreground text-xs">
|
||||
{t("comparison.toggleHint")}
|
||||
</p>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{isLoading || !layout ? (
|
||||
<div className="text-muted-foreground flex items-center gap-2 text-sm">
|
||||
<Loader2 className="h-4 w-4 animate-spin" />
|
||||
|
||||
Reference in new issue
Block a user