Skip to main content

EventBuilder Component

EventBuilder Component

Component: src/features/events/components/EventBuilder.tsx Route: /app/fundraising/events/new or /app/fundraising/events/:id/edit Access: Authenticated users with Fundraising Hub write access Last Updated: January 29, 2026

---

Recent Updates (January 2026)

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_ID
  • https://youtu.be/VIDEO_ID
  • https://www.youtube.com/embed/VIDEO_ID
  • Display: YouTube videos appear as embedded player in hero section on public event page
  • Files Modified:
  • src/features/events/components/EventBuilder.tsx - Added 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-media Supabase 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:
  • src/features/events/components/EventBuilder.tsx - Added media upload UI
  • src/lib/db.ts - Added uploadEventImage() and deleteEventMedia() 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 localTicketTypes state for updates but displayed ticketTypes from database when editing existing events
  • Solution: updateTicketType() now calls updateTicketTypeMutation for 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 localCustomFields state for mutations but displayed customFields from database when editing existing events
  • Solution:
  • updateCustomField() and updateChecklistItem() now call database mutations for existing events
  • handleCustomFieldDragEnd() and handleChecklistDragEnd() now use correct state source and persist sort_order to database
  • Technical Details:
  • Added useUpdateCustomField and useUpdateChecklistItem hooks
  • For existing events: mutations update database directly, React Query cache invalidates and UI refreshes
  • 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_tax field on Event record
  • Passed: Setting flows through to create-event-checkout Edge Function

Feature: Custom Field & Checklist Reordering (Jan 21, 2026)

  • Added: Up/down arrow controls for reordering custom fields and checklist items
  • Uses: Shared ReorderControls component from src/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 | Appointment scheduling |
| **Standard** | Basics, Tickets, Fields, Promo | General events |
| **Retreat** | Basics, Tickets, Fields, Checklist, Promo | Weekend retreats |
| **Camp** | Basics, Tickets, Fields, Checklist, Promo | Youth camps |
| **Trip** | Basics, Tickets, Fields, Checklist, Promo | Mission trips |
| **Gala** | Basics, Tickets, Fields, Promo | Fundraising dinners |
| **Auction** | Basics, Tickets, Auction, Promo | 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

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 view
  • EventDashboard - Post-creation metrics

Related Documentation

  • Main Events Doc: ../06-EVENTS-TICKETING.md
  • Events Manager: ./01-EVENTS-MANAGER.md

Synced from IFMmvp-Frontend documentation: pages/marketing/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

Ready to get started?Start Plus Trial