+ // v1.18.10 — body block rhythm matches the sibling insights cards
+ // (`correlation-card` uses `space-y-3` between its chart /
+ // interpretation / source blocks). The briefing card carried a
+ // looser `space-y-4`, which read as taller than its neighbours on
+ // the overview. One token, no new spacing scale.
+
{/* v1.4.27 B1 — the leading narrative paragraph dropped.
The hero strip subtitle on `/insights` already renders
the same `briefing.paragraph` text directly above this
diff --git a/src/components/insights/use-insights-advisor.ts b/src/components/insights/use-insights-advisor.ts
index 64dd690d2..53deb92cd 100644
--- a/src/components/insights/use-insights-advisor.ts
+++ b/src/components/insights/use-insights-advisor.ts
@@ -330,6 +330,15 @@ export function useInsightsAdvisorQuery(
// their query subtree so the per-section text below the advisor card
// re-fetches.
queryClient.invalidateQueries({ queryKey: queryKeys.insightsRoot() });
+ // v1.18.10 — the dashboard hero surfaces the briefing headline from the
+ // SNAPSHOT (server-lifted `User.insightsCachedText`), not from this
+ // advisor cache. A regenerate rewrites that server cache, so invalidate
+ // the snapshot too — otherwise returning to the Startseite shows the
+ // previous briefing under the greeting until the snapshot's own stale
+ // window lapses. Closes "the briefing doesn't update" on the dashboard.
+ queryClient.invalidateQueries({
+ queryKey: queryKeys.dashboardSnapshot(),
+ });
},
});
diff --git a/src/components/measurement-reminders/vorsorge-section.tsx b/src/components/measurement-reminders/vorsorge-section.tsx
index e1c1c59f8..b1a68df33 100644
--- a/src/components/measurement-reminders/vorsorge-section.tsx
+++ b/src/components/measurement-reminders/vorsorge-section.tsx
@@ -770,11 +770,19 @@ function VorsorgeCard({
? t(`measurementReminders.types.${reminder.measurementType}`)
: t("measurementReminders.selfPlanned");
- // v1.18.6.1 — the cadence renders as a chip DIRECTLY to the right of the
- // metric name (the med-card convention: cadence sits next to the name).
- const cadenceChip = cadence ? (
- {cadence}
- ) : null;
+ // v1.18.10 — the cadence reads as part of the METRIC ROW (the category
+ // line), styled like the metric label itself — same muted weight/colour,
+ // no shaded pill. The card heading stays clean (nothing beside it); the
+ // cadence rides next to "Blutdruck" et al. via `metricMeta` below.
+ const cadenceText = cadence || null;
+
+ // v1.18.10 — a measurement-linked reminder's heading links to the
+ // measurements history pre-filtered to its type (the same target the
+ // "Messergebnisse anzeigen" kebab item uses). A free-text reminder has no
+ // readings to show, so it stays a plain (non-linking) heading.
+ const measurementsHref = isLinked
+ ? `/measurements?type=${encodeURIComponent(reminder.measurementType as string)}`
+ : undefined;
// Provenance / disabled state badges keep their own row below the category.
// Omit the row entirely when neither applies (don't render an empty band).
@@ -900,13 +908,15 @@ function VorsorgeCard({
{resolveReminderLabel(reminder, t)}
- {cadenceChip}
+ {/* v1.18.10 — cadence rides on the metric line, styled like the
+ metric text (no shaded pill). */}
{t(`measurementReminders.${due.key}`, { days: due.days })}
{categoryLabel}
+ {cadenceText ? {cadenceText} : null}
@@ -991,7 +1001,13 @@ function VorsorgeCard({
name={resolveReminderLabel(reminder, t)}
dose=""
categoryLabel={categoryLabel}
- nameChip={cadenceChip}
+ metricMeta={cadenceText}
+ href={measurementsHref}
+ linkLabel={
+ measurementsHref
+ ? t("measurementReminders.showMeasurements")
+ : undefined
+ }
stateBadges={stateBadges}
actions={headerActions}
/>
diff --git a/src/components/medications/medication-card-header.tsx b/src/components/medications/medication-card-header.tsx
index 1a7af3740..f3713df2a 100644
--- a/src/components/medications/medication-card-header.tsx
+++ b/src/components/medications/medication-card-header.tsx
@@ -43,6 +43,15 @@ export interface MedicationCardHeaderProps {
* from `stateBadges` (which ride their own row below the category).
*/
nameChip?: ReactNode;
+ /**
+ * v1.18.10 — inline text rendered DIRECTLY beside the category label on
+ * line 2 (e.g. the Vorsorge cadence "alle 7 Tage"). Unlike `nameChip`
+ * (a chip on the title line) this reads as part of the metric row itself,
+ * inheriting the muted line's weight/colour rather than a shaded pill, so
+ * the cadence sits next to the metric name without an alarming grey
+ * background.
+ */
+ metricMeta?: ReactNode;
stateBadges?: ReactNode;
actions?: ReactNode;
/**
@@ -60,6 +69,7 @@ export function MedicationCardHeader({
dose,
categoryLabel,
nameChip,
+ metricMeta,
stateBadges,
actions,
href,
@@ -81,10 +91,16 @@ export function MedicationCardHeader({
State badges now ride their own row below the category
badge so the canonical row stays two lines on narrow
viewports. */}
-
+
{categoryLabel}
+ {/* v1.18.10 — cadence (and any future metric meta) reads as part of
+ the metric line itself: same muted weight/colour as the row, no
+ shaded pill. */}
+ {metricMeta ? (
+ {metricMeta}
+ ) : null}
{stateBadges ? (
{
queryClient.setQueryData(queryKeys.dashboardWidgets(), saved);
+ // v1.18.10 — the Startseite reads its layout (tile/chart visibility,
+ // order, comparison baseline, hero toggle) from the dashboard
+ // SNAPSHOT, not from this `dashboardWidgets` key. Without an
+ // invalidation the snapshot keeps its cached layout and the dashboard
+ // looks stale until a manual reload. Invalidate the snapshot so the
+ // next visit (or open tab) re-reads the saved layout immediately.
+ queryClient.invalidateQueries({
+ queryKey: queryKeys.dashboardSnapshot(),
+ });
setDraft(null);
toast.success(t("dashboard.layoutSaveSuccess"));
},
@@ -216,6 +225,11 @@ export function DashboardLayoutSection({ id }: { id: string }) {
},
onSuccess: (saved) => {
queryClient.setQueryData(queryKeys.dashboardWidgets(), saved);
+ // v1.18.10 — same snapshot invalidation as the save path so a reset
+ // to defaults reflects on the Startseite without a manual reload.
+ queryClient.invalidateQueries({
+ queryKey: queryKeys.dashboardSnapshot(),
+ });
setDraft(null);
toast.success(t("dashboard.layoutResetSuccess"));
},