Skip to content

Instantly share code, notes, and snippets.

@panchicore
Last active February 20, 2026 23:16
Show Gist options
  • Select an option

  • Save panchicore/8828d33f9ec07ee254f5db15a7567126 to your computer and use it in GitHub Desktop.

Select an option

Save panchicore/8828d33f9ec07ee254f5db15a7567126 to your computer and use it in GitHub Desktop.
Plan v2: Wire Document Outline Editor to Existing Content Block APIs

Plan v2: Wire Document Outline Editor to Existing Content Block APIs

Discovery

The backend APIs for content block mutations already exist — no new backend code needed.

Existing Backend Endpoints (routes/ui_model_document_blocks.py)

Method Endpoint Action Handler
POST /inventory-models/<cuid>/documents/<slug>/blocks Add block InventoryModel.add_block()
PATCH /inventory-models/<cuid>/documents/<slug>/blocks Remove block InventoryModel.remove_block()
POST /inventory-models/<cuid>/documents/<slug>/blocks/from-library Add from library InventoryModel.add_block() + Metadata.log_from_json()

Existing Frontend API Functions (api/API.ts)

Function Line Signature
AddBlockToSection 2132 (inventoryModel, documentSlug, templateSection, index, content)
RemoveBlockFromSection 2147 (inventoryModel, documentSlug, templateSection, index, content)

Payload Format

Add:

{ "templateSection": { "id": "...", "title": "...", ... }, "index": 0, "content": { "content_id": "...", "content_type": "text|guideline|assessment_summary" } }

Remove:

{ "templateSection": { "id": "...", "title": "...", ... }, "index": 0, "content": { "content_id": "..." } }

What Needs to Change (Frontend Only)

1. Accordion: Add API-backed content override props

File: src/components/TemplateEditor/Accordion/index.tsx

Replace outlineOnly prop with content override handlers (same pattern as section ops):

interface AccordionProps {
  // ... existing props ...
  onAddContentBlock?: (sectionId: string, content: TemplateSectionContents, index: number) => void;
  onRemoveContentBlock?: (sectionId: string, content: TemplateSectionContents) => void;
}

Gate the 6 content handlers with override check:

  • handleAddBlocks (line ~388) → if onAddContentBlock, call it per block, else local
  • handleAddGuidelines (line ~269) → same
  • handleAddAssessmentSummary (line ~333) → same
  • handleRemoveBlock (line ~426) → if onRemoveContentBlock, call it, else local
  • handleRemoveGuideline (line ~303) → same
  • handleRemoveAssessmentSummary (line ~358) → same

Remove outlineOnly prop (no longer needed — content menu can show when handlers are wired).

2. Document Overview: Wire mutations to existing APIs

File: src/pages/ModelInventoryOverview/Overview/Main/index.tsx

Add two useMutation hooks calling existing API functions:

const addBlock = useMutation(async (params: { section: TemplateSectionTree, content: TemplateSectionContents, index: number }) => {
  return API.AddBlockToSection(inventoryModel, documentSlug, params.section, params.index, params.content);
}, { onSuccess: () => refetchDocument() });

const removeBlock = useMutation(async (params: { section: TemplateSectionTree, content: TemplateSectionContents }) => {
  return API.RemoveBlockFromSection(inventoryModel, documentSlug, params.section, 0, params.content);
}, { onSuccess: () => refetchDocument() });

Add useCallback wrappers that find the section from editorSectionTree by id, then call the mutation.

Pass onAddContentBlock and onRemoveContentBlock to <Accordion>. Remove outlineOnly={true}.

3. For block library blocks specifically

The from-library endpoint needs the blockLibraryItem CUID. The Accordion's handleAddBlocks already receives TBlockLibraryItem[] with cuid. The override handler should call the /blocks/from-library endpoint when options.block_key is present.


Files to Modify

File Change
src/components/TemplateEditor/Accordion/index.tsx Replace outlineOnly with content override props, gate handlers
src/pages/ModelInventoryOverview/Overview/Main/index.tsx Add useMutation hooks, wire to <Accordion>

No backend changes.

Verification

  1. Open document overview → Edit Outline → click + on leaf section → CONTENT menu shows
  2. Add a Text Block from library → persists on refresh
  3. Add a Guideline Block → persists on refresh
  4. Add Assessment Summary → persists on refresh
  5. Remove each block type → persists on refresh
  6. Template Editor in Settings still works (local-only, no regression)

Payload Compatibility Audit

Backend _update_content_block() expects:

  • on_template_section: either a string (section ID, backend looks it up) or full section dict (needs id + title)
  • content_data: dict with content_id (required), content_type, options (optional)
  • position_index: None → append at end, -1 → prepend, N → insert after index N

Frontend AddBlockToSection / RemoveBlockFromSection send:

{ "templateSection": TemplateSection, "index": number, "content": TemplateSectionContents }

These need a full TemplateSection object (not just id string) and an index number.

Gap Analysis per Handler

Handler Data available What API needs Gap
handleAddGuidelines(sectionId, guidelines[]) sectionId (string), content_id per guideline full section, index, content Need section lookup + index
handleAddAssessmentSummary(sectionId) sectionId (string) full section, index=-1, content Need section lookup
handleAddBlocks(sectionId, blocks[]) sectionId (string), TBlockLibraryItem[] with .cuid + .name full section, index, content, blockLibraryItem CUID Need section lookup + must use /from-library endpoint
handleRemoveGuideline(sectionId, guideline) sectionId, guideline.content_id full section, content {content_id} Need section lookup
handleRemoveAssessmentSummary(sectionId) sectionId full section, content {content_id: 'assessment_summary'} Need section lookup
handleRemoveBlock(sectionId, blockKey, contentId) sectionId, blockKey, contentId full section, content {content_id} Need section lookup

Resolution

The override callbacks need access to the full section object, not just sectionId. Two options:

Option A (recommended): Override callbacks receive (sectionId, ...) and the Document Overview page looks up the full section from editorSectionTree using a helper:

const findSection = (id: string): TemplateSectionTree | undefined => {
  const flat = flattenTrees(editorSectionTree);
  return flat.find(s => s.id === id);
};

Option B: Backend already supports on_template_section as a string (section ID) — line 3080. But the route layer in ui_model_document_blocks.py passes payload["templateSection"] directly. Would need to verify the backend handles string-only section input through the route → handler path. Risk: the route model validates templateSection as fields.Raw(required=True) so a string should pass, and _update_content_block handles strings at line 3080.

Additional Notes

  1. from-library endpoint is required for text blockshandleAddBlocks uses TBlockLibraryItem with .cuid. Must call /blocks/from-library (not /blocks) to create Metadata records. Payload: { templateSection, index, content, blockLibraryItem: block.cuid }

  2. Assessment summary uses index: -1 — backend interprets this as "insert at position 0" (prepend)

  3. Guidelines/text blocks use index: null — backend interprets position_index=None as "append at end". But the API sends index as a number. The route passes payload["index"]on_position_index=index. Need to send a value that maps to append. Looking at the backend: if position_index is None → append. So we can send index: -2 or any value and handle it, OR we rely on the fact that index defaults to end-of-list. Actually: add_block() default is on_position_index=-1 which means "insert at beginning". To append, we need None. But the route always passes payload["index"]. Fix: send index: <length of contents array> to insert at the end position.

  4. Remove backend matches by content_id only (line 3157) — does NOT use content_type or block_key. Pre-existing behavior, same as Template Editor. Low risk since content_id is typically unique within a section.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment