/** * Activity Settings tab — event fields in the same section layout as Create Event (Basic, Schedule & Location, Media & Tags, Registration). */ import React, { useCallback, useEffect, useRef, useState } from "react"; import { Plus, Trash2, Search } from "lucide-react"; import { eventsApi } from "../../../services/api/events"; import { sessionsApi, type ApiSession } from "../../../services/api/sessions"; import type { Event } from "../../../services/api/types"; import { effectiveEnableQnA, effectiveEnableQnP, sessionToPayload } from "../../utils/sessionFlags"; import { buildEventCreatePayload, labelToCode, needsChoiceOptions, type EventCreateFormInput } from "../panels/createEventPayload"; import { IS, SS, Label, Field, Row, SelectWrap, RadioGroup, UploadZone, AccordionSection, } from "../panels/eventFormFields"; import { FIELD_TYPE_OPTIONS, AUTHENTICATION_TYPE_OPTIONS, REGISTRATION_BASE_FIELDS_OPTIONS, apiEventToForm, newCustomField, mergeActivitySettingsFormWithStored, validateActivitySettingsForSave, TOTAL_SECTIONS, type EventForm, } from "../panels/eventFormModel"; export function ActivityEventSettings({ event, session, selectedEventId, onSaved, }: { event: Event; session: ApiSession; selectedEventId: string; onSaved: () => void | Promise; }) { const [openSection, setOpenSection] = useState(1); const [completed, setCompleted] = useState>(() => new Set([1, 2, 3, 4])); const [form, setForm] = useState(() => apiEventToForm(event as unknown as Record)); const [enableQnA, setEnableQnA] = useState(() => effectiveEnableQnA(session)); const [enableQnP, setEnableQnP] = useState(() => effectiveEnableQnP(session)); const [fieldErrors, setFieldErrors] = useState>({}); const [submitting, setSubmitting] = useState(false); const [msg, setMsg] = useState(null); const logoRef = useRef(null); const bannerRef = useRef(null); useEffect(() => { setForm(apiEventToForm(event as unknown as Record)); setEnableQnA(effectiveEnableQnA(session)); setEnableQnP(effectiveEnableQnP(session)); setFieldErrors({}); setMsg(null); }, [event._id, event.updatedAt, session._id, session]); const f = useCallback((k: K, v: EventForm[K]) => { setForm((p) => ({ ...p, [k]: v })); }, []); const err = (k: string) => fieldErrors[k]; const toggle = (n: number) => setOpenSection((prev) => (prev === n ? 0 : n)); const addCustomField = () => setForm((p) => ({ ...p, customFields: [...p.customFields, newCustomField()] })); const removeField = (id: string) => { setForm((p) => ({ ...p, customFields: p.customFields.filter((x) => x.id !== id) })); setFieldErrors((prev) => { const n = { ...prev }; Object.keys(n).forEach((k) => { if (k.startsWith(`cf_${id}_`) || k === `cf_${id}_label`) delete n[k]; }); return n; }); }; const setCfLabel = (id: string, label: string) => { setForm((p) => ({ ...p, customFields: p.customFields.map((cf) => { if (cf.id !== id) return cf; const next = { ...cf, label }; if (cf.codeAuto) next.code = labelToCode(label); return next; }), })); }; const setCfCode = (id: string, code: string) => { setForm((p) => ({ ...p, customFields: p.customFields.map((cf) => (cf.id === id ? { ...cf, code, codeAuto: false } : cf)), })); }; const setCfType = (id: string, fieldType: string) => { setForm((p) => ({ ...p, customFields: p.customFields.map((cf) => { if (cf.id !== id) return cf; const next = { ...cf, fieldType }; if (needsChoiceOptions(fieldType) && cf.options.length === 0) { next.options = [{ id: Math.random().toString(36).slice(2), value: "", label: "" }]; } if (!needsChoiceOptions(fieldType)) next.options = []; return next; }), })); }; const addOption = (cfId: string) => { setForm((p) => ({ ...p, customFields: p.customFields.map((cf) => cf.id === cfId ? { ...cf, options: [...cf.options, { id: Math.random().toString(36).slice(2), value: "", label: "" }] } : cf, ), })); }; const removeOption = (cfId: string, optId: string) => { setForm((p) => ({ ...p, customFields: p.customFields.map((cf) => (cf.id === cfId ? { ...cf, options: cf.options.filter((o) => o.id !== optId) } : cf)), })); setFieldErrors((prev) => { const n = { ...prev }; delete n[`cf_${cfId}_opt_${optId}`]; return n; }); }; const setOption = (cfId: string, optId: string, key: "value" | "label", val: string) => { setForm((p) => ({ ...p, customFields: p.customFields.map((cf) => cf.id === cfId ? { ...cf, options: cf.options.map((o) => (o.id === optId ? { ...o, [key]: val } : o)) } : cf, ), })); }; const tryAdvance = (section: number) => { setMsg(null); setFieldErrors({}); setCompleted((prev) => new Set([...prev, section])); if (section < TOTAL_SECTIONS) setOpenSection(section + 1); }; const saveAll = async () => { setMsg(null); const mergedForm = mergeActivitySettingsFormWithStored(form, event as unknown as Record); const customFields = mergedForm.customFields.filter((cf) => { if (!cf.label.trim()) return false; if (needsChoiceOptions(cf.fieldType)) { const complete = cf.options.filter((o) => o.label.trim() && o.value.trim()); if (complete.length === 0) return false; } return true; }); const merged = { ...mergedForm, customFields }; const softErrs = validateActivitySettingsForSave(merged); if (Object.keys(softErrs).length > 0) { setFieldErrors(softErrs); if (softErrs.virtualLink) setOpenSection(2); else if (softErrs.endDate) setOpenSection(2); setMsg("Fix the highlighted fields, then save again."); return; } setFieldErrors({}); setSubmitting(true); try { const base = buildEventCreatePayload(merged as EventCreateFormInput); const payload = { ...base, recordKind: event.recordKind ?? "activity", activitySubtype: event.activitySubtype, }; const evRes = await eventsApi.update(selectedEventId, payload as never); if (evRes.error) { setMsg(evRes.error); return; } const sesRes = await sessionsApi.update(session._id, { ...sessionToPayload(session), enableQnA, enableQnP, }); if (sesRes.error) { setMsg(sesRes.error); return; } setMsg("Saved."); await onSaved(); } finally { setSubmitting(false); } }; const progress = Math.round((completed.size / TOTAL_SECTIONS) * 100); return (

Activity settings

Empty fields keep existing saved values. Session Q&A and quiz toggles are under Basic.

{msg && (

{msg}

)}
{completed.size} of {TOTAL_SECTIONS} sections complete {progress}%
toggle(1)} onNext={() => tryAdvance(1)} nextLabel="Continue to Schedule & Location" nextDisabled={submitting} >