Skip to content

feat(events): application enrollment with form builder #243

@danielhe4rt

Description

@danielhe4rt

Parent

#237

What to build

Implement the Application enrollment method: participant fills a dynamic form, enrollment goes to pending, organizer approves or rejects in Admin panel.

Dynamic form schema:

  • events_enrollment_policies.application_form_schema stores a JSONB array defining form fields
  • Schema structure: [{type: "text|textarea|select|checkbox", label: "...", required: bool, options?: [...]}]
  • Filament form builder in Admin for organizer to define questions when creating/editing the policy
  • App panel renders the form dynamically from the schema using Filament form components

Modify EnrollUserAction (application path):

  • When enrollment_method = 'application':
    • Validates submitted data against the form schema
    • Creates enrollment with status pending, stores answers in application_data (JSONB)
    • Records transition (from=null, to=pending, triggered_by=user)
    • Does NOT check capacity yet (only checked on approval)

Action: ApproveApplicationAction

  • Validates: enrollment status must be pending
  • Checks capacity (with lockForUpdate) — if full + has_waitlist → waitlisted, if full + no waitlist → reject approval
  • Transitions to confirmed (or waitlisted), sets confirmed_at
  • Records transition (triggered_by=admin, actor_user_id=organizer)
  • Dispatches EnrollmentConfirmed domain event

Action: RejectApplicationAction

  • Validates: enrollment status must be pending
  • Transitions to rejected, stores rejection_reason
  • Records transition (triggered_by=admin, actor_user_id=organizer, reason=rejection_reason)

Admin panel:

  • "Pending Applications" view/filter on enrollments — shows application_data in a readable format
  • "Approve" and "Reject" actions (reject requires reason text field)
  • Application data displayed in enrollment detail view

App panel:

  • Dynamic form rendered from policy schema when user clicks "Apply"
  • Shows "Application pending" status with submitted data (read-only)
  • Shows "Rejected" status with rejection reason when applicable

Acceptance criteria

  • Organizer can define a dynamic form schema via Filament form builder in Admin
  • Participant sees and fills the dynamic form in App panel
  • Submission creates enrollment with status pending and application_data populated
  • Organizer can approve → enrollment transitions to confirmed (or waitlisted if full)
  • Organizer can reject with reason → enrollment transitions to rejected with reason stored
  • Capacity is checked on approval, not on submission
  • Application data is displayed in Admin enrollment detail
  • Transition audit trail records approver/rejector identity
  • Feature tests: submit application, approve, reject, approve when full (waitlist), approve when full (no waitlist)
  • Pint passes

Blocked by

Metadata

Metadata

Assignees

No one assigned

    Type

    No type
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions