mirror of
https://github.com/MBombeck/HealthLog.git
synced 2026-11-10 21:32:18 +00:00
Merge branch 'fix/v1437-review' into release/v1.43.7
# Conflicts: # src/app/api/analytics/route.ts
This commit is contained in:
commit
45c027aee2
217 files changed
+3636
-2127
No files matched your search
@@ -40,7 +40,7 @@ import {
|
||||
|
||||
export function GrantsGivenCard() {
|
||||
const { t } = useTranslations();
|
||||
const { data, isLoading, isError, refetch } = useAccountGrants();
|
||||
const { data, isLoading, isError, error, refetch } = useAccountGrants();
|
||||
const revoke = useRevokeGrant();
|
||||
|
||||
const given = data?.given ?? [];
|
||||
@@ -62,6 +62,7 @@ export function GrantsGivenCard() {
|
||||
)}
|
||||
{isError && (
|
||||
<QueryErrorCard
|
||||
error={error}
|
||||
title={t("recordSharing.given.loadError")}
|
||||
onRetry={() => void refetch()}
|
||||
/>
|
||||
|
||||
@@ -54,7 +54,7 @@ import {
|
||||
*/
|
||||
export function GrantsReceivedCard() {
|
||||
const { t } = useTranslations();
|
||||
const { data, isLoading, isError, refetch } = useAccountGrants();
|
||||
const { data, isLoading, isError, error, refetch } = useAccountGrants();
|
||||
const accept = useAcceptGrant();
|
||||
const renounce = useRenounceGrant();
|
||||
const switchAccount = useAccountSwitch();
|
||||
@@ -80,6 +80,7 @@ export function GrantsReceivedCard() {
|
||||
)}
|
||||
{isError && (
|
||||
<QueryErrorCard
|
||||
error={error}
|
||||
title={t("recordSharing.received.loadError")}
|
||||
onRetry={() => void refetch()}
|
||||
/>
|
||||
|
||||
@@ -229,6 +229,7 @@ function ManagedProfileRow({
|
||||
shows no destructive control for it. */}
|
||||
{guardians.isError && (
|
||||
<QueryErrorCard
|
||||
error={guardians.error}
|
||||
title={t("recordSharing.managed.rosterLoadError")}
|
||||
onRetry={() => void guardians.refetch()}
|
||||
/>
|
||||
|
||||
@@ -81,6 +81,7 @@ export function ManagedProfileEditForm({
|
||||
if (profile.isError) {
|
||||
return (
|
||||
<QueryErrorCard
|
||||
error={profile.error}
|
||||
title={t("recordSharing.managed.editLoadError")}
|
||||
onRetry={() => void profile.refetch()}
|
||||
/>
|
||||
|
||||
@@ -88,6 +88,7 @@ export function ManagedProfileHandover({
|
||||
if (status.isError) {
|
||||
return (
|
||||
<QueryErrorCard
|
||||
error={status.error}
|
||||
title={t("recordSharing.managed.handover.loadError")}
|
||||
onRetry={() => void status.refetch()}
|
||||
/>
|
||||
|
||||
@@ -39,7 +39,7 @@ import {
|
||||
*/
|
||||
export function RecordActivityCard() {
|
||||
const { t } = useTranslations();
|
||||
const { data, isLoading, isError, refetch } = useRecordActivity();
|
||||
const { data, isLoading, isError, error, refetch } = useRecordActivity();
|
||||
|
||||
const entries = data?.entries ?? [];
|
||||
|
||||
@@ -88,6 +88,7 @@ export function RecordActivityCard() {
|
||||
)}
|
||||
{isError && (
|
||||
<QueryErrorCard
|
||||
error={error}
|
||||
title={t("recordSharing.activity.loadError")}
|
||||
onRetry={() => void refetch()}
|
||||
/>
|
||||
|
||||
@@ -26,7 +26,7 @@ import { NativeSelect } from "@/components/ui/native-select";
|
||||
import { QueryErrorCard } from "@/components/ui/query-error-card";
|
||||
import { SettingsCard } from "@/components/settings/settings-card";
|
||||
import { SettingsCardHeader } from "@/components/settings/_card-header";
|
||||
import { apiFetchRaw } from "@/lib/api/api-fetch";
|
||||
import { apiGet } from "@/lib/api/api-fetch";
|
||||
import { useTranslations } from "@/lib/i18n/context";
|
||||
import { queryKeys } from "@/lib/query-keys";
|
||||
|
||||
@@ -71,15 +71,11 @@ export function AiInsightsCard({
|
||||
data: insightsSettings,
|
||||
isPending: settingsPending,
|
||||
isError: settingsError,
|
||||
error: settingsFailure,
|
||||
refetch: refetchSettings,
|
||||
} = useQuery({
|
||||
queryKey: queryKeys.insightsSettings(),
|
||||
queryFn: async () => {
|
||||
const res = await apiFetchRaw("/api/insights/settings");
|
||||
if (!res.ok) throw new Error("insights settings read failed");
|
||||
const json = await res.json();
|
||||
return json.data as InsightsSettings;
|
||||
},
|
||||
queryFn: () => apiGet<InsightsSettings>("/api/insights/settings"),
|
||||
enabled: isAuthenticated,
|
||||
});
|
||||
|
||||
@@ -87,15 +83,11 @@ export function AiInsightsCard({
|
||||
data: userProvider,
|
||||
isPending: providerPending,
|
||||
isError: providerError,
|
||||
error: providerFailure,
|
||||
refetch: refetchProvider,
|
||||
} = useQuery({
|
||||
queryKey: queryKeys.userAiProvider(),
|
||||
queryFn: async () => {
|
||||
const res = await apiFetchRaw("/api/user/ai-provider");
|
||||
if (!res.ok) throw new Error("user AI provider read failed");
|
||||
const json = await res.json();
|
||||
return json.data as UserAIProvider;
|
||||
},
|
||||
queryFn: () => apiGet<UserAIProvider>("/api/user/ai-provider"),
|
||||
enabled: isAuthenticated,
|
||||
});
|
||||
|
||||
@@ -103,15 +95,11 @@ export function AiInsightsCard({
|
||||
data: chainData,
|
||||
isPending: chainPending,
|
||||
isError: chainError,
|
||||
error: chainFailure,
|
||||
refetch: refetchChain,
|
||||
} = useQuery({
|
||||
queryKey: queryKeys.insightsProviderChain(),
|
||||
queryFn: async () => {
|
||||
const res = await apiFetchRaw("/api/insights/provider-chain");
|
||||
if (!res.ok) throw new Error("insights provider chain read failed");
|
||||
const json = await res.json();
|
||||
return json.data as ProviderChainData;
|
||||
},
|
||||
queryFn: () => apiGet<ProviderChainData>("/api/insights/provider-chain"),
|
||||
enabled: isAuthenticated,
|
||||
});
|
||||
|
||||
@@ -185,7 +173,10 @@ export function AiInsightsCard({
|
||||
}
|
||||
/>
|
||||
{hasReadError ? (
|
||||
<QueryErrorCard onRetry={retryReads} />
|
||||
<QueryErrorCard
|
||||
error={settingsFailure ?? providerFailure ?? chainFailure}
|
||||
onRetry={retryReads}
|
||||
/>
|
||||
) : readsPending ? (
|
||||
<Skeleton className="h-40 w-full rounded-lg" />
|
||||
) : (
|
||||
|
||||
@@ -440,6 +440,7 @@ function ApiTokensCard() {
|
||||
const {
|
||||
data: tokens,
|
||||
isError,
|
||||
error,
|
||||
isLoading,
|
||||
refetch,
|
||||
} = useQuery({
|
||||
@@ -511,7 +512,7 @@ function ApiTokensCard() {
|
||||
{isError ? (
|
||||
// A read failure must not read as "no active tokens" on this
|
||||
// security surface — the §6 fall-through the token list must avoid.
|
||||
<QueryErrorCard onRetry={() => refetch()} />
|
||||
<QueryErrorCard error={error} onRetry={() => refetch()} />
|
||||
) : isLoading ? (
|
||||
<div data-testid="settings-api-tokens-loading">
|
||||
<Skeleton className="h-40 w-full rounded-lg" />
|
||||
|
||||
@@ -79,7 +79,10 @@ export function CoachConversationsMemoryCard({
|
||||
description={t("settings.ai.coachConversations.description")}
|
||||
/>
|
||||
{history.isError ? (
|
||||
<QueryErrorRow onRetry={() => void history.refetch()} />
|
||||
<QueryErrorRow
|
||||
error={history.error}
|
||||
onRetry={() => void history.refetch()}
|
||||
/>
|
||||
) : visible.length === 0 ? (
|
||||
<p className="text-muted-foreground text-sm">
|
||||
{history.isLoading
|
||||
|
||||
@@ -326,6 +326,7 @@ export function CoachMemorySection({
|
||||
|
||||
{query.isError && (
|
||||
<QueryErrorRow
|
||||
error={query.error}
|
||||
message={t("settings.ai.coachMemory.loadError")}
|
||||
onRetry={() => query.refetch()}
|
||||
/>
|
||||
|
||||
@@ -188,6 +188,7 @@ export function CoachRemindersSection({
|
||||
|
||||
{query.isError && (
|
||||
<QueryErrorRow
|
||||
error={query.error}
|
||||
message={t("settings.ai.coachReminders.loadError")}
|
||||
onRetry={() => query.refetch()}
|
||||
/>
|
||||
|
||||
@@ -10,6 +10,7 @@
|
||||
import { useQuery } from "@tanstack/react-query";
|
||||
|
||||
import { apiGet } from "@/lib/api/api-fetch";
|
||||
import type { GoogleHealthBackfillEndedEntry } from "@/lib/google-health/backfill-progress";
|
||||
import type { ConnectionHealth } from "@/lib/integrations/connection-health";
|
||||
import type { MetricFreshnessEntry } from "@/lib/integrations/sync-verdict";
|
||||
import { queryKeys } from "@/lib/query-keys";
|
||||
@@ -58,11 +59,7 @@ export interface IntegrationStatusViewModel {
|
||||
* 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";
|
||||
}>;
|
||||
backfillEnded?: GoogleHealthBackfillEndedEntry[];
|
||||
/**
|
||||
* Polar / Oura / Strava: usable credentials resolve (BYO or the shared
|
||||
* app). Greys out "Connect" when false.
|
||||
|
||||
@@ -73,7 +73,12 @@ export function ManagedIntegrationStatus() {
|
||||
});
|
||||
|
||||
if (statusQuery.isError) {
|
||||
return <QueryErrorCard onRetry={() => void statusQuery.refetch()} />;
|
||||
return (
|
||||
<QueryErrorCard
|
||||
error={statusQuery.error}
|
||||
onRetry={() => void statusQuery.refetch()}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
const loading = statusQuery.isLoading || !statusQuery.data;
|
||||
|
||||
@@ -1115,7 +1115,12 @@ export function ManagedRecordSettingsSection({
|
||||
});
|
||||
|
||||
if (query.isError) {
|
||||
return <QueryErrorCard onRetry={() => void query.refetch()} />;
|
||||
return (
|
||||
<QueryErrorCard
|
||||
error={query.error}
|
||||
onRetry={() => void query.refetch()}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
|
||||
@@ -93,6 +93,7 @@ function McpConnectionsCard() {
|
||||
const {
|
||||
data: connections,
|
||||
isError,
|
||||
error,
|
||||
isLoading,
|
||||
refetch,
|
||||
} = useQuery({
|
||||
@@ -134,7 +135,7 @@ function McpConnectionsCard() {
|
||||
|
||||
<div className="space-y-4">
|
||||
{isError ? (
|
||||
<QueryErrorCard onRetry={() => refetch()} />
|
||||
<QueryErrorCard error={error} onRetry={() => refetch()} />
|
||||
) : isLoading ? (
|
||||
<Skeleton className="h-24 w-full rounded-lg" />
|
||||
) : list.length === 0 ? (
|
||||
@@ -285,6 +286,7 @@ function McpTokensCard() {
|
||||
const {
|
||||
data: tokens,
|
||||
isError,
|
||||
error,
|
||||
isLoading,
|
||||
refetch,
|
||||
} = useQuery({
|
||||
@@ -457,7 +459,7 @@ function McpTokensCard() {
|
||||
{t("settings.activeTokensTitle")}
|
||||
</p>
|
||||
{isError ? (
|
||||
<QueryErrorCard onRetry={() => refetch()} />
|
||||
<QueryErrorCard error={error} onRetry={() => refetch()} />
|
||||
) : isLoading ? (
|
||||
<Skeleton className="h-24 w-full rounded-lg" />
|
||||
) : activeTokens.length === 0 ? (
|
||||
|
||||
@@ -354,6 +354,7 @@ export function ScoreSection() {
|
||||
|
||||
{configQuery.isError ? (
|
||||
<QueryErrorCard
|
||||
error={configQuery.error}
|
||||
title={t("settings.sections.score.loadFailed")}
|
||||
onRetry={() => void configQuery.refetch()}
|
||||
/>
|
||||
@@ -444,6 +445,7 @@ export function ScoreSection() {
|
||||
|
||||
{analyticsQuery.isError ? (
|
||||
<QueryErrorRow
|
||||
error={analyticsQuery.error}
|
||||
slot="score-config-eligibility-error"
|
||||
message={t("settings.sections.score.eligibilityFailed")}
|
||||
onRetry={() => void analyticsQuery.refetch()}
|
||||
|
||||
@@ -68,7 +68,7 @@ export function SecurityActivityCard({
|
||||
const [open, setOpen] = useState(false);
|
||||
const regionId = useId();
|
||||
|
||||
const { data, isLoading, isError, refetch } = useQuery({
|
||||
const { data, isLoading, isError, error, refetch } = useQuery({
|
||||
queryKey: queryKeys.securityActivity(),
|
||||
queryFn: () =>
|
||||
apiGet<{ events: ActivityRow[] }>("/api/auth/me/security-activity"),
|
||||
@@ -110,6 +110,7 @@ export function SecurityActivityCard({
|
||||
|
||||
{isError && (
|
||||
<QueryErrorRow
|
||||
error={error}
|
||||
message={t("settings.security.activityLoadError")}
|
||||
onRetry={() => refetch()}
|
||||
/>
|
||||
|
||||
@@ -76,7 +76,7 @@ export function SecuritySessionsCard({
|
||||
const [keptGrants, setKeptGrants] = useState<KeptGrant[]>([]);
|
||||
const revokeGrant = useRevokeGrant();
|
||||
|
||||
const { data, isLoading, isError, refetch } = useQuery({
|
||||
const { data, isLoading, isError, error, refetch } = useQuery({
|
||||
queryKey: queryKeys.sessions(),
|
||||
queryFn: () => apiGet<{ sessions: SessionRow[] }>("/api/auth/me/sessions"),
|
||||
enabled: isAuthenticated,
|
||||
@@ -165,6 +165,7 @@ export function SecuritySessionsCard({
|
||||
|
||||
{isError && (
|
||||
<QueryErrorRow
|
||||
error={error}
|
||||
message={t("settings.security.sessionsLoadError")}
|
||||
onRetry={() => refetch()}
|
||||
/>
|
||||
|
||||
@@ -154,6 +154,7 @@ export function ShareDocumentPicker({
|
||||
</div>
|
||||
) : list.isError ? (
|
||||
<QueryErrorRow
|
||||
error={list.error}
|
||||
message={t("documents.list.loadError")}
|
||||
onRetry={() => list.refetch()}
|
||||
/>
|
||||
|
||||
@@ -111,6 +111,7 @@ function ShareLinksCard() {
|
||||
const {
|
||||
data: links,
|
||||
isError,
|
||||
error,
|
||||
isLoading,
|
||||
refetch,
|
||||
} = useQuery({
|
||||
@@ -168,7 +169,7 @@ function ShareLinksCard() {
|
||||
{isError ? (
|
||||
// A read failure must not read as "no active links" on a clinical
|
||||
// sharing surface — that is the §6 fall-through this card must avoid.
|
||||
<QueryErrorCard onRetry={() => refetch()} />
|
||||
<QueryErrorCard error={error} onRetry={() => refetch()} />
|
||||
) : isLoading ? (
|
||||
<div className="space-y-2" data-testid="share-links-loading">
|
||||
<Skeleton className="h-24 w-full rounded-lg" />
|
||||
|
||||
@@ -40,7 +40,7 @@ export function TrustedDevicesCard({
|
||||
const queryClient = useQueryClient();
|
||||
const [status, setStatus] = useState<string | null>(null);
|
||||
|
||||
const { data, isLoading, isError, refetch } = useQuery({
|
||||
const { data, isLoading, isError, error, refetch } = useQuery({
|
||||
queryKey: queryKeys.trustedDevices(),
|
||||
queryFn: () =>
|
||||
apiGet<{ devices: TrustedDeviceRow[] }>("/api/auth/me/trusted-devices"),
|
||||
@@ -105,6 +105,7 @@ export function TrustedDevicesCard({
|
||||
|
||||
{isError && (
|
||||
<QueryErrorRow
|
||||
error={error}
|
||||
message={t("settings.security.trustedDevices.loadError")}
|
||||
onRetry={() => refetch()}
|
||||
/>
|
||||
|
||||
Reference in new issue
Block a user