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