Owner Burhan

Table Repository — PRD

Table Repository

Scope

MVP — Tables become first-class, reusable content fundamentals:

  1. Create / edit a Table as a standalone entity (PlateJS table object), with cell background fill (fill the cell, not just text color), title (required), hide_title, and optional description.
  2. Repository list + search — browse all Tables; search by short_uid and title.
  3. Insert inline into a Block or MCQ — reference a repository Table inside a Block’s or MCQ’s PlateJS rich text via a custom Table-reference tag (the same pattern as the existing inline-image / image-bank flow), by short_uid.
  4. Cross-linking & “where used” — one Table referenceable by many Blocks/MCQs; the Table surfaces its linked_entities (where it is attached).
  5. Filter the list by L1 / L2 / L3 usage — via denormalized taxonomy usage on the Table (Option 1), derived from each linked entity’s immutable chain (see Consistency rule).
  6. Status lifecyclepublished / archived; no hard delete (archive is the only termination, per platform rule). A Table can be archived only when it has no linked entities (linked_entities is empty). While any linkage remains, archive is blocked — the editor is shown the list of linked Block/MCQs and must remove all linkages first, then archive.

Data Model (requirements-level)

The shape of a Table. Consistency rule (decided): all denormalized fields are maintained synchronously through a single write path — one operation updates the entity’s rich text (PlateJS ref node), linked_table_ids, linked_entities, and used_in_l*_ids together. No async / background recompute. The only trigger is link / unlink of a Table to a Block/MCQ — Block→Docket moves and Docket re-parenting do not exist (disallowed by the platform), so a linked entity’s taxonomy chain is immutable and used_in_l*_ids never needs re-deriving after the fact.

FieldTypeNotes
idstring (ObjectId)System-generated PK.
short_uidstringSystem-generated; prefix TBL (e.g. TBL5RT8W). Unique per Course.
course_idintCourse scope (UPSC=1, TEST=80085).
titlestringRequired.
hide_titlebooleanIf true, title is not rendered as a caption.
descriptionstringOptional.
table_contentstringPlateJS table JSON (same structure as an inline Block table; supports cell fill).
linked_entitiesarray[{ content_id, content_type: "block" | "mcq" }] — where the Table is attached (denormalized to save read cost).
used_in_l1_ids / used_in_l2_ids / used_in_l3_idsarrayDenormalized taxonomy usage derived from linked_entities chain; powers the L1/L2/L3 filter (Option 1).
statusenumpublished | archived.
created_at / updated_atint (Unix ms)Audit.
created_by / updated_bystring (User ObjectId)Audit refs.
is_deletedbooleanLegacy soft-delete; deferred to Sync convention.

Block & MCQ changes

  • linked_table_ids — on both Block and MCQ — denormalized list of Tables referenced in that entity’s rich text (the inverse side of Table.linked_entities; both stored to save read cost).
  • A custom PlateJS Table-reference node in the Block’s / MCQ’s rich text (mirrors the inline-image ref node), which drives linked_table_ids.

Delivery Milestones

#MilestoneOutcomeStatusPlan
1Table entity + repository storeEditors can create, edit, and list standalone Tables (with cell fill)pending
2SearchEditors find a Table by short_uid or titlepending
3Inline reference into Block & MCQEditors insert a repository Table into a Block or MCQ via a PlateJS Table tag; linked_table_ids / linked_entities stay in syncpending
4Cross-linking & “where used”A Table shows every Block/MCQ it is attached topending
5L1/L2/L3 usage filterEditors filter the Table list by taxonomy of usage (denormalized)pending
6Status lifecycleTables can be published/archived; no hard deletepending

Open Questions

  • None remaining — all resolved during PRD review.

Risks

RiskLikelihoodImpactMitigation
Ref-node ↔ denormalization drift. PlateJS Table ref nodes in a Block/MCQ’s rich text and the denormalized lists (linked_table_ids, linked_entities, used_in_l*_ids) can diverge if updated separately.MediumMedium — wrong “where used” / filter resultsSingle write path (confirmed): one atomic operation updates rich text + all denormalized lists together; validate on save.
Archive friction. A Table reused across many entities can only be archived after every reference is unlinked by hand.LowLow — editor effortThe “where used” view (milestone 4) lists every linkage so the editor can unlink quickly before archiving.