Repository navigation
Add Sprint 3 forms schema, member types, and shared contracts - #304
Merged
Merged
Conversation
Six Sprint 3 tickets build on the same tables and types in parallel, so land them, and their one migration, before the sprint: - Forms tables (forms, form_components, form_submissions, form_answers, form_uploads) with relations, plus FormStatus and FormComponentType - lib/forms: per-type config and answer schemas, form settings, FormDefinition, and the submission payload - Config keys for forms, member types, auto-approved signups, and theme, with get/set for FormsEnabled (off by default) only; the others' handlers come with their tickets - members.member_type, registered with BetterAuth as server-only - Test helpers for forms, submissions, and uploads, and a fixture of the Visionaries camp application form_answers.value uses a jsonb column that doesn't re-parse strings: Drizzle's jsonb JSON.parses the already-parsed value, so an answer like "4045550123" read back as a number. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_017RYaE2zBcm7SfFBJdydeuR
✅ Deploy Preview for servicestart ready!
To edit notification comments on pull requests, go to your Netlify project configuration. |
Contributor
|
Disclaimer: This comment was AI-generated and is not necessarily completely accurate. Please take code comments with a grain of salt.
|
4 of 6 tasks
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_017RYaE2zBcm7SfFBJdydeuR
5 of 6 tasks
From review: - form_answers gets a form_id with composite foreign keys to the submission and the component, so an answer can't pair a submission with another form's question. - Deleting a user clears form_submissions.user_id and form_uploads.uploaded_by instead of deleting the org's responses. - FormSettingsSchema rejects unknown keys, like the config schemas. - createFormSubmission inserts the submission and its answers in one transaction. The migration is regenerated. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_017RYaE2zBcm7SfFBJdydeuR
Contributor
Author
|
Two red checks here are about the preview environment, not the code:
Generated by Claude Code |
4 of 6 tasks
This branch was successfully deployed
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.
Part of #295: the schema, shared types, config keys, migration, and test helpers. The theme tokens and navbar prefix matching were in #305 (merged). The Visionaries seed is in #306, which is stacked on this PR and #276 (merged).
Merge order: this PR, then #306 (retarget it to
mainafter this merges).What's here
lib/schema.ts:FormStatus,FormComponentType(the seven application types), andMemberType, each with a derivedpgEnum.forms,form_components,form_submissions,form_answers(unique per submission and component), andform_uploads(kept apart frommedia).lib/forms/schema.ts:FORM_COMPONENT_CONFIG_SCHEMAS) and the{ type, config }discriminated unionFORM_ANSWER_VALUE_SCHEMAS)FormSettingsSchema(strict) with defaults inlib/forms/constants.tsFormDefinition, which requires components in position order with unique IDsFormIdSchema, using [Forms] Build form management: service, admin API, forms list, and CSV export #303's rule: lowercase letters, numbers, and hyphens, up to 64 characters.FormsEnabled,MemberTypesEnabled,AutoApproveSignups, and the six theme keys. OnlyFormsEnabledhas a get/set (default off) and joinsToggleableOrganizationFeature. [Visionaries] Make the app theme configurable per organization #300 and [Visionaries] Add applicant and attendee member types, auto-approved signups, and admin invites #302 add the rest.members.member_type(nullable), registered in BetterAuth'smember.additionalFields.pnpm run db:generate.tests/unit/testUtils.ts:createForm(any status; defaults to a copy of the Visionaries application),createFormSubmission,createFormUpload, andbuildSampleAnswers.VISIONARIES_APPLICATION_FORMholds the 12 questions in the order from [Pre-sprint] Sprint 3 schema, shared contracts, and Visionaries org setup #295.Things sprint devs should know
formIdmeans two things.forms.formIdis the human key in URLs (camp-application-2027). TheformIdcolumns on the child tables referenceforms.id, the UUID. A slug passed where a UUID is expected fails loudly in Postgres, and the schema has comments on each column.form_answershas its ownformId, and composite foreign keys require the submission and the question to be on that form. PassformIdwhen inserting answers.jsonbcolumn validated byFormSettingsSchema. That way, adding a setting mid-sprint needs no migration. Open and close times are ISO strings, and unknown keys are rejected.OrganizationConfigServicenow accepts enum keys that have no handler yet:setConfigthrows "Invalid key" andgetConfigskips them. It also usesObject.hasOwn, so keys likeconstructorno longer crashgetConfig.memberTypeisinput: false. Clients can't set it through BetterAuth input, since [Visionaries] Add applicant and attendee member types, auto-approved signups, and admin invites #302 uses it to gate the member directory. Set it from server code instead, e.g. abeforeAddMemberhook that returns{ data: { memberType } }, orMembersService.jsonbbug: Drizzle'sjsonbre-parses strings node-postgres has already parsed, so a stored"4045550123"or"7"read back as a number.form_answers.valuenow uses ajsonbcustom type without that second parse. The SQL type is unchanged. A regression test covers bothselectand relational queries, and it fails on Drizzle's plainjsonb.form_submissions.user_idandform_uploads.uploaded_by, so the org keeps its responses. Deleting a question deletes its answers, but only drafts' questions get deleted, and drafts have no answers.Testing
FormDefinition, table constraints and cascades (including cross-form answers and user deletion),FormsEnableddefaults and isolation, andmemberTyperead and input stripping.pnpm lint,pnpm format,tsc --noEmit, andpnpm run db:check.CD / migrate-dbpass. The preview DB was rebuilt, and it migrates and seeds with this migration.Setup Preview / create-dbfails only at its last step, the Juno email registration, as it does on other PRs.Checklist:
🤖 Generated with Claude Code
https://claude.ai/code/session_017RYaE2zBcm7SfFBJdydeuR