feat: forms builder - #67
Merged
Merged
Conversation
…ite instead of the branchpoint
… add response/answer schemas
…link-aware permission checks
…r and add status/description/membership-trigger fields
…eates_membership_on_submit
… and nested creation
…nt-wide uniqueness
…ow non-members on creates_membership_on_submit forms
…drop shift_select type
…ead presets.py, align routes on payload naming
…next_field_id/action
…orms and dangling branches
… only on reachable fields
… fix pre-existing config-shape test breakage
…rs a tall question
…n opt-in ButtonGroup prop
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
|
🚅 Deployed to the nexus-pr-67 environment in nexus
|
This was
linked to
issues
Aug 24, 2026
Closed
…id, drop the broken sync test suite
…ield id doesn't fail validation
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Builds NEXUS's own form system as a replacement for the external Google Form + sheet-sync pipeline — models, routes, branching/validation, edit lifecycle, and availability/lunch write-through on the backend, plus the TD-facing form builder and a respondent-facing preview page on the frontend. This is one PR across the Forms tracking issues (#63 parent; closes #56, #57, #62, #64, #66).
Forms is deliberately "dumb" — it has no concept of tournament onboarding or membership side effects. Nothing in this PR lets a new member actually encounter and fill out a form as part of joining a tournament; that orchestration is a separate, later piece of work (see Out of scope).
What changed
Backend — core model, branching, validation, edit lifecycle
Form/FormField/FormResponse/FormAnswer, owned by exactly one of tournament or chapter (ck_form_owner_exclusive),status: draft / published / archived.configvalidation (pydantic schemas perquestion_type), reservedfield_key↔question_typeenforcement (availability_*,event_preference_*,lunch_{date}_{category}), option/branching validation, whole-form publish-time validation with reachability checks over the branching graph.PUT /forms/{id}/fields/replaces per-field CRUD entirely — full ordered target list, diffed server-side. Draft forms apply changes directly; published forms archive-not-delete removed/type-changed fields and options instead of losing them, and flag affected responses viaFormResponsePendingUpdaterather than silently going stale.option_idis server-generated and durable — used for branching targets, archiving, and write-through matching instead of array position or label text.Backend — availability & lunch write-through
TournamentMembershipAvailabilityandTournamentMembershipLunchtables. Reserved-key answers (availability_*,lunch_{date}_{category}) diff-sync into them on response submission — only the delta is touched, not a replace-all.TournamentShiftdeletion is blocked while referenced by a live availability option, in addition to the pre-existing guard for shifts someone's already answered with.Backend — chapter support
owner_type: "chapter"is fully implemented at the route layer:POST/GET /chapters/{id}/forms/, gated on officer/lead, covered intest_forms.py. No chapter-side frontend consumes it yet (see Out of scope).Frontend — form builder
/forms/{id}/editand/forms/{id}/preview, deliberately outsideapp/dashboard/since a form's URL doesn't encode which owner (tournament vs. chapter) it belongs to.Save.QuestionRendererin fill mode, floating submit bar with error summary and leave-warning, auto-scroll to the first error/next question.RadioCircle,RadioList,CheckboxList,RankedList,EditableText, reworkedSplitButton(used for the draft/publish/archive status control),FloatingSaveBarextensions,Combobox/Input/Textarea/Cardfixes.Frontend — draft / publish / archive
StatusControlwiresPATCH /forms/{id}/(draft→published, validated) andPOST /forms/{id}/archive/. This is what's currently built and working, not a settled design — see Out of scope.Out of scope
GET /forms/{id}/responses/exists on the backend, but how that data actually gets displayed depends on decisions the onboarding PR still needs to make about response/answer shape.Test plan
backend/tests/api/test_forms.py,backend/tests/core/test_forms.py,test_form_branching.py,test_form_validation.py,test_form_write_through.py— model/route CRUD, config validation, branching reachability, edit-lifecycle archiving, availability/lunch diff-sync, shift-deletion guards, chapter routes.backend/tests/api/tournament/test_shifts.py,test_memberships.py— updated for the availability write-through/deletion-guard changes.