From 3048dd663727d54d110ed5274181427722a7a042 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marc-Andr=C3=A9=20Bombeck?= Date: Sun, 10 May 2026 11:05:40 +0200 Subject: [PATCH] style(prettier): sweep formatting across the tree Pre-existing format drift accumulated across v1.4.x; running prettier brings the whole tree back to baseline so future diffs read clean. No behaviour change. Co-Authored-By: Claude Opus 4.7 (1M context) --- e2e/achievements.spec.ts | 10 +- e2e/admin-api-tokens-mobile.spec.ts | 6 +- e2e/ai-provider-dropdown.spec.ts | 8 +- e2e/chart-overlay-controls.spec.ts | 23 +- e2e/sidebar-admin-no-expand.spec.ts | 4 +- src/app/api/analytics/route.ts | 7 +- .../dashboard/chart-overlay-prefs/route.ts | 3 +- src/app/api/dashboard/widgets/route.ts | 4 +- .../api/gamification/achievements/route.ts | 175 ++++++----- src/app/api/insights/feedback/route.ts | 7 +- .../provider-chain/__tests__/route.test.ts | 5 +- .../admin/__tests__/admin-shell.test.tsx | 5 +- .../api-token-overview-responsive.test.tsx | 8 +- src/components/admin/ai-quality-section.tsx | 11 +- .../admin/system-status-section.tsx | 278 +++++++++--------- .../__tests__/chart-empty-state.test.tsx | 4 +- .../__tests__/chart-overlay-controls.test.tsx | 4 +- .../__tests__/health-chart-mini.test.tsx | 9 +- .../__tests__/health-chart-polish.test.tsx | 2 +- .../medication-chart-polish.test.tsx | 7 +- .../medication-overlay-toggles.test.tsx | 5 +- .../__tests__/mood-chart-polish.test.tsx | 2 +- .../charts/chart-overlay-controls.tsx | 6 +- src/components/charts/health-chart.tsx | 11 +- src/components/charts/mini-window.ts | 6 +- src/components/charts/mood-chart.tsx | 12 +- .../__tests__/insight-advisor-card.test.tsx | 4 +- .../insights/__tests__/insights-card.test.tsx | 4 +- .../__tests__/insights-page-hero.test.tsx | 4 +- ...ation-card-confidence-integration.test.tsx | 7 +- .../recommendation-card-confidence.test.tsx | 5 +- .../__tests__/recommendation-card.test.tsx | 4 +- .../recommendation-feedback.test.tsx | 25 +- .../recommendations-grid-integration.test.tsx | 9 +- .../__tests__/recommendations-grid.test.tsx | 10 +- src/components/insights/confidence-meter.tsx | 14 +- .../insights/recommendation-card.tsx | 9 +- .../insights/recommendation-feedback.tsx | 10 +- .../insights/recommendations-grid.tsx | 4 +- .../layout/__tests__/bottom-nav.test.tsx | 10 +- .../__tests__/tour-launcher-keys.test.ts | 5 +- .../__tests__/sections-i18n-parity.test.ts | 22 +- src/components/settings/ai-section.tsx | 16 +- src/hooks/use-chart-overlay-prefs.ts | 4 +- .../ai/__tests__/confidence-schema.test.ts | 8 +- .../ai/__tests__/confidence-wrapper.test.ts | 9 +- .../ai/__tests__/medical-references.test.ts | 12 +- src/lib/ai/__tests__/provider-chain.test.ts | 3 +- src/lib/ai/__tests__/provider-runner.test.ts | 20 +- src/lib/ai/__tests__/rationale-schema.test.ts | 11 +- src/lib/ai/__tests__/resolve-chain.test.ts | 79 +++-- src/lib/ai/feedback-attribution.ts | 3 +- src/lib/ai/medical-references.ts | 16 +- src/lib/ai/provider-chain.ts | 4 +- src/lib/ai/provider-runner.ts | 6 +- src/lib/ai/provider.ts | 5 +- src/lib/ai/types.ts | 4 +- src/lib/auth/profile-update.ts | 4 +- src/lib/charts/comparison-shift.ts | 4 +- src/lib/charts/reduced-motion.ts | 5 +- src/lib/dashboard-layout.ts | 4 +- .../__tests__/achievements.test.ts | 5 +- .../__tests__/expansion-metrics.test.ts | 12 +- src/lib/gamification/achievements.ts | 4 +- .../__tests__/prompt-comparison.test.ts | 6 +- src/lib/insights/prompt.ts | 8 +- .../__tests__/feedback-aggregator.test.ts | 32 +- tests/integration/admin-host-metrics.test.ts | 4 +- tests/integration/bp-in-target.test.ts | 8 +- tests/integration/chart-overlay-prefs.test.ts | 21 +- .../insights-provider-chain-put.test.ts | 8 +- .../insights-provider-chain.test.ts | 21 +- 72 files changed, 508 insertions(+), 606 deletions(-) diff --git a/e2e/achievements.spec.ts b/e2e/achievements.spec.ts index 2f6713548..160ff16ce 100644 --- a/e2e/achievements.spec.ts +++ b/e2e/achievements.spec.ts @@ -115,8 +115,7 @@ test.describe("achievements page renders all four card states", () => { metric: "totalTakenIntakes", category: "medication", titleKey: "achievements.badges.intakeTotal1.title", - descriptionKey: - "achievements.badges.intakeTotal1.description", + descriptionKey: "achievements.badges.intakeTotal1.description", icon: "Pill", format: "count", target: 1, @@ -133,8 +132,7 @@ test.describe("achievements page renders all four card states", () => { metric: "moodDayStreak", category: "mood", titleKey: "achievements.badges.moodStreak7.title", - descriptionKey: - "achievements.badges.moodStreak7.description", + descriptionKey: "achievements.badges.moodStreak7.description", icon: "Smile", format: "days", target: 7, @@ -209,9 +207,7 @@ test.describe("achievements page renders all four card states", () => { // Hidden-locked — opaque placeholder visible. The real title / // description must NOT appear in the rendered HTML. - await expect( - page.getByText("Hidden achievement").first(), - ).toBeVisible(); + await expect(page.getByText("Hidden achievement").first()).toBeVisible(); const html = await page.content(); expect(html).not.toContain("Night owl"); expect(html).not.toContain("between 02:00"); diff --git a/e2e/admin-api-tokens-mobile.spec.ts b/e2e/admin-api-tokens-mobile.spec.ts index edddc1e8f..944e837b5 100644 --- a/e2e/admin-api-tokens-mobile.spec.ts +++ b/e2e/admin-api-tokens-mobile.spec.ts @@ -61,9 +61,9 @@ test.describe("/admin/api-tokens mobile no-overflow", () => { // The mobile card-list must be visible and the desktop table must // be display:none at this viewport. - await expect( - page.getByTestId("admin-tokens-mobile-list"), - ).toBeVisible({ timeout: 10_000 }); + await expect(page.getByTestId("admin-tokens-mobile-list")).toBeVisible({ + timeout: 10_000, + }); const dims = await page.evaluate(() => ({ scrollWidth: document.documentElement.scrollWidth, diff --git a/e2e/ai-provider-dropdown.spec.ts b/e2e/ai-provider-dropdown.spec.ts index d64a38122..ff19ab433 100644 --- a/e2e/ai-provider-dropdown.spec.ts +++ b/e2e/ai-provider-dropdown.spec.ts @@ -99,7 +99,9 @@ test.describe("Settings → AI provider dropdown UX (B2)", () => { // 2c. Switch to Anthropic → Anthropic form rendered. await select.selectOption("anthropic"); - await expect(page.getByTestId("ai-provider-config-anthropic")).toBeVisible(); + await expect( + page.getByTestId("ai-provider-config-anthropic"), + ).toBeVisible(); // 2d. Switch to Local → Local form rendered. await select.selectOption("local"); @@ -108,9 +110,7 @@ test.describe("Settings → AI provider dropdown UX (B2)", () => { // 3. Fallback chain card surfaces with the four chain rows. await expect(page.getByTestId("ai-fallback-chain")).toBeVisible(); for (const slug of ["codex", "openai", "anthropic", "local"]) { - await expect( - page.locator(`[data-chain-row="${slug}"]`), - ).toBeVisible(); + await expect(page.locator(`[data-chain-row="${slug}"]`)).toBeVisible(); } }); }); diff --git a/e2e/chart-overlay-controls.spec.ts b/e2e/chart-overlay-controls.spec.ts index 9ca13e3d3..f428f9acb 100644 --- a/e2e/chart-overlay-controls.spec.ts +++ b/e2e/chart-overlay-controls.spec.ts @@ -76,19 +76,16 @@ test.describe("chart overlay controls", () => { page, }) => { let putRequestCount = 0; - await page.route( - "**/api/dashboard/chart-overlay-prefs", - async (route) => { - if (route.request().method() === "PUT") { - putRequestCount += 1; - } - return route.fulfill({ - status: 200, - contentType: "application/json", - body: JSON.stringify({ data: { saved: true }, error: null }), - }); - }, - ); + await page.route("**/api/dashboard/chart-overlay-prefs", async (route) => { + if (route.request().method() === "PUT") { + putRequestCount += 1; + } + return route.fulfill({ + status: 200, + contentType: "application/json", + body: JSON.stringify({ data: { saved: true }, error: null }), + }); + }); await page.goto("/", { waitUntil: "domcontentloaded" }); diff --git a/e2e/sidebar-admin-no-expand.spec.ts b/e2e/sidebar-admin-no-expand.spec.ts index de0acdf10..bc11d5e23 100644 --- a/e2e/sidebar-admin-no-expand.spec.ts +++ b/e2e/sidebar-admin-no-expand.spec.ts @@ -42,9 +42,7 @@ test.describe("sidebar Admin entry no-expansion", () => { // No sub-section anchors inside the sidebar — those belong to // ``'s in-page nav, not the global sidebar. - const subLinks = sidebar.locator( - "a[href^='/admin/']:not([href='/admin'])", - ); + const subLinks = sidebar.locator("a[href^='/admin/']:not([href='/admin'])"); await expect(subLinks).toHaveCount(0); }); diff --git a/src/app/api/analytics/route.ts b/src/app/api/analytics/route.ts index 3f212fe83..2d4a805a8 100644 --- a/src/app/api/analytics/route.ts +++ b/src/app/api/analytics/route.ts @@ -83,12 +83,7 @@ export const GET = apiHandler(async () => { // for the dashboard tile in a single pair-and-count pass. The // headline (`bpInTargetPct`) reuses the 30-day result so we don't // re-pair the same data twice. See lib/analytics/bp-in-target.ts. - const windows = computeBpInTargetWindows( - sysData, - diaData, - bpTargets, - now, - ); + const windows = computeBpInTargetWindows(sysData, diaData, bpTargets, now); bpInTargetPct = windows.last30Days?.pct ?? null; bpInTargetPct7d = windows.last7Days?.pct ?? null; bpInTargetPct30d = windows.last30Days?.pct ?? null; diff --git a/src/app/api/dashboard/chart-overlay-prefs/route.ts b/src/app/api/dashboard/chart-overlay-prefs/route.ts index 3f75f3f8e..aea567343 100644 --- a/src/app/api/dashboard/chart-overlay-prefs/route.ts +++ b/src/app/api/dashboard/chart-overlay-prefs/route.ts @@ -76,8 +76,7 @@ export const PUT = apiHandler(async (request: NextRequest) => { await tx.user.update({ where: { id: user.id }, data: { - dashboardWidgetsJson: - normalized as unknown as Prisma.InputJsonValue, + dashboardWidgetsJson: normalized as unknown as Prisma.InputJsonValue, }, }); }, diff --git a/src/app/api/dashboard/widgets/route.ts b/src/app/api/dashboard/widgets/route.ts index 5675b0422..011d0b186 100644 --- a/src/app/api/dashboard/widgets/route.ts +++ b/src/app/api/dashboard/widgets/route.ts @@ -98,8 +98,8 @@ export const PUT = apiHandler(async (request: NextRequest) => { // visibility / order; chart prefs are PUT through their own route // (`/api/dashboard/chart-overlay-prefs`) and would otherwise be // wiped here on a subsequent layout save. - let mergedChartOverlayPrefs: ChartOverlayPrefsMap | undefined = - parsed.data.chartOverlayPrefs as ChartOverlayPrefsMap | undefined; + let mergedChartOverlayPrefs: ChartOverlayPrefsMap | undefined = parsed.data + .chartOverlayPrefs as ChartOverlayPrefsMap | undefined; if (mergedChartOverlayPrefs === undefined) { const existing = await prisma.user.findUnique({ where: { id: user.id }, diff --git a/src/app/api/gamification/achievements/route.ts b/src/app/api/gamification/achievements/route.ts index 62d32c81f..fc4efe2e0 100644 --- a/src/app/api/gamification/achievements/route.ts +++ b/src/app/api/gamification/achievements/route.ts @@ -473,94 +473,94 @@ export const GET = apiHandler(async (request: NextRequest) => { auditEvents, moodEntries, ] = await Promise.all([ - prisma.measurement.findMany({ - where: { - userId, - measuredAt: { gte: startDate, lte: now }, - source: { not: "IMPORT" }, - type: { - in: ["WEIGHT", "BLOOD_PRESSURE_SYS", "BLOOD_PRESSURE_DIA", "PULSE"], + prisma.measurement.findMany({ + where: { + userId, + measuredAt: { gte: startDate, lte: now }, + source: { not: "IMPORT" }, + type: { + in: ["WEIGHT", "BLOOD_PRESSURE_SYS", "BLOOD_PRESSURE_DIA", "PULSE"], + }, + }, + select: { + type: true, + value: true, + measuredAt: true, + }, + }), + prisma.medicationIntakeEvent.findMany({ + where: { + userId, + source: { not: "IMPORT" }, + scheduledFor: { gte: startDate, lte: now }, + }, + select: { + medicationId: true, + scheduledFor: true, + takenAt: true, + skipped: true, + }, + }), + prisma.medication.findMany({ + where: { userId }, + select: { + id: true, + schedules: { + select: { + windowStart: true, + windowEnd: true, + daysOfWeek: true, }, }, - select: { - type: true, - value: true, - measuredAt: true, + }, + }), + prisma.passkey.findMany({ + where: { + userId, + createdAt: { gte: startDate, lte: now }, + }, + select: { + createdAt: true, + }, + }), + prisma.auditLog.findMany({ + where: { + userId, + createdAt: { gte: startDate, lte: now }, + action: { + in: [ + "auth.login.passkey", + "auth.login.password", + "bugreport.submit", + // v1.4.18 — hidden Easter-egg triggers + "doctor-report.generate", + "doctor-report.pdf.generate", + "settings.locale.update", + ], }, - }), - prisma.medicationIntakeEvent.findMany({ - where: { - userId, - source: { not: "IMPORT" }, - scheduledFor: { gte: startDate, lte: now }, - }, - select: { - medicationId: true, - scheduledFor: true, - takenAt: true, - skipped: true, - }, - }), - prisma.medication.findMany({ - where: { userId }, - select: { - id: true, - schedules: { - select: { - windowStart: true, - windowEnd: true, - daysOfWeek: true, - }, - }, - }, - }), - prisma.passkey.findMany({ - where: { - userId, - createdAt: { gte: startDate, lte: now }, - }, - select: { - createdAt: true, - }, - }), - prisma.auditLog.findMany({ - where: { - userId, - createdAt: { gte: startDate, lte: now }, - action: { - in: [ - "auth.login.passkey", - "auth.login.password", - "bugreport.submit", - // v1.4.18 — hidden Easter-egg triggers - "doctor-report.generate", - "doctor-report.pdf.generate", - "settings.locale.update", - ], - }, - }, - select: { - action: true, - createdAt: true, - }, - }), - // v1.4.18 — mood entries feed the new mood badges + the - // entry-streak/consistent-month engagement metrics. Synced from - // moodLog.app or entered directly; we intentionally include all - // sources because consistency-of-tracking is what the badge - // rewards. - prisma.moodEntry.findMany({ - where: { - userId, - moodLoggedAt: { gte: startDate, lte: now }, - }, - select: { - date: true, - score: true, - moodLoggedAt: true, - }, - }), - ]); + }, + select: { + action: true, + createdAt: true, + }, + }), + // v1.4.18 — mood entries feed the new mood badges + the + // entry-streak/consistent-month engagement metrics. Synced from + // moodLog.app or entered directly; we intentionally include all + // sources because consistency-of-tracking is what the badge + // rewards. + prisma.moodEntry.findMany({ + where: { + userId, + moodLoggedAt: { gte: startDate, lte: now }, + }, + select: { + date: true, + score: true, + moodLoggedAt: true, + }, + }), + ]); const schedulesByMedicationId = new Map( medications.map((med) => [med.id, med.schedules]), @@ -820,10 +820,7 @@ export const GET = apiHandler(async (request: NextRequest) => { // discovered set. Hidden achievements that are still locked are // counted toward `totalCount` so the user sees they exist. const visibleUnlocked = visibleAchievements.filter((a) => a.unlocked); - const visibleEarned = visibleUnlocked.reduce( - (acc, a) => acc + a.points, - 0, - ); + const visibleEarned = visibleUnlocked.reduce((acc, a) => acc + a.points, 0); const visibleTotalPoints = visibleAchievements.reduce( (acc, a) => acc + a.points, 0, diff --git a/src/app/api/insights/feedback/route.ts b/src/app/api/insights/feedback/route.ts index b0ba423f5..938a194eb 100644 --- a/src/app/api/insights/feedback/route.ts +++ b/src/app/api/insights/feedback/route.ts @@ -1,7 +1,12 @@ import type { NextRequest } from "next/server"; import { apiHandler, requireAuth } from "@/lib/api-handler"; -import { apiError, apiSuccess, getClientIp, safeJson } from "@/lib/api-response"; +import { + apiError, + apiSuccess, + getClientIp, + safeJson, +} from "@/lib/api-response"; import { auditLog } from "@/lib/auth/audit"; import { prisma } from "@/lib/db"; import { Prisma } from "@/generated/prisma/client"; diff --git a/src/app/api/insights/provider-chain/__tests__/route.test.ts b/src/app/api/insights/provider-chain/__tests__/route.test.ts index 102f21e95..fff8209ea 100644 --- a/src/app/api/insights/provider-chain/__tests__/route.test.ts +++ b/src/app/api/insights/provider-chain/__tests__/route.test.ts @@ -15,7 +15,10 @@ vi.mock("@/lib/api-handler", () => ({ session: { id: "s-1" }, })), HttpError: class HttpError extends Error { - constructor(public status: number, message: string) { + constructor( + public status: number, + message: string, + ) { super(message); } }, diff --git a/src/components/admin/__tests__/admin-shell.test.tsx b/src/components/admin/__tests__/admin-shell.test.tsx index cd30df326..d3e1fb5cf 100644 --- a/src/components/admin/__tests__/admin-shell.test.tsx +++ b/src/components/admin/__tests__/admin-shell.test.tsx @@ -22,10 +22,7 @@ vi.mock("next/navigation", () => ({ })); import { I18nProvider } from "@/lib/i18n/context"; -import { - ADMIN_SECTIONS, - AdminShell, -} from "../admin-shell"; +import { ADMIN_SECTIONS, AdminShell } from "../admin-shell"; function renderShell(props: { active?: (typeof ADMIN_SECTIONS)[number]["slug"]; diff --git a/src/components/admin/__tests__/api-token-overview-responsive.test.tsx b/src/components/admin/__tests__/api-token-overview-responsive.test.tsx index b79131265..79d1500f6 100644 --- a/src/components/admin/__tests__/api-token-overview-responsive.test.tsx +++ b/src/components/admin/__tests__/api-token-overview-responsive.test.tsx @@ -97,9 +97,7 @@ describe("ApiTokenOverviewSection — responsive", () => { // forcing a scroll container — that was the v1.4.15 bug Marc // re-reported. Confirm there's no overflow-x-auto INSIDE the // mobile
    . - const mobileMatch = html.match( - /]*md:hidden[^>]*>([\s\S]*?)<\/ul>/, - ); + const mobileMatch = html.match(/]*md:hidden[^>]*>([\s\S]*?)<\/ul>/); expect(mobileMatch).not.toBeNull(); expect(mobileMatch![1]).not.toContain("overflow-x-auto"); }); @@ -123,9 +121,7 @@ describe("ApiTokenOverviewSection — responsive", () => { it("mobile timestamp lines wrap (no nowrap on the meta paragraphs)", () => { const html = render(); - const mobileMatch = html.match( - /]*md:hidden[^>]*>([\s\S]*?)<\/ul>/, - ); + const mobileMatch = html.match(/]*md:hidden[^>]*>([\s\S]*?)<\/ul>/); expect(mobileMatch).not.toBeNull(); // The Last-used + Created lines use

    — no // whitespace-nowrap so a long German date+time can flow onto a diff --git a/src/components/admin/ai-quality-section.tsx b/src/components/admin/ai-quality-section.tsx index fc3be70bc..9c3de650d 100644 --- a/src/components/admin/ai-quality-section.tsx +++ b/src/components/admin/ai-quality-section.tsx @@ -132,12 +132,9 @@ export function AiQualitySection() {

    - +
    - + @@ -183,7 +180,9 @@ export function AiQualitySection() { {bucket.promptVersion} - +
    {t("admin.aiQuality.colSeverity")} {bucket.helpful}{bucket.notHelpful} + {bucket.notHelpful} +
    -
    - -
    {t("admin.systemStatus")}
    -
    - {status ? ( -
    - - - - - - - - {status.worker.lastReminderCheck && ( +
    + +
    {t("admin.systemStatus")}
    +
    + {status ? ( +
    + + - )} - {status.integrations.umami && ( + + + - )} - {status.integrations.glitchtip && ( - - )} - {status.integrations.webPush && ( - - )} - {status.integrations.bugReport && ( - - )} -
    - ) : isError ? ( - // P19: surface load failures inline instead of leaving the - // section spinning forever. The hook throws on non-OK - // responses, so isError is true on 500s, network errors, etc. - // v1.4.16 Wave-C MED — pair the alert with a Retry button so a - // transient 500 (rolling deploy, DB blip) doesn't require a - // full page reload to recover. -
    - {t("admin.systemStatusLoadError")} - -
    - ) : ( -
    - - - {t("admin.loadingStatus")} - -
    - )} + {status.integrations.umami && ( + + )} + {status.integrations.glitchtip && ( + + )} + {status.integrations.webPush && ( + + )} + {status.integrations.bugReport && ( + + )} +
    + ) : isError ? ( + // P19: surface load failures inline instead of leaving the + // section spinning forever. The hook throws on non-OK + // responses, so isError is true on 500s, network errors, etc. + // v1.4.16 Wave-C MED — pair the alert with a Retry button so a + // transient 500 (rolling deploy, DB blip) doesn't require a + // full page reload to recover. +
    + {t("admin.systemStatusLoadError")} + +
    + ) : ( +
    + + + {t("admin.loadingStatus")} + +
    + )}
    ); diff --git a/src/components/charts/__tests__/chart-empty-state.test.tsx b/src/components/charts/__tests__/chart-empty-state.test.tsx index e76bc54aa..d1b1c83d9 100644 --- a/src/components/charts/__tests__/chart-empty-state.test.tsx +++ b/src/components/charts/__tests__/chart-empty-state.test.tsx @@ -24,9 +24,7 @@ describe("", () => { />, ); expect(html).toContain("Not enough mood data"); - expect(html).toContain( - "Log at least 3 entries to unlock the trend line.", - ); + expect(html).toContain("Log at least 3 entries to unlock the trend line."); }); it("uses a custom height when supplied", () => { diff --git a/src/components/charts/__tests__/chart-overlay-controls.test.tsx b/src/components/charts/__tests__/chart-overlay-controls.test.tsx index c8b2e0452..c73b1a20f 100644 --- a/src/components/charts/__tests__/chart-overlay-controls.test.tsx +++ b/src/components/charts/__tests__/chart-overlay-controls.test.tsx @@ -64,9 +64,7 @@ describe("", () => { const start: ChartOverlayPrefs = DEFAULT_CHART_OVERLAY_PREFS; // Render once — this binds the callback closure. renderToStaticMarkup( - withProvider( - , - ), + withProvider(), ); // Simulate the merge the component performs: setKey("showTargetRange", true) diff --git a/src/components/charts/__tests__/health-chart-mini.test.tsx b/src/components/charts/__tests__/health-chart-mini.test.tsx index 7c603cc44..5eb620752 100644 --- a/src/components/charts/__tests__/health-chart-mini.test.tsx +++ b/src/components/charts/__tests__/health-chart-mini.test.tsx @@ -38,10 +38,7 @@ vi.mock("@/hooks/use-auth", () => ({ import { I18nProvider } from "@/lib/i18n/context"; import { HealthChart } from "../health-chart"; -import { - resolveMiniRangePoints, - type DataWindow, -} from "../mini-window"; +import { resolveMiniRangePoints, type DataWindow } from "../mini-window"; /** * v1.4.16 phase B5c — chart mini-mode + windowOverride. @@ -85,9 +82,7 @@ describe("resolveMiniRangePoints()", () => { describe("", () => { it("does not render the range tabs / mode-switch row in mini mode", () => { - const html = render( - , - ); + const html = render(); // No "7d" / "30d" / "90d" range tabs. expect(html).not.toMatch(/data-slot="chart-range-tab"/); // No "7-day trend" toggle. diff --git a/src/components/charts/__tests__/health-chart-polish.test.tsx b/src/components/charts/__tests__/health-chart-polish.test.tsx index 79556a304..2fa1b2e6d 100644 --- a/src/components/charts/__tests__/health-chart-polish.test.tsx +++ b/src/components/charts/__tests__/health-chart-polish.test.tsx @@ -141,7 +141,7 @@ describe(" v1.4.18 clean-line revert", () => { , ); - expect(html).not.toContain("data-slot=\"chart-linear-gradient\""); + expect(html).not.toContain('data-slot="chart-linear-gradient"'); expect(html).not.toContain("chart-gradient-BLOOD_PRESSURE_SYS"); expect(html).not.toContain("linearGradient"); }); diff --git a/src/components/charts/__tests__/medication-chart-polish.test.tsx b/src/components/charts/__tests__/medication-chart-polish.test.tsx index 2b667e2b4..3fdd48e49 100644 --- a/src/components/charts/__tests__/medication-chart-polish.test.tsx +++ b/src/components/charts/__tests__/medication-chart-polish.test.tsx @@ -36,9 +36,8 @@ vi.mock("@/hooks/use-auth", () => ({ describe(" v1.4.18 clean-line revert", () => { it("does NOT paint a gradient fill under the line", async () => { const { I18nProvider } = await import("@/lib/i18n/context"); - const { MedicationComplianceChart } = await import( - "../medication-compliance-chart" - ); + const { MedicationComplianceChart } = + await import("../medication-compliance-chart"); const html = renderToStaticMarkup( @@ -46,7 +45,7 @@ describe(" v1.4.18 clean-line revert", () => { , ); - expect(html).not.toContain("data-slot=\"chart-linear-gradient\""); + expect(html).not.toContain('data-slot="chart-linear-gradient"'); expect(html).not.toContain("chart-gradient-medication"); expect(html).not.toContain("linearGradient"); }); diff --git a/src/components/charts/__tests__/medication-overlay-toggles.test.tsx b/src/components/charts/__tests__/medication-overlay-toggles.test.tsx index c3bb46fcd..f5b62ee98 100644 --- a/src/components/charts/__tests__/medication-overlay-toggles.test.tsx +++ b/src/components/charts/__tests__/medication-overlay-toggles.test.tsx @@ -60,9 +60,8 @@ vi.mock("@/hooks/use-chart-overlay-prefs", () => ({ async function renderChart(): Promise { // Re-import to pick up the freshly-set mock value. - const { MedicationComplianceChart } = await import( - "../medication-compliance-chart" - ); + const { MedicationComplianceChart } = + await import("../medication-compliance-chart"); return renderToStaticMarkup( diff --git a/src/components/charts/__tests__/mood-chart-polish.test.tsx b/src/components/charts/__tests__/mood-chart-polish.test.tsx index 0d07586a9..d259d2403 100644 --- a/src/components/charts/__tests__/mood-chart-polish.test.tsx +++ b/src/components/charts/__tests__/mood-chart-polish.test.tsx @@ -61,7 +61,7 @@ describe(" v1.4.18 clean-line revert", () => { , ); - expect(html).not.toContain("data-slot=\"chart-linear-gradient\""); + expect(html).not.toContain('data-slot="chart-linear-gradient"'); expect(html).not.toContain("chart-gradient-mood"); expect(html).not.toContain("linearGradient"); }); diff --git a/src/components/charts/chart-overlay-controls.tsx b/src/components/charts/chart-overlay-controls.tsx index e7fc8c586..1ca74a05d 100644 --- a/src/components/charts/chart-overlay-controls.tsx +++ b/src/components/charts/chart-overlay-controls.tsx @@ -78,10 +78,7 @@ export function ChartOverlayControls({ const trendArrowId = useId(); const targetRangeId = useId(); - const setKey = ( - key: keyof ChartOverlayPrefs, - value: boolean, - ): void => { + const setKey = (key: keyof ChartOverlayPrefs, value: boolean): void => { onChange({ ...prefs, [key]: value }); }; @@ -163,4 +160,3 @@ export function ChartOverlayControls({ ); } - diff --git a/src/components/charts/health-chart.tsx b/src/components/charts/health-chart.tsx index 45d722820..9cfba442d 100644 --- a/src/components/charts/health-chart.tsx +++ b/src/components/charts/health-chart.tsx @@ -31,10 +31,7 @@ import { computeWindowTrend, SPLIT_HALF_THRESHOLD_DAYS, } from "@/lib/analytics/window-trend"; -import { - resolveMiniRangePoints, - type DataWindow, -} from "./mini-window"; +import { resolveMiniRangePoints, type DataWindow } from "./mini-window"; import { shiftDailySeriesForward } from "@/lib/charts/comparison-shift"; import type { ChartOverlayKey, @@ -1229,11 +1226,7 @@ export function HealthChart({ } if (rows.length === 0) return null; return ( - + ); }} /> diff --git a/src/components/charts/mini-window.ts b/src/components/charts/mini-window.ts index a1f890408..765498e6b 100644 --- a/src/components/charts/mini-window.ts +++ b/src/components/charts/mini-window.ts @@ -16,11 +16,7 @@ * pin the contract without depending on Recharts internals. */ -export type DataWindow = - | "last7days" - | "last30days" - | "last90days" - | "allTime"; +export type DataWindow = "last7days" | "last30days" | "last90days" | "allTime"; const POINTS_BY_WINDOW: Record = { last7days: 7, diff --git a/src/components/charts/mood-chart.tsx b/src/components/charts/mood-chart.tsx index 9a74a6404..e272c9c07 100644 --- a/src/components/charts/mood-chart.tsx +++ b/src/components/charts/mood-chart.tsx @@ -745,9 +745,9 @@ export function MoodChart({ delta = t("charts.deltaUnchanged"); } else { const sign = diff > 0 ? "+" : "−"; - const formatted = `${sign}${( - Math.abs(diff) - ).toFixed(1)}`; + const formatted = `${sign}${Math.abs(diff).toFixed( + 1, + )}`; delta = t("charts.deltaVsBaseline").replace( "{delta}", formatted, @@ -776,11 +776,7 @@ export function MoodChart({ } if (rows.length === 0) return null; return ( - + ); }} /> diff --git a/src/components/insights/__tests__/insight-advisor-card.test.tsx b/src/components/insights/__tests__/insight-advisor-card.test.tsx index 1b9085ed4..3d16c3b0a 100644 --- a/src/components/insights/__tests__/insight-advisor-card.test.tsx +++ b/src/components/insights/__tests__/insight-advisor-card.test.tsx @@ -216,9 +216,7 @@ describe(" — summary typography polish (B1b)", () => { // The summary's inline-chart wrapper must carry `data-mini="true"` // so the chart shrinks to sparkline form. Per-finding charts use // the default detail mode. - expect(html).toMatch( - /data-slot="insight-summary"[\s\S]*?data-mini="true"/, - ); + expect(html).toMatch(/data-slot="insight-summary"[\s\S]*?data-mini="true"/); }); }); diff --git a/src/components/insights/__tests__/insights-card.test.tsx b/src/components/insights/__tests__/insights-card.test.tsx index 9da6d158a..d2d3eb893 100644 --- a/src/components/insights/__tests__/insights-card.test.tsx +++ b/src/components/insights/__tests__/insights-card.test.tsx @@ -64,9 +64,7 @@ function rec( }; } -function buildInsight( - recommendations: InsightRecommendation[], -): InsightResult { +function buildInsight(recommendations: InsightRecommendation[]): InsightResult { return { insightType: "general", summary: "Test summary", diff --git a/src/components/insights/__tests__/insights-page-hero.test.tsx b/src/components/insights/__tests__/insights-page-hero.test.tsx index 10f860b70..513f2163e 100644 --- a/src/components/insights/__tests__/insights-page-hero.test.tsx +++ b/src/components/insights/__tests__/insights-page-hero.test.tsx @@ -80,7 +80,9 @@ describe("", () => { const html = render( {}} regenerating />, ); - expect(html).toMatch(/data-slot="insights-page-hero-regenerate"[^>]*disabled/); + expect(html).toMatch( + /data-slot="insights-page-hero-regenerate"[^>]*disabled/, + ); }); it("German locale — heading + subtitle translate", () => { diff --git a/src/components/insights/__tests__/recommendation-card-confidence-integration.test.tsx b/src/components/insights/__tests__/recommendation-card-confidence-integration.test.tsx index 1c1da0e76..fdc8b3a29 100644 --- a/src/components/insights/__tests__/recommendation-card-confidence-integration.test.tsx +++ b/src/components/insights/__tests__/recommendation-card-confidence-integration.test.tsx @@ -130,8 +130,7 @@ describe("end-to-end — generateInsight() confidence override flows into the ca // Model still claims 99 — wrapper still discards. confidence: 99, metricSource: { - ...strictPayloadWithModelConfidence.recommendations[0] - .metricSource, + ...strictPayloadWithModelConfidence.recommendations[0].metricSource, n: 1, }, }, @@ -145,7 +144,9 @@ describe("end-to-end — generateInsight() confidence override flows into the ca userPrompt: "u", }); - expect(outcome.parsed.recommendations[0].confidence).toBeLessThanOrEqual(15); + expect(outcome.parsed.recommendations[0].confidence).toBeLessThanOrEqual( + 15, + ); const insight: InsightResult = { insightType: "general", diff --git a/src/components/insights/__tests__/recommendation-card-confidence.test.tsx b/src/components/insights/__tests__/recommendation-card-confidence.test.tsx index 8c16dd7f3..c45c48f1f 100644 --- a/src/components/insights/__tests__/recommendation-card-confidence.test.tsx +++ b/src/components/insights/__tests__/recommendation-card-confidence.test.tsx @@ -80,10 +80,7 @@ function render(node: React.ReactNode, locale: "en" | "de" = "en") { describe(" — confidence slot wiring", () => { it("renders ConfidenceMeter inside rec-confidence-slot when confidence is set", () => { const html = render( - , + , ); // Slot wraps the meter expect(html).toMatch( diff --git a/src/components/insights/__tests__/recommendation-card.test.tsx b/src/components/insights/__tests__/recommendation-card.test.tsx index b53038fea..a37ba0b3b 100644 --- a/src/components/insights/__tests__/recommendation-card.test.tsx +++ b/src/components/insights/__tests__/recommendation-card.test.tsx @@ -230,7 +230,7 @@ describe("", () => { const html = render( , ); - expect(html).not.toContain('data-feedback-thumb='); + expect(html).not.toContain("data-feedback-thumb="); }); it("does NOT render feedback thumbs when the metricSource.timeRange is out of vocabulary", () => { @@ -241,6 +241,6 @@ describe("", () => { const html = render( , ); - expect(html).not.toContain('data-feedback-thumb='); + expect(html).not.toContain("data-feedback-thumb="); }); }); diff --git a/src/components/insights/__tests__/recommendation-feedback.test.tsx b/src/components/insights/__tests__/recommendation-feedback.test.tsx index 5c00d580a..33d97f1b7 100644 --- a/src/components/insights/__tests__/recommendation-feedback.test.tsx +++ b/src/components/insights/__tests__/recommendation-feedback.test.tsx @@ -40,14 +40,14 @@ function render(node: React.ReactNode, locale: "en" | "de" = "en") { describe("", () => { it("renders thumbs-up + thumbs-down buttons", () => { const html = render(); - expect(html).toContain("data-feedback-thumb=\"up\""); - expect(html).toContain("data-feedback-thumb=\"down\""); + expect(html).toContain('data-feedback-thumb="up"'); + expect(html).toContain('data-feedback-thumb="down"'); }); it("attaches accessible aria-label keys", () => { const html = render(); - expect(html).toContain("aria-label=\"Helpful"); - expect(html).toContain("aria-label=\"Not helpful"); + expect(html).toContain('aria-label="Helpful'); + expect(html).toContain('aria-label="Not helpful'); }); it("translates the labels to German when locale=de", () => { @@ -63,16 +63,19 @@ describe("", () => { // Confirmation text replaces both buttons. expect(html).toContain("Thanks"); // Highlighted thumb-up still rendered for context. - expect(html).toContain("data-feedback-confirmed=\"up\""); + expect(html).toContain('data-feedback-confirmed="up"'); // The opposite button should NOT be re-clickable. expect(html).not.toContain('data-feedback-thumb="down"'); }); it("renders the 'already rated' state with the prior verdict highlighted", () => { const html = render( - , + , ); - expect(html).toContain("data-feedback-already-rated=\"down\""); + expect(html).toContain('data-feedback-already-rated="down"'); // Buttons are not interactive in this state. expect(html).not.toContain('data-feedback-thumb="up"'); expect(html).not.toContain('data-feedback-thumb="down"'); @@ -81,13 +84,11 @@ describe("", () => { it("buttons carry type=button so they don't accidentally submit a parent form", () => { const html = render(); // Both up + down buttons must declare type=button. - const upMatch = html.match( - /]*data-feedback-thumb="up"[^>]*>/, - ); + const upMatch = html.match(/]*data-feedback-thumb="up"[^>]*>/); const downMatch = html.match( /]*data-feedback-thumb="down"[^>]*>/, ); - expect(upMatch?.[0]).toContain("type=\"button\""); - expect(downMatch?.[0]).toContain("type=\"button\""); + expect(upMatch?.[0]).toContain('type="button"'); + expect(downMatch?.[0]).toContain('type="button"'); }); }); diff --git a/src/components/insights/__tests__/recommendations-grid-integration.test.tsx b/src/components/insights/__tests__/recommendations-grid-integration.test.tsx index 5a3806cdd..a6b37959a 100644 --- a/src/components/insights/__tests__/recommendations-grid-integration.test.tsx +++ b/src/components/insights/__tests__/recommendations-grid-integration.test.tsx @@ -85,9 +85,7 @@ function rec( }; } -function buildInsight( - recommendations: InsightRecommendation[], -): InsightResult { +function buildInsight(recommendations: InsightRecommendation[]): InsightResult { return { insightType: "general", summary: "Test summary", @@ -145,10 +143,7 @@ describe("InsightAdvisorCard + RecommendationsGrid full flow (B1b)", () => { ]; const html = renderToStaticMarkup( - + , ); diff --git a/src/components/insights/__tests__/recommendations-grid.test.tsx b/src/components/insights/__tests__/recommendations-grid.test.tsx index 65701ec03..ce7400963 100644 --- a/src/components/insights/__tests__/recommendations-grid.test.tsx +++ b/src/components/insights/__tests__/recommendations-grid.test.tsx @@ -120,10 +120,7 @@ describe("sortRecommendationsBySeverity", () => { describe("", () => { it("renders cards in a 2-col responsive grid", () => { - const recs = [ - rec("a", "info", "alpha"), - rec("b", "urgent", "bravo"), - ]; + const recs = [rec("a", "info", "alpha"), rec("b", "urgent", "bravo")]; const html = render(); expect(html).toMatch(/data-slot="rec-grid"/); expect(html).toMatch(/grid-cols-1/); @@ -131,10 +128,7 @@ describe("", () => { }); it("renders cards in severity-ordered sequence (urgent first)", () => { - const recs = [ - rec("a", "info", "alpha"), - rec("b", "urgent", "bravo"), - ]; + const recs = [rec("a", "info", "alpha"), rec("b", "urgent", "bravo")]; const html = render(); // bravo (urgent) must appear before alpha (info) in the output. expect(html.indexOf("bravo")).toBeLessThan(html.indexOf("alpha")); diff --git a/src/components/insights/confidence-meter.tsx b/src/components/insights/confidence-meter.tsx index 96ecdd181..ec1d5e43f 100644 --- a/src/components/insights/confidence-meter.tsx +++ b/src/components/insights/confidence-meter.tsx @@ -47,12 +47,14 @@ function litBarsFor(value: number): number { return lit; } -const BAR_LIT_COLOUR_BY_BAND: Record, string> = - { - high: "bg-dracula-green", - medium: "bg-dracula-yellow", - low: "bg-dracula-orange", - }; +const BAR_LIT_COLOUR_BY_BAND: Record< + Exclude, + string +> = { + high: "bg-dracula-green", + medium: "bg-dracula-yellow", + low: "bg-dracula-orange", +}; const RING_STROKE_COLOUR_BY_BAND: Record< Exclude, diff --git a/src/components/insights/recommendation-card.tsx b/src/components/insights/recommendation-card.tsx index ebdccc34c..464544afa 100644 --- a/src/components/insights/recommendation-card.tsx +++ b/src/components/insights/recommendation-card.tsx @@ -178,7 +178,9 @@ function RationaleCard({ feedbackProps, }: { rationale: InsightRecommendationRationale; - metricSource: { type: string; timeRange: string; summary: string } | undefined; + metricSource: + | { type: string; timeRange: string; summary: string } + | undefined; referenceId: string | undefined; confidence: number | undefined; locale: Locale; @@ -295,10 +297,7 @@ export function RecommendationCard({ // empty so a thumbs-click can never produce a 422. const feedbackTimeRange = asFeedbackTimeRange(norm.metricSource?.timeRange); const feedbackProps = - norm.id && - norm.severity && - norm.metricSource?.type && - feedbackTimeRange + norm.id && norm.severity && norm.metricSource?.type && feedbackTimeRange ? { recId: norm.id, recText: norm.text, diff --git a/src/components/insights/recommendation-feedback.tsx b/src/components/insights/recommendation-feedback.tsx index fdf3dfa83..c0e2311b0 100644 --- a/src/components/insights/recommendation-feedback.tsx +++ b/src/components/insights/recommendation-feedback.tsx @@ -113,10 +113,7 @@ function writeLocalState( ): void { if (typeof window === "undefined") return; try { - window.localStorage.setItem( - localCacheKey(userId, recId, recText), - verdict, - ); + window.localStorage.setItem(localCacheKey(userId, recId, recText), verdict); } catch { // Best-effort — server dedup is the source of truth. } @@ -259,10 +256,7 @@ export function RecommendationFeedback({ const disabled = submittingUp || submittingDown; return ( -
    +