8.2 KiB
Plan: Dynamic Custom Forms via Child Table
Context
Currently, custom forms are hardcoded to 3 types (Event Feedback, Talk Proposal, Sponsorship Enquiry) via CUSTOM_FORM_CONFIG dict in event_manager/api.py. Each form type requires a toggle field, deadline field, success message field on Pohodex Event Manager Event, a hardcoded config entry, and a dedicated Vue wrapper page + route. This doesn't scale. We want users to attach any DocType as a publishable form on their event via a child table.
Decision: Migrate all 3 existing forms into the child table system (no dual system). Allow any DocType — no validation on event/additional_fields fields existing.
Changes
1. New Child Table DocType: Pohodex Event Manager Event Form
New directory: event_manager/events/doctype/buzz_event_form/
| Field | Type | Notes |
|---|---|---|
form_doctype |
Link (DocType) | Which DocType to render as a form |
publish |
Check | Whether the form is live |
auto_close_at |
Datetime | Auto-close submissions after this time |
route |
Data, reqd, in_list_view | URL slug (e.g. feedback, propose-talk) |
success_message |
Markdown Editor | Shown after successful submission |
istable: 1, parent is Pohodex Event Manager Event- Controller validates
routeuniqueness within the event
2. Update Pohodex Event Manager Event DocType
File: event_manager/events/doctype/buzz_event/buzz_event.json
- Add
custom_formschild table field (Table → Pohodex Event Manager Event Form) in the Forms section - Remove the now-redundant fields:
accept_event_feedback,accept_talk_proposals,accept_sponsorship_enquiries(toggle checks)talk_proposals_close_at,sponsorship_proposals_close_at(deadlines —registrations_close_atstays, it's for ticketing)feedback_success_message,proposal_success_message,sponsorship_success_message(success messages)
- Keep
auto_closures_sectionandregistrations_close_at(those are for ticket registration, not forms)
3. Update Pohodex Event Manager Custom Field applied_to
File: event_manager/event_manager/doctype/buzz_custom_field/buzz_custom_field.json
- Add
Custom Formoption toapplied_toSelect:"Booking\nTicket\nOffline Payment Form\nCustom Form" - Remove the 3 hardcoded options:
Event Feedback,Talk Proposal,Sponsorship Enquiry - Add new field
custom_form_doctype(Link → DocType):depends_on:eval:doc.applied_to === 'Custom Form'mandatory_depends_on:eval:doc.applied_to === 'Custom Form'- Label: "Custom Form DocType"
- When filtering custom fields in the API, match on
applied_to == "Custom Form"ANDcustom_form_doctype == <the form's DocType>
4. Backend API Changes
File: event_manager/api.py
4a. Replace CUSTOM_FORM_CONFIG with a universal STANDARD_EXCLUDE_FIELDS set.
STANDARD_EXCLUDE_FIELDS = {
"name", "owner", "creation", "modified", "modified_by",
"docstatus", "idx", "additional_fields", "event",
"section_break_additional",
}
This covers meta/internal fields that should never appear on a public form. get_form_fields already handles hidden, read-only, and layout fields. Per-form config (deadline, success message, route, enabled) now comes from the child table row.
4b. Update validate_custom_form(event_route, form_route)
- Change signature: accept
form_route(the slug) instead ofform_type(DocType name) - Look up event by route, then find matching row in
event_doc.custom_formswhereroute == form_routeandpublish == 1 - Check event is published
- Return the matched child table row + event_doc
4c. Update get_custom_form_data(event_route, form_route)
- Get the child row via validate
form_type=row.form_doctype(the DocType name)- Build exclude_fields dynamically: standard meta fields (
name,owner,creation,modified,modified_by,docstatus,idx) +additional_fields+event+ any Section/Column break containingadditional - Deadline: check
row.auto_close_atdirectly - Custom fields: filter
Pohodex Event Manager Custom Fieldwhereapplied_to == "Custom Form"ANDcustom_form_doctype == form_typeANDevent == event_doc.nameANDenabled == 1 - Success message: from
row.success_message - Auto-set: if DocType has an
eventfield → auto-set from route; if it hassubmitted_by→ auto-set from session user
4d. Update submit_custom_form(event_route, form_route, data, custom_fields_data)
- Same flow but config comes from child table row
- DocType to create =
row.form_doctype - Auto-detect
eventandsubmitted_byfields from DocType meta
4e. New API: get_event_forms(event_route)
- Returns list of published forms:
[{route, doctype, label (from DocType meta)}] - Useful for frontend to build navigation/links to available forms
4f. Update get_form_fields
- Make the exclude set a parameter with sensible defaults (the standard fields)
5. Frontend Changes
File: dashboard/src/router.ts
- Remove the 3 hardcoded form routes (
/events/:eventRoute/feedback, etc.) - Add single dynamic route:
/events/:eventRoute/forms/:formRoute→CustomFormPage.vuemeta: { isPublic: true },props: true
New file: dashboard/src/pages/CustomFormPage.vue
- Props:
eventRoute,formRoute - Calls
get_custom_form_data(event_route, form_route)— the API now accepts route slug - Derives title from DocType label (returned by API)
- Passes to
BaseCustomEventForm(which needs minor update to acceptformRouteinstead offormType)
File: dashboard/src/components/BaseCustomEventForm.vue
- Update props: accept
formRoute(route slug) instead offormType(DocType name) - Update resource calls to pass
form_routeinstead ofform_type - Everything else stays the same — it already renders fields generically
Delete:
dashboard/src/pages/FeedbackForm.vuedashboard/src/pages/ProposeTalkForm.vuedashboard/src/pages/EnquireSponsorshipForm.vue
Files Summary
| File | Action | Change |
|---|---|---|
event_manager/events/doctype/buzz_event_form/ |
New | Child table DocType (4 files) |
event_manager/events/doctype/buzz_event/buzz_event.json |
Modify | Add custom_forms table, remove 8 form-related fields |
event_manager/event_manager/doctype/buzz_custom_field/buzz_custom_field.json |
Modify | Replace 3 hardcoded options with "Custom Form" + add custom_form_doctype Link |
event_manager/api.py |
Modify | Remove CUSTOM_FORM_CONFIG, rewrite validate/get/submit to use child table, add get_event_forms |
dashboard/src/router.ts |
Modify | Replace 3 routes with 1 dynamic route |
dashboard/src/pages/CustomFormPage.vue |
New | Single dynamic form wrapper |
dashboard/src/components/BaseCustomEventForm.vue |
Modify | Accept formRoute instead of formType |
dashboard/src/pages/FeedbackForm.vue |
Delete | No longer needed |
dashboard/src/pages/ProposeTalkForm.vue |
Delete | No longer needed |
dashboard/src/pages/EnquireSponsorshipForm.vue |
Delete | No longer needed |
Implementation Order
- Create
Pohodex Event Manager Event Formchild table DocType - Update Pohodex Event Manager Event DocType (add child table, remove old fields) +
bench migrate - Update Pohodex Event Manager Custom Field (new option + link field) +
bench migrate - Rewrite
event_manager/api.py— remove hardcoded config, dynamic resolution from child table - Frontend: new route +
CustomFormPage.vue+ updateBaseCustomEventForm.vue+ delete old wrappers - Test end-to-end
Verification
- On a Pohodex Event Manager Event, add "Event Feedback" to
custom_formswith route=feedback, publish=1 - Add "Talk Proposal" with route=
propose-talk, publish=1, auto_close_at=future - Navigate to
/dashboard/events/{eventRoute}/forms/feedback— verify form renders - Submit feedback → verify doc created with correct event link
- Add Pohodex Event Manager Custom Fields with
applied_to="Custom Form",custom_form_doctype="Event Feedback"→ verify they appear on the form - Set
auto_close_atto past → verify form shows closed message - Create a brand new DocType, add it to custom_forms → verify it works as a form
- Unpublish a form → verify 404