"use client"; import React, { useEffect, useLayoutEffect, useMemo, useRef, useState } from "react"; import { createPortal } from "react-dom"; import { useAuth } from "@/hooks/useAuth"; import { useRouter } from "next/navigation"; import { apiFetch, resolveToApiOrigin } from "@/lib/api"; import { useDismissingState } from "@/hooks/useDismissingState"; import { useSiteSettings } from "@/contexts/SiteSettingsContext"; import { Calendar } from "lucide-react"; // ─── helpers ──────────────────────────────────────────────────────────────── function toLocalDT(input: string | Date | null | undefined): string { if (!input) return ""; const d = new Date(input); if (isNaN(d.getTime())) return ""; const y = d.getFullYear(); const m = String(d.getMonth() + 1).padStart(2, "0"); const day = String(d.getDate()).padStart(2, "0"); const hh = String(d.getHours()).padStart(2, "0"); const mm = String(d.getMinutes()).padStart(2, "0"); return `${y}-${m}-${day}T${hh}:${mm}`; } const DT_MIN = "2000-01-01T00:00"; const DT_MAX = "2099-12-31T23:59"; function DTInput({ label, value, onChange, hint, required }: { label: string; value: string; onChange: (v: string) => void; hint?: string; required?: boolean }) { return (
{hint}
}No fields yet.
: (Price is required — enter 0 for a free option.
}Add options first.
; return (No variants — uses base price (R{opt.price || "0"}).
: variants.map((v, vi) => (Add options first.
; return (No early-bird tiers.
} {opt.earlyBirdTiers.map((t, ti) => (No tiers for this variant.
} {v.earlyBirdTiers.map((t, ti) => (No sections yet.
} {sections.map((sec, i) => (No sections yet.
} {sections.map(sec => (No recipients selected — falling back to the event creator.
) : (Choose who receives new registration, payment, and daily summary emails for this event. Leave empty to send them to the event creator{creator ? <> ({creator.name}, {creator.email})> : null} instead.
{error &&Defaults to your organisation's address — shown to attendees with a map link.
Auto-fills the first ticket option below — enter 0 for a free event.
}Define your ticket types or purchasable items. Variants and early-bird pricing are configured in the next two steps.
Add variants per option (e.g. Adult / Child, or t-shirt sizes). Leave blank if an option has only one price.
Set early-bird prices per variant (if variants exist) or per option. The cheapest applicable tier is used at registration time.
Group ticket options into named sections shown on the registration page (e.g. "Tickets", "Add-ons", "Accommodation").
{mode === "edit" && ev?.id ?Collect extra information from registrants (e.g. dietary requirements, t-shirt size, emergency contact).
Choose who receives new registration, payment, and daily summary emails for this event. Leave empty to send them to you (the event creator) instead.
Upload documents for attendees to download (e.g. waiver forms, info sheets, maps, schedules).
{mode === "edit" && ev?.id ?No files added. Files will be uploaded when the event is created.
: (Manage and create events
{events.length === 0 ? 'No events yet. Click "+ Add Event" to create one.' : "No events match the current filters."}
: