Files
event-manager/plans/Completed/dynamic-custom-forms.md
T
xadm 786cbc724f
CI / Server (push) Has been cancelled
Linters / Frappe Linter (push) Has been cancelled
Linters / Vulnerable Dependency Check (push) Has been cancelled
UI Tests / Playwright E2E Tests (push) Has been cancelled
Initialize fork and rebrand app to event_manager
2026-05-11 09:56:57 +02:00

155 lines
8.2 KiB
Markdown

# 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 `route` uniqueness within the event
### 2. Update Pohodex Event Manager Event DocType
**File:** `event_manager/events/doctype/buzz_event/buzz_event.json`
- Add `custom_forms` child 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_at` stays, it's for ticketing)
- `feedback_success_message`, `proposal_success_message`, `sponsorship_success_message` (success messages)
- Keep `auto_closures_section` and `registrations_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 Form` option to `applied_to` Select:
`"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"` AND `custom_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.**
```python
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 of `form_type` (DocType name)
- Look up event by route, then find matching row in `event_doc.custom_forms` where `route == form_route` and `publish == 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 containing `additional`
- Deadline: check `row.auto_close_at` directly
- Custom fields: filter `Pohodex Event Manager Custom Field` where `applied_to == "Custom Form"` AND `custom_form_doctype == form_type` AND `event == event_doc.name` AND `enabled == 1`
- Success message: from `row.success_message`
- Auto-set: if DocType has an `event` field → auto-set from route; if it has `submitted_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 `event` and `submitted_by` fields 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.vue`
- `meta: { 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 accept `formRoute` instead of `formType`)
**File:** `dashboard/src/components/BaseCustomEventForm.vue`
- Update props: accept `formRoute` (route slug) instead of `formType` (DocType name)
- Update resource calls to pass `form_route` instead of `form_type`
- Everything else stays the same — it already renders fields generically
**Delete:**
- `dashboard/src/pages/FeedbackForm.vue`
- `dashboard/src/pages/ProposeTalkForm.vue`
- `dashboard/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
1. Create `Pohodex Event Manager Event Form` child table DocType
2. Update Pohodex Event Manager Event DocType (add child table, remove old fields) + `bench migrate`
3. Update Pohodex Event Manager Custom Field (new option + link field) + `bench migrate`
4. Rewrite `event_manager/api.py` — remove hardcoded config, dynamic resolution from child table
5. Frontend: new route + `CustomFormPage.vue` + update `BaseCustomEventForm.vue` + delete old wrappers
6. Test end-to-end
## Verification
1. On a Pohodex Event Manager Event, add "Event Feedback" to `custom_forms` with route=`feedback`, publish=1
2. Add "Talk Proposal" with route=`propose-talk`, publish=1, auto_close_at=future
3. Navigate to `/dashboard/events/{eventRoute}/forms/feedback` — verify form renders
4. Submit feedback → verify doc created with correct event link
5. Add Pohodex Event Manager Custom Fields with `applied_to="Custom Form"`, `custom_form_doctype="Event Feedback"` → verify they appear on the form
6. Set `auto_close_at` to past → verify form shows closed message
7. Create a brand new DocType, add it to custom_forms → verify it works as a form
8. Unpublish a form → verify 404