fix(acadenice): pass content object directly to jsonb param + remove empty text nodes — Patch 028
Two bugs in template seed/instantiate:
1. ${JSON.stringify(content)}::jsonb made Postgres store the content as
a jsonb scalar string (jsonb_typeof = 'string'), not an object. The
instantiate read it back as a JSON-encoded string, which ProseMirror
tried to parse as a node tree and crashed with 'Unknown node type:
undefined' on the outer string. Pass the object directly with
${content as unknown as string}::jsonb so postgres-js binds it as
a JSONB value.
2. Built-in template seed used { type: 'paragraph', content: [{ type:
'text', text: '' }] } for empty paragraphs / list items / task
items. ProseMirror schema rejects empty text nodes ('Empty text
nodes are not allowed'). Replaced with content: [].
Verified via curl: POST /api/acadenice/templates/{id}/instantiate now
returns 201 with the new pageId/slugId.
Patch 028.
This commit is contained in:
parent
8c3d55024b
commit
9139fb8728
2 changed files with 16 additions and 16 deletions
|
|
@ -35,13 +35,13 @@ const BUILT_IN_TEMPLATES: ReadonlyArray<BuiltInSpec> = [
|
|||
{ type: 'heading', attrs: { level: 1 }, content: [{ type: 'text', text: 'Meeting Note' }] },
|
||||
{ type: 'paragraph', content: [{ type: 'text', marks: [{ type: 'bold' }], text: 'Date: ' }, { type: 'text', text: new Date().toLocaleDateString('en-US', { year: 'numeric', month: 'long', day: 'numeric' }) }] },
|
||||
{ type: 'heading', attrs: { level: 2 }, content: [{ type: 'text', text: 'Attendees' }] },
|
||||
{ type: 'bulletList', content: [{ type: 'listItem', content: [{ type: 'paragraph', content: [{ type: 'text', text: '' }] }] }] },
|
||||
{ type: 'bulletList', content: [{ type: 'listItem', content: [{ type: 'paragraph', content: [] }] }] },
|
||||
{ type: 'heading', attrs: { level: 2 }, content: [{ type: 'text', text: 'Agenda' }] },
|
||||
{ type: 'orderedList', content: [{ type: 'listItem', content: [{ type: 'paragraph', content: [{ type: 'text', text: '' }] }] }] },
|
||||
{ type: 'orderedList', content: [{ type: 'listItem', content: [{ type: 'paragraph', content: [] }] }] },
|
||||
{ type: 'heading', attrs: { level: 2 }, content: [{ type: 'text', text: 'Notes' }] },
|
||||
{ type: 'paragraph', content: [{ type: 'text', text: '' }] },
|
||||
{ type: 'paragraph', content: [] },
|
||||
{ type: 'heading', attrs: { level: 2 }, content: [{ type: 'text', text: 'Action Items' }] },
|
||||
{ type: 'taskList', content: [{ type: 'taskItem', attrs: { checked: false }, content: [{ type: 'paragraph', content: [{ type: 'text', text: '' }] }] }] },
|
||||
{ type: 'taskList', content: [{ type: 'taskItem', attrs: { checked: false }, content: [{ type: 'paragraph', content: [] }] }] },
|
||||
],
|
||||
},
|
||||
},
|
||||
|
|
@ -59,11 +59,11 @@ const BUILT_IN_TEMPLATES: ReadonlyArray<BuiltInSpec> = [
|
|||
{ type: 'heading', attrs: { level: 2 }, content: [{ type: 'text', text: 'Scope' }] },
|
||||
{ type: 'paragraph', content: [{ type: 'text', text: 'What is in scope / out of scope?' }] },
|
||||
{ type: 'heading', attrs: { level: 2 }, content: [{ type: 'text', text: 'Stakeholders' }] },
|
||||
{ type: 'bulletList', content: [{ type: 'listItem', content: [{ type: 'paragraph', content: [{ type: 'text', text: '' }] }] }] },
|
||||
{ type: 'bulletList', content: [{ type: 'listItem', content: [{ type: 'paragraph', content: [] }] }] },
|
||||
{ type: 'heading', attrs: { level: 2 }, content: [{ type: 'text', text: 'Timeline' }] },
|
||||
{ type: 'paragraph', content: [{ type: 'text', text: 'Key milestones and deadlines.' }] },
|
||||
{ type: 'heading', attrs: { level: 2 }, content: [{ type: 'text', text: 'Risks' }] },
|
||||
{ type: 'bulletList', content: [{ type: 'listItem', content: [{ type: 'paragraph', content: [{ type: 'text', text: '' }] }] }] },
|
||||
{ type: 'bulletList', content: [{ type: 'listItem', content: [{ type: 'paragraph', content: [] }] }] },
|
||||
],
|
||||
},
|
||||
},
|
||||
|
|
@ -77,11 +77,11 @@ const BUILT_IN_TEMPLATES: ReadonlyArray<BuiltInSpec> = [
|
|||
content: [
|
||||
{ type: 'heading', attrs: { level: 1 }, content: [{ type: 'text', text: 'Daily Standup' }] },
|
||||
{ type: 'heading', attrs: { level: 2 }, content: [{ type: 'text', text: 'Yesterday' }] },
|
||||
{ type: 'bulletList', content: [{ type: 'listItem', content: [{ type: 'paragraph', content: [{ type: 'text', text: '' }] }] }] },
|
||||
{ type: 'bulletList', content: [{ type: 'listItem', content: [{ type: 'paragraph', content: [] }] }] },
|
||||
{ type: 'heading', attrs: { level: 2 }, content: [{ type: 'text', text: 'Today' }] },
|
||||
{ type: 'bulletList', content: [{ type: 'listItem', content: [{ type: 'paragraph', content: [{ type: 'text', text: '' }] }] }] },
|
||||
{ type: 'bulletList', content: [{ type: 'listItem', content: [{ type: 'paragraph', content: [] }] }] },
|
||||
{ type: 'heading', attrs: { level: 2 }, content: [{ type: 'text', text: 'Blockers' }] },
|
||||
{ type: 'bulletList', content: [{ type: 'listItem', content: [{ type: 'paragraph', content: [{ type: 'text', text: '' }] }] }] },
|
||||
{ type: 'bulletList', content: [{ type: 'listItem', content: [{ type: 'paragraph', content: [] }] }] },
|
||||
],
|
||||
},
|
||||
},
|
||||
|
|
@ -95,11 +95,11 @@ const BUILT_IN_TEMPLATES: ReadonlyArray<BuiltInSpec> = [
|
|||
content: [
|
||||
{ type: 'heading', attrs: { level: 1 }, content: [{ type: 'text', text: 'Weekly Review' }] },
|
||||
{ type: 'heading', attrs: { level: 2 }, content: [{ type: 'text', text: 'Wins' }] },
|
||||
{ type: 'bulletList', content: [{ type: 'listItem', content: [{ type: 'paragraph', content: [{ type: 'text', text: '' }] }] }] },
|
||||
{ type: 'bulletList', content: [{ type: 'listItem', content: [{ type: 'paragraph', content: [] }] }] },
|
||||
{ type: 'heading', attrs: { level: 2 }, content: [{ type: 'text', text: 'Challenges' }] },
|
||||
{ type: 'bulletList', content: [{ type: 'listItem', content: [{ type: 'paragraph', content: [{ type: 'text', text: '' }] }] }] },
|
||||
{ type: 'bulletList', content: [{ type: 'listItem', content: [{ type: 'paragraph', content: [] }] }] },
|
||||
{ type: 'heading', attrs: { level: 2 }, content: [{ type: 'text', text: 'Next Week Priorities' }] },
|
||||
{ type: 'taskList', content: [{ type: 'taskItem', attrs: { checked: false }, content: [{ type: 'paragraph', content: [{ type: 'text', text: '' }] }] }] },
|
||||
{ type: 'taskList', content: [{ type: 'taskItem', attrs: { checked: false }, content: [{ type: 'paragraph', content: [] }] }] },
|
||||
],
|
||||
},
|
||||
},
|
||||
|
|
|
|||
|
|
@ -157,7 +157,7 @@ export class TemplateService {
|
|||
${dto.icon ?? null},
|
||||
${dto.coverUrl ?? null},
|
||||
${dto.category ?? null},
|
||||
${JSON.stringify(content)}::jsonb,
|
||||
${content as unknown as string}::jsonb,
|
||||
${dto.sourcePageId ?? null},
|
||||
false,
|
||||
${userId}
|
||||
|
|
@ -227,7 +227,7 @@ export class TemplateService {
|
|||
}
|
||||
|
||||
const contentParam = dto.content
|
||||
? sql`${JSON.stringify(dto.content)}::jsonb`
|
||||
? sql`${dto.content as unknown as string}::jsonb`
|
||||
: sql`content`;
|
||||
|
||||
const result = await sql<TemplateDto>`
|
||||
|
|
@ -331,7 +331,7 @@ export class TemplateService {
|
|||
${workspaceId},
|
||||
${userId},
|
||||
${userId},
|
||||
${JSON.stringify(content)}::jsonb,
|
||||
${content as unknown as string}::jsonb,
|
||||
${textContent ?? ''},
|
||||
${ydoc},
|
||||
${nextPosition},
|
||||
|
|
@ -461,7 +461,7 @@ export class TemplateService {
|
|||
${spec.description},
|
||||
${spec.icon ?? null},
|
||||
${spec.category},
|
||||
${JSON.stringify(spec.content)}::jsonb,
|
||||
${spec.content as unknown as string}::jsonb,
|
||||
true,
|
||||
${systemUserId}
|
||||
)
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue