Event Builder (wizard)
Event Builder (wizard)
Shell: src/features/events/components/EventBuilderShell.tsx Steps: EventBuilderBasicsStep, EventBuilderTicketsStep, EventBuilderTimeSlotsStep, EventBuilderFieldsStep, EventBuilderChecklistStep, EventBuilderAuctionStep, EventBuilderPromoStep, EventBuilderReviewStep (same directory) State/logic: useEventBuilderContext.ts Route: /app/fundraising/events/new or /app/fundraising/events/:id/edit Access: Authenticated users with Fundraising Hub write access Last Updated: March 22, 2026
---
i18n (events namespace)
- Builder components use
useTranslation('events')and keys under the `builder` group insrc/i18n/locales/{locale}/events.json. EVENT_TYPESandSTEPSinsrc/features/events/components/types/eventBuilder.tspass full dotted paths intot()(e.g.builder.typeStandard,builder.stepBasics). Each path must exist as nested JSON:builder→typeStandard, not only undermanageror another sibling group.- When adding labels, run
node scripts/i18n-sync-locales.jsandnode scripts/i18n-check.js. Dynamict(variable)sites are easy to miss in review — grep forlabelKey/descriptionKey/tooltipKeyand confirm every string resolves inevents.json. - Full rules, checklist, and Tiptap note: DEVELOPER-PLAYBOOK.md §21.11.
---
Recent Updates
Bug Fix: Checklist Item Name editing (jumbled or unreliable text) (Mar 22, 2026)
- Fixed: On the Checklist step, the Item Name field could show corrupted text (missing characters, merged fragments) or feel impossible to edit cleanly when changing an existing item.
- Reporter: Keith Medley (Journeyman NW).
- Root cause: Every keystroke called
useUpdateChecklistItem, whose success handler invalidated the checklist query. Many overlapping PATCH requests could finish out of order (older payloads overwriting newer ones on the server), and refetches could briefly replace the value the user was typing. - Fix:
- `useUpdateChecklistItem` (
src/hooks/useEvents.ts): Optimistic merge into the React Query cache inonMutate, restore previous cache ononError, and omit `invalidateQueries` on success so typing is not fighting constant refetches. Creating or deleting checklist items still invalidates as before. - `updateChecklistItem` for field
namewhen editing a saved event (useEventBuilderContext.ts): update the cache on every keystroke, debounce the API update (400ms), and flush any pending name on unmount so navigating away does not drop unsaved text. - `EventBuilderChecklistStep.tsx`:
min-w-0on the Item Name column so the grid can shrink correctly next to the drag handle and delete button. - Files modified:
src/hooks/useEvents.ts,src/features/events/components/useEventBuilderContext.ts,src/features/events/components/EventBuilderChecklistStep.tsx
Bug Fix: Raw builder.* keys in Event Builder UI (Mar 21, 2026)
- Fixed: Edit (and create) event wizard showed raw i18n keys such as
builder.stepBasics,builder.typeStandard,builder.slugPreviewinstead of translated text. - Reporter: Keith Medley (Journeyman NW).
- Root cause: Dotted keys like
t('builder.typeStandard')resolve toevents.json→builder→typeStandard. Several strings existed only undermanager.*or under different leaf names than the TSX calls. Dynamict(type.labelKey)was not caught by key parity alone. - Fix: Added missing
builder.*leaves (step labels, event type label/desc/tooltip, slug preview, field-step aliases, VideoBlast dialog keys, etc.) inen/es/zhevents.json. Removed duplicate Tiptap Underline extension fromrich-text-editor.tsx(StarterKit already includes it). - Prevention: See i18n (events namespace) above and Playbook §21.11.
Bug Fix: Event Date-Time Persistence (Mar 1, 2026)
1. parseISOToLocalInput treated Supabase TIMESTAMP strings ("2026-04-18 17:00:00", no Z) as browser-local time instead of UTC. Chrome interpreted new Date("2026-04-18 17:00:00") as local, so the form displayed wrong times. 2. localInputToISO used new Date(year, month-1, day, hours, minutes) (browser-local constructor) to compute timezone offsets — broke when browser TZ ≠ event TZ. 3. DatePicker date prop used new Date(formData.start_date) — §22 violation causing potential date shift near midnight.
- Fixed: Start/end date-times would not persist when creating or editing events — values reverted on reload
- Reporter: Keith Medley (Journeyman NW)
- Root Cause (3 bugs):
- Fix:
parseISOToLocalInput: Normalizes DB string to ISO withZsuffix before parsing (isoString.replace(' ', 'T') + 'Z')localInputToISO: Replaced browser-TZ-dependent offset withDate.UTC()-only math — correct regardless of browser timezone- All 4
DatePickerdateprops now usenew Date(y, m-1, d)constructor (§22 rule 1) - Also Fixed:
collect_sales_taxadded toCreateEventInputtype +createEvent()DB insert (was silently dropped on new events)- 28 hardcoded English toast/validation strings replaced with
t()calls (§21 compliance) - All 6 locale files updated with
builder.*i18n keys - Files Modified:
useEventBuilderContext.ts,EventBuilderBasicsStep.tsx, etc. — date helpers, DatePicker props, i18nsrc/types/events.ts—CreateEventInput.collect_sales_taxsrc/lib/db/events.ts—createEvent()insertsrc/i18n/locales/{en,es,fr,de,zh,th}/events.json—builder.*keys
Feature: YouTube Video URL Support (Jan 30, 2026)
- Added: YouTube video URL option alongside existing media options
- Options:
- None - No media (default)
- Upload Image - Upload JPG, PNG, GIF up to 10MB
- VideoBlast Library - Select a thumbnail from existing VideoBlast videos
- YouTube Video - Paste a YouTube URL to embed video on event page
- YouTube URL Formats Supported:
https://www.youtube.com/watch?v=VIDEO_IDhttps://youtu.be/VIDEO_IDhttps://www.youtube.com/embed/VIDEO_ID- Display: YouTube videos appear as embedded player in hero section on public event page
- Files Modified:
- Event builder Basics step / context — YouTube URL input and preview
src/features/events/components/PublicEventPage.tsx- Added YouTube embed rendering
Feature: Event Image Upload & VideoBlast Integration (Jan 29, 2026)
- Added: Optional event image upload in the Basics step
- Storage: Images uploaded to
event-mediaSupabase storage bucket - Display: Image appears as hero banner on public event page
- VideoBlast Integration: If no videos exist, shows "Create New" button linking to VideoBlast tool
- Files Modified:
- Event builder Basics step — media upload UI
src/lib/db.ts- AddeduploadEventImage()anddeleteEventMedia()functions
Bug Fix: Ticket Type Editing for Existing Events (Jan 29, 2026)
- Fixed: Ticket type fields (name, price, quantity) could not be edited when editing an existing event
- Typing in fields would not register / appear frozen
- Root Cause: Same issue as custom fields - component used
localTicketTypesstate for updates but displayedticketTypesfrom database when editing existing events - Solution:
updateTicketType()now callsupdateTicketTypeMutationfor existing events - Technical Details:
- Added
updateTicketTypeMutation = useUpdateTicketType()instantiation - For existing events: mutation updates database directly, React Query cache invalidates and UI refreshes
- For new events: local state is still used (unchanged behavior)
Bug Fix: Custom Fields & Checklist Editing for Existing Events (Jan 23, 2026)
- Fixed: Custom fields and checklist items could not be edited when editing an existing event
- Typing in field names would revert immediately
- Drag-and-drop reordering would slide back to original position
- Root Cause: Component used
localCustomFieldsstate for mutations but displayedcustomFieldsfrom database when editing existing events - Solution:
updateCustomField()andupdateChecklistItem()now call database mutations for existing eventshandleCustomFieldDragEnd()andhandleChecklistDragEnd()now use correct state source and persistsort_orderto database- Technical Details:
- Added
useUpdateCustomFieldanduseUpdateChecklistItemhooks - For existing events: mutations update the database directly; React Query refreshes custom fields via invalidation as before
- Checklist items (Mar 22, 2026 follow-up):
useUpdateChecklistItemnow keeps the checklist query in sync with optimistic cache updates instead of invalidating on every successful PATCH (see Bug Fix: Checklist Item Name editing above). Custom fields are unchanged. - For new events: local state is still used (unchanged behavior)
Feature: Sales Tax Collection (Jan 22, 2026)
- Added: "Collect Sales Tax" toggle in the Tickets step
- Integration: Stripe Tax automatically calculates and collects applicable sales tax
- Stored:
collect_sales_taxfield on Event record - Passed: Setting flows through to
create-event-checkoutEdge Function
Feature: Custom Field & Checklist Reordering (Jan 21, 2026)
- Added: Up/down arrow controls for reordering custom fields and checklist items
- Uses: Shared
ReorderControlscomponent fromsrc/components/shared/ReorderControls.tsx - Benefit: Users can now arrange fields in logical order (e.g., group spouse fields together)
Bug Fix: Unique Slug Generation (Jan 1, 2026)
- Fixed: Events with duplicate names caused "Failed to save event" error
- Root cause: Local slug generator didn't check for uniqueness
- Database has
UNIQUE(organization_id, slug)constraint - Solution: Now uses
generateUniqueEventSlug()which appends-1,-2, etc. if slug exists - Also Fixed: Update operations no longer pass
organization_id(type mismatch)
---
Overview
The EventBuilder is a multi-step wizard for creating and editing events. Steps are dynamically shown/hidden based on the selected event type.
Features
- Multi-step wizard - 6 possible steps based on event type
- Event type selection - Determines available features
- Auto-generated slugs - URL-safe identifier from event name
- Step validation - Blocks forward navigation until requirements met
- Draft saving - Save progress without publishing
Event Types
| Type | Steps Shown | Key Features |
|------|-------------|--------------|
| **Simple** | Basics, Time Slots, Fields, Review | Appointment scheduling |
| **Standard** | Basics, Tickets, Fields, Promo, Review | General events |
| **Retreat** | Basics, Tickets, Fields, Checklist, Promo, Review | Weekend retreats |
| **Camp** | Basics, Tickets, Fields, Checklist, Promo, Review | Youth camps |
| **Trip** | Basics, Tickets, Fields, Checklist, Promo, Review | Mission trips |
| **Gala** | Basics, Tickets, Promo, Review | Fundraising dinners (no custom fields) |
| **Auction** | Basics, Tickets, Auction, Promo, Review | Silent/live auctions |
Wizard Steps
1. Basics
- Event name and description
- Event type selection (cards with checkmarks)
- Start/end date and time
- Location (in-person vs virtual)
- Capacity and waitlist settings
- Registration open/close dates
- Event Image (Optional): Upload image or select from VideoBlast library
2. Tickets
- Collect Sales Tax toggle - Enable Stripe Tax for automatic tax calculation
- Add multiple ticket types
- Name, description, price
- Quantity available (optional)
- Max per order limit
3. Time Slots (Simple events only)
- Add time slots for appointments
- Start time, end time, capacity
4. Fields (Custom registration fields)
- Add custom form fields
- Types: text, textarea, select, checkbox, date, number
- Required/optional setting
- Applies to: registrant, guest, or both
- Reorderable: Use up/down arrows to arrange fields in desired order
5. Checklist (Retreat, Camp, Trip only)
- Add pre-event requirements
- Types: waiver, upload, confirmation, link
- Required vs optional
- Requires review toggle
- Due date setting
- Reorderable: Use up/down arrows to arrange items in desired order
- Waiver assignment: For
waiveritems, select a waiver from Waiver Management. These assignments now flow to public registration aswaiver_requirements. - Registration-time enforcement: Required waiver checklist items are enforced during registration before payment can complete (not only in post-registration checklist).
- Item Name (saved events): UI updates immediately; the API save is debounced (400ms) with optimistic React Query cache updates so fast typing does not corrupt the stored name (see Recent Updates, Mar 22, 2026).
6. Auction (Auction events only)
- Add auction items
- Item name, category, description
- Starting bid, bid increment
- Fair market value
7. Promo Codes
- Add discount codes
- Percentage or fixed amount
- Max uses limit
- Valid date range
8. Review
- Summary of all settings
- Publish or save as draft
UI Layout
┌─────────────────────────────────────────────────────────┐
│ ← Back Create Event │
├─────────────────────────────────────────────────────────┤
│ │
│ ○ Basics ○ Tickets ○ Fields ○ Promo ● Review │
│ │
├─────────────────────────────────────────────────────────┤
│ │
│ [Step Content Area] │
│ │
│ │
│ │
├─────────────────────────────────────────────────────────┤
│ [Back] [Next / Save / Publish]│
└─────────────────────────────────────────────────────────┘State Management
const [currentStep, setCurrentStep] = useState(0);
const [eventData, setEventData] = useState<EventFormData>({
name: '',
description: '',
event_type: 'standard',
start_date: '',
end_date: '',
// ... more fields
});
const [tickets, setTickets] = useState<TicketType[]>([]);
const [customFields, setCustomFields] = useState<CustomField[]>([]);
const [checklistItems, setChecklistItems] = useState<ChecklistItem[]>([]);
const [promoCodes, setPromoCodes] = useState<PromoCode[]>([]);
const [auctionItems, setAuctionItems] = useState<AuctionItem[]>([]);
const [timeSlots, setTimeSlots] = useState<TimeSlot[]>([]);Step Validation
| Step | Validation Rules |
|------|------------------|
| Basics | Name required, event type selected, start date required |
| Tickets | At least one ticket type required |
| Time Slots | At least one slot for Simple events |
| Fields | No validation (optional) |
| Checklist | No validation (optional) |
| Auction | At least one item for Auction events |
| Promo | No validation (optional) |
Save Logic
const handleSave = async () => {
// 1. Create/update event record
const event = await upsertEvent(eventData);
// 2. Save related records
await saveTicketTypes(event.id, tickets);
await saveCustomFields(event.id, customFields);
await saveChecklistItems(event.id, checklistItems);
await savePromoCodes(event.id, promoCodes);
await saveAuctionItems(event.id, auctionItems);
await saveTimeSlots(event.id, timeSlots);
// 3. Optionally publish
if (shouldPublish) {
await publishEvent(event.id);
}
};Related Components
EventsManager- Parent list viewEventDashboard- Post-creation metrics
Related Documentation
- Main Events Doc:
../06-EVENTS-TICKETING.md - Events Manager:
./01-EVENTS-MANAGER.md
Synced from IFMmvp-Frontend documentation: pages/fundraising/events/02-EVENT-BUILDER.md
Ready to Get Started?
See how Alignmint can simplify your nonprofit's operations. Schedule a free demo with our team and we'll walk you through everything.
Questions? Email us at steven@getalignmint.org