FormTemplate. If Mongo has no published bank, getPublishedOrJson() loads src/data/formQuestions.json.
Template statuses
draft | published | archived
version is unique. Publishing clones the draft into a new published version; existing forms keep questionBankVersion.
Super Admin routes
Authenticated users load the bank they should render:
GET /api/question-bank?schoolYear=2026-2027&version=3
Seed a plan from the published bank
Empty plans do not copy the question schema ontoFormSubmission. Super Admin can seed formData and pin questionBankVersion with POST /api/admin/forms/populate-bank.
Filled seeds mark checkboxes
true, yes/no yes (so gate questions stay visible), tables with two rows, and every step complete.
--answers writes sample responses instead of empty shells. Load env with node --env-file=.env.local when running outside Next.
How to configure a table question
Set Type to Table (Excel paste) on the question. Staff then get a grid on the plan (how they fill it). Column definitions live onquestion.columns and survive publish because FormTemplate questions are strict: false.
1
Open the draft question
Super Admin →
/admin/questions. Edit the step, find the question, set type to Table (Excel paste).2
Start from a preset (optional)
Click a preset to lock headers (and dropdowns, when the preset includes them):
3
Name columns, one per line
Column names is the blueprint. One header per line (or a single comma-separated line). Reorder by moving lines. Clear columns empties the list so staff can paste their own Excel headers on the form.
4
Make a column a dropdown
Either:
- In the blueprint, write
Certified | Yes, No(name, pipe, choices). Use;to separate choices that contain commas. - Or set Input to Dropdown and list options one per line under that column.
5
Publish
Save the draft, then publish with
{ confirm: true } so live plans pick up the columns. Existing answers keep their question.id; changing headers on a locked table reshapes the grid on the next edit (extra cells are trimmed, missing cells pad empty).Pipe syntax examples
Pipe syntax examples
parseColumnBlueprint in src/lib/tableAnswer.js is the same parser the admin editor and the form use.Migrate old contact notes into tables
Migrate old contact notes into tables
Year setup includes Contact table migrate. Dry-run a question id for a school year, review the JSON diff, then apply. Use this after you change a free-text contact question to type
table with a contact preset. Download the dry-run report before applying.Safe edits vs breaking edits
Safe
Safe
Change
title, placeholder, description, required, active, order, or table columns. Keep question.id unchanged.Unsafe
Unsafe
Renaming
id, deleting a question that already has answers, or reshuffling ids. Old formData[stepKey].data[oldId] would orphan.Publish requires
{ "confirm": true }. Optionally pass schoolYear to pin that version on SchoolYearSettings.questionBankVersion.