Initialize fork and rebrand app to event_manager
This commit is contained in:
@@ -0,0 +1,183 @@
|
||||
# Custom Forms Feature - Implementation Plan
|
||||
|
||||
## Context
|
||||
|
||||
Currently, Talk Proposals and Sponsorship Enquiries use Frappe Web Forms, and Event Feedback has a minimal DocType with no frontend form. We want to replace these with dashboard-based Vue forms using a shared `BaseCustomEventForm.vue` that renders doctype fields + Pohodex Event Manager Custom Fields, with a common success state. This removes the dependency on Frappe Web Forms and gives us full control over UX.
|
||||
|
||||
## Architecture
|
||||
|
||||
```
|
||||
Routes:
|
||||
/dashboard/events/:eventRoute/feedback → FeedbackForm.vue
|
||||
/dashboard/events/:eventRoute/propose-talk → ProposeTalkForm.vue
|
||||
/dashboard/events/:eventRoute/enquire-sponsorship → EnquireSponsorshipForm.vue
|
||||
|
||||
Components:
|
||||
FeedbackForm.vue ──────────┐
|
||||
ProposeTalkForm.vue ────────┼──► BaseCustomEventForm.vue
|
||||
EnquireSponsorshipForm.vue ─┘ │
|
||||
├── CustomFieldInput.vue (reused)
|
||||
├── CustomFieldsSection.vue (reused)
|
||||
└── FormSuccess.vue (new, inline success state)
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Phase 1: Backend DocType Schema Updates
|
||||
|
||||
### 1.1 Update Event Feedback DocType
|
||||
**File:** `event_manager/events/doctype/event_feedback/event_feedback.json`
|
||||
- Add only `additional_fields` (Table → Additional Field) — keep the DocType minimal for now
|
||||
- Add permission: `Pohodex Event Manager User` role with `create: 1, read: 1` (if_owner)
|
||||
|
||||
### 1.2 Add `additional_fields` to Talk Proposal & Sponsorship Enquiry
|
||||
**Files:**
|
||||
- `event_manager/proposals/doctype/talk_proposal/talk_proposal.json` — add `additional_fields` (Table → Additional Field)
|
||||
- `event_manager/proposals/doctype/sponsorship_enquiry/sponsorship_enquiry.json` — add `additional_fields` (Table → Additional Field)
|
||||
|
||||
Reuses existing `Additional Field` child table (`event_manager/ticketing/doctype/additional_field/`).
|
||||
|
||||
### 1.3 Update Pohodex Event Manager Custom Field `applied_to` options
|
||||
**File:** `event_manager/event_manager/doctype/buzz_custom_field/buzz_custom_field.json`
|
||||
- Add options: `Event Feedback`, `Talk Proposal`, `Sponsorship Enquiry`
|
||||
|
||||
### 1.4 Run `bench migrate`
|
||||
|
||||
---
|
||||
|
||||
## Phase 2: Backend API Endpoints
|
||||
|
||||
**File:** `event_manager/api.py`
|
||||
|
||||
### 2.1 `get_custom_form_data(event_route, form_type)`
|
||||
- Whitelist check: `form_type` must be in `CUSTOM_FORM_CONFIG` dict
|
||||
- Resolve event from `event_route`
|
||||
- Check deadline (`talk_proposals_close_at`, `sponsorship_proposals_close_at`, none for feedback, show a nice closed message / banner if the form is closed)
|
||||
- Return `form_fields` (from doctype meta, excluding internal fields like `status`, `submitted_by`, `owner`, `event`), `custom_fields` (Pohodex Event Manager Custom Fields for this event + `applied_to`), `event` details, `closed` flag
|
||||
- we should also check if event itself is published or not, if not 404
|
||||
|
||||
### 2.2 `submit_custom_form(event_route, form_type, data, custom_fields_data)`
|
||||
- Whitelist check on `form_type`
|
||||
- Auto-set: `event` from route, `submitted_by` from session user (for Talk Proposal)
|
||||
- Validate submitted fields against allowed fieldnames
|
||||
- Create doc, append `additional_fields` rows for custom field values
|
||||
- Return `{name, doctype}`
|
||||
|
||||
Config dict:
|
||||
```python
|
||||
CUSTOM_FORM_CONFIG = {
|
||||
"Event Feedback": {
|
||||
"applied_to": "Event Feedback",
|
||||
"exclude_fields": {"name", "owner", "creation", "modified", "modified_by",
|
||||
"docstatus", "idx", "additional_fields", "event"},
|
||||
"auto_set": {"event": "from_route"},
|
||||
"deadline_field": None,
|
||||
},
|
||||
"Talk Proposal": {
|
||||
"applied_to": "Talk Proposal",
|
||||
"exclude_fields": {"name", "owner", "creation", "modified", "modified_by",
|
||||
"docstatus", "idx", "submitted_by", "status",
|
||||
"additional_fields", "event"},
|
||||
"auto_set": {"event": "from_route", "submitted_by": "session_user"},
|
||||
"deadline_field": "talk_proposals_close_at",
|
||||
},
|
||||
"Sponsorship Enquiry": {
|
||||
"applied_to": "Sponsorship Enquiry",
|
||||
"exclude_fields": {"name", "owner", "creation", "modified", "modified_by",
|
||||
"docstatus", "idx", "status", "additional_fields", "event"},
|
||||
"auto_set": {"event": "from_route"},
|
||||
"deadline_field": "sponsorship_proposals_close_at",
|
||||
},
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Phase 3: Frontend
|
||||
|
||||
### 3.1 Add routes in `dashboard/src/router.ts`
|
||||
Three new routes with `props: true`, marked `isPublic` but depending on weather the event has guest booking enabled or not, it should handle that
|
||||
|
||||
### 3.2 Create `BaseCustomEventForm.vue`
|
||||
**File:** `dashboard/src/components/BaseCustomEventForm.vue`
|
||||
|
||||
**Props:** `eventRoute`, `formType`, `title`, `successTitle`, `successMessage`
|
||||
|
||||
**Behavior:**
|
||||
1. Fetch form data via `createResource` → `event_manager.api.get_custom_form_data`
|
||||
2. If `closed`, show "submissions closed" message
|
||||
3. Render standard fields using `CustomFieldInput.vue` (normalized to same shape)
|
||||
4. Render Pohodex Event Manager Custom Fields via `CustomFieldsSection.vue`
|
||||
5. Submit via `event_manager.api.submit_custom_form`
|
||||
6. On success, show inline success state (no separate page)
|
||||
|
||||
**Special field type handling** (in BaseCustomEventForm, not CustomFieldInput):
|
||||
- `Text Editor` → frappe-ui `TextEditor`
|
||||
- `Attach Image` → frappe-ui `FileUploader` (upload first, set URL on doc)
|
||||
- `Link` → `Autocomplete` with debounced search on linked doctype (check apps/crm on how it renders link field n the CRM frontend, you might also find other types of fields being rendered in the Field.vue of CRM)
|
||||
- `Table` (e.g. speakers) → List view component showing added rows + "Add" button. Clicking "Add" opens a dialog with the child table's fields. On dialog submit, row is appended. No inline editing — edit button to open the dialog again.
|
||||
|
||||
### 3.3 Create wrapper page components
|
||||
- `dashboard/src/pages/FeedbackForm.vue` — passes `form-type="Event Feedback"`, default success strings
|
||||
- `dashboard/src/pages/ProposeTalkForm.vue` — passes `form-type="Talk Proposal"`, default success strings
|
||||
- `dashboard/src/pages/EnquireSponsorshipForm.vue` — passes `form-type="Sponsorship Enquiry"`, default success strings
|
||||
|
||||
Each is ~15 lines: just a template with `BaseCustomEventForm` and `defineProps`.
|
||||
Default success strings are fallbacks — event-level configured messages take priority (see 3.5).
|
||||
|
||||
### 3.4 Extend `CustomFieldInput.vue` (if needed)
|
||||
**File:** `dashboard/src/components/CustomFieldInput.vue`
|
||||
- May need minor extensions for `Rating` fieldtype
|
||||
- Complex types (Table) handled directly in `BaseCustomEventForm.vue`
|
||||
- Text Editor, Attach Image, Link, should be added to custom field input
|
||||
- in the backend the value column should be changed to `Code` type to support various values like this
|
||||
- The attachments should be attached to the document being created, the custom field should have the path (check File doctype) to the file
|
||||
|
||||
### 3.5 Configurable success messages per event
|
||||
**DocType change:** Add 3 markdown fields to `Pohodex Event Manager Event` DocType:
|
||||
- `feedback_success_message` (Markdown)
|
||||
- `proposal_success_message` (Markdown)
|
||||
- `sponsorship_success_message` (Markdown)
|
||||
|
||||
**API:** `get_custom_form_data` returns the relevant success message field for the form type.
|
||||
**Frontend:** `BaseCustomEventForm.vue` uses event-configured message if set, otherwise falls back to wrapper's default props.
|
||||
|
||||
---
|
||||
|
||||
## Files Summary
|
||||
|
||||
### New files (6)
|
||||
| File | Purpose |
|
||||
|------|---------|
|
||||
| `dashboard/src/components/BaseCustomEventForm.vue` | Shared form renderer |
|
||||
| `dashboard/src/pages/FeedbackForm.vue` | Feedback wrapper |
|
||||
| `dashboard/src/pages/ProposeTalkForm.vue` | Talk proposal wrapper |
|
||||
| `dashboard/src/pages/EnquireSponsorshipForm.vue` | Sponsorship wrapper |
|
||||
|
||||
### Modified files (7)
|
||||
| File | Change |
|
||||
|------|--------|
|
||||
| `event_manager/events/doctype/event_feedback/event_feedback.json` | Add additional_fields + permissions |
|
||||
| `event_manager/proposals/doctype/talk_proposal/talk_proposal.json` | Add additional_fields table |
|
||||
| `event_manager/proposals/doctype/sponsorship_enquiry/sponsorship_enquiry.json` | Add additional_fields table |
|
||||
| `event_manager/event_manager/doctype/buzz_custom_field/buzz_custom_field.json` | Add 3 new applied_to options |
|
||||
| `event_manager/events/doctype/buzz_event/buzz_event.json` | Add 3 success message markdown fields |
|
||||
| `event_manager/api.py` | Add `get_custom_form_data` + `submit_custom_form` |
|
||||
| `dashboard/src/router.ts` | Add 3 routes |
|
||||
|
||||
---
|
||||
|
||||
## Implementation Order
|
||||
1. DocType schema changes + `bench migrate`
|
||||
2. API endpoints in `event_manager/api.py`
|
||||
3. `BaseCustomEventForm.vue`
|
||||
4. Three wrapper pages + router routes
|
||||
5. Test each form end-to-end
|
||||
|
||||
## Verification
|
||||
- Create a Pohodex Event Manager Event with a route
|
||||
- Add Pohodex Event Manager Custom Fields for each `applied_to` type
|
||||
- Navigate to each form URL, verify fields render (standard + custom)
|
||||
- Submit each form, verify doc created with correct data + additional_fields
|
||||
- Test deadline enforcement (set `talk_proposals_close_at` to past, verify form shows closed)
|
||||
- Test without login → should redirect to login page
|
||||
@@ -0,0 +1,154 @@
|
||||
# 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
|
||||
@@ -0,0 +1,108 @@
|
||||
# Plan: Event Proposal Public Form
|
||||
|
||||
## Context
|
||||
|
||||
Event Proposal DocType exists but is only accessible via the Frappe desk. We want a public-facing form at `/dashboard/event-proposal` that anyone can submit, controlled by a toggle in Pohodex Event Manager Settings.
|
||||
|
||||
---
|
||||
|
||||
## Changes
|
||||
|
||||
### 1. Pohodex Event Manager Settings DocType
|
||||
|
||||
**File:** `event_manager/events/doctype/buzz_settings/buzz_settings.json`
|
||||
|
||||
Add a new **Proposals** Tab Break (before Communications tab) with:
|
||||
|
||||
| Field | Type | Notes |
|
||||
|-------|------|-------|
|
||||
| `proposals_tab` | Tab Break | Label: "Proposals" |
|
||||
| `event_proposals_section` | Section Break | Label: "Event Proposals" |
|
||||
| `accept_event_proposals` | Check | Label: "Accept Event Proposals", default 0 |
|
||||
| `allow_guest_event_proposals` | Check | Label: "Allow Guest Submission", depends_on: `eval:doc.accept_event_proposals`, default 0 |
|
||||
| `event_proposal_success_message` | Markdown Editor | depends_on: `eval:doc.accept_event_proposals`, Label: "Success Message" |
|
||||
|
||||
### 2. Backend API
|
||||
|
||||
**File:** `event_manager/api/forms.py`
|
||||
|
||||
**2a. Add `EVENT_PROPOSAL_EXCLUDE_FIELDS`**
|
||||
|
||||
```python
|
||||
EVENT_PROPOSAL_EXCLUDE_FIELDS = STANDARD_EXCLUDE_FIELDS | {
|
||||
"naming_series",
|
||||
"amended_from",
|
||||
"host",
|
||||
}
|
||||
```
|
||||
|
||||
`status` and `submitted_by` are already in `STANDARD_EXCLUDE_FIELDS`.
|
||||
|
||||
**2b. `get_event_proposal_form_data()` — whitelist, allow_guest**
|
||||
|
||||
- Read `Pohodex Event Manager Settings` — if `accept_event_proposals` is falsy, throw DoesNotExistError
|
||||
- If guest not allowed and user is Guest, throw AuthenticationError
|
||||
- Call `get_form_fields("Event Proposal", EVENT_PROPOSAL_EXCLUDE_FIELDS)`
|
||||
- Read success message from settings
|
||||
- Return `{form_fields, form_title, success_title, success_message, closed}`
|
||||
|
||||
**2c. `submit_event_proposal(data)` — whitelist, allow_guest**
|
||||
|
||||
- Same settings + guest check
|
||||
- Parse data via `frappe.parse_json`
|
||||
- Build doc from allowed fields only (filter through `get_form_fields`)
|
||||
- `frappe.get_doc(doc_data).insert(ignore_permissions=True)`
|
||||
|
||||
### 3. Frontend Route
|
||||
|
||||
**File:** `dashboard/src/router.ts`
|
||||
|
||||
Add route:
|
||||
```typescript
|
||||
{
|
||||
path: "/event-proposal",
|
||||
name: "event-proposal",
|
||||
meta: { isPublic: true },
|
||||
component: () => import("@/pages/EventProposalForm.vue"),
|
||||
}
|
||||
```
|
||||
|
||||
### 4. New Page: EventProposalForm.vue
|
||||
|
||||
**File:** `dashboard/src/pages/EventProposalForm.vue`
|
||||
|
||||
Slimmed-down version of BaseCustomEventForm:
|
||||
- No `eventRoute`/`formRoute` props
|
||||
- No `EventDetailsHeader`
|
||||
- No `CustomFieldsSection`
|
||||
- Calls `get_event_proposal_form_data` on mount
|
||||
- Submits to `submit_event_proposal`
|
||||
- Reuses `CustomFieldInput` for field rendering
|
||||
- Reuses table dialog pattern for Table fields
|
||||
- Shows success/closed/error states same as BaseCustomEventForm
|
||||
|
||||
---
|
||||
|
||||
## Files Summary
|
||||
|
||||
| File | Action | Change |
|
||||
|------|--------|--------|
|
||||
| `event_manager/events/doctype/buzz_settings/buzz_settings.json` | Modify | Add Proposals tab with toggle + success message |
|
||||
| `event_manager/api/forms.py` | Modify | Add 2 endpoints + exclude set |
|
||||
| `dashboard/src/router.ts` | Modify | Add 1 route |
|
||||
| `dashboard/src/pages/EventProposalForm.vue` | New | Public form page |
|
||||
|
||||
## Implementation Order
|
||||
|
||||
1. Update Pohodex Event Manager Settings JSON + `bench migrate`
|
||||
2. Add API endpoints in `event_manager/api/forms.py`
|
||||
3. Add route + create `EventProposalForm.vue`
|
||||
4. Test end-to-end
|
||||
|
||||
## Verification
|
||||
|
||||
1. Enable "Accept Event Proposals" in Pohodex Event Manager Settings
|
||||
2. Navigate to `/dashboard/event-proposal` — form renders with Event Proposal fields (minus status, naming_series, amended_from, host)
|
||||
3. Submit form — Event Proposal doc created with status "Received"
|
||||
4. Disable toggle — form shows not found
|
||||
5. Check success message renders after submission
|
||||
@@ -0,0 +1,60 @@
|
||||
# Plan: In-App Login Modal
|
||||
|
||||
## Status: Implemented ✓
|
||||
|
||||
## Goal
|
||||
Replace the redirect to Frappe's `/login` page with an in-app modal dialog that supports all login features. Users stay on the current page (booking, custom form, etc.) and authenticate without leaving context.
|
||||
|
||||
## What Was Done
|
||||
|
||||
### New Files Created
|
||||
1. **`event_manager/api/auth.py`** — `get_login_context()` whitelisted API (allow_guest) returning login settings (disable_signup, disable_user_pass_login, login_with_email_link), Google OAuth URL, and login_banner from Pohodex Event Manager Settings
|
||||
2. **`dashboard/src/components/LoginDialog.vue`** — Multi-view modal with 4 views: login, signup, forgot-password, email-link. Uses native HTML `<form>` validation with `required` attributes. Each view wrapped in `<form @submit.prevent>`. Google OAuth with SVG icon. Banner with localStorage hash persistence.
|
||||
3. **`dashboard/src/composables/useLoginDialog.ts`** — Shared composable with `open(onSuccess?)` and `close()` to control the modal from anywhere
|
||||
|
||||
### Files Modified
|
||||
1. **`event_manager/events/doctype/buzz_settings/buzz_settings.json`** — Added new "Login" tab with `login_banner` Markdown Editor field
|
||||
2. **`dashboard/src/App.vue`** — Mounted `<LoginDialog />` globally inside `<FrappeUIProvider>`
|
||||
3. **`dashboard/src/layouts/Layout.vue`** — Added auth gate: shows `<LoginRequired />` instead of page content for non-public routes when user is not logged in
|
||||
4. **`dashboard/src/components/LoginRequired.vue`** — Changed "Log In" button to open login modal via `useLoginDialog().open()`. Fixed `__()` in `defineProps` default (was causing app crash at module load time before `window.__` was defined)
|
||||
5. **`dashboard/src/components/Navbar.vue`** — "Log In" button opens login modal instead of redirecting
|
||||
6. **`dashboard/src/components/BookingForm.vue`** — "Log In" link opens login modal. Submit button shows "Login to Checkout" when user is not logged in and event doesn't allow guest booking. On submit with login required, opens login modal with callback to reload page (form data persisted via `useBookingFormStorage` localStorage)
|
||||
7. **`dashboard/src/pages/BookTickets.vue`** — Removed `LoginRequired` gate that blocked the entire booking form. Users can now fill in the form and are only prompted to login at checkout time
|
||||
8. **`dashboard/src/data/session.ts`** — Removed auto-redirect on login success (modal handles post-login flow). Logout now reloads page instead of redirecting to `/login`
|
||||
9. **`dashboard/src/data/user.ts`** — Removed redirect to `/login` on AuthenticationError
|
||||
10. **`dashboard/src/router.ts`** — Simplified guard to just fetch user data. Removed redirect to `/login` for unauthenticated users (Layout handles showing LoginRequired). Removed unused `session` import
|
||||
11. **`dashboard/src/utils/index.ts`** — Removed unused `redirectToLogin()` function
|
||||
|
||||
## Key Behaviors
|
||||
|
||||
### Booking Flow (non-guest events)
|
||||
- Form shows fully for all users, even unauthenticated
|
||||
- Submit button says **"Login to Checkout"** when login is required
|
||||
- Clicking it opens the login modal on top of the filled form
|
||||
- After login, page reloads — form data is preserved via `useBookingFormStorage` (localStorage)
|
||||
|
||||
### Protected Routes (account, bookings, tickets, etc.)
|
||||
- Layout shows `LoginRequired` component with "Log In" button
|
||||
- Button opens login modal instead of redirecting to Frappe's `/login`
|
||||
- After login, page re-renders with authenticated state
|
||||
|
||||
### Login Modal Features
|
||||
- **Login**: email + password (required, native validation), "Forgot Password?" link
|
||||
- **Social OAuth**: "Continue with {Provider}" buttons for all enabled Social Login Keys (shown on both login and signup views)
|
||||
- **Email Link**: "Login with Email Link" button (conditional on system setting)
|
||||
- **Signup**: full name + email (conditional on disable_signup setting), social login buttons shown here too
|
||||
- **Forgot Password**: email field, sends reset link
|
||||
- **Banner**: configurable via Pohodex Event Manager Settings → Login tab, always visible when modal opens (no dismiss/timer logic)
|
||||
|
||||
### Form Validation
|
||||
- All views use `<form @submit.prevent>` with native HTML validation
|
||||
- Email fields use `type="email"` + `required`
|
||||
- Password field uses `required`
|
||||
- Full Name field uses `required` in signup view
|
||||
- No custom JS validation for empty checks — browser handles it
|
||||
|
||||
## Notes
|
||||
|
||||
1. **Social login redirect**: OAuth requires a full page redirect to Google. After auth, Frappe redirects back to `/dashboard`. No way around this.
|
||||
2. **`__()` in defineProps**: Using `__()` in prop defaults causes crashes because it runs at module-load time before the translation plugin installs `window.__`. Fixed by using plain strings for defaults and wrapping with `__()` in the template.
|
||||
3. **Logout behavior**: Changed from redirecting to `/login` to `window.location.reload()` — the Layout auth gate handles showing LoginRequired after reload.
|
||||
Reference in New Issue
Block a user