Skip to content

Conversation

@mmcintosh
Copy link
Contributor

Description

Integrates form submissions into the unified content management system. Each form auto-creates a "shadow collection" in the
collections table. When a submission arrives, it dual-writes to both form_submissions (preserving metadata like IP, user agent, UTM)
and content (enabling unified listing). Form submissions appear at /admin/content, filterable by model, status, and searchable — just
like regular content.

Changes

Database Migration (032_form_content_integration.sql)

  • Add source_type and source_id columns to collections for form-derived collections
  • Add content_id column to form_submissions to link submissions to content items
  • Create system-form-submission user for anonymous submissions (satisfies author_id FK constraint)

New Service (form-collection-sync.ts)

  • Derives collection JSON schema from Form.io component definitions
  • Creates/updates shadow collections (form_{name}) for each form
  • Dual-writes form submissions to content table with _submission_metadata
  • Backfills existing submissions that lack content items on bootstrap
  • Maps form statuses to content statuses (submissions default to published)

Bootstrap Integration

  • syncAllFormCollections() runs after syncCollections() on worker startup
  • Ensures all forms have shadow collections and backfills existing submissions

Admin UI Updates

  • Content list: "Form" badge on model column for form-sourced content
  • Content edit: read-only Submission Info panel (form name, email, IP, user agent, timestamp)
  • Model filter: form collections appear in the content filter dropdown
  • Submissions page: /admin/forms/:id/submissions redirects to /admin/content?model=form_{name}

Collection Isolation

  • Form-derived collections hidden from /admin/collections page
  • Form-derived collections excluded from new-content picker (users can't manually create content in form collections)
  • getManagedCollections() excludes source_type='form' so cleanupRemovedCollections won't deactivate shadow collections

Form API Enhancement

  • PUT /admin/forms/:id now accepts optional turnstile_enabled and turnstile_settings fields

Testing

Unit Tests

  • Added/updated unit tests
  • All unit tests passing (1237 tests)

E2E Tests

  • Added/updated E2E tests (53-forms-as-content.spec.ts — 12 tests)
  • All E2E tests passing (282 E2E tests, 0 failures)

E2E coverage:

  • Create form with real Form.io fields (contact, feedback), set schema, disable turnstile via API
  • Submit form data via public API, verify content item created with correct title
  • Verify Form badge on content list model column
  • Verify submission metadata panel in content edit view
  • Verify submissions page redirects to content list
  • Verify form collections hidden from collections page and new-content picker
  • Verify content search finds form submissions by name
  • Verify published status default for submissions
  • Verify multiple form models appear in content filter

Screenshots/Videos

New-Forms 1- SonicJS AI Admin New-Content-Management-Form- SonicJS AI Admin

Checklist

  • Code follows project conventions
  • Tests added/updated and passing
  • Type checking passes
  • No console errors or warnings
  • Documentation updated (if needed)

mmcintosh and others added 9 commits January 31, 2026 13:46
Form submissions now dual-write to both form_submissions and content
tables, enabling unified content listing with filtering, search, and
status management. Each form auto-creates a shadow collection, and
the submissions page redirects to the content list filtered by form.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
…ation

getManagedCollections() now filters by source_type to only return
config-managed collections (user or NULL). Form-derived shadow
collections (source_type='form') are managed by form-collection-sync
and would be incorrectly deactivated by cleanupRemovedCollections
since they don't appear in loadCollectionConfigs().

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Tests cover shadow collection creation, submissions-to-content redirect,
form collection exclusion from new-content picker, Form badge display,
submission metadata panel, content search, and status filtering.
Submission-dependent tests gracefully skip when Turnstile is enabled.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Form-derived collections are an implementation detail managed through
the forms UI. They should not appear on /admin/collections alongside
user-defined collections. Filter by source_type to exclude them.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Extend PUT /admin/forms/:id to accept optional turnstile_enabled and
turnstile_settings fields. Rewrite E2E tests to create forms with real
Form.io schemas (contact form with name/email/subject/message, feedback
form with rating selector), disable turnstile per-form, submit actual
data, and verify content items appear with correct titles, Form badges,
submission metadata, draft status, and search results. All 12 tests pass.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
A form submission is complete data — it should never be in draft state.
Changed the default content status for new submissions from 'draft' to
'published'. Updated mapFormStatusToContentStatus so 'pending' maps to
'published' instead of 'draft'. Updated E2E test accordingly.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
The CI pipeline runs migrations from my-sonicjs-app/migrations/, not
packages/core/migrations/. Without this file, the source_type,
source_id, and content_id columns are never created, causing the
form-to-content dual-write to silently fail.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
D1 enforces foreign key constraints by default. The INSERT into content
with author_id='system-form-submission' fails if the system user doesn't
exist. Also auto-creates the shadow collection on-the-fly if it's missing
during form submission, instead of silently returning null.

- Check for and create system user at content creation time
- Auto-create shadow collection if lookup returns null
- Return contentId in submission API response
- Add diagnostic logging and retry logic to E2E tests

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

1 participant