From 17822aa9ebd12932ae3df9cd7ceb143f26f8dafe Mon Sep 17 00:00:00 2001 From: Maurice Date: Fri, 19 Jun 2026 17:56:41 +0200 Subject: [PATCH] fix(days): align note time limit to 250 and keep toasts above modal blur (#1252) The day-note 'time' field capped at 150 server-side while the dialog and shared schema allow 250, so 151-250 char notes 400'd with a confusing 'time must be 150...' message. Raise the controller and MCP limits to 250. Also lift the toast container above modal overlays so the error toast isn't rendered behind the modal's backdrop blur. --- client/src/components/shared/Toast.tsx | 4 +++- server/src/mcp/tools/days.ts | 4 ++-- server/src/nest/days/day-notes.controller.ts | 7 ++++--- 3 files changed, 9 insertions(+), 6 deletions(-) diff --git a/client/src/components/shared/Toast.tsx b/client/src/components/shared/Toast.tsx index 6bb5d3c3..97478499 100644 --- a/client/src/components/shared/Toast.tsx +++ b/client/src/components/shared/Toast.tsx @@ -102,7 +102,9 @@ export function ToastContainer() { `}
{toasts.map(toast => ( diff --git a/server/src/mcp/tools/days.ts b/server/src/mcp/tools/days.ts index 6bed7bb1..4b73b3fa 100644 --- a/server/src/mcp/tools/days.ts +++ b/server/src/mcp/tools/days.ts @@ -230,7 +230,7 @@ export function registerDayTools(server: McpServer, userId: number, scopes: stri tripId: z.number().int().positive(), dayId: z.number().int().positive(), text: z.string().min(1).max(500), - time: z.string().max(150).optional().describe('Time label (e.g. "09:00" or "Morning")'), + time: z.string().max(250).optional().describe('Time label (e.g. "09:00" or "Morning")'), icon: z.string().optional().describe('Emoji icon for the note'), }, annotations: TOOL_ANNOTATIONS_NON_IDEMPOTENT, @@ -255,7 +255,7 @@ export function registerDayTools(server: McpServer, userId: number, scopes: stri dayId: z.number().int().positive(), noteId: z.number().int().positive(), text: z.string().min(1).max(500).optional(), - time: z.string().max(150).nullable().optional().describe('Time label (e.g. "09:00" or "Morning"), or null to clear'), + time: z.string().max(250).nullable().optional().describe('Time label (e.g. "09:00" or "Morning"), or null to clear'), icon: z.string().optional().describe('Emoji icon for the note'), }, annotations: TOOL_ANNOTATIONS_WRITE, diff --git a/server/src/nest/days/day-notes.controller.ts b/server/src/nest/days/day-notes.controller.ts index a293e950..fbbe45fb 100644 --- a/server/src/nest/days/day-notes.controller.ts +++ b/server/src/nest/days/day-notes.controller.ts @@ -17,9 +17,10 @@ import { CurrentUser } from '../auth/current-user.decorator'; type DayNoteBody = { text?: string; time?: string; icon?: string; sort_order?: number }; -// Mirrors the legacy validateStringLengths({ text: 500, time: 150 }) middleware, -// which runs BEFORE the trip-access check — so an over-long field 400s first. -const MAX_LENGTHS: Record = { text: 500, time: 150 }; +// Runs BEFORE the trip-access check, so an over-long field 400s first. The `time` +// cap matches the shared dayNote schema (max 250) and the note dialog's counter; +// it was 150 here, which rejected valid 151–250 char notes with a confusing error. +const MAX_LENGTHS: Record = { text: 500, time: 250 }; function validateLengths(body: Record): void { for (const [field, max] of Object.entries(MAX_LENGTHS)) {