mirror of
https://github.com/MBombeck/HealthLog.git
synced 2026-11-11 13:52:20 +00:00
feat(integrations): say where an imported Google Health history ends
The two status routes publish backfillEnded: each data type whose backfill stopped short of the start of its history, with the instant it reaches back to and whether the horizon or a walk that stopped moving ended it. The integration card lists them in place of an import that would otherwise read as still loading, in all seven locales. Refs #1220
This commit is contained in:
1 parent
72c259d85c
commit
f6b09f07cd
15 files changed
+213
No files matched your search
@@ -137,6 +137,35 @@ describe("<IntegrationCard> per state", () => {
|
||||
);
|
||||
});
|
||||
|
||||
it("history ended short: no longer loading, and says where it ends and why", () => {
|
||||
const html = render(
|
||||
"google-health",
|
||||
connected(healthFixture(), {
|
||||
backfillCompleted: true,
|
||||
backfillEnded: [
|
||||
{
|
||||
type: "PULSE",
|
||||
endsAt: "2024-05-11T08:00:00.000Z",
|
||||
reason: "no_progress",
|
||||
},
|
||||
{
|
||||
type: "WEIGHT",
|
||||
endsAt: "2006-10-17T12:00:00.000Z",
|
||||
reason: "horizon",
|
||||
},
|
||||
],
|
||||
}),
|
||||
);
|
||||
expect(html).not.toContain("Older history is loading");
|
||||
expect(html).toContain('data-testid="google-health-history-ends"');
|
||||
expect(html).toMatch(
|
||||
/data-reason="no_progress"[^>]*>Pulse: imported back to [^<]*2024[^<]*\. Google stopped returning older readings/,
|
||||
);
|
||||
expect(html).toMatch(
|
||||
/data-reason="horizon"[^>]*>Weight: imported back to [^<]*2006[^<]*\. Older readings lie beyond the import limit\./,
|
||||
);
|
||||
});
|
||||
|
||||
it("limited by plan: the sentence names up to three, counts the rest, no button", () => {
|
||||
const html = render(
|
||||
"withings",
|
||||
|
||||
@@ -0,0 +1,46 @@
|
||||
"use client";
|
||||
|
||||
/**
|
||||
* Where an integration's imported history ends, when its backfill stopped
|
||||
* before the beginning of a data type's history (#1220).
|
||||
*
|
||||
* Without it a backfill that could not go further either kept the card
|
||||
* saying older history was loading, forever, or (once it completes) would
|
||||
* leave a gap nobody explained. The server resolves where each type ends and
|
||||
* why; this only words it. Each line is two sentences about the user's own
|
||||
* data, so it reads as body text (`text-sm`, foreground), not as muted meta.
|
||||
*/
|
||||
|
||||
import { MEASUREMENT_TYPE_LABEL_KEYS } from "@/components/measurements/measurement-list-meta";
|
||||
import { useFormatters, useTranslations } from "@/lib/i18n/context";
|
||||
import type { IntegrationStatusViewModel } from "./shared";
|
||||
|
||||
export function HistoryEndsNote({
|
||||
provider,
|
||||
entries,
|
||||
}: {
|
||||
provider: string;
|
||||
entries: IntegrationStatusViewModel["backfillEnded"];
|
||||
}) {
|
||||
const { t } = useTranslations();
|
||||
const fmt = useFormatters();
|
||||
if (!entries?.length) return null;
|
||||
return (
|
||||
<ul className="space-y-1 text-sm" data-testid={`${provider}-history-ends`}>
|
||||
{entries.map((entry) => {
|
||||
const labelKey = MEASUREMENT_TYPE_LABEL_KEYS[entry.type];
|
||||
const values = {
|
||||
type: labelKey ? t(labelKey) : entry.type,
|
||||
date: fmt.date(entry.endsAt),
|
||||
};
|
||||
return (
|
||||
<li key={entry.type} data-reason={entry.reason}>
|
||||
{entry.reason === "horizon"
|
||||
? t("settings.integrationCard.historyEnds.horizon", values)
|
||||
: t("settings.integrationCard.historyEnds.noProgress", values)}
|
||||
</li>
|
||||
);
|
||||
})}
|
||||
</ul>
|
||||
);
|
||||
}
|
||||
@@ -55,6 +55,7 @@ import {
|
||||
IntegrationCard,
|
||||
type IntegrationMenuAction,
|
||||
} from "./integration-card";
|
||||
import { HistoryEndsNote } from "./history-ends-note";
|
||||
import { MetricFreshnessList } from "./metric-freshness-list";
|
||||
import {
|
||||
CallbackMismatchNotice,
|
||||
@@ -505,6 +506,7 @@ export function OAuthIntegrationCardView({
|
||||
) : undefined
|
||||
}
|
||||
>
|
||||
<HistoryEndsNote provider={provider} entries={viewModel?.backfillEnded} />
|
||||
{syncResults ?? (
|
||||
<ActionResultLines provider={provider} sync={sync} test={test} />
|
||||
)}
|
||||
|
||||
@@ -54,6 +54,15 @@ export interface IntegrationStatusViewModel {
|
||||
connected?: boolean;
|
||||
/** WHOOP / Fitbit / Google Health: older history still loading. */
|
||||
backfillCompleted?: boolean | null;
|
||||
/**
|
||||
* Google Health: data types whose history backfill ended before the start
|
||||
* of their history, with where and why.
|
||||
*/
|
||||
backfillEnded?: Array<{
|
||||
type: string;
|
||||
endsAt: string;
|
||||
reason: "horizon" | "no_progress";
|
||||
}>;
|
||||
/**
|
||||
* Polar / Oura / Strava: usable credentials resolve (BYO or the shared
|
||||
* app). Greys out "Connect" when false.
|
||||
|
||||
Reference in new issue
Block a user