mirror of
https://github.com/MBombeck/HealthLog.git
synced 2026-11-10 13:22:14 +00:00
style: apply prettier across the tree
One-shot prettier pass so the new format:check gate starts from a clean baseline. Formatting only — no behaviour change. Generator-owned and vendored artifacts stay untouched via .prettierignore.
This commit is contained in:
1 parent
b3fea2ef59
commit
1f4ba5939d
1000 files changed
+12130
-8385
No files matched your search
@@ -147,8 +147,6 @@ describe("Settings — DisableCoachCard", () => {
|
||||
const html = render();
|
||||
// v1.18.1 (D7) — polarity flipped to activate/default-on.
|
||||
expect(html).toContain("Activate Coach");
|
||||
expect(html).toContain(
|
||||
"Show the Coach button and drawer. On by default",
|
||||
);
|
||||
expect(html).toContain("Show the Coach button and drawer. On by default");
|
||||
});
|
||||
});
|
||||
@@ -7,10 +7,7 @@
|
||||
*/
|
||||
import { describe, it, expect } from "vitest";
|
||||
|
||||
import {
|
||||
parseOAuthOutcome,
|
||||
oauthReasonKey,
|
||||
} from "../integrations-section";
|
||||
import { parseOAuthOutcome, oauthReasonKey } from "../integrations-section";
|
||||
|
||||
describe("parseOAuthOutcome", () => {
|
||||
it("reads a connected outcome for every OAuth provider", () => {
|
||||
|
||||
@@ -140,9 +140,7 @@ function ResearchModeCard() {
|
||||
}
|
||||
|
||||
return (
|
||||
<SettingsCard
|
||||
data-slot="settings-research-mode-card"
|
||||
>
|
||||
<SettingsCard data-slot="settings-research-mode-card">
|
||||
<SettingsCardHeader
|
||||
icon={BookOpenCheck}
|
||||
title={t("settings.researchMode.sectionTitle")}
|
||||
@@ -254,9 +252,7 @@ function DataResetCard() {
|
||||
}
|
||||
|
||||
return (
|
||||
<SettingsCard
|
||||
data-slot="settings-data-reset-card"
|
||||
>
|
||||
<SettingsCard data-slot="settings-data-reset-card">
|
||||
{/* v1.4.43 QoL (L5) — dropped the `AlertTriangle` icon and
|
||||
neutralised the title colour so the danger-zone shaping is
|
||||
GitHub-style (red CTA only) rather than red-on-red-on-red.
|
||||
@@ -401,9 +397,7 @@ function AccountDeleteCard() {
|
||||
}
|
||||
|
||||
return (
|
||||
<SettingsCard
|
||||
data-slot="settings-account-delete-card"
|
||||
>
|
||||
<SettingsCard data-slot="settings-account-delete-card">
|
||||
<div className="flex flex-col gap-3 sm:flex-row sm:items-start sm:justify-between sm:gap-2">
|
||||
<div className="space-y-1">
|
||||
<h2 className="text-foreground text-lg font-semibold">
|
||||
|
||||
@@ -95,7 +95,9 @@ export function DisableCoachCard({
|
||||
// the activated-state view of it. The SSR default is ON (checked) so the
|
||||
// default-on contract holds before the wire value resolves.
|
||||
const mounted = useMounted();
|
||||
const activated = mounted ? (optimistic ?? !(user?.disableCoach ?? false)) : true;
|
||||
const activated = mounted
|
||||
? (optimistic ?? !(user?.disableCoach ?? false))
|
||||
: true;
|
||||
|
||||
const mutation = useMutation({
|
||||
// `next` is the desired ACTIVATED state; the column stores the inverse.
|
||||
|
||||
@@ -250,11 +250,7 @@ export function FallbackChainCard({
|
||||
</Button>
|
||||
<AlertDialog>
|
||||
<AlertDialogTrigger asChild>
|
||||
<Button
|
||||
size="sm"
|
||||
variant="outline"
|
||||
className="min-h-11 sm:min-h-9"
|
||||
>
|
||||
<Button size="sm" variant="outline" className="min-h-11 sm:min-h-9">
|
||||
<RotateCcw className="h-4 w-4" />
|
||||
{t("settings.ai.providerChain.resetDefaults")}
|
||||
</Button>
|
||||
|
||||
@@ -107,9 +107,7 @@ export function CoachPrefsSection({ isAuthenticated }: CoachPrefsSectionProps) {
|
||||
const isLoading = isAuthenticated && !persisted;
|
||||
|
||||
return (
|
||||
<SettingsCard
|
||||
data-slot="coach-prefs-section"
|
||||
>
|
||||
<SettingsCard data-slot="coach-prefs-section">
|
||||
<SettingsCardHeader
|
||||
icon={SlidersHorizontal}
|
||||
title={t("insights.coach.settingsTitle")}
|
||||
|
||||
@@ -317,10 +317,7 @@ export function DashboardLayoutSection({ id }: { id: string }) {
|
||||
const dirty = draft !== null && layout !== null;
|
||||
|
||||
return (
|
||||
<SettingsCard
|
||||
id={id}
|
||||
className="scroll-mt-28 space-y-4"
|
||||
>
|
||||
<SettingsCard id={id} className="scroll-mt-28 space-y-4">
|
||||
{/* v1.4.19 A6 — title / action row uses the same stack-on-mobile,
|
||||
right-align-on-desktop contract as Account → Password +
|
||||
Restart onboarding tour. Avoids the long German "Auf Standard
|
||||
|
||||
@@ -129,7 +129,11 @@ export function EmailCard({ isAuthenticated }: { isAuthenticated: boolean }) {
|
||||
endpoint="/api/settings/email/test"
|
||||
disabled={!settings?.enabled}
|
||||
/>
|
||||
<Button type="submit" disabled={save.isPending} className="min-h-11">
|
||||
<Button
|
||||
type="submit"
|
||||
disabled={save.isPending}
|
||||
className="min-h-11"
|
||||
>
|
||||
{save.isPending && (
|
||||
<Loader2 className="h-3.5 w-3.5 animate-spin motion-reduce:animate-none" />
|
||||
)}
|
||||
|
||||
@@ -103,10 +103,7 @@ function ImportCardShell({
|
||||
children,
|
||||
}: ImportCardShellProps) {
|
||||
return (
|
||||
<SettingsCard
|
||||
data-testid={testId}
|
||||
className="flex h-full flex-col"
|
||||
>
|
||||
<SettingsCard data-testid={testId} className="flex h-full flex-col">
|
||||
<div className="flex items-center gap-2">
|
||||
<Icon
|
||||
className="text-muted-foreground h-5 w-5 shrink-0"
|
||||
@@ -727,12 +724,15 @@ function CsvImportCard() {
|
||||
}
|
||||
setBusy(true);
|
||||
try {
|
||||
const res = await apiFetchRaw(`/api/import/csv${dryRun ? "?dryRun=1" : ""}`, {
|
||||
method: "POST",
|
||||
headers: { "Content-Type": "text/csv" },
|
||||
credentials: "include",
|
||||
body: text,
|
||||
});
|
||||
const res = await apiFetchRaw(
|
||||
`/api/import/csv${dryRun ? "?dryRun=1" : ""}`,
|
||||
{
|
||||
method: "POST",
|
||||
headers: { "Content-Type": "text/csv" },
|
||||
credentials: "include",
|
||||
body: text,
|
||||
},
|
||||
);
|
||||
if (res.status === 429) {
|
||||
setError(t("settings.sections.export.import.csv.rateLimited"));
|
||||
return;
|
||||
|
||||
@@ -106,7 +106,11 @@ export function parseOAuthOutcome(search: string): OAuthOutcome | null {
|
||||
const v = params.get(provider);
|
||||
if (v === "connected") return { provider, kind: "connected" };
|
||||
if (v === "error") {
|
||||
return { provider, kind: "error", reason: params.get("reason") ?? "unknown" };
|
||||
return {
|
||||
provider,
|
||||
kind: "error",
|
||||
reason: params.get("reason") ?? "unknown",
|
||||
};
|
||||
}
|
||||
}
|
||||
return null;
|
||||
@@ -218,7 +222,9 @@ export function ConnectionsPanel() {
|
||||
toast.success(t(`settings.${provider}OauthConnected`));
|
||||
// Polar/Oura own a per-card status query; WHOOP/Fitbit read off the
|
||||
// consolidated envelope — invalidate both so the card repaints either way.
|
||||
queryClient.invalidateQueries({ queryKey: OAUTH_OUTCOME_KEYS[provider]() });
|
||||
queryClient.invalidateQueries({
|
||||
queryKey: OAUTH_OUTCOME_KEYS[provider](),
|
||||
});
|
||||
queryClient.invalidateQueries({
|
||||
queryKey: queryKeys.integrationsStatus(),
|
||||
});
|
||||
|
||||
@@ -189,9 +189,7 @@ export function FitbitCard({
|
||||
: null;
|
||||
|
||||
return (
|
||||
<SettingsCard
|
||||
data-testid="fitbit-card"
|
||||
>
|
||||
<SettingsCard data-testid="fitbit-card">
|
||||
<SettingsCardHeader
|
||||
icon={HeartPulse}
|
||||
title={t("settings.fitbit")}
|
||||
|
||||
@@ -10,7 +10,14 @@
|
||||
import { useRef, useState } from "react";
|
||||
import Link from "next/link";
|
||||
import { useMutation, useQuery, useQueryClient } from "@tanstack/react-query";
|
||||
import { ArrowRight, Droplet, Link2, Loader2, Save, Unlink } from "lucide-react";
|
||||
import {
|
||||
ArrowRight,
|
||||
Droplet,
|
||||
Link2,
|
||||
Loader2,
|
||||
Save,
|
||||
Unlink,
|
||||
} from "lucide-react";
|
||||
|
||||
import {
|
||||
AlertDialog,
|
||||
@@ -37,7 +44,11 @@ import {
|
||||
import { TestConnectionButton } from "@/components/settings/test-connection-button";
|
||||
import { apiFetchRaw, apiGet, apiPost } from "@/lib/api/api-fetch";
|
||||
import { useTranslations } from "@/lib/i18n/context";
|
||||
import { invalidateKeys, measurementDependentKeys, queryKeys } from "@/lib/query-keys";
|
||||
import {
|
||||
invalidateKeys,
|
||||
measurementDependentKeys,
|
||||
queryKeys,
|
||||
} from "@/lib/query-keys";
|
||||
|
||||
import { IntegrationErrorMessage } from "./shared";
|
||||
import { IntegrationCardDescription } from "./setup-guide-link";
|
||||
@@ -47,14 +58,21 @@ interface NightscoutStatus {
|
||||
configured: boolean;
|
||||
hasToken?: boolean;
|
||||
allowPrivateHost?: boolean;
|
||||
state?: "connected" | "error_transient" | "error_reauth" | "disconnected" | "parked";
|
||||
state?:
|
||||
| "connected"
|
||||
| "error_transient"
|
||||
| "error_reauth"
|
||||
| "disconnected"
|
||||
| "parked";
|
||||
lastSuccessAt?: string | null;
|
||||
lastAttemptAt?: string | null;
|
||||
lastError?: string | null;
|
||||
}
|
||||
|
||||
/** Map the shared ledger state onto the pill's display state. */
|
||||
function pillStateFor(status: NightscoutStatus | undefined): IntegrationPillState {
|
||||
function pillStateFor(
|
||||
status: NightscoutStatus | undefined,
|
||||
): IntegrationPillState {
|
||||
if (!status?.connected) return "disconnected";
|
||||
switch (status.state) {
|
||||
case "parked":
|
||||
@@ -159,9 +177,7 @@ export function NightscoutCard({ enabled = true }: { enabled?: boolean }) {
|
||||
: null;
|
||||
|
||||
return (
|
||||
<SettingsCard
|
||||
data-testid="nightscout-card"
|
||||
>
|
||||
<SettingsCard data-testid="nightscout-card">
|
||||
<SettingsCardHeader
|
||||
icon={Droplet}
|
||||
title={t("settings.nightscout")}
|
||||
@@ -226,7 +242,9 @@ export function NightscoutCard({ enabled = true }: { enabled?: boolean }) {
|
||||
|
||||
<form ref={formRef} onSubmit={handleConnect} className="space-y-3">
|
||||
<div className="space-y-1.5">
|
||||
<Label htmlFor="nightscout-url">{t("settings.nightscoutUrl")}</Label>
|
||||
<Label htmlFor="nightscout-url">
|
||||
{t("settings.nightscoutUrl")}
|
||||
</Label>
|
||||
<Input
|
||||
id="nightscout-url"
|
||||
type="url"
|
||||
|
||||
@@ -11,7 +11,14 @@
|
||||
import { useState } from "react";
|
||||
import Link from "next/link";
|
||||
import { useMutation, useQuery, useQueryClient } from "@tanstack/react-query";
|
||||
import { ArrowRight, Link2, Loader2, Save, Unlink, type LucideIcon } from "lucide-react";
|
||||
import {
|
||||
ArrowRight,
|
||||
Link2,
|
||||
Loader2,
|
||||
Save,
|
||||
Unlink,
|
||||
type LucideIcon,
|
||||
} from "lucide-react";
|
||||
|
||||
import {
|
||||
AlertDialog,
|
||||
@@ -64,7 +71,9 @@ export interface OAuthProviderStatus {
|
||||
lastError?: string | null;
|
||||
}
|
||||
|
||||
function pillStateFor(status: OAuthProviderStatus | undefined): IntegrationPillState {
|
||||
function pillStateFor(
|
||||
status: OAuthProviderStatus | undefined,
|
||||
): IntegrationPillState {
|
||||
if (!status?.connected) return "disconnected";
|
||||
switch (status.state) {
|
||||
case "parked":
|
||||
@@ -202,9 +211,7 @@ export function OAuthProviderCard({
|
||||
}
|
||||
|
||||
return (
|
||||
<SettingsCard
|
||||
data-testid={`${provider}-card`}
|
||||
>
|
||||
<SettingsCard data-testid={`${provider}-card`}>
|
||||
<SettingsCardHeader
|
||||
icon={icon}
|
||||
title={t(`${i18nPrefix}`)}
|
||||
|
||||
@@ -20,8 +20,7 @@ import { useTranslations } from "@/lib/i18n/context";
|
||||
* path segment, e.g. `https://docs.healthlog.dev/integrations/whoop`. Kept as
|
||||
* a single constant so the host never drifts across cards.
|
||||
*/
|
||||
export const INTEGRATION_DOCS_BASE =
|
||||
"https://docs.healthlog.dev/integrations";
|
||||
export const INTEGRATION_DOCS_BASE = "https://docs.healthlog.dev/integrations";
|
||||
|
||||
export type IntegrationDocsProvider =
|
||||
| "whoop"
|
||||
|
||||
@@ -101,7 +101,8 @@ export function LowStockCard({
|
||||
prefs?.medication?.reorderLeadDays === undefined
|
||||
? DEFAULT_LEAD
|
||||
: prefs.medication.reorderLeadDays;
|
||||
const leadDays = leadOptimistic === undefined ? persistedLead : leadOptimistic;
|
||||
const leadDays =
|
||||
leadOptimistic === undefined ? persistedLead : leadOptimistic;
|
||||
|
||||
async function patchPrefs(
|
||||
body: Record<string, unknown>,
|
||||
|
||||
@@ -80,10 +80,7 @@ export function MedicationsSection() {
|
||||
<div className="space-y-6">
|
||||
{/* View preference — cards vs table. The shared header toggle
|
||||
writes optimistically, so there is no Save button here. */}
|
||||
<SettingsCard
|
||||
id="medications-view"
|
||||
className="scroll-mt-28 space-y-4"
|
||||
>
|
||||
<SettingsCard id="medications-view" className="scroll-mt-28 space-y-4">
|
||||
<SettingsCardHeader
|
||||
icon={LayoutGrid}
|
||||
title={t("medications.viewToggleLabel")}
|
||||
@@ -101,10 +98,7 @@ export function MedicationsSection() {
|
||||
</SettingsCard>
|
||||
|
||||
{/* Manual order — applies to both list views. */}
|
||||
<SettingsCard
|
||||
id="medications-order"
|
||||
className="scroll-mt-28 space-y-4"
|
||||
>
|
||||
<SettingsCard id="medications-order" className="scroll-mt-28 space-y-4">
|
||||
<SettingsCardHeader
|
||||
icon={Pill}
|
||||
title={t("medications.reorderTitle")}
|
||||
|
||||
@@ -156,10 +156,7 @@ export function MoodReminderCard({
|
||||
}
|
||||
|
||||
return (
|
||||
<SettingsCard
|
||||
as="section"
|
||||
aria-labelledby="settings-mood-reminder-title"
|
||||
>
|
||||
<SettingsCard as="section" aria-labelledby="settings-mood-reminder-title">
|
||||
<SettingsCardHeader
|
||||
icon={SmilePlus}
|
||||
title={t("notifications.moodReminder.title")}
|
||||
|
||||
@@ -46,10 +46,7 @@ export function MoodSection() {
|
||||
return (
|
||||
<div className="space-y-6">
|
||||
{/* Groups — picker group order + own groups. */}
|
||||
<SettingsCard
|
||||
id="mood-groups"
|
||||
className="scroll-mt-28 space-y-4"
|
||||
>
|
||||
<SettingsCard id="mood-groups" className="scroll-mt-28 space-y-4">
|
||||
<SettingsCardHeader
|
||||
icon={Folders}
|
||||
title={t("mood.manage.groupsTitle")}
|
||||
@@ -58,19 +55,13 @@ export function MoodSection() {
|
||||
</SettingsCard>
|
||||
|
||||
{/* Tags — visibility, order, edit, move, create. */}
|
||||
<SettingsCard
|
||||
id="mood-tags"
|
||||
className="scroll-mt-28 space-y-4"
|
||||
>
|
||||
<SettingsCard id="mood-tags" className="scroll-mt-28 space-y-4">
|
||||
<SettingsCardHeader icon={Tags} title={t("mood.manage.tagsTitle")} />
|
||||
{isLoading || !catalog ? loading : <TagManagerCard catalog={catalog} />}
|
||||
</SettingsCard>
|
||||
|
||||
{/* Archived custom tags — restore / hard delete. */}
|
||||
<SettingsCard
|
||||
id="mood-archived"
|
||||
className="scroll-mt-28 space-y-4"
|
||||
>
|
||||
<SettingsCard id="mood-archived" className="scroll-mt-28 space-y-4">
|
||||
<SettingsCardHeader
|
||||
icon={Archive}
|
||||
title={t("mood.manage.archivedTitle")}
|
||||
|
||||
@@ -129,9 +129,7 @@ export function NotificationStatusCard() {
|
||||
const channels = Array.isArray(data) ? data : [];
|
||||
if (channels.length === 0) {
|
||||
return (
|
||||
<SettingsCard
|
||||
data-testid="notification-status-empty"
|
||||
>
|
||||
<SettingsCard data-testid="notification-status-empty">
|
||||
<SettingsCardHeader
|
||||
icon={Bell}
|
||||
title={t("settings.notificationStatus.title")}
|
||||
|
||||
@@ -59,8 +59,7 @@ interface SettingsSectionFrameSlugProps extends SettingsSectionFrameBaseProps {
|
||||
* an optional heading id. This lets `ModuleSettingsFrame` delegate here so
|
||||
* every settings page shares one heading frame.
|
||||
*/
|
||||
interface SettingsSectionFrameExplicitProps
|
||||
extends SettingsSectionFrameBaseProps {
|
||||
interface SettingsSectionFrameExplicitProps extends SettingsSectionFrameBaseProps {
|
||||
title: string;
|
||||
subtitle: string;
|
||||
/** Heading element id (defaults to a stable slug-less id). */
|
||||
|
||||
@@ -430,7 +430,9 @@ export function SettingsShell({
|
||||
{/* v1.18.6.1 — on mobile the heading sits above the chip strip; on
|
||||
desktop it is rendered inside the grid (row 1 / content column) so
|
||||
it does not paint twice. */}
|
||||
{headingBlock ? <div className="mb-4 md:hidden">{headingBlock}</div> : null}
|
||||
{headingBlock ? (
|
||||
<div className="mb-4 md:hidden">{headingBlock}</div>
|
||||
) : null}
|
||||
|
||||
{/* Mobile section strip — horizontal scroll, hidden on md+.
|
||||
`no-scrollbar` (defined in `globals.css`) suppresses the
|
||||
|
||||
@@ -270,9 +270,7 @@ function ShareLinksCard() {
|
||||
</p>
|
||||
</div>
|
||||
<div className="space-y-1.5">
|
||||
<Label htmlFor="share-expiry">
|
||||
{t("settings.sharing.expiry")}
|
||||
</Label>
|
||||
<Label htmlFor="share-expiry">{t("settings.sharing.expiry")}</Label>
|
||||
<Input
|
||||
id="share-expiry"
|
||||
type="number"
|
||||
|
||||
@@ -116,10 +116,7 @@ export function ThresholdsEditorSection({ id }: { id: string }) {
|
||||
});
|
||||
|
||||
return (
|
||||
<SettingsCard
|
||||
id={id}
|
||||
className="scroll-mt-28 space-y-4"
|
||||
>
|
||||
<SettingsCard id={id} className="scroll-mt-28 space-y-4">
|
||||
{/* v1.4.19 A8 / F-07: page header `settings.sections.thresholds.*`
|
||||
already provides the title + description for this route, so the
|
||||
card-level title + subtitle were a duplicate of the page header.
|
||||
|
||||
@@ -43,7 +43,9 @@ export function WebhookCard({ isAuthenticated }: { isAuthenticated: boolean }) {
|
||||
});
|
||||
|
||||
// React-recommended sync-from-server pattern (no setState-in-effect).
|
||||
const settingsKey = settings ? `${settings.url}|${settings.headerName}` : null;
|
||||
const settingsKey = settings
|
||||
? `${settings.url}|${settings.headerName}`
|
||||
: null;
|
||||
const [seededKey, setSeededKey] = useState<string | null>(null);
|
||||
if (settingsKey && settingsKey !== seededKey) {
|
||||
setSeededKey(settingsKey);
|
||||
@@ -161,7 +163,11 @@ export function WebhookCard({ isAuthenticated }: { isAuthenticated: boolean }) {
|
||||
endpoint="/api/settings/webhook/test"
|
||||
disabled={!settings?.enabled}
|
||||
/>
|
||||
<Button type="submit" disabled={save.isPending} className="min-h-11">
|
||||
<Button
|
||||
type="submit"
|
||||
disabled={save.isPending}
|
||||
className="min-h-11"
|
||||
>
|
||||
{save.isPending && (
|
||||
<Loader2 className="h-3.5 w-3.5 animate-spin motion-reduce:animate-none" />
|
||||
)}
|
||||
|
||||
Reference in new issue
Block a user