feat(integrations): list imported cycle days in the details

The details of the Garmin and Oura cards listed measurement types and
workouts only, so cycle days a wearable imported appeared nowhere, and a
bug report could not say whether any had arrived. The status envelope now
carries a `CYCLE_DAYS` entry per cycle-importing source with the newest
day and the count, and the freshness list renders it. It counts as a
sparse type, so a month between periods never reads as a dead pipe.

Refs #1333
This commit is contained in:
Marc-André Bombeck committed 2026-11-04 22:03:18 +01:00
1 parent afdb8163ce
commit 27a1cd6c2f
19 files changed
+200 -28

No files matched your search

@@ -72,6 +72,25 @@ function connected(keyExpiry: GarminKeyExpiry | null) {
}
describe("GarminCard on the shared card", () => {
it("lists the imported cycle days in the details", () => {
const html = render({
connected: true,
configured: true,
health: healthFixture(),
keyExpiry: null,
metricFreshness: [
{
type: "CYCLE_DAYS",
lastSeenAt: "2026-10-20T12:00:00.000Z",
count: 42,
stale: false,
},
],
});
expect(html).toContain('data-metric="CYCLE_DAYS"');
expect(html).toContain("Cycle days (42)");
});
it("shows the key form inside the shared card while not connected", () => {
const html = render({ connected: false, configured: false, health: null });
expect(html).toContain('data-slot="integration-card"');
@@ -32,6 +32,8 @@ import { cn } from "@/lib/utils";
const WORKOUT_TYPE = "WORKOUTS";
/** The pseudo-type carrying Apple Health medication doses. */
const MEDICATION_DOSE_TYPE = "MEDICATION_DOSE";
/** The pseudo-type carrying the cycle days a wearable imported. */
const CYCLE_DAY_TYPE = "CYCLE_DAYS";
/**
* Turn an unmapped enum name into something readable. `MEASUREMENT_TYPE_LABEL_KEYS`
@@ -67,7 +69,11 @@ export function MetricFreshnessList<
if (!entries?.length) return null;
const reference = now ?? new Date();
const label = (type: string): string => {
const label = (type: string, count?: number): string => {
if (type === CYCLE_DAY_TYPE)
return count === undefined
? t("settings.integrationFreshness.cycleDays")
: t("settings.integrationFreshness.cycleDaysCount", { count });
if (type === WORKOUT_TYPE)
return t("settings.integrationFreshness.workouts");
if (type === MEDICATION_DOSE_TYPE)
@@ -142,7 +148,7 @@ export function MetricFreshnessList<
extra && !entry.stale && "text-foreground",
)}
>
{label(entry.type)}
{label(entry.type, entry.count)}
</span>
</span>
<time