diff --git a/shared/src/collab/collab.schema.spec.ts b/shared/src/collab/collab.schema.spec.ts index d309b89bc..893560fcf 100644 --- a/shared/src/collab/collab.schema.spec.ts +++ b/shared/src/collab/collab.schema.spec.ts @@ -1,5 +1,6 @@ import { collabNoteCreateRequestSchema, + collabNoteUpdateRequestSchema, collabPollCreateRequestSchema, collabPollVoteRequestSchema, collabMessageCreateRequestSchema, @@ -14,6 +15,35 @@ describe('collabNoteCreateRequestSchema', () => { expect(collabNoteCreateRequestSchema.safeParse({ title: '' }).success).toBe(false); expect(collabNoteCreateRequestSchema.safeParse({}).success).toBe(false); }); + + it('accepts explicit null for the optional fields (desktop null-clearing)', () => { + expect( + collabNoteCreateRequestSchema.safeParse({ + title: 'Idea', + content: 'Body', + category: null, + color: '#6366f1', + website: null, + }).success, + ).toBe(true); + }); +}); + +describe('collabNoteUpdateRequestSchema', () => { + it('accepts explicit null for the optional fields (desktop null-clearing)', () => { + expect( + collabNoteUpdateRequestSchema.safeParse({ + title: 'Idea', + content: null, + category: null, + color: null, + website: null, + pinned: 1, + }).success, + ).toBe(true); + expect(collabNoteUpdateRequestSchema.safeParse({ pinned: true }).success).toBe(true); + expect(collabNoteUpdateRequestSchema.safeParse({ title: null }).success).toBe(false); + }); }); describe('collabPollCreateRequestSchema', () => { diff --git a/shared/src/collab/collab.schema.ts b/shared/src/collab/collab.schema.ts index 38ef7a941..b3a314ffe 100644 --- a/shared/src/collab/collab.schema.ts +++ b/shared/src/collab/collab.schema.ts @@ -13,20 +13,24 @@ import { z } from 'zod'; export const collabNoteCreateRequestSchema = z.object({ title: z.string().min(1), - content: z.string().optional(), - category: z.string().optional(), - color: z.string().optional(), - website: z.string().optional(), + // The desktop notes form clears optional fields by sending explicit null + // (the service coerces falsy to its defaults), so they are all nullable. + content: z.string().nullable().optional(), + category: z.string().nullable().optional(), + color: z.string().nullable().optional(), + website: z.string().nullable().optional(), }); export type CollabNoteCreateRequest = z.infer; export const collabNoteUpdateRequestSchema = z.object({ title: z.string().optional(), - content: z.string().optional(), - category: z.string().optional(), - color: z.string().optional(), + // Same null-clearing protocol as create (the desktop form resends the whole + // note object, with cleared fields as explicit null). + content: z.string().nullable().optional(), + category: z.string().nullable().optional(), + color: z.string().nullable().optional(), pinned: z.union([z.boolean(), z.number()]).optional(), - website: z.string().optional(), + website: z.string().nullable().optional(), }); export type CollabNoteUpdateRequest = z.infer;