diff --git a/src/app/api/auth/me/diabetes/route.ts b/src/app/api/auth/me/diabetes/route.ts index 56d72da64..2b3e48e15 100644 --- a/src/app/api/auth/me/diabetes/route.ts +++ b/src/app/api/auth/me/diabetes/route.ts @@ -19,12 +19,13 @@ */ import { z } from "zod"; -import { apiHandler, requireAuth, HttpError } from "@/lib/api-handler"; +import { apiHandler, requireAuth } from "@/lib/api-handler"; import { apiError, apiSuccess, getClientIp, returnAllZodIssues, + safeJson, } from "@/lib/api-response"; import { annotate } from "@/lib/logging/context"; import { auditLog } from "@/lib/auth/audit"; @@ -74,12 +75,12 @@ export const PATCH = apiHandler(async (req: Request) => { return response; } - let body: unknown; - try { - body = await req.json(); - } catch { - throw new HttpError(422, "diabetes.body.invalid_json"); - } + // The body is a single boolean — bound the parse so a malformed or + // oversized payload is rejected before it is materialised. + const { data: body, error: jsonError } = await safeJson(req, { + maxBytes: 1024, + }); + if (jsonError) return jsonError; const parsed = patchBodySchema.safeParse(body); if (!parsed.success) { diff --git a/src/components/illness/illness-view.tsx b/src/components/illness/illness-view.tsx index 10101938c..7af11279d 100644 --- a/src/components/illness/illness-view.tsx +++ b/src/components/illness/illness-view.tsx @@ -15,7 +15,7 @@ * per-day timeline and correlation card live. Retrospective-only copy — a * journal, not a medical device, and it does not diagnose. */ -import { useMemo, useState } from "react"; +import { useId, useMemo, useState } from "react"; import Link from "next/link"; import { ChevronDown, Plus, Stethoscope, Wrench } from "lucide-react"; @@ -245,10 +245,12 @@ function EpisodeGroup({ }) { // Hooks must run unconditionally; bail on the empty render afterwards. const [open, setOpen] = useState(false); + const gridId = useId(); if (parents.length === 0) return null; const grid = (
setOpen((v) => !v)} aria-expanded={open} + aria-controls={gridId} className="text-muted-foreground hover:text-foreground -mx-1 flex w-fit items-center gap-1.5 rounded-md px-1 py-0.5 text-xs font-medium uppercase tracking-wide transition-colors" > setShowAdvanced((v) => !v)} aria-expanded={showAdvanced} + aria-controls="chronotype-advanced" className="text-muted-foreground hover:text-foreground flex items-center gap-1 text-xs font-medium transition-colors" > {showAdvanced ? ( @@ -114,7 +115,7 @@ export function ChronotypeCard({ chronotype }: { chronotype: ChronotypeDto }) { {showAdvanced && ( -
+
{chronotype.socialJetlagMinutes != null && (() => { const { hours, minutes } = hoursMinutes( diff --git a/src/components/measurement-reminders/vorsorge-settings.tsx b/src/components/measurement-reminders/vorsorge-settings.tsx index 47710d026..1d2820fa0 100644 --- a/src/components/measurement-reminders/vorsorge-settings.tsx +++ b/src/components/measurement-reminders/vorsorge-settings.tsx @@ -100,7 +100,10 @@ export function VorsorgeSettings() { key={reminder.id} className="flex items-center justify-between gap-3 px-4 py-3" > - + {resolveLabel(reminder, t)}
))}