@burdenoff/microfe-eventfullymanaged 2026.730.1 → 2026.801.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/eventfullymanaged/EventfullyManagedRoutes.d.ts.map +1 -1
- package/dist/eventfullymanaged/EventfullyManagedRoutes.js +61 -77
- package/dist/eventfullymanaged/EventfullyManagedRoutes.js.map +1 -1
- package/dist/eventfullymanaged/api/eventApi.d.ts +208 -0
- package/dist/eventfullymanaged/api/eventApi.d.ts.map +1 -1
- package/dist/eventfullymanaged/api/eventApi.js +293 -136
- package/dist/eventfullymanaged/api/eventApi.js.map +1 -1
- package/dist/eventfullymanaged/hooks/useEvmSubscription.d.ts +18 -0
- package/dist/eventfullymanaged/hooks/useEvmSubscription.d.ts.map +1 -0
- package/dist/eventfullymanaged/hooks/useEvmSubscription.js +110 -0
- package/dist/eventfullymanaged/hooks/useEvmSubscription.js.map +1 -0
- package/dist/eventfullymanaged/hooks/useVirtualCall.d.ts +47 -0
- package/dist/eventfullymanaged/hooks/useVirtualCall.d.ts.map +1 -0
- package/dist/eventfullymanaged/hooks/useVirtualCall.js +209 -0
- package/dist/eventfullymanaged/hooks/useVirtualCall.js.map +1 -0
- package/dist/eventfullymanaged/pages/events/EventDetailPage.d.ts.map +1 -1
- package/dist/eventfullymanaged/pages/events/EventDetailPage.js +80 -72
- package/dist/eventfullymanaged/pages/events/EventDetailPage.js.map +1 -1
- package/dist/eventfullymanaged/pages/events/EventFormPage.js +1 -1
- package/dist/eventfullymanaged/pages/events/EventFormPage.js.map +1 -1
- package/dist/eventfullymanaged/pages/events/SessionFormPage.js +1 -1
- package/dist/eventfullymanaged/pages/events/SessionFormPage.js.map +1 -1
- package/dist/eventfullymanaged/pages/events/TicketTypeFormPage.js +1 -1
- package/dist/eventfullymanaged/pages/events/TicketTypeFormPage.js.map +1 -1
- package/dist/eventfullymanaged/pages/events/VirtualSessionPage.d.ts +2 -0
- package/dist/eventfullymanaged/pages/events/VirtualSessionPage.d.ts.map +1 -0
- package/dist/eventfullymanaged/pages/events/VirtualSessionPage.js +353 -0
- package/dist/eventfullymanaged/pages/events/VirtualSessionPage.js.map +1 -0
- package/dist/eventfullymanaged/pages/exhibitors/ExhibitorFormPage.js +1 -1
- package/dist/eventfullymanaged/pages/exhibitors/ExhibitorFormPage.js.map +1 -1
- package/dist/eventfullymanaged/pages/modules/AttendeeProfilePage.js +1 -1
- package/dist/eventfullymanaged/pages/modules/AttendeeProfilePage.js.map +1 -1
- package/dist/eventfullymanaged/pages/modules/IntegrationsPage.d.ts +2 -0
- package/dist/eventfullymanaged/pages/modules/IntegrationsPage.d.ts.map +1 -0
- package/dist/eventfullymanaged/pages/modules/IntegrationsPage.js +336 -0
- package/dist/eventfullymanaged/pages/modules/IntegrationsPage.js.map +1 -0
- package/dist/eventfullymanaged/pages/modules/SponsorDetailsPage.js +1 -1
- package/dist/eventfullymanaged/pages/modules/SponsorDetailsPage.js.map +1 -1
- package/dist/eventfullymanaged/pages/modules/SponsorLeadsPage.js +1 -1
- package/dist/eventfullymanaged/pages/modules/SponsorLeadsPage.js.map +1 -1
- package/dist/eventfullymanaged/pages/modules/VenueResourcesPage.js +1 -1
- package/dist/eventfullymanaged/pages/modules/VenueResourcesPage.js.map +1 -1
- package/dist/eventfullymanaged/pages/networking/MeetingFormPage.js +1 -1
- package/dist/eventfullymanaged/pages/networking/MeetingFormPage.js.map +1 -1
- package/dist/eventfullymanaged/pages/networking/NetworkingProfileFormPage.js +1 -1
- package/dist/eventfullymanaged/pages/networking/NetworkingProfileFormPage.js.map +1 -1
- package/dist/eventfullymanaged/pages/registration/AttendeeFormPage.js +1 -1
- package/dist/eventfullymanaged/pages/registration/AttendeeFormPage.js.map +1 -1
- package/dist/eventfullymanaged/pages/registration/PromoCodeFormPage.js +1 -1
- package/dist/eventfullymanaged/pages/registration/PromoCodeFormPage.js.map +1 -1
- package/dist/eventfullymanaged/pages/speakers/SpeakerFormPage.js +1 -1
- package/dist/eventfullymanaged/pages/speakers/SpeakerFormPage.js.map +1 -1
- package/dist/eventfullymanaged/pages/sponsors/SponsorFormPage.js +1 -1
- package/dist/eventfullymanaged/pages/sponsors/SponsorFormPage.js.map +1 -1
- package/dist/eventfullymanaged/pages/venues/FloorPlansPage.js +1 -1
- package/dist/eventfullymanaged/pages/venues/FloorPlansPage.js.map +1 -1
- package/dist/eventfullymanaged/pages/venues/VenueDetailPage.js +1 -1
- package/dist/eventfullymanaged/pages/venues/VenueDetailPage.js.map +1 -1
- package/dist/eventfullymanaged/pages/venues/VenueFormPage.js +1 -1
- package/dist/eventfullymanaged/pages/venues/VenueFormPage.js.map +1 -1
- package/dist/eventfullymanaged/utils/shellBridge.d.ts +46 -0
- package/dist/eventfullymanaged/utils/shellBridge.d.ts.map +1 -0
- package/dist/eventfullymanaged/utils/shellBridge.js +19 -0
- package/dist/eventfullymanaged/utils/shellBridge.js.map +1 -0
- package/package.json +9 -9
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"EventDetailPage.js","names":[],"sources":["../../../../src/eventfullymanaged/pages/events/EventDetailPage.tsx"],"sourcesContent":["import { gql } from '@apollo/client';\nimport { useQuery } from '@apollo/client/react';\nimport {\n ArrowLeft,\n CalendarClock,\n Clock,\n Edit,\n Eye,\n MapPin,\n Mic2,\n Plus,\n Ticket as TicketIcon,\n Users,\n} from 'lucide-react';\nimport {\n Badge,\n Button,\n GlassCard,\n IllustratedEmptyState,\n Skeleton,\n Tabs,\n TabsContent,\n TabsList,\n TabsTrigger,\n} from '@burdenoff/fe-libs/ui';\nimport { useMemo, useState } from 'react';\nimport { useParams } from 'react-router-dom';\n\nimport {\n useDeleteEventSessionMutation,\n useDeleteSpeakerMutation,\n useDeleteTicketTypeMutation,\n useEventQuery,\n useSpeakersQuery,\n useTicketTypesQuery,\n useVenuesQuery,\n type TicketTypeGql,\n} from '../../api/eventApi';\nimport { ActionMenu } from '../../components/ActionMenu';\nimport { CHART_COLORS } from '../../components/EventCharts';\nimport { Pagination, usePagination } from '../../components/Pagination';\nimport { useEventfullyManaged } from '../../context/EventfullyManagedContext';\nimport { notify } from '../../lib/toast';\nimport { formatDateTime } from './eventFormUtils';\n\nfunction statusVariant(status: string): 'success' | 'warning' | 'secondary' | 'info' {\n const value = status.toLowerCase();\n if (['active', 'published', 'live', 'confirmed', 'completed', 'approved'].includes(value)) {\n return 'success';\n }\n if (['inactive', 'cancelled', 'archived', 'closed', 'expired'].includes(value)) {\n return 'warning';\n }\n if (['draft', 'created', 'pending', 'scheduled'].includes(value)) {\n return 'info';\n }\n return 'secondary';\n}\n\nfunction formatDuration(startsAt: string, endsAt: string): string {\n const start = new Date(startsAt).getTime();\n const end = new Date(endsAt).getTime();\n if (Number.isNaN(start) || Number.isNaN(end) || end <= start) {\n return '';\n }\n const totalMinutes = Math.round((end - start) / 60000);\n const hours = Math.floor(totalMinutes / 60);\n const minutes = totalMinutes % 60;\n if (hours && minutes) {\n return `${hours}h ${minutes}m`;\n }\n if (hours) {\n return `${hours}h`;\n }\n return `${minutes}m`;\n}\n\nfunction formatHour(hour: number): string {\n const period = hour < 12 ? 'AM' : 'PM';\n const display = hour % 12 === 0 ? 12 : hour % 12;\n return `${display}:00 ${period}`;\n}\n\nfunction formatClock(iso: string): string {\n const date = new Date(iso);\n if (Number.isNaN(date.getTime())) {\n return '';\n }\n return date.toLocaleTimeString(undefined, { hour: 'numeric', minute: '2-digit' });\n}\n\n// ---------------------------------------------------------------------------\n// Sessions tab — multi-track schedule grid.\n//\n// The shared EVENT_SESSIONS_QUERY omits `metadata` (which carries speakerId) and\n// `description`/`capacity`, so this tab owns a fuller read that selects them; the\n// grid resolves room names from venues and speaker names from speakers.\n// ---------------------------------------------------------------------------\n\nconst SCHEDULE_SESSIONS_QUERY = gql`\n query EventfullymanagedScheduleSessions($eventId: ID, $limit: Int, $offset: Int) {\n eventSessions: eventfullyManagedEventSessions(eventId: $eventId, limit: $limit, offset: $offset) {\n total\n items {\n id\n eventId\n venueId\n title\n sessionType\n track\n startsAt\n endsAt\n status\n metadata\n }\n }\n }\n`;\n\ntype ScheduleSession = {\n id: string;\n eventId: string;\n venueId?: string | null;\n title: string;\n sessionType: string;\n track?: string | null;\n startsAt: string;\n endsAt: string;\n status: string;\n metadata?: Record<string, unknown> | null;\n};\n\ntype ScheduleSessionsResponse = { eventSessions: { total: number; items: ScheduleSession[] } };\n\nconst UNTRACKED = 'General';\n\nfunction dayKey(iso: string): string {\n const date = new Date(iso);\n return Number.isNaN(date.getTime()) ? 'unknown' : date.toISOString().slice(0, 10);\n}\n\nfunction SessionBlock({\n session,\n color,\n speakerName,\n roomName,\n onEdit,\n onDelete,\n}: {\n session: ScheduleSession;\n color: string;\n speakerName?: string;\n roomName?: string;\n onEdit: () => void;\n onDelete: () => void | Promise<void>;\n}) {\n const duration = formatDuration(session.startsAt, session.endsAt);\n return (\n <div\n className=\"relative rounded-card border border-border-subtle p-3 shadow-elevation-1\"\n style={{\n borderLeftWidth: '3px',\n borderLeftColor: color,\n background: `color-mix(in srgb, ${color} 8%, var(--color-bg-surface))`,\n }}\n >\n <div className=\"flex items-start justify-between gap-2\">\n <p className=\"min-w-0 pr-1 text-sm font-semibold leading-snug text-text-primary\">{session.title}</p>\n <div className=\"-mr-1 -mt-1 shrink-0\">\n <ActionMenu\n ariaLabel={`Session actions for ${session.title}`}\n entityName={session.title}\n deleteLabel=\"Delete session\"\n onEdit={onEdit}\n onDelete={onDelete}\n />\n </div>\n </div>\n <dl className=\"mt-2 space-y-1 text-xs text-text-secondary\">\n <div className=\"flex items-center gap-1.5\">\n <Mic2 className=\"h-3.5 w-3.5 shrink-0 text-text-muted\" />\n <span className=\"truncate\">{speakerName ?? 'Speaker TBD'}</span>\n </div>\n {roomName ? (\n <div className=\"flex items-center gap-1.5\">\n <MapPin className=\"h-3.5 w-3.5 shrink-0 text-text-muted\" />\n <span className=\"truncate\">{roomName}</span>\n </div>\n ) : null}\n <div className=\"flex items-center gap-1.5\">\n <Clock className=\"h-3.5 w-3.5 shrink-0 text-text-muted\" />\n <span>\n {formatClock(session.startsAt)}\n {duration ? ` · ${duration}` : ''}\n </span>\n </div>\n </dl>\n </div>\n );\n}\n\nfunction SessionsTab({ eventId }: { eventId: string }) {\n const { navigate } = useEventfullyManaged();\n const pagination = usePagination();\n const { data, loading, refetch } = useQuery<\n ScheduleSessionsResponse,\n { eventId?: string; limit?: number; offset?: number }\n >(SCHEDULE_SESSIONS_QUERY, {\n variables: { eventId, limit: pagination.limit, offset: pagination.offset },\n });\n const { data: venuesData } = useVenuesQuery({ limit: 100, offset: 0 });\n const { data: speakersData } = useSpeakersQuery({ limit: 100, offset: 0 });\n const [deleteEventSession] = useDeleteEventSessionMutation();\n\n const total = data?.eventSessions?.total ?? 0;\n const rows = useMemo(() => data?.eventSessions?.items ?? [], [data]);\n\n const venueNameById = useMemo(() => {\n const map = new Map<string, string>();\n for (const venue of venuesData?.venues?.items ?? []) {\n map.set(venue.id, venue.name);\n }\n return map;\n }, [venuesData]);\n\n const speakerNameById = useMemo(() => {\n const map = new Map<string, string>();\n for (const speaker of speakersData?.speakers?.items ?? []) {\n map.set(speaker.id, speaker.fullName);\n }\n return map;\n }, [speakersData]);\n\n // Distinct tracks (ordered by first appearance) → stable colour per track.\n const trackColor = useMemo(() => {\n const map = new Map<string, string>();\n let index = 0;\n for (const session of rows) {\n const track = session.track?.trim() || UNTRACKED;\n if (!map.has(track)) {\n map.set(track, CHART_COLORS[index % CHART_COLORS.length]);\n index += 1;\n }\n }\n return map;\n }, [rows]);\n\n // Group sessions by day, then by track, keyed by the hour they start.\n const days = useMemo(() => {\n const byDay = new Map<string, ScheduleSession[]>();\n for (const session of [...rows].sort((a, b) => new Date(a.startsAt).getTime() - new Date(b.startsAt).getTime())) {\n const key = dayKey(session.startsAt);\n const list = byDay.get(key) ?? [];\n list.push(session);\n byDay.set(key, list);\n }\n return Array.from(byDay.entries()).map(([key, sessions]) => {\n const tracks = Array.from(new Set(sessions.map((s) => s.track?.trim() || UNTRACKED)));\n const hours = sessions.map((s) => new Date(s.startsAt).getHours()).filter((h) => !Number.isNaN(h));\n const minHour = hours.length ? Math.min(...hours) : 9;\n const maxHour = hours.length ? Math.max(...hours) : 17;\n const hourRange: number[] = [];\n for (let hour = minHour; hour <= maxHour; hour += 1) {\n hourRange.push(hour);\n }\n return { key, sessions, tracks, hourRange };\n });\n }, [rows]);\n\n const onDelete = async (session: ScheduleSession) => {\n const response = await deleteEventSession({ variables: { id: session.id } });\n if (response.error) {\n notify.error('Could not delete session', response.error.message);\n throw response.error;\n }\n if (!response.data?.deleteEventSession) {\n notify.error('Could not delete session', 'The server did not confirm the change.');\n throw new Error('delete-unconfirmed');\n }\n notify.success('Session deleted', session.title);\n await refetch();\n };\n\n const resolveSpeaker = (session: ScheduleSession): string | undefined => {\n const metadata = (session.metadata ?? {}) as Record<string, unknown>;\n const speakerId = typeof metadata.speakerId === 'string' ? metadata.speakerId : '';\n return speakerId ? speakerNameById.get(speakerId) : undefined;\n };\n const resolveRoom = (session: ScheduleSession): string | undefined =>\n session.venueId ? venueNameById.get(session.venueId) : undefined;\n\n return (\n <div className=\"space-y-5\">\n <div className=\"flex items-center justify-between gap-3\">\n <div>\n <h2 className=\"text-lg font-semibold text-text-primary\">Agenda & Sessions</h2>\n <p className=\"text-sm text-text-secondary\">A multi-track schedule of everything happening on the day.</p>\n </div>\n <Button type=\"button\" size=\"sm\" onClick={() => navigate(`/events/${eventId}/sessions/create`)}>\n <Plus className=\"h-4 w-4\" />\n New Session\n </Button>\n </div>\n\n {loading ? (\n <Skeleton className=\"h-64 w-full rounded-card\" />\n ) : rows.length === 0 ? (\n <IllustratedEmptyState\n illustration=\"empty-data\"\n title=\"No sessions yet.\"\n description=\"Build the agenda by adding your first session.\"\n action={\n <Button type=\"button\" size=\"sm\" onClick={() => navigate(`/events/${eventId}/sessions/create`)}>\n <Plus className=\"h-4 w-4\" />\n New Session\n </Button>\n }\n className=\"py-10\"\n />\n ) : (\n <div className=\"space-y-8\">\n {days.map((day) => (\n <section key={day.key} className=\"space-y-3\">\n <h3 className=\"text-sm font-semibold text-text-primary\">\n {new Date(`${day.key}T00:00:00`).toLocaleDateString(undefined, {\n weekday: 'long',\n month: 'long',\n day: 'numeric',\n })}\n </h3>\n\n {/* Desktop / tablet: multi-track grid */}\n <div className=\"hidden overflow-x-auto rounded-card border border-border-seam md:block\">\n <div\n className=\"grid min-w-max\"\n style={{ gridTemplateColumns: `88px repeat(${day.tracks.length}, minmax(200px, 1fr))` }}\n >\n {/* Header row */}\n <div className=\"sticky left-0 z-10 border-b border-border-seam bg-bg-sunken px-3 py-2.5\" />\n {day.tracks.map((track) => (\n <div\n key={track}\n className=\"border-b border-l border-border-seam bg-bg-sunken px-3 py-2.5 text-center text-sm font-semibold text-text-primary\"\n >\n <span className=\"inline-flex items-center gap-2\">\n <span\n className=\"h-2 w-2 rounded-full\"\n style={{ background: trackColor.get(track) }}\n aria-hidden=\"true\"\n />\n {track}\n </span>\n </div>\n ))}\n\n {/* Hour rows */}\n {day.hourRange.map((hour) => (\n <div key={hour} className=\"contents\">\n <div className=\"sticky left-0 z-10 border-b border-border-subtle bg-bg-surface px-3 py-3 text-xs font-medium text-text-muted\">\n {formatHour(hour)}\n </div>\n {day.tracks.map((track) => {\n const cellSessions = day.sessions.filter(\n (session) =>\n (session.track?.trim() || UNTRACKED) === track &&\n new Date(session.startsAt).getHours() === hour\n );\n return (\n <div\n key={`${hour}-${track}`}\n className=\"space-y-2 border-b border-l border-border-subtle p-2 align-top\"\n >\n {cellSessions.map((session) => (\n <SessionBlock\n key={session.id}\n session={session}\n color={trackColor.get(track) ?? CHART_COLORS[0]}\n speakerName={resolveSpeaker(session)}\n roomName={resolveRoom(session)}\n onEdit={() => navigate(`/events/${eventId}/sessions/${session.id}/edit`)}\n onDelete={() => onDelete(session)}\n />\n ))}\n </div>\n );\n })}\n </div>\n ))}\n </div>\n </div>\n\n {/* Mobile: clean time-ordered day list */}\n <ul className=\"space-y-2.5 md:hidden\">\n {day.sessions.map((session) => {\n const track = session.track?.trim() || UNTRACKED;\n return (\n <li key={session.id}>\n <SessionBlock\n session={session}\n color={trackColor.get(track) ?? CHART_COLORS[0]}\n speakerName={resolveSpeaker(session)}\n roomName={resolveRoom(session) ?? track}\n onEdit={() => navigate(`/events/${eventId}/sessions/${session.id}/edit`)}\n onDelete={() => onDelete(session)}\n />\n </li>\n );\n })}\n </ul>\n </section>\n ))}\n </div>\n )}\n\n <Pagination\n offset={pagination.offset}\n limit={pagination.limit}\n loadedCount={rows.length}\n total={total}\n onPrev={pagination.prevPage}\n onNext={pagination.nextPage}\n itemLabel=\"sessions\"\n loading={loading}\n />\n </div>\n );\n}\n\n// ---------------------------------------------------------------------------\n\ntype SpeakerRow = { id: string; fullName: string; title?: string | null; organization?: string | null };\n\nfunction SpeakersTab() {\n const { navigate } = useEventfullyManaged();\n const pagination = usePagination();\n const { data, loading, refetch } = useSpeakersQuery({ limit: pagination.limit, offset: pagination.offset });\n const [deleteSpeaker] = useDeleteSpeakerMutation();\n\n const total = data?.speakers?.total ?? 0;\n const rows = (data?.speakers?.items ?? []) as unknown as SpeakerRow[];\n\n const onDelete = async (speaker: SpeakerRow) => {\n const response = await deleteSpeaker({ variables: { id: speaker.id } });\n if (response.error) {\n notify.error('Could not delete speaker', response.error.message);\n throw response.error;\n }\n if (!response.data?.deleteSpeaker) {\n notify.error('Could not delete speaker', 'The server did not confirm the change.');\n throw new Error('delete-unconfirmed');\n }\n notify.success('Speaker deleted', speaker.fullName);\n await refetch();\n };\n\n return (\n <div className=\"space-y-4\">\n <div className=\"flex items-center justify-between gap-3\">\n <h2 className=\"text-lg font-semibold text-text-primary\">Speakers</h2>\n <div className=\"flex items-center gap-2\">\n <Button type=\"button\" variant=\"outline\" size=\"sm\" onClick={() => navigate('/speakers')}>\n Manage speakers\n </Button>\n <Button type=\"button\" size=\"sm\" onClick={() => navigate('/speakers/create')}>\n <Plus className=\"h-4 w-4\" />\n New Speaker\n </Button>\n </div>\n </div>\n\n {loading ? (\n <Skeleton className=\"h-40 w-full rounded-card\" />\n ) : rows.length === 0 ? (\n <IllustratedEmptyState\n illustration=\"empty-people\"\n title=\"No speakers yet.\"\n description=\"Add speakers so you can assign them to sessions.\"\n className=\"py-8\"\n />\n ) : (\n <ul className=\"grid grid-cols-1 gap-2.5 sm:grid-cols-2\">\n {rows.map((speaker) => (\n <li\n key={speaker.id}\n className=\"rounded-card border border-border-seam bg-bg-surface p-3 transition-[box-shadow,border-color] duration-200 hover:border-border-strong hover:shadow-elevation-1\"\n >\n <div className=\"flex items-center justify-between gap-3\">\n <div className=\"flex min-w-0 items-center gap-3\">\n <span className=\"flex h-9 w-9 shrink-0 items-center justify-center rounded-full bg-accent-wash text-sm font-semibold text-action-primary-bg\">\n {speaker.fullName.slice(0, 1).toUpperCase()}\n </span>\n <div className=\"min-w-0\">\n <p className=\"truncate font-medium text-text-primary\">{speaker.fullName}</p>\n <p className=\"mt-0.5 truncate text-sm text-text-secondary\">\n {[speaker.title, speaker.organization].filter(Boolean).join(' • ') || 'Speaker'}\n </p>\n </div>\n </div>\n <ActionMenu\n ariaLabel={`Speaker actions for ${speaker.fullName}`}\n entityName={speaker.fullName}\n deleteLabel=\"Delete speaker\"\n onEdit={() => navigate(`/speakers/${speaker.id}/edit`)}\n onDelete={() => onDelete(speaker)}\n />\n </div>\n </li>\n ))}\n </ul>\n )}\n\n <Pagination\n offset={pagination.offset}\n limit={pagination.limit}\n loadedCount={rows.length}\n total={total}\n onPrev={pagination.prevPage}\n onNext={pagination.nextPage}\n itemLabel=\"speakers\"\n loading={loading}\n />\n </div>\n );\n}\n\n// ---------------------------------------------------------------------------\n\nfunction TicketsTab({ eventId }: { eventId: string }) {\n const { navigate } = useEventfullyManaged();\n const pagination = usePagination();\n const { data, loading, refetch } = useTicketTypesQuery({\n eventId,\n limit: pagination.limit,\n offset: pagination.offset,\n });\n const [deleteTicketType] = useDeleteTicketTypeMutation();\n\n const total = data?.ticketTypes?.total ?? 0;\n const rows = (data?.ticketTypes?.items ?? []) as TicketTypeGql[];\n\n const onDelete = async (ticket: TicketTypeGql) => {\n const response = await deleteTicketType({ variables: { id: ticket.id } });\n if (response.error) {\n notify.error('Could not delete ticket type', response.error.message);\n throw response.error;\n }\n if (!response.data?.deleteTicketType) {\n notify.error('Could not delete ticket type', 'The server did not confirm the change.');\n throw new Error('delete-unconfirmed');\n }\n notify.success('Ticket type deleted');\n await refetch();\n };\n\n return (\n <div className=\"space-y-4\">\n <div className=\"flex items-center justify-between gap-3\">\n <h2 className=\"text-lg font-semibold text-text-primary\">Ticket Types</h2>\n <Button type=\"button\" size=\"sm\" onClick={() => navigate(`/events/${eventId}/tickets/create`)}>\n <Plus className=\"h-4 w-4\" />\n New Ticket Type\n </Button>\n </div>\n\n {loading ? (\n <Skeleton className=\"h-40 w-full rounded-card\" />\n ) : rows.length === 0 ? (\n <IllustratedEmptyState\n illustration=\"empty-data\"\n title=\"No ticket types yet.\"\n description=\"Add a ticket type so attendees can register.\"\n className=\"py-8\"\n />\n ) : (\n <ul className=\"space-y-3\">\n {rows.map((ticket) => {\n const hasCapacity = ticket.capacity != null && ticket.capacity > 0;\n const pct = hasCapacity\n ? Math.min(100, Math.round((ticket.soldCount / (ticket.capacity as number)) * 100))\n : 0;\n const nearlyFull = pct >= 90;\n return (\n <li\n key={ticket.id}\n className=\"rounded-card border border-border-seam bg-bg-surface p-4 transition-[box-shadow,border-color] duration-200 hover:border-border-strong hover:shadow-elevation-1\"\n >\n <div className=\"flex items-start justify-between gap-3\">\n <div className=\"min-w-0\">\n <p className=\"truncate font-medium text-text-primary\">{ticket.name}</p>\n <p className=\"mt-0.5 text-sm text-text-secondary\">\n {ticket.price} {ticket.currency}\n </p>\n </div>\n <div className=\"flex shrink-0 items-center gap-2\">\n <Badge variant={ticket.isActive ? 'success' : 'warning'} className=\"uppercase\">\n {ticket.isActive ? 'Active' : 'Inactive'}\n </Badge>\n <ActionMenu\n ariaLabel={`Ticket actions for ${ticket.name}`}\n entityName={ticket.name}\n deleteLabel=\"Delete ticket type\"\n onEdit={() => navigate(`/events/${eventId}/tickets/${ticket.id}/edit`)}\n onDelete={() => onDelete(ticket)}\n />\n </div>\n </div>\n\n <div className=\"mt-3\">\n <div className=\"flex items-center justify-between text-xs text-text-secondary\">\n <span>\n <span className=\"font-medium tabular-nums text-text-primary\">\n {ticket.soldCount.toLocaleString()}\n </span>{' '}\n sold\n {hasCapacity ? (\n <> / {(ticket.capacity as number).toLocaleString()} capacity</>\n ) : (\n ' • unlimited capacity'\n )}\n </span>\n {hasCapacity ? (\n <span className={nearlyFull ? 'font-medium text-text-primary' : ''}>{pct}%</span>\n ) : null}\n </div>\n {hasCapacity ? (\n <div\n className=\"mt-1.5 h-2 overflow-hidden rounded-full bg-bg-sunken\"\n role=\"progressbar\"\n aria-valuenow={pct}\n aria-valuemin={0}\n aria-valuemax={100}\n aria-label={`${ticket.name} sold`}\n >\n <div\n className=\"h-full rounded-full transition-[width] duration-500\"\n style={{\n width: `${pct}%`,\n background: 'linear-gradient(90deg, var(--color-chart-1), var(--color-chart-2))',\n }}\n />\n </div>\n ) : null}\n </div>\n </li>\n );\n })}\n </ul>\n )}\n\n <Pagination\n offset={pagination.offset}\n limit={pagination.limit}\n loadedCount={rows.length}\n total={total}\n onPrev={pagination.prevPage}\n onNext={pagination.nextPage}\n itemLabel=\"ticket types\"\n loading={loading}\n />\n </div>\n );\n}\n\n// ---------------------------------------------------------------------------\n\nfunction MetricTile({\n label,\n value,\n icon: Icon,\n}: {\n label: string;\n value: string;\n icon: React.ComponentType<{ className?: string }>;\n}) {\n return (\n <GlassCard treatment=\"metric\" className=\"p-4\">\n <span className=\"flex h-8 w-8 items-center justify-center rounded-button bg-accent-wash text-action-primary-bg\">\n <Icon className=\"h-4 w-4\" />\n </span>\n <div className=\"mt-2.5 text-xl font-semibold tabular-nums text-text-primary\">{value}</div>\n <div className=\"mt-0.5 text-xs text-text-muted\">{label}</div>\n </GlassCard>\n );\n}\n\nexport default function EventDetailPage() {\n const { id = '' } = useParams<{ id: string }>();\n const { navigate } = useEventfullyManaged();\n const { data, loading } = useEventQuery(id);\n const { data: sessionsData } = useQuery<ScheduleSessionsResponse, { eventId?: string; limit?: number }>(\n SCHEDULE_SESSIONS_QUERY,\n { variables: { eventId: id, limit: 200 }, skip: !id }\n );\n const { data: ticketsData } = useTicketTypesQuery({ eventId: id, limit: 100, offset: 0 });\n const { data: venuesData } = useVenuesQuery({ limit: 100, offset: 0 });\n const [tab, setTab] = useState('overview');\n\n const event = data?.event ?? null;\n const venueId = useMemo(() => {\n const metadata = (event?.metadata ?? {}) as Record<string, unknown>;\n return typeof metadata.venueId === 'string' ? (metadata.venueId as string) : '';\n }, [event]);\n const venueName = useMemo(\n () => (venueId ? venuesData?.venues?.items?.find((venue) => venue.id === venueId)?.name : undefined),\n [venueId, venuesData]\n );\n\n const sessionCount = sessionsData?.eventSessions?.total ?? 0;\n const ticketsSold = useMemo(\n () => (ticketsData?.ticketTypes?.items ?? []).reduce((sum, ticket) => sum + (ticket.soldCount ?? 0), 0),\n [ticketsData]\n );\n\n if (loading) {\n return (\n <div className=\"space-y-4 p-6\">\n <Skeleton className=\"h-8 w-40\" />\n <Skeleton className=\"h-24 w-full rounded-card\" />\n <Skeleton className=\"h-64 w-full rounded-card\" />\n </div>\n );\n }\n\n if (!event) {\n return (\n <div className=\"space-y-4 p-6\">\n <Button type=\"button\" variant=\"ghost\" size=\"sm\" onClick={() => navigate('/events')}>\n <ArrowLeft className=\"h-4 w-4\" />\n Back to Events\n </Button>\n <GlassCard treatment=\"plain\" className=\"border-border-seam\">\n <IllustratedEmptyState\n illustration=\"empty-search\"\n title=\"Event not found.\"\n description=\"This event may have been removed or is outside your current scope.\"\n />\n </GlassCard>\n </div>\n );\n }\n\n return (\n <div className=\"space-y-6 p-6\">\n <div className=\"flex flex-col gap-4 sm:flex-row sm:items-start sm:justify-between\">\n <div className=\"space-y-3\">\n <Button type=\"button\" variant=\"ghost\" size=\"sm\" onClick={() => navigate('/events')}>\n <ArrowLeft className=\"h-4 w-4\" />\n Back to Events\n </Button>\n <div>\n <div className=\"flex flex-wrap items-center gap-3\">\n <h1 className=\"text-2xl font-bold text-text-primary\">{event.name}</h1>\n <Badge variant={statusVariant(event.status)} className=\"uppercase\">\n {event.status}\n </Badge>\n </div>\n <p className=\"mt-1 flex flex-wrap items-center gap-x-4 gap-y-1 text-sm text-text-secondary\">\n <span className=\"flex items-center gap-1.5\">\n <CalendarClock className=\"h-4 w-4 text-text-muted\" />\n {formatDateTime(event.startAt)} — {formatDateTime(event.endAt)}\n </span>\n {venueName ? (\n <span className=\"flex items-center gap-1.5\">\n <MapPin className=\"h-4 w-4 text-text-muted\" />\n {venueName}\n </span>\n ) : null}\n </p>\n </div>\n </div>\n <Button type=\"button\" onClick={() => navigate(`/events/${id}/edit`)}>\n <Edit className=\"h-4 w-4\" />\n Edit Event\n </Button>\n </div>\n\n {/* Metrics strip */}\n <div className=\"grid grid-cols-2 gap-3 sm:grid-cols-4\">\n <MetricTile label=\"Capacity\" value={(event.capacity ?? 0).toLocaleString()} icon={Users} />\n <MetricTile label=\"Sessions\" value={sessionCount.toLocaleString()} icon={CalendarClock} />\n <MetricTile\n label=\"Ticket types\"\n value={(ticketsData?.ticketTypes?.total ?? 0).toLocaleString()}\n icon={TicketIcon}\n />\n <MetricTile label=\"Tickets sold\" value={ticketsSold.toLocaleString()} icon={Eye} />\n </div>\n\n <Tabs value={tab} onValueChange={setTab}>\n <TabsList>\n <TabsTrigger value=\"overview\">Overview</TabsTrigger>\n <TabsTrigger value=\"sessions\">Sessions</TabsTrigger>\n <TabsTrigger value=\"speakers\">Speakers</TabsTrigger>\n <TabsTrigger value=\"tickets\">Tickets</TabsTrigger>\n </TabsList>\n\n <TabsContent value=\"overview\" className=\"mt-4\">\n <div className=\"grid grid-cols-1 gap-4 lg:grid-cols-3\">\n <GlassCard treatment=\"plain\" className=\"border-border-seam p-5 lg:col-span-2\">\n <h2 className=\"text-lg font-semibold text-text-primary\">Event Details</h2>\n <dl className=\"mt-4 space-y-4 text-sm\">\n <div className=\"grid grid-cols-1 gap-1 sm:grid-cols-[140px_1fr]\">\n <dt className=\"text-text-muted\">Dates</dt>\n <dd className=\"text-text-primary\">\n {formatDateTime(event.startAt)} — {formatDateTime(event.endAt)}\n </dd>\n </div>\n <div className=\"grid grid-cols-1 gap-1 sm:grid-cols-[140px_1fr]\">\n <dt className=\"text-text-muted\">Venue</dt>\n <dd className=\"text-text-primary\">\n {venueName ? (\n <Button\n type=\"button\"\n variant=\"link\"\n size=\"sm\"\n className=\"h-auto p-0\"\n onClick={() => navigate(`/venues/${venueId}`)}\n >\n {venueName}\n </Button>\n ) : (\n 'No venue assigned'\n )}\n </dd>\n </div>\n <div className=\"grid grid-cols-1 gap-1 sm:grid-cols-[140px_1fr]\">\n <dt className=\"text-text-muted\">Capacity</dt>\n <dd className=\"text-text-primary\">{(event.capacity ?? 0).toLocaleString()} attendees</dd>\n </div>\n <div className=\"grid grid-cols-1 gap-1 sm:grid-cols-[140px_1fr]\">\n <dt className=\"text-text-muted\">Type</dt>\n <dd className=\"capitalize text-text-primary\">{event.type.toLowerCase().replace(/_/g, ' ')}</dd>\n </div>\n <div className=\"grid grid-cols-1 gap-1 sm:grid-cols-[140px_1fr]\">\n <dt className=\"text-text-muted\">Timezone</dt>\n <dd className=\"text-text-primary\">{event.timezone}</dd>\n </div>\n <div className=\"grid grid-cols-1 gap-1 sm:grid-cols-[140px_1fr]\">\n <dt className=\"text-text-muted\">Visibility</dt>\n <dd className=\"capitalize text-text-primary\">{event.visibility}</dd>\n </div>\n <div className=\"grid grid-cols-1 gap-1 sm:grid-cols-[140px_1fr]\">\n <dt className=\"text-text-muted\">Description</dt>\n <dd className=\"text-text-secondary\">\n {event.description?.trim() ? event.description : 'No description provided.'}\n </dd>\n </div>\n </dl>\n <div className=\"mt-5\">\n <Button type=\"button\" variant=\"outline\" size=\"sm\" onClick={() => navigate(`/events/${id}/edit`)}>\n <Edit className=\"h-4 w-4\" />\n Edit event details\n </Button>\n </div>\n </GlassCard>\n\n <GlassCard treatment=\"glass\" glow className=\"p-5\">\n <h2 className=\"text-lg font-semibold text-text-primary\">Key Metrics</h2>\n <dl className=\"mt-4 space-y-4\">\n <div>\n <dt className=\"text-sm text-text-secondary\">Capacity</dt>\n <dd className=\"text-2xl font-semibold tabular-nums text-text-primary\">\n {(event.capacity ?? 0).toLocaleString()}\n </dd>\n </div>\n <div>\n <dt className=\"text-sm text-text-secondary\">Sessions</dt>\n <dd className=\"text-2xl font-semibold tabular-nums text-text-primary\">\n {sessionCount.toLocaleString()}\n </dd>\n </div>\n <div>\n <dt className=\"text-sm text-text-secondary\">Ticket types</dt>\n <dd className=\"text-2xl font-semibold tabular-nums text-text-primary\">\n {(ticketsData?.ticketTypes?.total ?? 0).toLocaleString()}\n </dd>\n </div>\n <div>\n <dt className=\"text-sm text-text-secondary\">Tickets sold</dt>\n <dd className=\"text-2xl font-semibold tabular-nums text-text-primary\">\n {ticketsSold.toLocaleString()}\n </dd>\n </div>\n </dl>\n </GlassCard>\n </div>\n </TabsContent>\n\n <TabsContent value=\"sessions\" className=\"mt-4\">\n <SessionsTab eventId={id} />\n </TabsContent>\n\n <TabsContent value=\"speakers\" className=\"mt-4\">\n <SpeakersTab />\n </TabsContent>\n\n <TabsContent value=\"tickets\" className=\"mt-4\">\n <TicketsTab eventId={id} />\n </TabsContent>\n </Tabs>\n </div>\n );\n}\n"],"mappings":";;;;;;;;;;;;;;;AA6CA,SAAS,EAAc,GAA8D;CACnF,IAAM,IAAQ,EAAO,aAAa;AAUlC,QATI;EAAC;EAAU;EAAa;EAAQ;EAAa;EAAa;EAAW,CAAC,SAAS,EAAM,GAChF,YAEL;EAAC;EAAY;EAAa;EAAY;EAAU;EAAU,CAAC,SAAS,EAAM,GACrE,YAEL;EAAC;EAAS;EAAW;EAAW;EAAY,CAAC,SAAS,EAAM,GACvD,SAEF;;AAGT,SAAS,EAAe,GAAkB,GAAwB;CAChE,IAAM,IAAQ,IAAI,KAAK,EAAS,CAAC,SAAS,EACpC,IAAM,IAAI,KAAK,EAAO,CAAC,SAAS;AACtC,KAAI,OAAO,MAAM,EAAM,IAAI,OAAO,MAAM,EAAI,IAAI,KAAO,EACrD,QAAO;CAET,IAAM,IAAe,KAAK,OAAO,IAAM,KAAS,IAAM,EAChD,IAAQ,KAAK,MAAM,IAAe,GAAG,EACrC,IAAU,IAAe;AAO/B,QANI,KAAS,IACJ,GAAG,EAAM,IAAI,EAAQ,KAE1B,IACK,GAAG,EAAM,KAEX,GAAG,EAAQ;;AAGpB,SAAS,EAAW,GAAsB;CACxC,IAAM,IAAS,IAAO,KAAK,OAAO;AAElC,QAAO,GADS,IAAO,MAAO,IAAI,KAAK,IAAO,GAC5B,MAAM;;AAG1B,SAAS,EAAY,GAAqB;CACxC,IAAM,IAAO,IAAI,KAAK,EAAI;AAI1B,QAHI,OAAO,MAAM,EAAK,SAAS,CAAC,GACvB,KAEF,EAAK,mBAAmB,KAAA,GAAW;EAAE,MAAM;EAAW,QAAQ;EAAW,CAAC;;AAWnF,IAAM,IAA0B,CAAG;;;;;;;;;;;;;;;;;;GAmC7B,IAAY;AAElB,SAAS,EAAO,GAAqB;CACnC,IAAM,IAAO,IAAI,KAAK,EAAI;AAC1B,QAAO,OAAO,MAAM,EAAK,SAAS,CAAC,GAAG,YAAY,EAAK,aAAa,CAAC,MAAM,GAAG,GAAG;;AAGnF,SAAS,EAAa,EACpB,YACA,UACA,gBACA,aACA,WACA,eAQC;CACD,IAAM,IAAW,EAAe,EAAQ,UAAU,EAAQ,OAAO;AACjE,QACE,kBAAC,OAAD;EACE,WAAU;EACV,OAAO;GACL,iBAAiB;GACjB,iBAAiB;GACjB,YAAY,sBAAsB,EAAM;GACzC;YANH,CAQE,kBAAC,OAAD;GAAK,WAAU;aAAf,CACE,kBAAC,KAAD;IAAG,WAAU;cAAqE,EAAQ;IAAU,CAAA,EACpG,kBAAC,OAAD;IAAK,WAAU;cACb,kBAAC,GAAD;KACE,WAAW,uBAAuB,EAAQ;KAC1C,YAAY,EAAQ;KACpB,aAAY;KACJ;KACE;KACV,CAAA;IACE,CAAA,CACF;MACN,kBAAC,MAAD;GAAI,WAAU;aAAd;IACE,kBAAC,OAAD;KAAK,WAAU;eAAf,CACE,kBAAC,GAAD,EAAM,WAAU,wCAAyC,CAAA,EACzD,kBAAC,QAAD;MAAM,WAAU;gBAAY,KAAe;MAAqB,CAAA,CAC5D;;IACL,IACC,kBAAC,OAAD;KAAK,WAAU;eAAf,CACE,kBAAC,GAAD,EAAQ,WAAU,wCAAyC,CAAA,EAC3D,kBAAC,QAAD;MAAM,WAAU;gBAAY;MAAgB,CAAA,CACxC;SACJ;IACJ,kBAAC,OAAD;KAAK,WAAU;eAAf,CACE,kBAAC,GAAD,EAAO,WAAU,wCAAyC,CAAA,EAC1D,kBAAC,QAAD,EAAA,UAAA,CACG,EAAY,EAAQ,SAAS,EAC7B,IAAW,MAAM,MAAa,GAC1B,EAAA,CAAA,CACH;;IACH;KACD;;;AAIV,SAAS,EAAY,EAAE,cAAgC;CACrD,IAAM,EAAE,gBAAa,GAAsB,EACrC,IAAa,GAAe,EAC5B,EAAE,SAAM,YAAS,eAAY,EAGjC,GAAyB,EACzB,WAAW;EAAE;EAAS,OAAO,EAAW;EAAO,QAAQ,EAAW;EAAQ,EAC3E,CAAC,EACI,EAAE,MAAM,MAAe,EAAe;EAAE,OAAO;EAAK,QAAQ;EAAG,CAAC,EAChE,EAAE,MAAM,MAAiB,EAAiB;EAAE,OAAO;EAAK,QAAQ;EAAG,CAAC,EACpE,CAAC,KAAsB,GAA+B,EAEtD,IAAQ,GAAM,eAAe,SAAS,GACtC,IAAO,QAAc,GAAM,eAAe,SAAS,EAAE,EAAE,CAAC,EAAK,CAAC,EAE9D,IAAgB,QAAc;EAClC,IAAM,oBAAM,IAAI,KAAqB;AACrC,OAAK,IAAM,KAAS,GAAY,QAAQ,SAAS,EAAE,CACjD,GAAI,IAAI,EAAM,IAAI,EAAM,KAAK;AAE/B,SAAO;IACN,CAAC,EAAW,CAAC,EAEV,IAAkB,QAAc;EACpC,IAAM,oBAAM,IAAI,KAAqB;AACrC,OAAK,IAAM,KAAW,GAAc,UAAU,SAAS,EAAE,CACvD,GAAI,IAAI,EAAQ,IAAI,EAAQ,SAAS;AAEvC,SAAO;IACN,CAAC,EAAa,CAAC,EAGZ,IAAa,QAAc;EAC/B,IAAM,oBAAM,IAAI,KAAqB,EACjC,IAAQ;AACZ,OAAK,IAAM,KAAW,GAAM;GAC1B,IAAM,IAAQ,EAAQ,OAAO,MAAM,IAAI;AACvC,GAAK,EAAI,IAAI,EAAM,KACjB,EAAI,IAAI,GAAO,EAAa,IAAQ,EAAa,QAAQ,EACzD,KAAS;;AAGb,SAAO;IACN,CAAC,EAAK,CAAC,EAGJ,IAAO,QAAc;EACzB,IAAM,oBAAQ,IAAI,KAAgC;AAClD,OAAK,IAAM,KAAW,CAAC,GAAG,EAAK,CAAC,MAAM,GAAG,MAAM,IAAI,KAAK,EAAE,SAAS,CAAC,SAAS,GAAG,IAAI,KAAK,EAAE,SAAS,CAAC,SAAS,CAAC,EAAE;GAC/G,IAAM,IAAM,EAAO,EAAQ,SAAS,EAC9B,IAAO,EAAM,IAAI,EAAI,IAAI,EAAE;AAEjC,GADA,EAAK,KAAK,EAAQ,EAClB,EAAM,IAAI,GAAK,EAAK;;AAEtB,SAAO,MAAM,KAAK,EAAM,SAAS,CAAC,CAAC,KAAK,CAAC,GAAK,OAAc;GAC1D,IAAM,IAAS,MAAM,KAAK,IAAI,IAAI,EAAS,KAAK,MAAM,EAAE,OAAO,MAAM,IAAI,EAAU,CAAC,CAAC,EAC/E,IAAQ,EAAS,KAAK,MAAM,IAAI,KAAK,EAAE,SAAS,CAAC,UAAU,CAAC,CAAC,QAAQ,MAAM,CAAC,OAAO,MAAM,EAAE,CAAC,EAC5F,IAAU,EAAM,SAAS,KAAK,IAAI,GAAG,EAAM,GAAG,GAC9C,IAAU,EAAM,SAAS,KAAK,IAAI,GAAG,EAAM,GAAG,IAC9C,IAAsB,EAAE;AAC9B,QAAK,IAAI,IAAO,GAAS,KAAQ,GAAS,KAAQ,EAChD,GAAU,KAAK,EAAK;AAEtB,UAAO;IAAE;IAAK;IAAU;IAAQ;IAAW;IAC3C;IACD,CAAC,EAAK,CAAC,EAEJ,IAAW,OAAO,MAA6B;EACnD,IAAM,IAAW,MAAM,EAAmB,EAAE,WAAW,EAAE,IAAI,EAAQ,IAAI,EAAE,CAAC;AAC5E,MAAI,EAAS,MAEX,OADA,EAAO,MAAM,4BAA4B,EAAS,MAAM,QAAQ,EAC1D,EAAS;AAEjB,MAAI,CAAC,EAAS,MAAM,mBAElB,OADA,EAAO,MAAM,4BAA4B,yCAAyC,EACxE,MAAM,qBAAqB;AAGvC,EADA,EAAO,QAAQ,mBAAmB,EAAQ,MAAM,EAChD,MAAM,GAAS;IAGX,KAAkB,MAAiD;EACvE,IAAM,IAAY,EAAQ,YAAY,EAAE,EAClC,IAAY,OAAO,EAAS,aAAc,WAAW,EAAS,YAAY;AAChF,SAAO,IAAY,EAAgB,IAAI,EAAU,GAAG,KAAA;IAEhD,KAAe,MACnB,EAAQ,UAAU,EAAc,IAAI,EAAQ,QAAQ,GAAG,KAAA;AAEzD,QACE,kBAAC,OAAD;EAAK,WAAU;YAAf;GACE,kBAAC,OAAD;IAAK,WAAU;cAAf,CACE,kBAAC,OAAD,EAAA,UAAA,CACE,kBAAC,MAAD;KAAI,WAAU;eAA0C;KAA0B,CAAA,EAClF,kBAAC,KAAD;KAAG,WAAU;eAA8B;KAA8D,CAAA,CACrG,EAAA,CAAA,EACN,kBAAC,GAAD;KAAQ,MAAK;KAAS,MAAK;KAAK,eAAe,EAAS,WAAW,EAAQ,kBAAkB;eAA7F,CACE,kBAAC,GAAD,EAAM,WAAU,WAAY,CAAA,EAAA,cAErB;OACL;;GAEL,IACC,kBAAC,GAAD,EAAU,WAAU,4BAA6B,CAAA,GAC/C,EAAK,WAAW,IAClB,kBAAC,GAAD;IACE,cAAa;IACb,OAAM;IACN,aAAY;IACZ,QACE,kBAAC,GAAD;KAAQ,MAAK;KAAS,MAAK;KAAK,eAAe,EAAS,WAAW,EAAQ,kBAAkB;eAA7F,CACE,kBAAC,GAAD,EAAM,WAAU,WAAY,CAAA,EAAA,cAErB;;IAEX,WAAU;IACV,CAAA,GAEF,kBAAC,OAAD;IAAK,WAAU;cACZ,EAAK,KAAK,MACT,kBAAC,WAAD;KAAuB,WAAU;eAAjC;MACE,kBAAC,MAAD;OAAI,WAAU;kCACX,IAAI,KAAK,GAAG,EAAI,IAAI,WAAW,EAAC,mBAAmB,KAAA,GAAW;QAC7D,SAAS;QACT,OAAO;QACP,KAAK;QACN,CAAC;OACC,CAAA;MAGL,kBAAC,OAAD;OAAK,WAAU;iBACb,kBAAC,OAAD;QACE,WAAU;QACV,OAAO,EAAE,qBAAqB,eAAe,EAAI,OAAO,OAAO,wBAAwB;kBAFzF;SAKE,kBAAC,OAAD,EAAK,WAAU,2EAA4E,CAAA;SAC1F,EAAI,OAAO,KAAK,MACf,kBAAC,OAAD;UAEE,WAAU;oBAEV,kBAAC,QAAD;WAAM,WAAU;qBAAhB,CACE,kBAAC,QAAD;YACE,WAAU;YACV,OAAO,EAAE,YAAY,EAAW,IAAI,EAAM,EAAE;YAC5C,eAAY;YACZ,CAAA,EACD,EACI;;UACH,EAXC,EAWD,CACN;SAGD,EAAI,UAAU,KAAK,MAClB,kBAAC,OAAD;UAAgB,WAAU;oBAA1B,CACE,kBAAC,OAAD;WAAK,WAAU;qBACZ,EAAW,EAAK;WACb,CAAA,EACL,EAAI,OAAO,KAAK,MAOb,kBAAC,OAAD;WAEE,WAAU;qBARO,EAAI,SAAS,QAC/B,OACE,EAAQ,OAAO,MAAM,IAAI,OAAe,KACzC,IAAI,KAAK,EAAQ,SAAS,CAAC,UAAU,KAAK,EAC7C,CAMiB,KAAK,MACjB,kBAAC,GAAD;YAEW;YACT,OAAO,EAAW,IAAI,EAAM,IAAI,EAAa;YAC7C,aAAa,EAAe,EAAQ;YACpC,UAAU,EAAY,EAAQ;YAC9B,cAAc,EAAS,WAAW,EAAQ,YAAY,EAAQ,GAAG,OAAO;YACxE,gBAAgB,EAAS,EAAQ;YACjC,EAPK,EAAQ,GAOb,CACF;WACE,EAdC,GAAG,EAAK,GAAG,IAcZ,CAER,CACE;YA7BI,EA6BJ,CACN;SACE;;OACF,CAAA;MAGN,kBAAC,MAAD;OAAI,WAAU;iBACX,EAAI,SAAS,KAAK,MAAY;QAC7B,IAAM,IAAQ,EAAQ,OAAO,MAAM,IAAI;AACvC,eACE,kBAAC,MAAD,EAAA,UACE,kBAAC,GAAD;SACW;SACT,OAAO,EAAW,IAAI,EAAM,IAAI,EAAa;SAC7C,aAAa,EAAe,EAAQ;SACpC,UAAU,EAAY,EAAQ,IAAI;SAClC,cAAc,EAAS,WAAW,EAAQ,YAAY,EAAQ,GAAG,OAAO;SACxE,gBAAgB,EAAS,EAAQ;SACjC,CAAA,EACC,EATI,EAAQ,GASZ;SAEP;OACC,CAAA;MACG;OAvFI,EAAI,IAuFR,CACV;IACE,CAAA;GAGR,kBAAC,GAAD;IACE,QAAQ,EAAW;IACnB,OAAO,EAAW;IAClB,aAAa,EAAK;IACX;IACP,QAAQ,EAAW;IACnB,QAAQ,EAAW;IACnB,WAAU;IACD;IACT,CAAA;GACE;;;AAQV,SAAS,IAAc;CACrB,IAAM,EAAE,gBAAa,GAAsB,EACrC,IAAa,GAAe,EAC5B,EAAE,SAAM,YAAS,eAAY,EAAiB;EAAE,OAAO,EAAW;EAAO,QAAQ,EAAW;EAAQ,CAAC,EACrG,CAAC,KAAiB,GAA0B,EAE5C,IAAQ,GAAM,UAAU,SAAS,GACjC,IAAQ,GAAM,UAAU,SAAS,EAAE,EAEnC,IAAW,OAAO,MAAwB;EAC9C,IAAM,IAAW,MAAM,EAAc,EAAE,WAAW,EAAE,IAAI,EAAQ,IAAI,EAAE,CAAC;AACvE,MAAI,EAAS,MAEX,OADA,EAAO,MAAM,4BAA4B,EAAS,MAAM,QAAQ,EAC1D,EAAS;AAEjB,MAAI,CAAC,EAAS,MAAM,cAElB,OADA,EAAO,MAAM,4BAA4B,yCAAyC,EACxE,MAAM,qBAAqB;AAGvC,EADA,EAAO,QAAQ,mBAAmB,EAAQ,SAAS,EACnD,MAAM,GAAS;;AAGjB,QACE,kBAAC,OAAD;EAAK,WAAU;YAAf;GACE,kBAAC,OAAD;IAAK,WAAU;cAAf,CACE,kBAAC,MAAD;KAAI,WAAU;eAA0C;KAAa,CAAA,EACrE,kBAAC,OAAD;KAAK,WAAU;eAAf,CACE,kBAAC,GAAD;MAAQ,MAAK;MAAS,SAAQ;MAAU,MAAK;MAAK,eAAe,EAAS,YAAY;gBAAE;MAE/E,CAAA,EACT,kBAAC,GAAD;MAAQ,MAAK;MAAS,MAAK;MAAK,eAAe,EAAS,mBAAmB;gBAA3E,CACE,kBAAC,GAAD,EAAM,WAAU,WAAY,CAAA,EAAA,cAErB;QACL;OACF;;GAEL,IACC,kBAAC,GAAD,EAAU,WAAU,4BAA6B,CAAA,GAC/C,EAAK,WAAW,IAClB,kBAAC,GAAD;IACE,cAAa;IACb,OAAM;IACN,aAAY;IACZ,WAAU;IACV,CAAA,GAEF,kBAAC,MAAD;IAAI,WAAU;cACX,EAAK,KAAK,MACT,kBAAC,MAAD;KAEE,WAAU;eAEV,kBAAC,OAAD;MAAK,WAAU;gBAAf,CACE,kBAAC,OAAD;OAAK,WAAU;iBAAf,CACE,kBAAC,QAAD;QAAM,WAAU;kBACb,EAAQ,SAAS,MAAM,GAAG,EAAE,CAAC,aAAa;QACtC,CAAA,EACP,kBAAC,OAAD;QAAK,WAAU;kBAAf,CACE,kBAAC,KAAD;SAAG,WAAU;mBAA0C,EAAQ;SAAa,CAAA,EAC5E,kBAAC,KAAD;SAAG,WAAU;mBACV,CAAC,EAAQ,OAAO,EAAQ,aAAa,CAAC,OAAO,QAAQ,CAAC,KAAK,MAAM,IAAI;SACpE,CAAA,CACA;UACF;UACN,kBAAC,GAAD;OACE,WAAW,uBAAuB,EAAQ;OAC1C,YAAY,EAAQ;OACpB,aAAY;OACZ,cAAc,EAAS,aAAa,EAAQ,GAAG,OAAO;OACtD,gBAAgB,EAAS,EAAQ;OACjC,CAAA,CACE;;KACH,EAvBE,EAAQ,GAuBV,CACL;IACC,CAAA;GAGP,kBAAC,GAAD;IACE,QAAQ,EAAW;IACnB,OAAO,EAAW;IAClB,aAAa,EAAK;IACX;IACP,QAAQ,EAAW;IACnB,QAAQ,EAAW;IACnB,WAAU;IACD;IACT,CAAA;GACE;;;AAMV,SAAS,EAAW,EAAE,cAAgC;CACpD,IAAM,EAAE,gBAAa,GAAsB,EACrC,IAAa,GAAe,EAC5B,EAAE,SAAM,YAAS,eAAY,EAAoB;EACrD;EACA,OAAO,EAAW;EAClB,QAAQ,EAAW;EACpB,CAAC,EACI,CAAC,KAAoB,GAA6B,EAElD,IAAQ,GAAM,aAAa,SAAS,GACpC,IAAQ,GAAM,aAAa,SAAS,EAAE,EAEtC,IAAW,OAAO,MAA0B;EAChD,IAAM,IAAW,MAAM,EAAiB,EAAE,WAAW,EAAE,IAAI,EAAO,IAAI,EAAE,CAAC;AACzE,MAAI,EAAS,MAEX,OADA,EAAO,MAAM,gCAAgC,EAAS,MAAM,QAAQ,EAC9D,EAAS;AAEjB,MAAI,CAAC,EAAS,MAAM,iBAElB,OADA,EAAO,MAAM,gCAAgC,yCAAyC,EAC5E,MAAM,qBAAqB;AAGvC,EADA,EAAO,QAAQ,sBAAsB,EACrC,MAAM,GAAS;;AAGjB,QACE,kBAAC,OAAD;EAAK,WAAU;YAAf;GACE,kBAAC,OAAD;IAAK,WAAU;cAAf,CACE,kBAAC,MAAD;KAAI,WAAU;eAA0C;KAAiB,CAAA,EACzE,kBAAC,GAAD;KAAQ,MAAK;KAAS,MAAK;KAAK,eAAe,EAAS,WAAW,EAAQ,iBAAiB;eAA5F,CACE,kBAAC,GAAD,EAAM,WAAU,WAAY,CAAA,EAAA,kBAErB;OACL;;GAEL,IACC,kBAAC,GAAD,EAAU,WAAU,4BAA6B,CAAA,GAC/C,EAAK,WAAW,IAClB,kBAAC,GAAD;IACE,cAAa;IACb,OAAM;IACN,aAAY;IACZ,WAAU;IACV,CAAA,GAEF,kBAAC,MAAD;IAAI,WAAU;cACX,EAAK,KAAK,MAAW;KACpB,IAAM,IAAc,EAAO,YAAY,QAAQ,EAAO,WAAW,GAC3D,IAAM,IACR,KAAK,IAAI,KAAK,KAAK,MAAO,EAAO,YAAa,EAAO,WAAuB,IAAI,CAAC,GACjF,GACE,IAAa,KAAO;AAC1B,YACE,kBAAC,MAAD;MAEE,WAAU;gBAFZ,CAIE,kBAAC,OAAD;OAAK,WAAU;iBAAf,CACE,kBAAC,OAAD;QAAK,WAAU;kBAAf,CACE,kBAAC,KAAD;SAAG,WAAU;mBAA0C,EAAO;SAAS,CAAA,EACvE,kBAAC,KAAD;SAAG,WAAU;mBAAb;UACG,EAAO;UAAM;UAAE,EAAO;UACrB;WACA;WACN,kBAAC,OAAD;QAAK,WAAU;kBAAf,CACE,kBAAC,GAAD;SAAO,SAAS,EAAO,WAAW,YAAY;SAAW,WAAU;mBAChE,EAAO,WAAW,WAAW;SACxB,CAAA,EACR,kBAAC,GAAD;SACE,WAAW,sBAAsB,EAAO;SACxC,YAAY,EAAO;SACnB,aAAY;SACZ,cAAc,EAAS,WAAW,EAAQ,WAAW,EAAO,GAAG,OAAO;SACtE,gBAAgB,EAAS,EAAO;SAChC,CAAA,CACE;UACF;UAEN,kBAAC,OAAD;OAAK,WAAU;iBAAf,CACE,kBAAC,OAAD;QAAK,WAAU;kBAAf,CACE,kBAAC,QAAD,EAAA,UAAA;SACE,kBAAC,QAAD;UAAM,WAAU;oBACb,EAAO,UAAU,gBAAgB;UAC7B,CAAA;SAAC;SAAI;SAEX,IACC,kBAAA,GAAA,EAAA,UAAA;UAAE;UAAK,EAAO,SAAoB,gBAAgB;UAAC;UAAY,EAAA,CAAA,GAE/D;SAEG,EAAA,CAAA,EACN,IACC,kBAAC,QAAD;SAAM,WAAW,IAAa,kCAAkC;mBAAhE,CAAqE,GAAI,IAAQ;aAC/E,KACA;WACL,IACC,kBAAC,OAAD;QACE,WAAU;QACV,MAAK;QACL,iBAAe;QACf,iBAAe;QACf,iBAAe;QACf,cAAY,GAAG,EAAO,KAAK;kBAE3B,kBAAC,OAAD;SACE,WAAU;SACV,OAAO;UACL,OAAO,GAAG,EAAI;UACd,YAAY;UACb;SACD,CAAA;QACE,CAAA,GACJ,KACA;SACH;QA5DE,EAAO,GA4DT;MAEP;IACC,CAAA;GAGP,kBAAC,GAAD;IACE,QAAQ,EAAW;IACnB,OAAO,EAAW;IAClB,aAAa,EAAK;IACX;IACP,QAAQ,EAAW;IACnB,QAAQ,EAAW;IACnB,WAAU;IACD;IACT,CAAA;GACE;;;AAMV,SAAS,EAAW,EAClB,UACA,UACA,MAAM,KAKL;AACD,QACE,kBAAC,GAAD;EAAW,WAAU;EAAS,WAAU;YAAxC;GACE,kBAAC,QAAD;IAAM,WAAU;cACd,kBAAC,GAAD,EAAM,WAAU,WAAY,CAAA;IACvB,CAAA;GACP,kBAAC,OAAD;IAAK,WAAU;cAA+D;IAAY,CAAA;GAC1F,kBAAC,OAAD;IAAK,WAAU;cAAkC;IAAY,CAAA;GACnD;;;AAIhB,SAAwB,IAAkB;CACxC,IAAM,EAAE,QAAK,OAAO,GAA2B,EACzC,EAAE,gBAAa,GAAsB,EACrC,EAAE,SAAM,eAAY,EAAc,EAAG,EACrC,EAAE,MAAM,MAAiB,EAC7B,GACA;EAAE,WAAW;GAAE,SAAS;GAAI,OAAO;GAAK;EAAE,MAAM,CAAC;EAAI,CACtD,EACK,EAAE,MAAM,MAAgB,EAAoB;EAAE,SAAS;EAAI,OAAO;EAAK,QAAQ;EAAG,CAAC,EACnF,EAAE,MAAM,MAAe,EAAe;EAAE,OAAO;EAAK,QAAQ;EAAG,CAAC,EAChE,CAAC,GAAK,KAAU,EAAS,WAAW,EAEpC,IAAQ,GAAM,SAAS,MACvB,IAAU,QAAc;EAC5B,IAAM,IAAY,GAAO,YAAY,EAAE;AACvC,SAAO,OAAO,EAAS,WAAY,WAAY,EAAS,UAAqB;IAC5E,CAAC,EAAM,CAAC,EACL,IAAY,QACT,IAAU,GAAY,QAAQ,OAAO,MAAM,MAAU,EAAM,OAAO,EAAQ,EAAE,OAAO,KAAA,GAC1F,CAAC,GAAS,EAAW,CACtB,EAEK,IAAe,GAAc,eAAe,SAAS,GACrD,IAAc,SACX,GAAa,aAAa,SAAS,EAAE,EAAE,QAAQ,GAAK,MAAW,KAAO,EAAO,aAAa,IAAI,EAAE,EACvG,CAAC,EAAY,CACd;AA8BD,QA5BI,IAEA,kBAAC,OAAD;EAAK,WAAU;YAAf;GACE,kBAAC,GAAD,EAAU,WAAU,YAAa,CAAA;GACjC,kBAAC,GAAD,EAAU,WAAU,4BAA6B,CAAA;GACjD,kBAAC,GAAD,EAAU,WAAU,4BAA6B,CAAA;GAC7C;MAIL,IAmBH,kBAAC,OAAD;EAAK,WAAU;YAAf;GACE,kBAAC,OAAD;IAAK,WAAU;cAAf,CACE,kBAAC,OAAD;KAAK,WAAU;eAAf,CACE,kBAAC,GAAD;MAAQ,MAAK;MAAS,SAAQ;MAAQ,MAAK;MAAK,eAAe,EAAS,UAAU;gBAAlF,CACE,kBAAC,GAAD,EAAW,WAAU,WAAY,CAAA,EAAA,iBAE1B;SACT,kBAAC,OAAD,EAAA,UAAA,CACE,kBAAC,OAAD;MAAK,WAAU;gBAAf,CACE,kBAAC,MAAD;OAAI,WAAU;iBAAwC,EAAM;OAAU,CAAA,EACtE,kBAAC,GAAD;OAAO,SAAS,EAAc,EAAM,OAAO;OAAE,WAAU;iBACpD,EAAM;OACD,CAAA,CACJ;SACN,kBAAC,KAAD;MAAG,WAAU;gBAAb,CACE,kBAAC,QAAD;OAAM,WAAU;iBAAhB;QACE,kBAAC,GAAD,EAAe,WAAU,2BAA4B,CAAA;QACpD,EAAe,EAAM,QAAQ;QAAC;QAAI,EAAe,EAAM,MAAM;QACzD;UACN,IACC,kBAAC,QAAD;OAAM,WAAU;iBAAhB,CACE,kBAAC,GAAD,EAAQ,WAAU,2BAA4B,CAAA,EAC7C,EACI;WACL,KACF;QACA,EAAA,CAAA,CACF;QACN,kBAAC,GAAD;KAAQ,MAAK;KAAS,eAAe,EAAS,WAAW,EAAG,OAAO;eAAnE,CACE,kBAAC,GAAD,EAAM,WAAU,WAAY,CAAA,EAAA,aAErB;OACL;;GAGN,kBAAC,OAAD;IAAK,WAAU;cAAf;KACE,kBAAC,GAAD;MAAY,OAAM;MAAW,QAAQ,EAAM,YAAY,GAAG,gBAAgB;MAAE,MAAM;MAAS,CAAA;KAC3F,kBAAC,GAAD;MAAY,OAAM;MAAW,OAAO,EAAa,gBAAgB;MAAE,MAAM;MAAiB,CAAA;KAC1F,kBAAC,GAAD;MACE,OAAM;MACN,QAAQ,GAAa,aAAa,SAAS,GAAG,gBAAgB;MAC9D,MAAM;MACN,CAAA;KACF,kBAAC,GAAD;MAAY,OAAM;MAAe,OAAO,EAAY,gBAAgB;MAAE,MAAM;MAAO,CAAA;KAC/E;;GAEN,kBAAC,GAAD;IAAM,OAAO;IAAK,eAAe;cAAjC;KACE,kBAAC,GAAD,EAAA,UAAA;MACE,kBAAC,GAAD;OAAa,OAAM;iBAAW;OAAsB,CAAA;MACpD,kBAAC,GAAD;OAAa,OAAM;iBAAW;OAAsB,CAAA;MACpD,kBAAC,GAAD;OAAa,OAAM;iBAAW;OAAsB,CAAA;MACpD,kBAAC,GAAD;OAAa,OAAM;iBAAU;OAAqB,CAAA;MACzC,EAAA,CAAA;KAEX,kBAAC,GAAD;MAAa,OAAM;MAAW,WAAU;gBACtC,kBAAC,OAAD;OAAK,WAAU;iBAAf,CACE,kBAAC,GAAD;QAAW,WAAU;QAAQ,WAAU;kBAAvC;SACE,kBAAC,MAAD;UAAI,WAAU;oBAA0C;UAAkB,CAAA;SAC1E,kBAAC,MAAD;UAAI,WAAU;oBAAd;WACE,kBAAC,OAAD;YAAK,WAAU;sBAAf,CACE,kBAAC,MAAD;aAAI,WAAU;uBAAkB;aAAU,CAAA,EAC1C,kBAAC,MAAD;aAAI,WAAU;uBAAd;cACG,EAAe,EAAM,QAAQ;cAAC;cAAI,EAAe,EAAM,MAAM;cAC3D;eACD;;WACN,kBAAC,OAAD;YAAK,WAAU;sBAAf,CACE,kBAAC,MAAD;aAAI,WAAU;uBAAkB;aAAU,CAAA,EAC1C,kBAAC,MAAD;aAAI,WAAU;uBACX,IACC,kBAAC,GAAD;cACE,MAAK;cACL,SAAQ;cACR,MAAK;cACL,WAAU;cACV,eAAe,EAAS,WAAW,IAAU;wBAE5C;cACM,CAAA,GAET;aAEC,CAAA,CACD;;WACN,kBAAC,OAAD;YAAK,WAAU;sBAAf,CACE,kBAAC,MAAD;aAAI,WAAU;uBAAkB;aAAa,CAAA,EAC7C,kBAAC,MAAD;aAAI,WAAU;uBAAd,EAAoC,EAAM,YAAY,GAAG,gBAAgB,EAAC,aAAe;eACrF;;WACN,kBAAC,OAAD;YAAK,WAAU;sBAAf,CACE,kBAAC,MAAD;aAAI,WAAU;uBAAkB;aAAS,CAAA,EACzC,kBAAC,MAAD;aAAI,WAAU;uBAAgC,EAAM,KAAK,aAAa,CAAC,QAAQ,MAAM,IAAI;aAAM,CAAA,CAC3F;;WACN,kBAAC,OAAD;YAAK,WAAU;sBAAf,CACE,kBAAC,MAAD;aAAI,WAAU;uBAAkB;aAAa,CAAA,EAC7C,kBAAC,MAAD;aAAI,WAAU;uBAAqB,EAAM;aAAc,CAAA,CACnD;;WACN,kBAAC,OAAD;YAAK,WAAU;sBAAf,CACE,kBAAC,MAAD;aAAI,WAAU;uBAAkB;aAAe,CAAA,EAC/C,kBAAC,MAAD;aAAI,WAAU;uBAAgC,EAAM;aAAgB,CAAA,CAChE;;WACN,kBAAC,OAAD;YAAK,WAAU;sBAAf,CACE,kBAAC,MAAD;aAAI,WAAU;uBAAkB;aAAgB,CAAA,EAChD,kBAAC,MAAD;aAAI,WAAU;uBACX,EAAM,aAAa,MAAM,GAAG,EAAM,cAAc;aAC9C,CAAA,CACD;;WACH;;SACL,kBAAC,OAAD;UAAK,WAAU;oBACb,kBAAC,GAAD;WAAQ,MAAK;WAAS,SAAQ;WAAU,MAAK;WAAK,eAAe,EAAS,WAAW,EAAG,OAAO;qBAA/F,CACE,kBAAC,GAAD,EAAM,WAAU,WAAY,CAAA,EAAA,qBAErB;;UACL,CAAA;SACI;WAEZ,kBAAC,GAAD;QAAW,WAAU;QAAQ,MAAA;QAAK,WAAU;kBAA5C,CACE,kBAAC,MAAD;SAAI,WAAU;mBAA0C;SAAgB,CAAA,EACxE,kBAAC,MAAD;SAAI,WAAU;mBAAd;UACE,kBAAC,OAAD,EAAA,UAAA,CACE,kBAAC,MAAD;WAAI,WAAU;qBAA8B;WAAa,CAAA,EACzD,kBAAC,MAAD;WAAI,WAAU;sBACV,EAAM,YAAY,GAAG,gBAAgB;WACpC,CAAA,CACD,EAAA,CAAA;UACN,kBAAC,OAAD,EAAA,UAAA,CACE,kBAAC,MAAD;WAAI,WAAU;qBAA8B;WAAa,CAAA,EACzD,kBAAC,MAAD;WAAI,WAAU;qBACX,EAAa,gBAAgB;WAC3B,CAAA,CACD,EAAA,CAAA;UACN,kBAAC,OAAD,EAAA,UAAA,CACE,kBAAC,MAAD;WAAI,WAAU;qBAA8B;WAAiB,CAAA,EAC7D,kBAAC,MAAD;WAAI,WAAU;sBACV,GAAa,aAAa,SAAS,GAAG,gBAAgB;WACrD,CAAA,CACD,EAAA,CAAA;UACN,kBAAC,OAAD,EAAA,UAAA,CACE,kBAAC,MAAD;WAAI,WAAU;qBAA8B;WAAiB,CAAA,EAC7D,kBAAC,MAAD;WAAI,WAAU;qBACX,EAAY,gBAAgB;WAC1B,CAAA,CACD,EAAA,CAAA;UACH;WACK;UACR;;MACM,CAAA;KAEd,kBAAC,GAAD;MAAa,OAAM;MAAW,WAAU;gBACtC,kBAAC,GAAD,EAAa,SAAS,GAAM,CAAA;MAChB,CAAA;KAEd,kBAAC,GAAD;MAAa,OAAM;MAAW,WAAU;gBACtC,kBAAC,GAAD,EAAe,CAAA;MACH,CAAA;KAEd,kBAAC,GAAD;MAAa,OAAM;MAAU,WAAU;gBACrC,kBAAC,GAAD,EAAY,SAAS,GAAM,CAAA;MACf,CAAA;KACT;;GACH;MA/KJ,kBAAC,OAAD;EAAK,WAAU;YAAf,CACE,kBAAC,GAAD;GAAQ,MAAK;GAAS,SAAQ;GAAQ,MAAK;GAAK,eAAe,EAAS,UAAU;aAAlF,CACE,kBAAC,GAAD,EAAW,WAAU,WAAY,CAAA,EAAA,iBAE1B;MACT,kBAAC,GAAD;GAAW,WAAU;GAAQ,WAAU;aACrC,kBAAC,GAAD;IACE,cAAa;IACb,OAAM;IACN,aAAY;IACZ,CAAA;GACQ,CAAA,CACR"}
|
|
1
|
+
{"version":3,"file":"EventDetailPage.js","names":[],"sources":["../../../../src/eventfullymanaged/pages/events/EventDetailPage.tsx"],"sourcesContent":["import { gql } from '@apollo/client';\nimport { useQuery } from '@apollo/client/react';\nimport {\n ArrowLeft,\n CalendarClock,\n Clock,\n Edit,\n Eye,\n MapPin,\n Mic2,\n Plus,\n Ticket as TicketIcon,\n Users,\n Video,\n} from 'lucide-react';\nimport {\n Badge,\n Button,\n GlassCard,\n IllustratedEmptyState,\n Skeleton,\n Tabs,\n TabsContent,\n TabsList,\n TabsTrigger,\n} from '@burdenoff/fe-libs/ui';\nimport { useMemo, useState } from 'react';\nimport { useParams } from 'react-router';\n\nimport {\n useDeleteEventSessionMutation,\n useDeleteSpeakerMutation,\n useDeleteTicketTypeMutation,\n useEventQuery,\n useSpeakersQuery,\n useTicketTypesQuery,\n useVenuesQuery,\n type TicketTypeGql,\n} from '../../api/eventApi';\nimport { ActionMenu } from '../../components/ActionMenu';\nimport { CHART_COLORS } from '../../components/EventCharts';\nimport { Pagination, usePagination } from '../../components/Pagination';\nimport { useEventfullyManaged } from '../../context/EventfullyManagedContext';\nimport { notify } from '../../lib/toast';\nimport { formatDateTime } from './eventFormUtils';\n\nfunction statusVariant(status: string): 'success' | 'warning' | 'secondary' | 'info' {\n const value = status.toLowerCase();\n if (['active', 'published', 'live', 'confirmed', 'completed', 'approved'].includes(value)) {\n return 'success';\n }\n if (['inactive', 'cancelled', 'archived', 'closed', 'expired'].includes(value)) {\n return 'warning';\n }\n if (['draft', 'created', 'pending', 'scheduled'].includes(value)) {\n return 'info';\n }\n return 'secondary';\n}\n\nfunction formatDuration(startsAt: string, endsAt: string): string {\n const start = new Date(startsAt).getTime();\n const end = new Date(endsAt).getTime();\n if (Number.isNaN(start) || Number.isNaN(end) || end <= start) {\n return '';\n }\n const totalMinutes = Math.round((end - start) / 60000);\n const hours = Math.floor(totalMinutes / 60);\n const minutes = totalMinutes % 60;\n if (hours && minutes) {\n return `${hours}h ${minutes}m`;\n }\n if (hours) {\n return `${hours}h`;\n }\n return `${minutes}m`;\n}\n\nfunction formatHour(hour: number): string {\n const period = hour < 12 ? 'AM' : 'PM';\n const display = hour % 12 === 0 ? 12 : hour % 12;\n return `${display}:00 ${period}`;\n}\n\nfunction formatClock(iso: string): string {\n const date = new Date(iso);\n if (Number.isNaN(date.getTime())) {\n return '';\n }\n return date.toLocaleTimeString(undefined, { hour: 'numeric', minute: '2-digit' });\n}\n\n// ---------------------------------------------------------------------------\n// Sessions tab — multi-track schedule grid.\n//\n// The shared EVENT_SESSIONS_QUERY omits `metadata` (which carries speakerId) and\n// `description`/`capacity`, so this tab owns a fuller read that selects them; the\n// grid resolves room names from venues and speaker names from speakers.\n// ---------------------------------------------------------------------------\n\nconst SCHEDULE_SESSIONS_QUERY = gql`\n query EventfullymanagedScheduleSessions($eventId: ID, $limit: Int, $offset: Int) {\n eventSessions: eventfullyManagedEventSessions(eventId: $eventId, limit: $limit, offset: $offset) {\n total\n items {\n id\n eventId\n venueId\n title\n sessionType\n track\n startsAt\n endsAt\n status\n metadata\n }\n }\n }\n`;\n\ntype ScheduleSession = {\n id: string;\n eventId: string;\n venueId?: string | null;\n title: string;\n sessionType: string;\n track?: string | null;\n startsAt: string;\n endsAt: string;\n status: string;\n metadata?: Record<string, unknown> | null;\n};\n\ntype ScheduleSessionsResponse = { eventSessions: { total: number; items: ScheduleSession[] } };\n\nconst UNTRACKED = 'General';\n\nfunction dayKey(iso: string): string {\n const date = new Date(iso);\n return Number.isNaN(date.getTime()) ? 'unknown' : date.toISOString().slice(0, 10);\n}\n\nfunction SessionBlock({\n session,\n color,\n speakerName,\n roomName,\n onEdit,\n onDelete,\n}: {\n session: ScheduleSession;\n color: string;\n speakerName?: string;\n roomName?: string;\n onEdit: () => void;\n onDelete: () => void | Promise<void>;\n}) {\n const duration = formatDuration(session.startsAt, session.endsAt);\n return (\n <div\n className=\"relative rounded-card border border-border-subtle p-3 shadow-elevation-1\"\n style={{\n borderLeftWidth: '3px',\n borderLeftColor: color,\n background: `color-mix(in srgb, ${color} 8%, var(--color-bg-surface))`,\n }}\n >\n <div className=\"flex items-start justify-between gap-2\">\n <p className=\"min-w-0 pr-1 text-sm font-semibold leading-snug text-text-primary\">{session.title}</p>\n <div className=\"-mr-1 -mt-1 shrink-0\">\n <ActionMenu\n ariaLabel={`Session actions for ${session.title}`}\n entityName={session.title}\n deleteLabel=\"Delete session\"\n onEdit={onEdit}\n onDelete={onDelete}\n />\n </div>\n </div>\n <dl className=\"mt-2 space-y-1 text-xs text-text-secondary\">\n <div className=\"flex items-center gap-1.5\">\n <Mic2 className=\"h-3.5 w-3.5 shrink-0 text-text-muted\" />\n <span className=\"truncate\">{speakerName ?? 'Speaker TBD'}</span>\n </div>\n {roomName ? (\n <div className=\"flex items-center gap-1.5\">\n <MapPin className=\"h-3.5 w-3.5 shrink-0 text-text-muted\" />\n <span className=\"truncate\">{roomName}</span>\n </div>\n ) : null}\n <div className=\"flex items-center gap-1.5\">\n <Clock className=\"h-3.5 w-3.5 shrink-0 text-text-muted\" />\n <span>\n {formatClock(session.startsAt)}\n {duration ? ` · ${duration}` : ''}\n </span>\n </div>\n </dl>\n </div>\n );\n}\n\nfunction SessionsTab({ eventId }: { eventId: string }) {\n const { navigate } = useEventfullyManaged();\n const pagination = usePagination();\n const { data, loading, refetch } = useQuery<\n ScheduleSessionsResponse,\n { eventId?: string; limit?: number; offset?: number }\n >(SCHEDULE_SESSIONS_QUERY, {\n variables: { eventId, limit: pagination.limit, offset: pagination.offset },\n });\n const { data: venuesData } = useVenuesQuery({ limit: 100, offset: 0 });\n const { data: speakersData } = useSpeakersQuery({ limit: 100, offset: 0 });\n const [deleteEventSession] = useDeleteEventSessionMutation();\n\n const total = data?.eventSessions?.total ?? 0;\n const rows = useMemo(() => data?.eventSessions?.items ?? [], [data]);\n\n const venueNameById = useMemo(() => {\n const map = new Map<string, string>();\n for (const venue of venuesData?.venues?.items ?? []) {\n map.set(venue.id, venue.name);\n }\n return map;\n }, [venuesData]);\n\n const speakerNameById = useMemo(() => {\n const map = new Map<string, string>();\n for (const speaker of speakersData?.speakers?.items ?? []) {\n map.set(speaker.id, speaker.fullName);\n }\n return map;\n }, [speakersData]);\n\n // Distinct tracks (ordered by first appearance) → stable colour per track.\n const trackColor = useMemo(() => {\n const map = new Map<string, string>();\n let index = 0;\n for (const session of rows) {\n const track = session.track?.trim() || UNTRACKED;\n if (!map.has(track)) {\n map.set(track, CHART_COLORS[index % CHART_COLORS.length]);\n index += 1;\n }\n }\n return map;\n }, [rows]);\n\n // Group sessions by day, then by track, keyed by the hour they start.\n const days = useMemo(() => {\n const byDay = new Map<string, ScheduleSession[]>();\n for (const session of [...rows].sort((a, b) => new Date(a.startsAt).getTime() - new Date(b.startsAt).getTime())) {\n const key = dayKey(session.startsAt);\n const list = byDay.get(key) ?? [];\n list.push(session);\n byDay.set(key, list);\n }\n return Array.from(byDay.entries()).map(([key, sessions]) => {\n const tracks = Array.from(new Set(sessions.map((s) => s.track?.trim() || UNTRACKED)));\n const hours = sessions.map((s) => new Date(s.startsAt).getHours()).filter((h) => !Number.isNaN(h));\n const minHour = hours.length ? Math.min(...hours) : 9;\n const maxHour = hours.length ? Math.max(...hours) : 17;\n const hourRange: number[] = [];\n for (let hour = minHour; hour <= maxHour; hour += 1) {\n hourRange.push(hour);\n }\n return { key, sessions, tracks, hourRange };\n });\n }, [rows]);\n\n const onDelete = async (session: ScheduleSession) => {\n const response = await deleteEventSession({ variables: { id: session.id } });\n if (response.error) {\n notify.error('Could not delete session', response.error.message);\n throw response.error;\n }\n if (!response.data?.deleteEventSession) {\n notify.error('Could not delete session', 'The server did not confirm the change.');\n throw new Error('delete-unconfirmed');\n }\n notify.success('Session deleted', session.title);\n await refetch();\n };\n\n const resolveSpeaker = (session: ScheduleSession): string | undefined => {\n const metadata = (session.metadata ?? {}) as Record<string, unknown>;\n const speakerId = typeof metadata.speakerId === 'string' ? metadata.speakerId : '';\n return speakerId ? speakerNameById.get(speakerId) : undefined;\n };\n const resolveRoom = (session: ScheduleSession): string | undefined =>\n session.venueId ? venueNameById.get(session.venueId) : undefined;\n\n return (\n <div className=\"space-y-5\">\n <div className=\"flex items-center justify-between gap-3\">\n <div>\n <h2 className=\"text-lg font-semibold text-text-primary\">Agenda & Sessions</h2>\n <p className=\"text-sm text-text-secondary\">A multi-track schedule of everything happening on the day.</p>\n </div>\n <Button type=\"button\" size=\"sm\" onClick={() => navigate(`/events/${eventId}/sessions/create`)}>\n <Plus className=\"h-4 w-4\" />\n New Session\n </Button>\n </div>\n\n {loading ? (\n <Skeleton className=\"h-64 w-full rounded-card\" />\n ) : rows.length === 0 ? (\n <IllustratedEmptyState\n illustration=\"empty-data\"\n title=\"No sessions yet.\"\n description=\"Build the agenda by adding your first session.\"\n action={\n <Button type=\"button\" size=\"sm\" onClick={() => navigate(`/events/${eventId}/sessions/create`)}>\n <Plus className=\"h-4 w-4\" />\n New Session\n </Button>\n }\n className=\"py-10\"\n />\n ) : (\n <div className=\"space-y-8\">\n {days.map((day) => (\n <section key={day.key} className=\"space-y-3\">\n <h3 className=\"text-sm font-semibold text-text-primary\">\n {new Date(`${day.key}T00:00:00`).toLocaleDateString(undefined, {\n weekday: 'long',\n month: 'long',\n day: 'numeric',\n })}\n </h3>\n\n {/* Desktop / tablet: multi-track grid */}\n <div className=\"hidden overflow-x-auto rounded-card border border-border-seam md:block\">\n <div\n className=\"grid min-w-max\"\n style={{ gridTemplateColumns: `88px repeat(${day.tracks.length}, minmax(200px, 1fr))` }}\n >\n {/* Header row */}\n <div className=\"sticky left-0 z-10 border-b border-border-seam bg-bg-sunken px-3 py-2.5\" />\n {day.tracks.map((track) => (\n <div\n key={track}\n className=\"border-b border-l border-border-seam bg-bg-sunken px-3 py-2.5 text-center text-sm font-semibold text-text-primary\"\n >\n <span className=\"inline-flex items-center gap-2\">\n <span\n className=\"h-2 w-2 rounded-full\"\n style={{ background: trackColor.get(track) }}\n aria-hidden=\"true\"\n />\n {track}\n </span>\n </div>\n ))}\n\n {/* Hour rows */}\n {day.hourRange.map((hour) => (\n <div key={hour} className=\"contents\">\n <div className=\"sticky left-0 z-10 border-b border-border-subtle bg-bg-surface px-3 py-3 text-xs font-medium text-text-muted\">\n {formatHour(hour)}\n </div>\n {day.tracks.map((track) => {\n const cellSessions = day.sessions.filter(\n (session) =>\n (session.track?.trim() || UNTRACKED) === track &&\n new Date(session.startsAt).getHours() === hour\n );\n return (\n <div\n key={`${hour}-${track}`}\n className=\"space-y-2 border-b border-l border-border-subtle p-2 align-top\"\n >\n {cellSessions.map((session) => (\n <SessionBlock\n key={session.id}\n session={session}\n color={trackColor.get(track) ?? CHART_COLORS[0]}\n speakerName={resolveSpeaker(session)}\n roomName={resolveRoom(session)}\n onEdit={() => navigate(`/events/${eventId}/sessions/${session.id}/edit`)}\n onDelete={() => onDelete(session)}\n />\n ))}\n </div>\n );\n })}\n </div>\n ))}\n </div>\n </div>\n\n {/* Mobile: clean time-ordered day list */}\n <ul className=\"space-y-2.5 md:hidden\">\n {day.sessions.map((session) => {\n const track = session.track?.trim() || UNTRACKED;\n return (\n <li key={session.id}>\n <SessionBlock\n session={session}\n color={trackColor.get(track) ?? CHART_COLORS[0]}\n speakerName={resolveSpeaker(session)}\n roomName={resolveRoom(session) ?? track}\n onEdit={() => navigate(`/events/${eventId}/sessions/${session.id}/edit`)}\n onDelete={() => onDelete(session)}\n />\n </li>\n );\n })}\n </ul>\n </section>\n ))}\n </div>\n )}\n\n <Pagination\n offset={pagination.offset}\n limit={pagination.limit}\n loadedCount={rows.length}\n total={total}\n onPrev={pagination.prevPage}\n onNext={pagination.nextPage}\n itemLabel=\"sessions\"\n loading={loading}\n />\n </div>\n );\n}\n\n// ---------------------------------------------------------------------------\n\ntype SpeakerRow = { id: string; fullName: string; title?: string | null; organization?: string | null };\n\nfunction SpeakersTab() {\n const { navigate } = useEventfullyManaged();\n const pagination = usePagination();\n const { data, loading, refetch } = useSpeakersQuery({ limit: pagination.limit, offset: pagination.offset });\n const [deleteSpeaker] = useDeleteSpeakerMutation();\n\n const total = data?.speakers?.total ?? 0;\n const rows = (data?.speakers?.items ?? []) as unknown as SpeakerRow[];\n\n const onDelete = async (speaker: SpeakerRow) => {\n const response = await deleteSpeaker({ variables: { id: speaker.id } });\n if (response.error) {\n notify.error('Could not delete speaker', response.error.message);\n throw response.error;\n }\n if (!response.data?.deleteSpeaker) {\n notify.error('Could not delete speaker', 'The server did not confirm the change.');\n throw new Error('delete-unconfirmed');\n }\n notify.success('Speaker deleted', speaker.fullName);\n await refetch();\n };\n\n return (\n <div className=\"space-y-4\">\n <div className=\"flex items-center justify-between gap-3\">\n <h2 className=\"text-lg font-semibold text-text-primary\">Speakers</h2>\n <div className=\"flex items-center gap-2\">\n <Button type=\"button\" variant=\"outline\" size=\"sm\" onClick={() => navigate('/speakers')}>\n Manage speakers\n </Button>\n <Button type=\"button\" size=\"sm\" onClick={() => navigate('/speakers/create')}>\n <Plus className=\"h-4 w-4\" />\n New Speaker\n </Button>\n </div>\n </div>\n\n {loading ? (\n <Skeleton className=\"h-40 w-full rounded-card\" />\n ) : rows.length === 0 ? (\n <IllustratedEmptyState\n illustration=\"empty-people\"\n title=\"No speakers yet.\"\n description=\"Add speakers so you can assign them to sessions.\"\n className=\"py-8\"\n />\n ) : (\n <ul className=\"grid grid-cols-1 gap-2.5 sm:grid-cols-2\">\n {rows.map((speaker) => (\n <li\n key={speaker.id}\n className=\"rounded-card border border-border-seam bg-bg-surface p-3 transition-[box-shadow,border-color] duration-200 hover:border-border-strong hover:shadow-elevation-1\"\n >\n <div className=\"flex items-center justify-between gap-3\">\n <div className=\"flex min-w-0 items-center gap-3\">\n <span className=\"flex h-9 w-9 shrink-0 items-center justify-center rounded-full bg-accent-wash text-sm font-semibold text-action-primary-bg\">\n {speaker.fullName.slice(0, 1).toUpperCase()}\n </span>\n <div className=\"min-w-0\">\n <p className=\"truncate font-medium text-text-primary\">{speaker.fullName}</p>\n <p className=\"mt-0.5 truncate text-sm text-text-secondary\">\n {[speaker.title, speaker.organization].filter(Boolean).join(' • ') || 'Speaker'}\n </p>\n </div>\n </div>\n <ActionMenu\n ariaLabel={`Speaker actions for ${speaker.fullName}`}\n entityName={speaker.fullName}\n deleteLabel=\"Delete speaker\"\n onEdit={() => navigate(`/speakers/${speaker.id}/edit`)}\n onDelete={() => onDelete(speaker)}\n />\n </div>\n </li>\n ))}\n </ul>\n )}\n\n <Pagination\n offset={pagination.offset}\n limit={pagination.limit}\n loadedCount={rows.length}\n total={total}\n onPrev={pagination.prevPage}\n onNext={pagination.nextPage}\n itemLabel=\"speakers\"\n loading={loading}\n />\n </div>\n );\n}\n\n// ---------------------------------------------------------------------------\n\nfunction TicketsTab({ eventId }: { eventId: string }) {\n const { navigate } = useEventfullyManaged();\n const pagination = usePagination();\n const { data, loading, refetch } = useTicketTypesQuery({\n eventId,\n limit: pagination.limit,\n offset: pagination.offset,\n });\n const [deleteTicketType] = useDeleteTicketTypeMutation();\n\n const total = data?.ticketTypes?.total ?? 0;\n const rows = (data?.ticketTypes?.items ?? []) as TicketTypeGql[];\n\n const onDelete = async (ticket: TicketTypeGql) => {\n const response = await deleteTicketType({ variables: { id: ticket.id } });\n if (response.error) {\n notify.error('Could not delete ticket type', response.error.message);\n throw response.error;\n }\n if (!response.data?.deleteTicketType) {\n notify.error('Could not delete ticket type', 'The server did not confirm the change.');\n throw new Error('delete-unconfirmed');\n }\n notify.success('Ticket type deleted');\n await refetch();\n };\n\n return (\n <div className=\"space-y-4\">\n <div className=\"flex items-center justify-between gap-3\">\n <h2 className=\"text-lg font-semibold text-text-primary\">Ticket Types</h2>\n <Button type=\"button\" size=\"sm\" onClick={() => navigate(`/events/${eventId}/tickets/create`)}>\n <Plus className=\"h-4 w-4\" />\n New Ticket Type\n </Button>\n </div>\n\n {loading ? (\n <Skeleton className=\"h-40 w-full rounded-card\" />\n ) : rows.length === 0 ? (\n <IllustratedEmptyState\n illustration=\"empty-data\"\n title=\"No ticket types yet.\"\n description=\"Add a ticket type so attendees can register.\"\n className=\"py-8\"\n />\n ) : (\n <ul className=\"space-y-3\">\n {rows.map((ticket) => {\n const hasCapacity = ticket.capacity != null && ticket.capacity > 0;\n const pct = hasCapacity\n ? Math.min(100, Math.round((ticket.soldCount / (ticket.capacity as number)) * 100))\n : 0;\n const nearlyFull = pct >= 90;\n return (\n <li\n key={ticket.id}\n className=\"rounded-card border border-border-seam bg-bg-surface p-4 transition-[box-shadow,border-color] duration-200 hover:border-border-strong hover:shadow-elevation-1\"\n >\n <div className=\"flex items-start justify-between gap-3\">\n <div className=\"min-w-0\">\n <p className=\"truncate font-medium text-text-primary\">{ticket.name}</p>\n <p className=\"mt-0.5 text-sm text-text-secondary\">\n {ticket.price} {ticket.currency}\n </p>\n </div>\n <div className=\"flex shrink-0 items-center gap-2\">\n <Badge variant={ticket.isActive ? 'success' : 'warning'} className=\"uppercase\">\n {ticket.isActive ? 'Active' : 'Inactive'}\n </Badge>\n <ActionMenu\n ariaLabel={`Ticket actions for ${ticket.name}`}\n entityName={ticket.name}\n deleteLabel=\"Delete ticket type\"\n onEdit={() => navigate(`/events/${eventId}/tickets/${ticket.id}/edit`)}\n onDelete={() => onDelete(ticket)}\n />\n </div>\n </div>\n\n <div className=\"mt-3\">\n <div className=\"flex items-center justify-between text-xs text-text-secondary\">\n <span>\n <span className=\"font-medium tabular-nums text-text-primary\">\n {ticket.soldCount.toLocaleString()}\n </span>{' '}\n sold\n {hasCapacity ? (\n <> / {(ticket.capacity as number).toLocaleString()} capacity</>\n ) : (\n ' • unlimited capacity'\n )}\n </span>\n {hasCapacity ? (\n <span className={nearlyFull ? 'font-medium text-text-primary' : ''}>{pct}%</span>\n ) : null}\n </div>\n {hasCapacity ? (\n <div\n className=\"mt-1.5 h-2 overflow-hidden rounded-full bg-bg-sunken\"\n role=\"progressbar\"\n aria-valuenow={pct}\n aria-valuemin={0}\n aria-valuemax={100}\n aria-label={`${ticket.name} sold`}\n >\n <div\n className=\"h-full rounded-full transition-[width] duration-500\"\n style={{\n width: `${pct}%`,\n background: 'linear-gradient(90deg, var(--color-chart-1), var(--color-chart-2))',\n }}\n />\n </div>\n ) : null}\n </div>\n </li>\n );\n })}\n </ul>\n )}\n\n <Pagination\n offset={pagination.offset}\n limit={pagination.limit}\n loadedCount={rows.length}\n total={total}\n onPrev={pagination.prevPage}\n onNext={pagination.nextPage}\n itemLabel=\"ticket types\"\n loading={loading}\n />\n </div>\n );\n}\n\n// ---------------------------------------------------------------------------\n\nfunction MetricTile({\n label,\n value,\n icon: Icon,\n}: {\n label: string;\n value: string;\n icon: React.ComponentType<{ className?: string }>;\n}) {\n return (\n <GlassCard treatment=\"metric\" className=\"p-4\">\n <span className=\"flex h-8 w-8 items-center justify-center rounded-button bg-accent-wash text-action-primary-bg\">\n <Icon className=\"h-4 w-4\" />\n </span>\n <div className=\"mt-2.5 text-xl font-semibold tabular-nums text-text-primary\">{value}</div>\n <div className=\"mt-0.5 text-xs text-text-muted\">{label}</div>\n </GlassCard>\n );\n}\n\nexport default function EventDetailPage() {\n const { id = '' } = useParams<{ id: string }>();\n const { navigate } = useEventfullyManaged();\n const { data, loading } = useEventQuery(id);\n const { data: sessionsData } = useQuery<ScheduleSessionsResponse, { eventId?: string; limit?: number }>(\n SCHEDULE_SESSIONS_QUERY,\n { variables: { eventId: id, limit: 200 }, skip: !id }\n );\n const { data: ticketsData } = useTicketTypesQuery({ eventId: id, limit: 100, offset: 0 });\n const { data: venuesData } = useVenuesQuery({ limit: 100, offset: 0 });\n const [tab, setTab] = useState('overview');\n\n const event = data?.event ?? null;\n const venueId = useMemo(() => {\n const metadata = (event?.metadata ?? {}) as Record<string, unknown>;\n return typeof metadata.venueId === 'string' ? (metadata.venueId as string) : '';\n }, [event]);\n const venueName = useMemo(\n () => (venueId ? venuesData?.venues?.items?.find((venue) => venue.id === venueId)?.name : undefined),\n [venueId, venuesData]\n );\n\n const sessionCount = sessionsData?.eventSessions?.total ?? 0;\n const ticketsSold = useMemo(\n () => (ticketsData?.ticketTypes?.items ?? []).reduce((sum, ticket) => sum + (ticket.soldCount ?? 0), 0),\n [ticketsData]\n );\n\n if (loading) {\n return (\n <div className=\"space-y-4 p-6\">\n <Skeleton className=\"h-8 w-40\" />\n <Skeleton className=\"h-24 w-full rounded-card\" />\n <Skeleton className=\"h-64 w-full rounded-card\" />\n </div>\n );\n }\n\n if (!event) {\n return (\n <div className=\"space-y-4 p-6\">\n <Button type=\"button\" variant=\"ghost\" size=\"sm\" onClick={() => navigate('/events')}>\n <ArrowLeft className=\"h-4 w-4\" />\n Back to Events\n </Button>\n <GlassCard treatment=\"plain\" className=\"border-border-seam\">\n <IllustratedEmptyState\n illustration=\"empty-search\"\n title=\"Event not found.\"\n description=\"This event may have been removed or is outside your current scope.\"\n />\n </GlassCard>\n </div>\n );\n }\n\n return (\n <div className=\"space-y-6 p-6\">\n <div className=\"flex flex-col gap-4 sm:flex-row sm:items-start sm:justify-between\">\n <div className=\"space-y-3\">\n <Button type=\"button\" variant=\"ghost\" size=\"sm\" onClick={() => navigate('/events')}>\n <ArrowLeft className=\"h-4 w-4\" />\n Back to Events\n </Button>\n <div>\n <div className=\"flex flex-wrap items-center gap-3\">\n <h1 className=\"text-2xl font-bold text-text-primary\">{event.name}</h1>\n <Badge variant={statusVariant(event.status)} className=\"uppercase\">\n {event.status}\n </Badge>\n </div>\n <p className=\"mt-1 flex flex-wrap items-center gap-x-4 gap-y-1 text-sm text-text-secondary\">\n <span className=\"flex items-center gap-1.5\">\n <CalendarClock className=\"h-4 w-4 text-text-muted\" />\n {formatDateTime(event.startAt)} — {formatDateTime(event.endAt)}\n </span>\n {venueName ? (\n <span className=\"flex items-center gap-1.5\">\n <MapPin className=\"h-4 w-4 text-text-muted\" />\n {venueName}\n </span>\n ) : null}\n </p>\n </div>\n </div>\n <div className=\"flex flex-wrap gap-2\">\n {event.type === 'VIRTUAL' || event.type === 'HYBRID' ? (\n <Button type=\"button\" variant=\"outline\" onClick={() => navigate(`/events/${id}/virtual`)}>\n <Video className=\"h-4 w-4\" />\n Virtual Session\n </Button>\n ) : null}\n <Button type=\"button\" onClick={() => navigate(`/events/${id}/edit`)}>\n <Edit className=\"h-4 w-4\" />\n Edit Event\n </Button>\n </div>\n </div>\n\n {/* Metrics strip */}\n <div className=\"grid grid-cols-2 gap-3 sm:grid-cols-4\">\n <MetricTile label=\"Capacity\" value={(event.capacity ?? 0).toLocaleString()} icon={Users} />\n <MetricTile label=\"Sessions\" value={sessionCount.toLocaleString()} icon={CalendarClock} />\n <MetricTile\n label=\"Ticket types\"\n value={(ticketsData?.ticketTypes?.total ?? 0).toLocaleString()}\n icon={TicketIcon}\n />\n <MetricTile label=\"Tickets sold\" value={ticketsSold.toLocaleString()} icon={Eye} />\n </div>\n\n <Tabs value={tab} onValueChange={setTab}>\n <TabsList>\n <TabsTrigger value=\"overview\">Overview</TabsTrigger>\n <TabsTrigger value=\"sessions\">Sessions</TabsTrigger>\n <TabsTrigger value=\"speakers\">Speakers</TabsTrigger>\n <TabsTrigger value=\"tickets\">Tickets</TabsTrigger>\n </TabsList>\n\n <TabsContent value=\"overview\" className=\"mt-4\">\n <div className=\"grid grid-cols-1 gap-4 lg:grid-cols-3\">\n <GlassCard treatment=\"plain\" className=\"border-border-seam p-5 lg:col-span-2\">\n <h2 className=\"text-lg font-semibold text-text-primary\">Event Details</h2>\n <dl className=\"mt-4 space-y-4 text-sm\">\n <div className=\"grid grid-cols-1 gap-1 sm:grid-cols-[140px_1fr]\">\n <dt className=\"text-text-muted\">Dates</dt>\n <dd className=\"text-text-primary\">\n {formatDateTime(event.startAt)} — {formatDateTime(event.endAt)}\n </dd>\n </div>\n <div className=\"grid grid-cols-1 gap-1 sm:grid-cols-[140px_1fr]\">\n <dt className=\"text-text-muted\">Venue</dt>\n <dd className=\"text-text-primary\">\n {venueName ? (\n <Button\n type=\"button\"\n variant=\"link\"\n size=\"sm\"\n className=\"h-auto p-0\"\n onClick={() => navigate(`/venues/${venueId}`)}\n >\n {venueName}\n </Button>\n ) : (\n 'No venue assigned'\n )}\n </dd>\n </div>\n <div className=\"grid grid-cols-1 gap-1 sm:grid-cols-[140px_1fr]\">\n <dt className=\"text-text-muted\">Capacity</dt>\n <dd className=\"text-text-primary\">{(event.capacity ?? 0).toLocaleString()} attendees</dd>\n </div>\n <div className=\"grid grid-cols-1 gap-1 sm:grid-cols-[140px_1fr]\">\n <dt className=\"text-text-muted\">Type</dt>\n <dd className=\"capitalize text-text-primary\">{event.type.toLowerCase().replace(/_/g, ' ')}</dd>\n </div>\n <div className=\"grid grid-cols-1 gap-1 sm:grid-cols-[140px_1fr]\">\n <dt className=\"text-text-muted\">Timezone</dt>\n <dd className=\"text-text-primary\">{event.timezone}</dd>\n </div>\n <div className=\"grid grid-cols-1 gap-1 sm:grid-cols-[140px_1fr]\">\n <dt className=\"text-text-muted\">Visibility</dt>\n <dd className=\"capitalize text-text-primary\">{event.visibility}</dd>\n </div>\n <div className=\"grid grid-cols-1 gap-1 sm:grid-cols-[140px_1fr]\">\n <dt className=\"text-text-muted\">Description</dt>\n <dd className=\"text-text-secondary\">\n {event.description?.trim() ? event.description : 'No description provided.'}\n </dd>\n </div>\n </dl>\n <div className=\"mt-5\">\n <Button type=\"button\" variant=\"outline\" size=\"sm\" onClick={() => navigate(`/events/${id}/edit`)}>\n <Edit className=\"h-4 w-4\" />\n Edit event details\n </Button>\n </div>\n </GlassCard>\n\n <GlassCard treatment=\"glass\" glow className=\"p-5\">\n <h2 className=\"text-lg font-semibold text-text-primary\">Key Metrics</h2>\n <dl className=\"mt-4 space-y-4\">\n <div>\n <dt className=\"text-sm text-text-secondary\">Capacity</dt>\n <dd className=\"text-2xl font-semibold tabular-nums text-text-primary\">\n {(event.capacity ?? 0).toLocaleString()}\n </dd>\n </div>\n <div>\n <dt className=\"text-sm text-text-secondary\">Sessions</dt>\n <dd className=\"text-2xl font-semibold tabular-nums text-text-primary\">\n {sessionCount.toLocaleString()}\n </dd>\n </div>\n <div>\n <dt className=\"text-sm text-text-secondary\">Ticket types</dt>\n <dd className=\"text-2xl font-semibold tabular-nums text-text-primary\">\n {(ticketsData?.ticketTypes?.total ?? 0).toLocaleString()}\n </dd>\n </div>\n <div>\n <dt className=\"text-sm text-text-secondary\">Tickets sold</dt>\n <dd className=\"text-2xl font-semibold tabular-nums text-text-primary\">\n {ticketsSold.toLocaleString()}\n </dd>\n </div>\n </dl>\n </GlassCard>\n </div>\n </TabsContent>\n\n <TabsContent value=\"sessions\" className=\"mt-4\">\n <SessionsTab eventId={id} />\n </TabsContent>\n\n <TabsContent value=\"speakers\" className=\"mt-4\">\n <SpeakersTab />\n </TabsContent>\n\n <TabsContent value=\"tickets\" className=\"mt-4\">\n <TicketsTab eventId={id} />\n </TabsContent>\n </Tabs>\n </div>\n );\n}\n"],"mappings":"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"}
|
|
@@ -5,7 +5,7 @@ import { EVENT_STATUS_OPTIONS as o, EVENT_TYPE_OPTIONS as s, EVENT_VISIBILITY_OP
|
|
|
5
5
|
import { EntityFormShell as d } from "../../components/EntityFormShell.js";
|
|
6
6
|
import { describeError as f, mutationSucceeded as p } from "../../lib/mutationResult.js";
|
|
7
7
|
import { useEffect as m, useMemo as h, useState as g } from "react";
|
|
8
|
-
import { useParams as _ } from "react-router
|
|
8
|
+
import { useParams as _ } from "react-router";
|
|
9
9
|
import { jsx as v, jsxs as y } from "react/jsx-runtime";
|
|
10
10
|
import { Input as b, Select as x, SelectContent as S, SelectItem as C, SelectTrigger as w, SelectValue as T, Textarea as E } from "@burdenoff/fe-libs/ui";
|
|
11
11
|
//#region src/eventfullymanaged/pages/events/EventFormPage.tsx
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"EventFormPage.js","names":[],"sources":["../../../../src/eventfullymanaged/pages/events/EventFormPage.tsx"],"sourcesContent":["/* eslint-disable react-hooks/set-state-in-effect */\nimport { Input, Select, SelectContent, SelectItem, SelectTrigger, SelectValue, Textarea } from '@burdenoff/fe-libs/ui';\nimport { useEffect, useMemo, useState } from 'react';\nimport { useParams } from 'react-router-dom';\n\nimport { useCreateEventMutation, useEventQuery, useUpdateEventMutation, useVenuesQuery } from '../../api/eventApi';\nimport { EntityFormShell } from '../../components/EntityFormShell';\nimport { useEventfullyManaged } from '../../context/EventfullyManagedContext';\nimport { describeError, mutationSucceeded } from '../../lib/mutationResult';\nimport { notify } from '../../lib/toast';\nimport {\n EVENT_STATUS_OPTIONS,\n EVENT_TYPE_OPTIONS,\n EVENT_VISIBILITY_OPTIONS,\n inputValueToIso,\n isoToInputValue,\n} from './eventFormUtils';\n\nconst VENUE_METADATA_KEY = 'venueId';\n\ntype EventFormState = {\n name: string;\n description: string;\n type: string;\n status: string;\n visibility: string;\n timezone: string;\n startAt: string;\n endAt: string;\n venueId: string;\n capacity: string;\n};\n\nfunction defaultState(): EventFormState {\n const now = new Date();\n const later = new Date(now.getTime() + 2 * 60 * 60 * 1000);\n return {\n name: '',\n description: '',\n type: 'IN_PERSON',\n status: 'DRAFT',\n visibility: 'private',\n timezone: Intl.DateTimeFormat().resolvedOptions().timeZone || 'Asia/Kolkata',\n startAt: isoToInputValue(now.toISOString()),\n endAt: isoToInputValue(later.toISOString()),\n venueId: '',\n capacity: '100',\n };\n}\n\nexport default function EventFormPage() {\n const { id = '' } = useParams<{ id: string }>();\n const isEdit = useMemo(() => Boolean(id), [id]);\n const { navigate } = useEventfullyManaged();\n\n const { data } = useEventQuery(id);\n const { data: venuesData } = useVenuesQuery({ limit: 100, offset: 0 });\n const [createEvent, { loading: creating }] = useCreateEventMutation();\n const [updateEvent, { loading: updating }] = useUpdateEventMutation();\n const [form, setForm] = useState<EventFormState>(defaultState);\n\n useEffect(() => {\n const event = data?.event;\n if (!isEdit || !event) {\n return;\n }\n const metadata = (event.metadata ?? {}) as Record<string, unknown>;\n setForm({\n name: event.name ?? '',\n description: event.description ?? '',\n type: event.type ?? 'IN_PERSON',\n status: event.status ?? 'DRAFT',\n visibility: event.visibility ?? 'private',\n timezone: event.timezone ?? 'Asia/Kolkata',\n startAt: isoToInputValue(event.startAt),\n endAt: isoToInputValue(event.endAt),\n venueId: typeof metadata[VENUE_METADATA_KEY] === 'string' ? (metadata[VENUE_METADATA_KEY] as string) : '',\n capacity: event.capacity != null ? String(event.capacity) : '',\n });\n }, [data, isEdit]);\n\n const venues = venuesData?.venues?.items ?? [];\n\n const onChange = (field: keyof EventFormState, value: string) => {\n setForm((prev) => ({ ...prev, [field]: value }));\n };\n\n const onSubmit = async (event: React.FormEvent<HTMLFormElement>) => {\n event.preventDefault();\n\n if (!form.name.trim()) {\n notify.error('Name required', 'Give the event a name before saving.');\n return;\n }\n const startIso = inputValueToIso(form.startAt);\n const endIso = inputValueToIso(form.endAt);\n if (!startIso || !endIso) {\n notify.error('Dates required', 'Set both a start and an end time.');\n return;\n }\n if (new Date(endIso).getTime() < new Date(startIso).getTime()) {\n notify.error('Invalid dates', 'The end time cannot be before the start time.');\n return;\n }\n\n const existingMetadata = (data?.event?.metadata ?? {}) as Record<string, unknown>;\n const input = {\n name: form.name.trim(),\n description: form.description,\n type: form.type,\n status: form.status,\n visibility: form.visibility,\n timezone: form.timezone,\n startAt: startIso,\n endAt: endIso,\n capacity: form.capacity ? Number(form.capacity) : null,\n metadata: { ...existingMetadata, [VENUE_METADATA_KEY]: form.venueId || null },\n };\n\n try {\n if (isEdit) {\n const response = await updateEvent({ variables: { id, input } });\n if (!mutationSucceeded(response, response.data?.updateEvent?.id, 'Could not update event')) {\n return;\n }\n notify.success('Event updated');\n navigate(`/events/${id}`);\n return;\n }\n const response = await createEvent({ variables: { input } });\n const createdId = response.data?.createEvent?.id;\n if (!mutationSucceeded(response, createdId, 'Could not create event')) {\n return;\n }\n notify.success('Event created');\n navigate(`/events/${createdId}`);\n } catch (error) {\n notify.error('Could not save event', describeError(error));\n }\n };\n\n const saving = creating || updating;\n\n return (\n <EntityFormShell\n title={isEdit ? 'Edit Event' : 'Create Event'}\n description=\"Define the event people register for and attend.\"\n purpose=\"An event is the thing you sell tickets to and run on the day. Set its name, when it happens and where, then publish it so ticket types and registrations can open. Capacity caps how many attendees you can admit across all ticket types.\"\n backLabel={isEdit ? 'Back to Event' : 'Back to Events'}\n onBack={() => navigate(isEdit ? `/events/${id}` : '/events')}\n onSubmit={onSubmit}\n submitLabel=\"Save Event\"\n saving={saving}\n >\n <p className=\"text-xs font-semibold uppercase tracking-wide text-text-muted\">Basics</p>\n <div className=\"grid grid-cols-1 gap-4 md:grid-cols-2\">\n <label className=\"space-y-1 text-sm\">\n <span className=\"font-medium text-text-primary\">Name</span>\n <Input value={form.name} onChange={(e) => onChange('name', e.target.value)} required />\n </label>\n <label className=\"space-y-1 text-sm\">\n <span className=\"font-medium text-text-primary\">Capacity</span>\n <Input type=\"number\" min=\"0\" value={form.capacity} onChange={(e) => onChange('capacity', e.target.value)} />\n </label>\n </div>\n\n <label className=\"block space-y-1 text-sm\">\n <span className=\"font-medium text-text-primary\">Description</span>\n <Textarea value={form.description} onChange={(e) => onChange('description', e.target.value)} rows={3} />\n </label>\n\n <div className=\"border-t border-border-subtle pt-4\" />\n <p className=\"text-xs font-semibold uppercase tracking-wide text-text-muted\">Schedule & location</p>\n <div className=\"grid grid-cols-1 gap-4 md:grid-cols-2\">\n <label className=\"space-y-1 text-sm\">\n <span className=\"font-medium text-text-primary\">Start</span>\n <Input\n type=\"datetime-local\"\n value={form.startAt}\n onChange={(e) => onChange('startAt', e.target.value)}\n required\n />\n </label>\n <label className=\"space-y-1 text-sm\">\n <span className=\"font-medium text-text-primary\">End</span>\n <Input\n type=\"datetime-local\"\n value={form.endAt}\n onChange={(e) => onChange('endAt', e.target.value)}\n required\n />\n </label>\n </div>\n\n <div className=\"grid grid-cols-1 gap-4 md:grid-cols-2\">\n <label className=\"space-y-1 text-sm\">\n <span className=\"font-medium text-text-primary\">Venue</span>\n <Select\n value={form.venueId || 'none'}\n onValueChange={(value) => onChange('venueId', value === 'none' ? '' : value)}\n >\n <SelectTrigger aria-label=\"Venue\">\n <SelectValue placeholder=\"Select a venue\" />\n </SelectTrigger>\n <SelectContent>\n <SelectItem value=\"none\">No venue</SelectItem>\n {venues.map((venue: { id: string; name: string }) => (\n <SelectItem key={venue.id} value={venue.id}>\n {venue.name}\n </SelectItem>\n ))}\n </SelectContent>\n </Select>\n </label>\n <label className=\"space-y-1 text-sm\">\n <span className=\"font-medium text-text-primary\">Timezone</span>\n <Input value={form.timezone} onChange={(e) => onChange('timezone', e.target.value)} />\n </label>\n </div>\n\n <div className=\"border-t border-border-subtle pt-4\" />\n <p className=\"text-xs font-semibold uppercase tracking-wide text-text-muted\">Classification</p>\n <div className=\"grid grid-cols-1 gap-4 md:grid-cols-3\">\n <label className=\"space-y-1 text-sm\">\n <span className=\"font-medium text-text-primary\">Type</span>\n <Select value={form.type} onValueChange={(value) => onChange('type', value)}>\n <SelectTrigger aria-label=\"Type\">\n <SelectValue />\n </SelectTrigger>\n <SelectContent>\n {EVENT_TYPE_OPTIONS.map((option) => (\n <SelectItem key={option.value} value={option.value}>\n {option.label}\n </SelectItem>\n ))}\n </SelectContent>\n </Select>\n </label>\n <label className=\"space-y-1 text-sm\">\n <span className=\"font-medium text-text-primary\">Status</span>\n <Select value={form.status} onValueChange={(value) => onChange('status', value)}>\n <SelectTrigger aria-label=\"Status\">\n <SelectValue />\n </SelectTrigger>\n <SelectContent>\n {EVENT_STATUS_OPTIONS.map((option) => (\n <SelectItem key={option.value} value={option.value}>\n {option.label}\n </SelectItem>\n ))}\n </SelectContent>\n </Select>\n </label>\n <label className=\"space-y-1 text-sm\">\n <span className=\"font-medium text-text-primary\">Visibility</span>\n <Select value={form.visibility} onValueChange={(value) => onChange('visibility', value)}>\n <SelectTrigger aria-label=\"Visibility\">\n <SelectValue />\n </SelectTrigger>\n <SelectContent>\n {EVENT_VISIBILITY_OPTIONS.map((option) => (\n <SelectItem key={option.value} value={option.value}>\n {option.label}\n </SelectItem>\n ))}\n </SelectContent>\n </Select>\n </label>\n </div>\n </EntityFormShell>\n );\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"EventFormPage.js","names":[],"sources":["../../../../src/eventfullymanaged/pages/events/EventFormPage.tsx"],"sourcesContent":["/* eslint-disable react-hooks/set-state-in-effect */\nimport { Input, Select, SelectContent, SelectItem, SelectTrigger, SelectValue, Textarea } from '@burdenoff/fe-libs/ui';\nimport { useEffect, useMemo, useState } from 'react';\nimport { useParams } from 'react-router';\n\nimport { useCreateEventMutation, useEventQuery, useUpdateEventMutation, useVenuesQuery } from '../../api/eventApi';\nimport { EntityFormShell } from '../../components/EntityFormShell';\nimport { useEventfullyManaged } from '../../context/EventfullyManagedContext';\nimport { describeError, mutationSucceeded } from '../../lib/mutationResult';\nimport { notify } from '../../lib/toast';\nimport {\n EVENT_STATUS_OPTIONS,\n EVENT_TYPE_OPTIONS,\n EVENT_VISIBILITY_OPTIONS,\n inputValueToIso,\n isoToInputValue,\n} from './eventFormUtils';\n\nconst VENUE_METADATA_KEY = 'venueId';\n\ntype EventFormState = {\n name: string;\n description: string;\n type: string;\n status: string;\n visibility: string;\n timezone: string;\n startAt: string;\n endAt: string;\n venueId: string;\n capacity: string;\n};\n\nfunction defaultState(): EventFormState {\n const now = new Date();\n const later = new Date(now.getTime() + 2 * 60 * 60 * 1000);\n return {\n name: '',\n description: '',\n type: 'IN_PERSON',\n status: 'DRAFT',\n visibility: 'private',\n timezone: Intl.DateTimeFormat().resolvedOptions().timeZone || 'Asia/Kolkata',\n startAt: isoToInputValue(now.toISOString()),\n endAt: isoToInputValue(later.toISOString()),\n venueId: '',\n capacity: '100',\n };\n}\n\nexport default function EventFormPage() {\n const { id = '' } = useParams<{ id: string }>();\n const isEdit = useMemo(() => Boolean(id), [id]);\n const { navigate } = useEventfullyManaged();\n\n const { data } = useEventQuery(id);\n const { data: venuesData } = useVenuesQuery({ limit: 100, offset: 0 });\n const [createEvent, { loading: creating }] = useCreateEventMutation();\n const [updateEvent, { loading: updating }] = useUpdateEventMutation();\n const [form, setForm] = useState<EventFormState>(defaultState);\n\n useEffect(() => {\n const event = data?.event;\n if (!isEdit || !event) {\n return;\n }\n const metadata = (event.metadata ?? {}) as Record<string, unknown>;\n setForm({\n name: event.name ?? '',\n description: event.description ?? '',\n type: event.type ?? 'IN_PERSON',\n status: event.status ?? 'DRAFT',\n visibility: event.visibility ?? 'private',\n timezone: event.timezone ?? 'Asia/Kolkata',\n startAt: isoToInputValue(event.startAt),\n endAt: isoToInputValue(event.endAt),\n venueId: typeof metadata[VENUE_METADATA_KEY] === 'string' ? (metadata[VENUE_METADATA_KEY] as string) : '',\n capacity: event.capacity != null ? String(event.capacity) : '',\n });\n }, [data, isEdit]);\n\n const venues = venuesData?.venues?.items ?? [];\n\n const onChange = (field: keyof EventFormState, value: string) => {\n setForm((prev) => ({ ...prev, [field]: value }));\n };\n\n const onSubmit = async (event: React.FormEvent<HTMLFormElement>) => {\n event.preventDefault();\n\n if (!form.name.trim()) {\n notify.error('Name required', 'Give the event a name before saving.');\n return;\n }\n const startIso = inputValueToIso(form.startAt);\n const endIso = inputValueToIso(form.endAt);\n if (!startIso || !endIso) {\n notify.error('Dates required', 'Set both a start and an end time.');\n return;\n }\n if (new Date(endIso).getTime() < new Date(startIso).getTime()) {\n notify.error('Invalid dates', 'The end time cannot be before the start time.');\n return;\n }\n\n const existingMetadata = (data?.event?.metadata ?? {}) as Record<string, unknown>;\n const input = {\n name: form.name.trim(),\n description: form.description,\n type: form.type,\n status: form.status,\n visibility: form.visibility,\n timezone: form.timezone,\n startAt: startIso,\n endAt: endIso,\n capacity: form.capacity ? Number(form.capacity) : null,\n metadata: { ...existingMetadata, [VENUE_METADATA_KEY]: form.venueId || null },\n };\n\n try {\n if (isEdit) {\n const response = await updateEvent({ variables: { id, input } });\n if (!mutationSucceeded(response, response.data?.updateEvent?.id, 'Could not update event')) {\n return;\n }\n notify.success('Event updated');\n navigate(`/events/${id}`);\n return;\n }\n const response = await createEvent({ variables: { input } });\n const createdId = response.data?.createEvent?.id;\n if (!mutationSucceeded(response, createdId, 'Could not create event')) {\n return;\n }\n notify.success('Event created');\n navigate(`/events/${createdId}`);\n } catch (error) {\n notify.error('Could not save event', describeError(error));\n }\n };\n\n const saving = creating || updating;\n\n return (\n <EntityFormShell\n title={isEdit ? 'Edit Event' : 'Create Event'}\n description=\"Define the event people register for and attend.\"\n purpose=\"An event is the thing you sell tickets to and run on the day. Set its name, when it happens and where, then publish it so ticket types and registrations can open. Capacity caps how many attendees you can admit across all ticket types.\"\n backLabel={isEdit ? 'Back to Event' : 'Back to Events'}\n onBack={() => navigate(isEdit ? `/events/${id}` : '/events')}\n onSubmit={onSubmit}\n submitLabel=\"Save Event\"\n saving={saving}\n >\n <p className=\"text-xs font-semibold uppercase tracking-wide text-text-muted\">Basics</p>\n <div className=\"grid grid-cols-1 gap-4 md:grid-cols-2\">\n <label className=\"space-y-1 text-sm\">\n <span className=\"font-medium text-text-primary\">Name</span>\n <Input value={form.name} onChange={(e) => onChange('name', e.target.value)} required />\n </label>\n <label className=\"space-y-1 text-sm\">\n <span className=\"font-medium text-text-primary\">Capacity</span>\n <Input type=\"number\" min=\"0\" value={form.capacity} onChange={(e) => onChange('capacity', e.target.value)} />\n </label>\n </div>\n\n <label className=\"block space-y-1 text-sm\">\n <span className=\"font-medium text-text-primary\">Description</span>\n <Textarea value={form.description} onChange={(e) => onChange('description', e.target.value)} rows={3} />\n </label>\n\n <div className=\"border-t border-border-subtle pt-4\" />\n <p className=\"text-xs font-semibold uppercase tracking-wide text-text-muted\">Schedule & location</p>\n <div className=\"grid grid-cols-1 gap-4 md:grid-cols-2\">\n <label className=\"space-y-1 text-sm\">\n <span className=\"font-medium text-text-primary\">Start</span>\n <Input\n type=\"datetime-local\"\n value={form.startAt}\n onChange={(e) => onChange('startAt', e.target.value)}\n required\n />\n </label>\n <label className=\"space-y-1 text-sm\">\n <span className=\"font-medium text-text-primary\">End</span>\n <Input\n type=\"datetime-local\"\n value={form.endAt}\n onChange={(e) => onChange('endAt', e.target.value)}\n required\n />\n </label>\n </div>\n\n <div className=\"grid grid-cols-1 gap-4 md:grid-cols-2\">\n <label className=\"space-y-1 text-sm\">\n <span className=\"font-medium text-text-primary\">Venue</span>\n <Select\n value={form.venueId || 'none'}\n onValueChange={(value) => onChange('venueId', value === 'none' ? '' : value)}\n >\n <SelectTrigger aria-label=\"Venue\">\n <SelectValue placeholder=\"Select a venue\" />\n </SelectTrigger>\n <SelectContent>\n <SelectItem value=\"none\">No venue</SelectItem>\n {venues.map((venue: { id: string; name: string }) => (\n <SelectItem key={venue.id} value={venue.id}>\n {venue.name}\n </SelectItem>\n ))}\n </SelectContent>\n </Select>\n </label>\n <label className=\"space-y-1 text-sm\">\n <span className=\"font-medium text-text-primary\">Timezone</span>\n <Input value={form.timezone} onChange={(e) => onChange('timezone', e.target.value)} />\n </label>\n </div>\n\n <div className=\"border-t border-border-subtle pt-4\" />\n <p className=\"text-xs font-semibold uppercase tracking-wide text-text-muted\">Classification</p>\n <div className=\"grid grid-cols-1 gap-4 md:grid-cols-3\">\n <label className=\"space-y-1 text-sm\">\n <span className=\"font-medium text-text-primary\">Type</span>\n <Select value={form.type} onValueChange={(value) => onChange('type', value)}>\n <SelectTrigger aria-label=\"Type\">\n <SelectValue />\n </SelectTrigger>\n <SelectContent>\n {EVENT_TYPE_OPTIONS.map((option) => (\n <SelectItem key={option.value} value={option.value}>\n {option.label}\n </SelectItem>\n ))}\n </SelectContent>\n </Select>\n </label>\n <label className=\"space-y-1 text-sm\">\n <span className=\"font-medium text-text-primary\">Status</span>\n <Select value={form.status} onValueChange={(value) => onChange('status', value)}>\n <SelectTrigger aria-label=\"Status\">\n <SelectValue />\n </SelectTrigger>\n <SelectContent>\n {EVENT_STATUS_OPTIONS.map((option) => (\n <SelectItem key={option.value} value={option.value}>\n {option.label}\n </SelectItem>\n ))}\n </SelectContent>\n </Select>\n </label>\n <label className=\"space-y-1 text-sm\">\n <span className=\"font-medium text-text-primary\">Visibility</span>\n <Select value={form.visibility} onValueChange={(value) => onChange('visibility', value)}>\n <SelectTrigger aria-label=\"Visibility\">\n <SelectValue />\n </SelectTrigger>\n <SelectContent>\n {EVENT_VISIBILITY_OPTIONS.map((option) => (\n <SelectItem key={option.value} value={option.value}>\n {option.label}\n </SelectItem>\n ))}\n </SelectContent>\n </Select>\n </label>\n </div>\n </EntityFormShell>\n );\n}\n"],"mappings":"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"}
|
|
@@ -8,7 +8,7 @@ import { EntityFormShell as m } from "../../components/EntityFormShell.js";
|
|
|
8
8
|
import { describeError as h, mutationSucceeded as g } from "../../lib/mutationResult.js";
|
|
9
9
|
import { EntityTagsField as _ } from "../../components/EntityTagsField.js";
|
|
10
10
|
import { useEffect as v, useMemo as y, useState as b } from "react";
|
|
11
|
-
import { useParams as x } from "react-router
|
|
11
|
+
import { useParams as x } from "react-router";
|
|
12
12
|
import { jsx as S, jsxs as C } from "react/jsx-runtime";
|
|
13
13
|
import { Input as w, Select as T, SelectContent as E, SelectItem as D, SelectTrigger as O, SelectValue as k, Textarea as A } from "@burdenoff/fe-libs/ui";
|
|
14
14
|
//#region src/eventfullymanaged/pages/events/SessionFormPage.tsx
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"SessionFormPage.js","names":[],"sources":["../../../../src/eventfullymanaged/pages/events/SessionFormPage.tsx"],"sourcesContent":["/* eslint-disable react-hooks/set-state-in-effect */\nimport { gql } from '@apollo/client';\nimport { useQuery } from '@apollo/client/react';\nimport { Input, Select, SelectContent, SelectItem, SelectTrigger, SelectValue, Textarea } from '@burdenoff/fe-libs/ui';\nimport { useEffect, useMemo, useState } from 'react';\nimport { useParams } from 'react-router-dom';\n\nimport {\n useCreateEventSessionMutation,\n useEventQuery,\n useSessionConflictsQuery,\n useSpeakersQuery,\n useUpdateEventSessionMutation,\n useVenuesQuery,\n} from '../../api/eventApi';\nimport { EntityFormShell } from '../../components/EntityFormShell';\nimport { EntityTagsField } from '../../components/EntityTagsField';\nimport { useEventfullyManaged } from '../../context/EventfullyManagedContext';\nimport { describeError, mutationSucceeded } from '../../lib/mutationResult';\nimport { notify } from '../../lib/toast';\nimport { inputValueToIso, isoToInputValue, SESSION_STATUS_OPTIONS, SESSION_TYPE_OPTIONS } from './eventFormUtils';\n\nconst SPEAKER_METADATA_KEY = 'speakerId';\n\n// The foundation EVENT_SESSIONS_QUERY omits description/capacity/metadata, but the\n// backend `updateEventSession` resolver is a FULL replace — editing with a partial\n// initial state would blank those columns. There is no by-id session resolver, so\n// this fuller list query is the edit-hydration source: it selects every editable\n// field and the form finds its row by id client-side.\nconst FULL_EVENT_SESSIONS_QUERY = gql`\n query EventfullymanagedSessionFormSessions($eventId: ID, $limit: Int, $offset: Int) {\n eventSessions: eventfullyManagedEventSessions(eventId: $eventId, limit: $limit, offset: $offset) {\n total\n items {\n id\n eventId\n venueId\n title\n description\n sessionType\n track\n startsAt\n endsAt\n capacity\n status\n metadata\n }\n }\n }\n`;\n\ntype FullSessionsResponse = {\n eventSessions: { total: number; items: SessionListItem[] };\n};\n\ntype SessionListItem = {\n id: string;\n eventId: string;\n venueId?: string | null;\n title: string;\n sessionType: string;\n track?: string | null;\n startsAt: string;\n endsAt: string;\n status: string;\n capacity?: number | null;\n description?: string | null;\n metadata?: Record<string, unknown> | null;\n};\n\ntype SessionFormState = {\n title: string;\n sessionType: string;\n track: string;\n venueId: string;\n speakerId: string;\n startsAt: string;\n endsAt: string;\n capacity: string;\n status: string;\n description: string;\n};\n\nfunction defaultState(): SessionFormState {\n const now = new Date();\n const later = new Date(now.getTime() + 60 * 60 * 1000);\n return {\n title: '',\n sessionType: 'TALK',\n track: '',\n venueId: '',\n speakerId: '',\n startsAt: isoToInputValue(now.toISOString()),\n endsAt: isoToInputValue(later.toISOString()),\n capacity: '',\n status: 'SCHEDULED',\n description: '',\n };\n}\n\nexport default function SessionFormPage() {\n const { id = '', sid = '' } = useParams<{ id: string; sid: string }>();\n const isEdit = useMemo(() => Boolean(sid), [sid]);\n const { navigate } = useEventfullyManaged();\n\n const { data: eventData } = useEventQuery(id);\n // No by-id session resolver — derive the edit initial state from the (fuller) list query.\n const { data: sessionsData } = useQuery<FullSessionsResponse, { eventId?: string; limit?: number; offset?: number }>(\n FULL_EVENT_SESSIONS_QUERY,\n { variables: { eventId: id, limit: 100, offset: 0 }, skip: !sid }\n );\n const { data: venuesData } = useVenuesQuery({ limit: 100, offset: 0 });\n const { data: speakersData } = useSpeakersQuery({ limit: 100, offset: 0 });\n // BOFF-4331 — surface venue/speaker double-bookings for this event's agenda\n // right where an organizer is scheduling a session.\n const { data: conflictsData } = useSessionConflictsQuery({ eventId: id });\n const relevantConflicts = useMemo(() => {\n const conflicts = conflictsData?.sessionConflicts ?? [];\n return isEdit ? conflicts.filter((c) => c.sessionAId === sid || c.sessionBId === sid) : conflicts;\n }, [conflictsData, isEdit, sid]);\n const [createSession, { loading: creating }] = useCreateEventSessionMutation();\n const [updateSession, { loading: updating }] = useUpdateEventSessionMutation();\n const [form, setForm] = useState<SessionFormState>(defaultState);\n\n const existingSession = useMemo(\n () => (sessionsData?.eventSessions?.items ?? []).find((item) => item.id === sid),\n [sessionsData, sid]\n );\n\n useEffect(() => {\n if (!isEdit || !existingSession) {\n return;\n }\n const metadata = (existingSession.metadata ?? {}) as Record<string, unknown>;\n setForm({\n title: existingSession.title ?? '',\n sessionType: existingSession.sessionType ?? 'TALK',\n track: existingSession.track ?? '',\n venueId: existingSession.venueId ?? '',\n speakerId: typeof metadata[SPEAKER_METADATA_KEY] === 'string' ? (metadata[SPEAKER_METADATA_KEY] as string) : '',\n startsAt: isoToInputValue(existingSession.startsAt),\n endsAt: isoToInputValue(existingSession.endsAt),\n capacity: existingSession.capacity != null ? String(existingSession.capacity) : '',\n status: existingSession.status ?? 'SCHEDULED',\n description: existingSession.description ?? '',\n });\n }, [existingSession, isEdit]);\n\n const venues = venuesData?.venues?.items ?? [];\n const speakers = (speakersData?.speakers?.items ?? []) as Array<{ id: string; fullName: string }>;\n const eventName = eventData?.event?.name ?? 'Event';\n\n const onChange = (field: keyof SessionFormState, value: string) => {\n setForm((prev) => ({ ...prev, [field]: value }));\n };\n\n const onSubmit = async (event: React.FormEvent<HTMLFormElement>) => {\n event.preventDefault();\n\n if (!form.title.trim()) {\n notify.error('Title required', 'Give the session a title before saving.');\n return;\n }\n const startIso = inputValueToIso(form.startsAt);\n const endIso = inputValueToIso(form.endsAt);\n if (!startIso || !endIso) {\n notify.error('Times required', 'Set both a start and an end time.');\n return;\n }\n if (new Date(endIso).getTime() < new Date(startIso).getTime()) {\n notify.error('Invalid times', 'The end time cannot be before the start time.');\n return;\n }\n\n const existingMetadata = (existingSession?.metadata ?? {}) as Record<string, unknown>;\n const input = {\n eventId: id,\n title: form.title.trim(),\n sessionType: form.sessionType,\n track: form.track || null,\n venueId: form.venueId || null,\n startsAt: startIso,\n endsAt: endIso,\n capacity: form.capacity ? Number(form.capacity) : null,\n status: form.status,\n description: form.description || null,\n metadata: { ...existingMetadata, [SPEAKER_METADATA_KEY]: form.speakerId || null },\n };\n\n try {\n if (isEdit) {\n const response = await updateSession({ variables: { id: sid, input } });\n if (!mutationSucceeded(response, response.data?.updateEventSession?.id, 'Could not update session')) {\n return;\n }\n notify.success('Session updated');\n navigate(`/events/${id}`);\n return;\n }\n const response = await createSession({ variables: { input } });\n if (!mutationSucceeded(response, response.data?.createEventSession?.id, 'Could not create session')) {\n return;\n }\n notify.success('Session created');\n navigate(`/events/${id}`);\n } catch (error) {\n notify.error('Could not save session', describeError(error));\n }\n };\n\n const saving = creating || updating;\n\n return (\n <EntityFormShell\n title={isEdit ? 'Edit Session' : 'Add Session'}\n description={`Agenda item for ${eventName}.`}\n purpose=\"Sessions make up the agenda attendees follow on the day — a talk, workshop or panel with a time, a room and a speaker. Set the slot and assign a room so two sessions never clash, and pick the speaker who will present it.\"\n backLabel=\"Back to Event\"\n onBack={() => navigate(`/events/${id}`)}\n onSubmit={onSubmit}\n submitLabel=\"Save Session\"\n saving={saving}\n >\n {relevantConflicts.length > 0 ? (\n <div className=\"rounded-card border border-status-warning-border bg-status-warning-bg-subtle p-3 text-sm text-status-warning-text\">\n <p className=\"font-medium\">\n {relevantConflicts.length === 1\n ? '1 agenda conflict detected'\n : `${relevantConflicts.length} agenda conflicts detected`}\n </p>\n <ul className=\"mt-1 list-inside list-disc space-y-0.5\">\n {relevantConflicts.map((conflict, index) => (\n <li key={`${conflict.type}-${conflict.sessionAId}-${conflict.sessionBId}-${index}`}>\n {conflict.message}\n </li>\n ))}\n </ul>\n </div>\n ) : null}\n <div className=\"grid grid-cols-1 gap-4 md:grid-cols-2\">\n <label className=\"space-y-1 text-sm\">\n <span className=\"font-medium text-text-primary\">Title</span>\n <Input value={form.title} onChange={(e) => onChange('title', e.target.value)} required />\n </label>\n <label className=\"space-y-1 text-sm\">\n <span className=\"font-medium text-text-primary\">Track</span>\n <Input\n value={form.track}\n onChange={(e) => onChange('track', e.target.value)}\n placeholder=\"Main stage, Product, Engineering…\"\n />\n </label>\n </div>\n\n <div className=\"grid grid-cols-1 gap-4 md:grid-cols-3\">\n <label className=\"space-y-1 text-sm\">\n <span className=\"font-medium text-text-primary\">Room / Venue</span>\n <Select\n value={form.venueId || 'none'}\n onValueChange={(value) => onChange('venueId', value === 'none' ? '' : value)}\n >\n <SelectTrigger aria-label=\"Room or venue\">\n <SelectValue placeholder=\"Select a room\" />\n </SelectTrigger>\n <SelectContent>\n <SelectItem value=\"none\">No room</SelectItem>\n {venues.map((venue: { id: string; name: string }) => (\n <SelectItem key={venue.id} value={venue.id}>\n {venue.name}\n </SelectItem>\n ))}\n </SelectContent>\n </Select>\n </label>\n <label className=\"space-y-1 text-sm\">\n <span className=\"font-medium text-text-primary\">Speaker</span>\n <Select\n value={form.speakerId || 'none'}\n onValueChange={(value) => onChange('speakerId', value === 'none' ? '' : value)}\n >\n <SelectTrigger aria-label=\"Speaker\">\n <SelectValue placeholder=\"Select a speaker\" />\n </SelectTrigger>\n <SelectContent>\n <SelectItem value=\"none\">No speaker</SelectItem>\n {speakers.map((speaker) => (\n <SelectItem key={speaker.id} value={speaker.id}>\n {speaker.fullName}\n </SelectItem>\n ))}\n </SelectContent>\n </Select>\n </label>\n <label className=\"space-y-1 text-sm\">\n <span className=\"font-medium text-text-primary\">Capacity</span>\n <Input type=\"number\" min=\"0\" value={form.capacity} onChange={(e) => onChange('capacity', e.target.value)} />\n </label>\n </div>\n\n <div className=\"grid grid-cols-1 gap-4 md:grid-cols-2\">\n <label className=\"space-y-1 text-sm\">\n <span className=\"font-medium text-text-primary\">Start</span>\n <Input\n type=\"datetime-local\"\n value={form.startsAt}\n onChange={(e) => onChange('startsAt', e.target.value)}\n required\n />\n </label>\n <label className=\"space-y-1 text-sm\">\n <span className=\"font-medium text-text-primary\">End</span>\n <Input\n type=\"datetime-local\"\n value={form.endsAt}\n onChange={(e) => onChange('endsAt', e.target.value)}\n required\n />\n </label>\n </div>\n\n <div className=\"grid grid-cols-1 gap-4 md:grid-cols-2\">\n <label className=\"space-y-1 text-sm\">\n <span className=\"font-medium text-text-primary\">Type</span>\n <Select value={form.sessionType} onValueChange={(value) => onChange('sessionType', value)}>\n <SelectTrigger aria-label=\"Session type\">\n <SelectValue />\n </SelectTrigger>\n <SelectContent>\n {SESSION_TYPE_OPTIONS.map((option) => (\n <SelectItem key={option.value} value={option.value}>\n {option.label}\n </SelectItem>\n ))}\n </SelectContent>\n </Select>\n </label>\n <label className=\"space-y-1 text-sm\">\n <span className=\"font-medium text-text-primary\">Status</span>\n <Select value={form.status} onValueChange={(value) => onChange('status', value)}>\n <SelectTrigger aria-label=\"Status\">\n <SelectValue />\n </SelectTrigger>\n <SelectContent>\n {SESSION_STATUS_OPTIONS.map((option) => (\n <SelectItem key={option.value} value={option.value}>\n {option.label}\n </SelectItem>\n ))}\n </SelectContent>\n </Select>\n </label>\n </div>\n\n <label className=\"block space-y-1 text-sm\">\n <span className=\"font-medium text-text-primary\">Description</span>\n <Textarea value={form.description} onChange={(e) => onChange('description', e.target.value)} rows={3} />\n </label>\n\n {isEdit ? <EntityTagsField entityType=\"EVENTFULLYMANAGED_SESSION\" entityId={sid} /> : null}\n </EntityFormShell>\n );\n}\n"],"mappings":";;;;;;;;;;;;;;AAsBA,IAAM,IAAuB,aAOvB,IAA4B,CAAG;;;;;;;;;;;;;;;;;;;;;AAsDrC,SAAS,IAAiC;CACxC,IAAM,oBAAM,IAAI,MAAM,EAChB,IAAQ,IAAI,KAAK,EAAI,SAAS,GAAG,OAAU,IAAK;AACtD,QAAO;EACL,OAAO;EACP,aAAa;EACb,OAAO;EACP,SAAS;EACT,WAAW;EACX,UAAU,EAAgB,EAAI,aAAa,CAAC;EAC5C,QAAQ,EAAgB,EAAM,aAAa,CAAC;EAC5C,UAAU;EACV,QAAQ;EACR,aAAa;EACd;;AAGH,SAAwB,IAAkB;CACxC,IAAM,EAAE,QAAK,IAAI,SAAM,OAAO,GAAwC,EAChE,IAAS,QAAc,EAAQ,GAAM,CAAC,EAAI,CAAC,EAC3C,EAAE,gBAAa,GAAsB,EAErC,EAAE,MAAM,MAAc,EAAc,EAAG,EAEvC,EAAE,MAAM,MAAiB,EAC7B,GACA;EAAE,WAAW;GAAE,SAAS;GAAI,OAAO;GAAK,QAAQ;GAAG;EAAE,MAAM,CAAC;EAAK,CAClE,EACK,EAAE,MAAM,MAAe,EAAe;EAAE,OAAO;EAAK,QAAQ;EAAG,CAAC,EAChE,EAAE,MAAM,MAAiB,EAAiB;EAAE,OAAO;EAAK,QAAQ;EAAG,CAAC,EAGpE,EAAE,MAAM,MAAkB,EAAyB,EAAE,SAAS,GAAI,CAAC,EACnE,IAAoB,QAAc;EACtC,IAAM,IAAY,GAAe,oBAAoB,EAAE;AACvD,SAAO,IAAS,EAAU,QAAQ,MAAM,EAAE,eAAe,KAAO,EAAE,eAAe,EAAI,GAAG;IACvF;EAAC;EAAe;EAAQ;EAAI,CAAC,EAC1B,CAAC,GAAe,EAAE,SAAS,OAAc,GAA+B,EACxE,CAAC,GAAe,EAAE,SAAS,OAAc,GAA+B,EACxE,CAAC,GAAM,KAAW,EAA2B,EAAa,EAE1D,IAAkB,SACf,GAAc,eAAe,SAAS,EAAE,EAAE,MAAM,MAAS,EAAK,OAAO,EAAI,EAChF,CAAC,GAAc,EAAI,CACpB;AAED,SAAgB;AACd,MAAI,CAAC,KAAU,CAAC,EACd;EAEF,IAAM,IAAY,EAAgB,YAAY,EAAE;AAChD,IAAQ;GACN,OAAO,EAAgB,SAAS;GAChC,aAAa,EAAgB,eAAe;GAC5C,OAAO,EAAgB,SAAS;GAChC,SAAS,EAAgB,WAAW;GACpC,WAAW,OAAO,EAAS,MAA0B,WAAY,EAAS,KAAmC;GAC7G,UAAU,EAAgB,EAAgB,SAAS;GACnD,QAAQ,EAAgB,EAAgB,OAAO;GAC/C,UAAU,EAAgB,YAAY,OAA0C,KAAnC,OAAO,EAAgB,SAAS;GAC7E,QAAQ,EAAgB,UAAU;GAClC,aAAa,EAAgB,eAAe;GAC7C,CAAC;IACD,CAAC,GAAiB,EAAO,CAAC;CAE7B,IAAM,IAAS,GAAY,QAAQ,SAAS,EAAE,EACxC,IAAY,GAAc,UAAU,SAAS,EAAE,EAC/C,IAAY,GAAW,OAAO,QAAQ,SAEtC,KAAY,GAA+B,MAAkB;AACjE,KAAS,OAAU;GAAE,GAAG;IAAO,IAAQ;GAAO,EAAE;IAG5C,KAAW,OAAO,MAA4C;AAGlE,MAFA,EAAM,gBAAgB,EAElB,CAAC,EAAK,MAAM,MAAM,EAAE;AACtB,KAAO,MAAM,kBAAkB,0CAA0C;AACzE;;EAEF,IAAM,IAAW,EAAgB,EAAK,SAAS,EACzC,IAAS,EAAgB,EAAK,OAAO;AAC3C,MAAI,CAAC,KAAY,CAAC,GAAQ;AACxB,KAAO,MAAM,kBAAkB,oCAAoC;AACnE;;AAEF,MAAI,IAAI,KAAK,EAAO,CAAC,SAAS,GAAG,IAAI,KAAK,EAAS,CAAC,SAAS,EAAE;AAC7D,KAAO,MAAM,iBAAiB,gDAAgD;AAC9E;;EAGF,IAAM,IAAoB,GAAiB,YAAY,EAAE,EACnD,IAAQ;GACZ,SAAS;GACT,OAAO,EAAK,MAAM,MAAM;GACxB,aAAa,EAAK;GAClB,OAAO,EAAK,SAAS;GACrB,SAAS,EAAK,WAAW;GACzB,UAAU;GACV,QAAQ;GACR,UAAU,EAAK,WAAW,OAAO,EAAK,SAAS,GAAG;GAClD,QAAQ,EAAK;GACb,aAAa,EAAK,eAAe;GACjC,UAAU;IAAE,GAAG;KAAmB,IAAuB,EAAK,aAAa;IAAM;GAClF;AAED,MAAI;AACF,OAAI,GAAQ;IACV,IAAM,IAAW,MAAM,EAAc,EAAE,WAAW;KAAE,IAAI;KAAK;KAAO,EAAE,CAAC;AACvE,QAAI,CAAC,EAAkB,GAAU,EAAS,MAAM,oBAAoB,IAAI,2BAA2B,CACjG;AAGF,IADA,EAAO,QAAQ,kBAAkB,EACjC,EAAS,WAAW,IAAK;AACzB;;GAEF,IAAM,IAAW,MAAM,EAAc,EAAE,WAAW,EAAE,UAAO,EAAE,CAAC;AAC9D,OAAI,CAAC,EAAkB,GAAU,EAAS,MAAM,oBAAoB,IAAI,2BAA2B,CACjG;AAGF,GADA,EAAO,QAAQ,kBAAkB,EACjC,EAAS,WAAW,IAAK;WAClB,GAAO;AACd,KAAO,MAAM,0BAA0B,EAAc,EAAM,CAAC;;IAI1D,KAAS,KAAY;AAE3B,QACE,kBAAC,GAAD;EACE,OAAO,IAAS,iBAAiB;EACjC,aAAa,mBAAmB,EAAU;EAC1C,SAAQ;EACR,WAAU;EACV,cAAc,EAAS,WAAW,IAAK;EAC7B;EACV,aAAY;EACJ;YARV;GAUG,EAAkB,SAAS,IAC1B,kBAAC,OAAD;IAAK,WAAU;cAAf,CACE,kBAAC,KAAD;KAAG,WAAU;eACV,EAAkB,WAAW,IAC1B,+BACA,GAAG,EAAkB,OAAO;KAC9B,CAAA,EACJ,kBAAC,MAAD;KAAI,WAAU;eACX,EAAkB,KAAK,GAAU,MAChC,kBAAC,MAAD,EAAA,UACG,EAAS,SACP,EAFI,GAAG,EAAS,KAAK,GAAG,EAAS,WAAW,GAAG,EAAS,WAAW,GAAG,IAEtE,CACL;KACC,CAAA,CACD;QACJ;GACJ,kBAAC,OAAD;IAAK,WAAU;cAAf,CACE,kBAAC,SAAD;KAAO,WAAU;eAAjB,CACE,kBAAC,QAAD;MAAM,WAAU;gBAAgC;MAAY,CAAA,EAC5D,kBAAC,GAAD;MAAO,OAAO,EAAK;MAAO,WAAW,MAAM,EAAS,SAAS,EAAE,OAAO,MAAM;MAAE,UAAA;MAAW,CAAA,CACnF;QACR,kBAAC,SAAD;KAAO,WAAU;eAAjB,CACE,kBAAC,QAAD;MAAM,WAAU;gBAAgC;MAAY,CAAA,EAC5D,kBAAC,GAAD;MACE,OAAO,EAAK;MACZ,WAAW,MAAM,EAAS,SAAS,EAAE,OAAO,MAAM;MAClD,aAAY;MACZ,CAAA,CACI;OACJ;;GAEN,kBAAC,OAAD;IAAK,WAAU;cAAf;KACE,kBAAC,SAAD;MAAO,WAAU;gBAAjB,CACE,kBAAC,QAAD;OAAM,WAAU;iBAAgC;OAAmB,CAAA,EACnE,kBAAC,GAAD;OACE,OAAO,EAAK,WAAW;OACvB,gBAAgB,MAAU,EAAS,WAAW,MAAU,SAAS,KAAK,EAAM;iBAF9E,CAIE,kBAAC,GAAD;QAAe,cAAW;kBACxB,kBAAC,GAAD,EAAa,aAAY,iBAAkB,CAAA;QAC7B,CAAA,EAChB,kBAAC,GAAD,EAAA,UAAA,CACE,kBAAC,GAAD;QAAY,OAAM;kBAAO;QAAoB,CAAA,EAC5C,EAAO,KAAK,MACX,kBAAC,GAAD;QAA2B,OAAO,EAAM;kBACrC,EAAM;QACI,EAFI,EAAM,GAEV,CACb,CACY,EAAA,CAAA,CACT;SACH;;KACR,kBAAC,SAAD;MAAO,WAAU;gBAAjB,CACE,kBAAC,QAAD;OAAM,WAAU;iBAAgC;OAAc,CAAA,EAC9D,kBAAC,GAAD;OACE,OAAO,EAAK,aAAa;OACzB,gBAAgB,MAAU,EAAS,aAAa,MAAU,SAAS,KAAK,EAAM;iBAFhF,CAIE,kBAAC,GAAD;QAAe,cAAW;kBACxB,kBAAC,GAAD,EAAa,aAAY,oBAAqB,CAAA;QAChC,CAAA,EAChB,kBAAC,GAAD,EAAA,UAAA,CACE,kBAAC,GAAD;QAAY,OAAM;kBAAO;QAAuB,CAAA,EAC/C,EAAS,KAAK,MACb,kBAAC,GAAD;QAA6B,OAAO,EAAQ;kBACzC,EAAQ;QACE,EAFI,EAAQ,GAEZ,CACb,CACY,EAAA,CAAA,CACT;SACH;;KACR,kBAAC,SAAD;MAAO,WAAU;gBAAjB,CACE,kBAAC,QAAD;OAAM,WAAU;iBAAgC;OAAe,CAAA,EAC/D,kBAAC,GAAD;OAAO,MAAK;OAAS,KAAI;OAAI,OAAO,EAAK;OAAU,WAAW,MAAM,EAAS,YAAY,EAAE,OAAO,MAAM;OAAI,CAAA,CACtG;;KACJ;;GAEN,kBAAC,OAAD;IAAK,WAAU;cAAf,CACE,kBAAC,SAAD;KAAO,WAAU;eAAjB,CACE,kBAAC,QAAD;MAAM,WAAU;gBAAgC;MAAY,CAAA,EAC5D,kBAAC,GAAD;MACE,MAAK;MACL,OAAO,EAAK;MACZ,WAAW,MAAM,EAAS,YAAY,EAAE,OAAO,MAAM;MACrD,UAAA;MACA,CAAA,CACI;QACR,kBAAC,SAAD;KAAO,WAAU;eAAjB,CACE,kBAAC,QAAD;MAAM,WAAU;gBAAgC;MAAU,CAAA,EAC1D,kBAAC,GAAD;MACE,MAAK;MACL,OAAO,EAAK;MACZ,WAAW,MAAM,EAAS,UAAU,EAAE,OAAO,MAAM;MACnD,UAAA;MACA,CAAA,CACI;OACJ;;GAEN,kBAAC,OAAD;IAAK,WAAU;cAAf,CACE,kBAAC,SAAD;KAAO,WAAU;eAAjB,CACE,kBAAC,QAAD;MAAM,WAAU;gBAAgC;MAAW,CAAA,EAC3D,kBAAC,GAAD;MAAQ,OAAO,EAAK;MAAa,gBAAgB,MAAU,EAAS,eAAe,EAAM;gBAAzF,CACE,kBAAC,GAAD;OAAe,cAAW;iBACxB,kBAAC,GAAD,EAAe,CAAA;OACD,CAAA,EAChB,kBAAC,GAAD,EAAA,UACG,EAAqB,KAAK,MACzB,kBAAC,GAAD;OAA+B,OAAO,EAAO;iBAC1C,EAAO;OACG,EAFI,EAAO,MAEX,CACb,EACY,CAAA,CACT;QACH;QACR,kBAAC,SAAD;KAAO,WAAU;eAAjB,CACE,kBAAC,QAAD;MAAM,WAAU;gBAAgC;MAAa,CAAA,EAC7D,kBAAC,GAAD;MAAQ,OAAO,EAAK;MAAQ,gBAAgB,MAAU,EAAS,UAAU,EAAM;gBAA/E,CACE,kBAAC,GAAD;OAAe,cAAW;iBACxB,kBAAC,GAAD,EAAe,CAAA;OACD,CAAA,EAChB,kBAAC,GAAD,EAAA,UACG,EAAuB,KAAK,MAC3B,kBAAC,GAAD;OAA+B,OAAO,EAAO;iBAC1C,EAAO;OACG,EAFI,EAAO,MAEX,CACb,EACY,CAAA,CACT;QACH;OACJ;;GAEN,kBAAC,SAAD;IAAO,WAAU;cAAjB,CACE,kBAAC,QAAD;KAAM,WAAU;eAAgC;KAAkB,CAAA,EAClE,kBAAC,GAAD;KAAU,OAAO,EAAK;KAAa,WAAW,MAAM,EAAS,eAAe,EAAE,OAAO,MAAM;KAAE,MAAM;KAAK,CAAA,CAClG;;GAEP,IAAS,kBAAC,GAAD;IAAiB,YAAW;IAA4B,UAAU;IAAO,CAAA,GAAG;GACtE"}
|
|
1
|
+
{"version":3,"file":"SessionFormPage.js","names":[],"sources":["../../../../src/eventfullymanaged/pages/events/SessionFormPage.tsx"],"sourcesContent":["/* eslint-disable react-hooks/set-state-in-effect */\nimport { gql } from '@apollo/client';\nimport { useQuery } from '@apollo/client/react';\nimport { Input, Select, SelectContent, SelectItem, SelectTrigger, SelectValue, Textarea } from '@burdenoff/fe-libs/ui';\nimport { useEffect, useMemo, useState } from 'react';\nimport { useParams } from 'react-router';\n\nimport {\n useCreateEventSessionMutation,\n useEventQuery,\n useSessionConflictsQuery,\n useSpeakersQuery,\n useUpdateEventSessionMutation,\n useVenuesQuery,\n} from '../../api/eventApi';\nimport { EntityFormShell } from '../../components/EntityFormShell';\nimport { EntityTagsField } from '../../components/EntityTagsField';\nimport { useEventfullyManaged } from '../../context/EventfullyManagedContext';\nimport { describeError, mutationSucceeded } from '../../lib/mutationResult';\nimport { notify } from '../../lib/toast';\nimport { inputValueToIso, isoToInputValue, SESSION_STATUS_OPTIONS, SESSION_TYPE_OPTIONS } from './eventFormUtils';\n\nconst SPEAKER_METADATA_KEY = 'speakerId';\n\n// The foundation EVENT_SESSIONS_QUERY omits description/capacity/metadata, but the\n// backend `updateEventSession` resolver is a FULL replace — editing with a partial\n// initial state would blank those columns. There is no by-id session resolver, so\n// this fuller list query is the edit-hydration source: it selects every editable\n// field and the form finds its row by id client-side.\nconst FULL_EVENT_SESSIONS_QUERY = gql`\n query EventfullymanagedSessionFormSessions($eventId: ID, $limit: Int, $offset: Int) {\n eventSessions: eventfullyManagedEventSessions(eventId: $eventId, limit: $limit, offset: $offset) {\n total\n items {\n id\n eventId\n venueId\n title\n description\n sessionType\n track\n startsAt\n endsAt\n capacity\n status\n metadata\n }\n }\n }\n`;\n\ntype FullSessionsResponse = {\n eventSessions: { total: number; items: SessionListItem[] };\n};\n\ntype SessionListItem = {\n id: string;\n eventId: string;\n venueId?: string | null;\n title: string;\n sessionType: string;\n track?: string | null;\n startsAt: string;\n endsAt: string;\n status: string;\n capacity?: number | null;\n description?: string | null;\n metadata?: Record<string, unknown> | null;\n};\n\ntype SessionFormState = {\n title: string;\n sessionType: string;\n track: string;\n venueId: string;\n speakerId: string;\n startsAt: string;\n endsAt: string;\n capacity: string;\n status: string;\n description: string;\n};\n\nfunction defaultState(): SessionFormState {\n const now = new Date();\n const later = new Date(now.getTime() + 60 * 60 * 1000);\n return {\n title: '',\n sessionType: 'TALK',\n track: '',\n venueId: '',\n speakerId: '',\n startsAt: isoToInputValue(now.toISOString()),\n endsAt: isoToInputValue(later.toISOString()),\n capacity: '',\n status: 'SCHEDULED',\n description: '',\n };\n}\n\nexport default function SessionFormPage() {\n const { id = '', sid = '' } = useParams<{ id: string; sid: string }>();\n const isEdit = useMemo(() => Boolean(sid), [sid]);\n const { navigate } = useEventfullyManaged();\n\n const { data: eventData } = useEventQuery(id);\n // No by-id session resolver — derive the edit initial state from the (fuller) list query.\n const { data: sessionsData } = useQuery<FullSessionsResponse, { eventId?: string; limit?: number; offset?: number }>(\n FULL_EVENT_SESSIONS_QUERY,\n { variables: { eventId: id, limit: 100, offset: 0 }, skip: !sid }\n );\n const { data: venuesData } = useVenuesQuery({ limit: 100, offset: 0 });\n const { data: speakersData } = useSpeakersQuery({ limit: 100, offset: 0 });\n // BOFF-4331 — surface venue/speaker double-bookings for this event's agenda\n // right where an organizer is scheduling a session.\n const { data: conflictsData } = useSessionConflictsQuery({ eventId: id });\n const relevantConflicts = useMemo(() => {\n const conflicts = conflictsData?.sessionConflicts ?? [];\n return isEdit ? conflicts.filter((c) => c.sessionAId === sid || c.sessionBId === sid) : conflicts;\n }, [conflictsData, isEdit, sid]);\n const [createSession, { loading: creating }] = useCreateEventSessionMutation();\n const [updateSession, { loading: updating }] = useUpdateEventSessionMutation();\n const [form, setForm] = useState<SessionFormState>(defaultState);\n\n const existingSession = useMemo(\n () => (sessionsData?.eventSessions?.items ?? []).find((item) => item.id === sid),\n [sessionsData, sid]\n );\n\n useEffect(() => {\n if (!isEdit || !existingSession) {\n return;\n }\n const metadata = (existingSession.metadata ?? {}) as Record<string, unknown>;\n setForm({\n title: existingSession.title ?? '',\n sessionType: existingSession.sessionType ?? 'TALK',\n track: existingSession.track ?? '',\n venueId: existingSession.venueId ?? '',\n speakerId: typeof metadata[SPEAKER_METADATA_KEY] === 'string' ? (metadata[SPEAKER_METADATA_KEY] as string) : '',\n startsAt: isoToInputValue(existingSession.startsAt),\n endsAt: isoToInputValue(existingSession.endsAt),\n capacity: existingSession.capacity != null ? String(existingSession.capacity) : '',\n status: existingSession.status ?? 'SCHEDULED',\n description: existingSession.description ?? '',\n });\n }, [existingSession, isEdit]);\n\n const venues = venuesData?.venues?.items ?? [];\n const speakers = (speakersData?.speakers?.items ?? []) as Array<{ id: string; fullName: string }>;\n const eventName = eventData?.event?.name ?? 'Event';\n\n const onChange = (field: keyof SessionFormState, value: string) => {\n setForm((prev) => ({ ...prev, [field]: value }));\n };\n\n const onSubmit = async (event: React.FormEvent<HTMLFormElement>) => {\n event.preventDefault();\n\n if (!form.title.trim()) {\n notify.error('Title required', 'Give the session a title before saving.');\n return;\n }\n const startIso = inputValueToIso(form.startsAt);\n const endIso = inputValueToIso(form.endsAt);\n if (!startIso || !endIso) {\n notify.error('Times required', 'Set both a start and an end time.');\n return;\n }\n if (new Date(endIso).getTime() < new Date(startIso).getTime()) {\n notify.error('Invalid times', 'The end time cannot be before the start time.');\n return;\n }\n\n const existingMetadata = (existingSession?.metadata ?? {}) as Record<string, unknown>;\n const input = {\n eventId: id,\n title: form.title.trim(),\n sessionType: form.sessionType,\n track: form.track || null,\n venueId: form.venueId || null,\n startsAt: startIso,\n endsAt: endIso,\n capacity: form.capacity ? Number(form.capacity) : null,\n status: form.status,\n description: form.description || null,\n metadata: { ...existingMetadata, [SPEAKER_METADATA_KEY]: form.speakerId || null },\n };\n\n try {\n if (isEdit) {\n const response = await updateSession({ variables: { id: sid, input } });\n if (!mutationSucceeded(response, response.data?.updateEventSession?.id, 'Could not update session')) {\n return;\n }\n notify.success('Session updated');\n navigate(`/events/${id}`);\n return;\n }\n const response = await createSession({ variables: { input } });\n if (!mutationSucceeded(response, response.data?.createEventSession?.id, 'Could not create session')) {\n return;\n }\n notify.success('Session created');\n navigate(`/events/${id}`);\n } catch (error) {\n notify.error('Could not save session', describeError(error));\n }\n };\n\n const saving = creating || updating;\n\n return (\n <EntityFormShell\n title={isEdit ? 'Edit Session' : 'Add Session'}\n description={`Agenda item for ${eventName}.`}\n purpose=\"Sessions make up the agenda attendees follow on the day — a talk, workshop or panel with a time, a room and a speaker. Set the slot and assign a room so two sessions never clash, and pick the speaker who will present it.\"\n backLabel=\"Back to Event\"\n onBack={() => navigate(`/events/${id}`)}\n onSubmit={onSubmit}\n submitLabel=\"Save Session\"\n saving={saving}\n >\n {relevantConflicts.length > 0 ? (\n <div className=\"rounded-card border border-status-warning-border bg-status-warning-bg-subtle p-3 text-sm text-status-warning-text\">\n <p className=\"font-medium\">\n {relevantConflicts.length === 1\n ? '1 agenda conflict detected'\n : `${relevantConflicts.length} agenda conflicts detected`}\n </p>\n <ul className=\"mt-1 list-inside list-disc space-y-0.5\">\n {relevantConflicts.map((conflict, index) => (\n <li key={`${conflict.type}-${conflict.sessionAId}-${conflict.sessionBId}-${index}`}>\n {conflict.message}\n </li>\n ))}\n </ul>\n </div>\n ) : null}\n <div className=\"grid grid-cols-1 gap-4 md:grid-cols-2\">\n <label className=\"space-y-1 text-sm\">\n <span className=\"font-medium text-text-primary\">Title</span>\n <Input value={form.title} onChange={(e) => onChange('title', e.target.value)} required />\n </label>\n <label className=\"space-y-1 text-sm\">\n <span className=\"font-medium text-text-primary\">Track</span>\n <Input\n value={form.track}\n onChange={(e) => onChange('track', e.target.value)}\n placeholder=\"Main stage, Product, Engineering…\"\n />\n </label>\n </div>\n\n <div className=\"grid grid-cols-1 gap-4 md:grid-cols-3\">\n <label className=\"space-y-1 text-sm\">\n <span className=\"font-medium text-text-primary\">Room / Venue</span>\n <Select\n value={form.venueId || 'none'}\n onValueChange={(value) => onChange('venueId', value === 'none' ? '' : value)}\n >\n <SelectTrigger aria-label=\"Room or venue\">\n <SelectValue placeholder=\"Select a room\" />\n </SelectTrigger>\n <SelectContent>\n <SelectItem value=\"none\">No room</SelectItem>\n {venues.map((venue: { id: string; name: string }) => (\n <SelectItem key={venue.id} value={venue.id}>\n {venue.name}\n </SelectItem>\n ))}\n </SelectContent>\n </Select>\n </label>\n <label className=\"space-y-1 text-sm\">\n <span className=\"font-medium text-text-primary\">Speaker</span>\n <Select\n value={form.speakerId || 'none'}\n onValueChange={(value) => onChange('speakerId', value === 'none' ? '' : value)}\n >\n <SelectTrigger aria-label=\"Speaker\">\n <SelectValue placeholder=\"Select a speaker\" />\n </SelectTrigger>\n <SelectContent>\n <SelectItem value=\"none\">No speaker</SelectItem>\n {speakers.map((speaker) => (\n <SelectItem key={speaker.id} value={speaker.id}>\n {speaker.fullName}\n </SelectItem>\n ))}\n </SelectContent>\n </Select>\n </label>\n <label className=\"space-y-1 text-sm\">\n <span className=\"font-medium text-text-primary\">Capacity</span>\n <Input type=\"number\" min=\"0\" value={form.capacity} onChange={(e) => onChange('capacity', e.target.value)} />\n </label>\n </div>\n\n <div className=\"grid grid-cols-1 gap-4 md:grid-cols-2\">\n <label className=\"space-y-1 text-sm\">\n <span className=\"font-medium text-text-primary\">Start</span>\n <Input\n type=\"datetime-local\"\n value={form.startsAt}\n onChange={(e) => onChange('startsAt', e.target.value)}\n required\n />\n </label>\n <label className=\"space-y-1 text-sm\">\n <span className=\"font-medium text-text-primary\">End</span>\n <Input\n type=\"datetime-local\"\n value={form.endsAt}\n onChange={(e) => onChange('endsAt', e.target.value)}\n required\n />\n </label>\n </div>\n\n <div className=\"grid grid-cols-1 gap-4 md:grid-cols-2\">\n <label className=\"space-y-1 text-sm\">\n <span className=\"font-medium text-text-primary\">Type</span>\n <Select value={form.sessionType} onValueChange={(value) => onChange('sessionType', value)}>\n <SelectTrigger aria-label=\"Session type\">\n <SelectValue />\n </SelectTrigger>\n <SelectContent>\n {SESSION_TYPE_OPTIONS.map((option) => (\n <SelectItem key={option.value} value={option.value}>\n {option.label}\n </SelectItem>\n ))}\n </SelectContent>\n </Select>\n </label>\n <label className=\"space-y-1 text-sm\">\n <span className=\"font-medium text-text-primary\">Status</span>\n <Select value={form.status} onValueChange={(value) => onChange('status', value)}>\n <SelectTrigger aria-label=\"Status\">\n <SelectValue />\n </SelectTrigger>\n <SelectContent>\n {SESSION_STATUS_OPTIONS.map((option) => (\n <SelectItem key={option.value} value={option.value}>\n {option.label}\n </SelectItem>\n ))}\n </SelectContent>\n </Select>\n </label>\n </div>\n\n <label className=\"block space-y-1 text-sm\">\n <span className=\"font-medium text-text-primary\">Description</span>\n <Textarea value={form.description} onChange={(e) => onChange('description', e.target.value)} rows={3} />\n </label>\n\n {isEdit ? <EntityTagsField entityType=\"EVENTFULLYMANAGED_SESSION\" entityId={sid} /> : null}\n </EntityFormShell>\n );\n}\n"],"mappings":"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"}
|
|
@@ -5,7 +5,7 @@ import { CURRENCY_OPTIONS as o, inputValueToIso as s, isoToInputValue as c } fro
|
|
|
5
5
|
import { EntityFormShell as l } from "../../components/EntityFormShell.js";
|
|
6
6
|
import { describeError as u, mutationSucceeded as d } from "../../lib/mutationResult.js";
|
|
7
7
|
import { useEffect as f, useMemo as p, useState as m } from "react";
|
|
8
|
-
import { useParams as h } from "react-router
|
|
8
|
+
import { useParams as h } from "react-router";
|
|
9
9
|
import { jsx as g, jsxs as _ } from "react/jsx-runtime";
|
|
10
10
|
import { Input as v, Select as y, SelectContent as b, SelectItem as x, SelectTrigger as S, SelectValue as C, Switch as w, Textarea as T } from "@burdenoff/fe-libs/ui";
|
|
11
11
|
//#region src/eventfullymanaged/pages/events/TicketTypeFormPage.tsx
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"TicketTypeFormPage.js","names":[],"sources":["../../../../src/eventfullymanaged/pages/events/TicketTypeFormPage.tsx"],"sourcesContent":["/* eslint-disable react-hooks/set-state-in-effect */\nimport {\n Input,\n Select,\n SelectContent,\n SelectItem,\n SelectTrigger,\n SelectValue,\n Switch,\n Textarea,\n} from '@burdenoff/fe-libs/ui';\nimport { useEffect, useMemo, useState } from 'react';\nimport { useParams } from 'react-router-dom';\n\nimport {\n useCreateTicketTypeMutation,\n useEventQuery,\n useTicketTypesQuery,\n useUpdateTicketTypeMutation,\n type TicketTypeGql,\n} from '../../api/eventApi';\nimport { EntityFormShell } from '../../components/EntityFormShell';\nimport { useEventfullyManaged } from '../../context/EventfullyManagedContext';\nimport { describeError, mutationSucceeded } from '../../lib/mutationResult';\nimport { notify } from '../../lib/toast';\nimport { CURRENCY_OPTIONS, inputValueToIso, isoToInputValue } from './eventFormUtils';\n\ntype TicketTypeFormState = {\n name: string;\n description: string;\n price: string;\n currency: string;\n capacity: string;\n salesStartAt: string;\n salesEndAt: string;\n isActive: boolean;\n};\n\nconst initialState: TicketTypeFormState = {\n name: '',\n description: '',\n price: '0',\n currency: 'INR',\n capacity: '',\n salesStartAt: '',\n salesEndAt: '',\n isActive: true,\n};\n\nexport default function TicketTypeFormPage() {\n const { id = '', tid = '' } = useParams<{ id: string; tid: string }>();\n const isEdit = useMemo(() => Boolean(tid), [tid]);\n const { navigate } = useEventfullyManaged();\n\n const { data: eventData } = useEventQuery(id);\n // No by-id ticket-type resolver — derive the edit initial state from the list query.\n const { data: ticketsData } = useTicketTypesQuery({ eventId: id, limit: 100, offset: 0 });\n const [createTicketType, { loading: creating }] = useCreateTicketTypeMutation();\n const [updateTicketType, { loading: updating }] = useUpdateTicketTypeMutation();\n const [form, setForm] = useState<TicketTypeFormState>(initialState);\n\n const existingTicket = useMemo(\n () => (ticketsData?.ticketTypes?.items ?? []).find((item) => item.id === tid),\n [ticketsData, tid]\n );\n\n useEffect(() => {\n if (!isEdit || !existingTicket) {\n return;\n }\n const ticket = existingTicket as TicketTypeGql;\n setForm({\n name: ticket.name ?? '',\n description: ticket.description ?? '',\n price: ticket.price ?? '0',\n currency: ticket.currency ?? 'INR',\n capacity: ticket.capacity != null ? String(ticket.capacity) : '',\n salesStartAt: isoToInputValue(ticket.salesStartAt),\n salesEndAt: isoToInputValue(ticket.salesEndAt),\n isActive: ticket.isActive ?? true,\n });\n }, [existingTicket, isEdit]);\n\n const eventName = eventData?.event?.name ?? 'Event';\n\n const onChange = <K extends keyof TicketTypeFormState>(field: K, value: TicketTypeFormState[K]) => {\n setForm((prev) => ({ ...prev, [field]: value }));\n };\n\n const onSubmit = async (event: React.FormEvent<HTMLFormElement>) => {\n event.preventDefault();\n\n if (!form.name.trim()) {\n notify.error('Name required', 'Give the ticket type a name before saving.');\n return;\n }\n const priceValue = Number(form.price);\n if (Number.isNaN(priceValue) || priceValue < 0) {\n notify.error('Invalid price', 'Enter a price of zero or more.');\n return;\n }\n const salesStartIso = inputValueToIso(form.salesStartAt);\n const salesEndIso = inputValueToIso(form.salesEndAt);\n if (salesStartIso && salesEndIso && new Date(salesEndIso).getTime() < new Date(salesStartIso).getTime()) {\n notify.error('Invalid sales window', 'Sales end cannot be before sales start.');\n return;\n }\n\n const input = {\n eventId: id,\n name: form.name.trim(),\n description: form.description || null,\n price: String(priceValue),\n currency: form.currency,\n capacity: form.capacity ? Number(form.capacity) : null,\n salesStartAt: salesStartIso || null,\n salesEndAt: salesEndIso || null,\n isActive: form.isActive,\n metadata: {},\n };\n\n try {\n if (isEdit) {\n const response = await updateTicketType({ variables: { id: tid, input } });\n if (!mutationSucceeded(response, response.data?.updateTicketType?.id, 'Could not update ticket type')) {\n return;\n }\n notify.success('Ticket type updated');\n navigate(`/events/${id}`);\n return;\n }\n const response = await createTicketType({ variables: { input } });\n if (!mutationSucceeded(response, response.data?.createTicketType?.id, 'Could not create ticket type')) {\n return;\n }\n notify.success('Ticket type created');\n navigate(`/events/${id}`);\n } catch (error) {\n notify.error('Could not save ticket type', describeError(error));\n }\n };\n\n const saving = creating || updating;\n\n return (\n <EntityFormShell\n title={isEdit ? 'Edit Ticket Type' : 'Add Ticket Type'}\n description={`Sellable ticket for ${eventName}.`}\n purpose=\"Ticket types are what attendees actually buy — Early Bird, General, VIP. Set a price and currency, cap how many can be sold, and define the sales window so the ticket opens and closes automatically. Toggle it inactive to pull it from sale without deleting the history.\"\n backLabel=\"Back to Event\"\n onBack={() => navigate(`/events/${id}`)}\n onSubmit={onSubmit}\n submitLabel=\"Save Ticket Type\"\n saving={saving}\n >\n <div className=\"grid grid-cols-1 gap-4 md:grid-cols-2\">\n <label className=\"space-y-1 text-sm\">\n <span className=\"font-medium text-text-primary\">Name</span>\n <Input value={form.name} onChange={(e) => onChange('name', e.target.value)} required />\n </label>\n <label className=\"space-y-1 text-sm\">\n <span className=\"font-medium text-text-primary\">Capacity</span>\n <Input\n type=\"number\"\n min=\"0\"\n value={form.capacity}\n onChange={(e) => onChange('capacity', e.target.value)}\n placeholder=\"Unlimited\"\n />\n </label>\n </div>\n\n <div className=\"grid grid-cols-1 gap-4 md:grid-cols-2\">\n <label className=\"space-y-1 text-sm\">\n <span className=\"font-medium text-text-primary\">Price</span>\n <Input\n type=\"number\"\n min=\"0\"\n step=\"0.01\"\n value={form.price}\n onChange={(e) => onChange('price', e.target.value)}\n required\n />\n </label>\n <label className=\"space-y-1 text-sm\">\n <span className=\"font-medium text-text-primary\">Currency</span>\n <Select value={form.currency} onValueChange={(value) => onChange('currency', value)}>\n <SelectTrigger aria-label=\"Currency\">\n <SelectValue />\n </SelectTrigger>\n <SelectContent>\n {CURRENCY_OPTIONS.map((option) => (\n <SelectItem key={option.value} value={option.value}>\n {option.label}\n </SelectItem>\n ))}\n </SelectContent>\n </Select>\n </label>\n </div>\n\n <div className=\"grid grid-cols-1 gap-4 md:grid-cols-2\">\n <label className=\"space-y-1 text-sm\">\n <span className=\"font-medium text-text-primary\">Sales start</span>\n <Input\n type=\"datetime-local\"\n value={form.salesStartAt}\n onChange={(e) => onChange('salesStartAt', e.target.value)}\n />\n </label>\n <label className=\"space-y-1 text-sm\">\n <span className=\"font-medium text-text-primary\">Sales end</span>\n <Input\n type=\"datetime-local\"\n value={form.salesEndAt}\n onChange={(e) => onChange('salesEndAt', e.target.value)}\n />\n </label>\n </div>\n\n <label className=\"block space-y-1 text-sm\">\n <span className=\"font-medium text-text-primary\">Description</span>\n <Textarea value={form.description} onChange={(e) => onChange('description', e.target.value)} rows={3} />\n </label>\n\n <div className=\"flex items-center justify-between rounded-card border border-border-seam p-3\">\n <div>\n <p className=\"text-sm font-medium text-text-primary\">Active</p>\n <p className=\"text-xs text-text-secondary\">Active ticket types are open for sale during their window.</p>\n </div>\n <Switch checked={form.isActive} onCheckedChange={(checked) => onChange('isActive', checked)} />\n </div>\n </EntityFormShell>\n );\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"TicketTypeFormPage.js","names":[],"sources":["../../../../src/eventfullymanaged/pages/events/TicketTypeFormPage.tsx"],"sourcesContent":["/* eslint-disable react-hooks/set-state-in-effect */\nimport {\n Input,\n Select,\n SelectContent,\n SelectItem,\n SelectTrigger,\n SelectValue,\n Switch,\n Textarea,\n} from '@burdenoff/fe-libs/ui';\nimport { useEffect, useMemo, useState } from 'react';\nimport { useParams } from 'react-router';\n\nimport {\n useCreateTicketTypeMutation,\n useEventQuery,\n useTicketTypesQuery,\n useUpdateTicketTypeMutation,\n type TicketTypeGql,\n} from '../../api/eventApi';\nimport { EntityFormShell } from '../../components/EntityFormShell';\nimport { useEventfullyManaged } from '../../context/EventfullyManagedContext';\nimport { describeError, mutationSucceeded } from '../../lib/mutationResult';\nimport { notify } from '../../lib/toast';\nimport { CURRENCY_OPTIONS, inputValueToIso, isoToInputValue } from './eventFormUtils';\n\ntype TicketTypeFormState = {\n name: string;\n description: string;\n price: string;\n currency: string;\n capacity: string;\n salesStartAt: string;\n salesEndAt: string;\n isActive: boolean;\n};\n\nconst initialState: TicketTypeFormState = {\n name: '',\n description: '',\n price: '0',\n currency: 'INR',\n capacity: '',\n salesStartAt: '',\n salesEndAt: '',\n isActive: true,\n};\n\nexport default function TicketTypeFormPage() {\n const { id = '', tid = '' } = useParams<{ id: string; tid: string }>();\n const isEdit = useMemo(() => Boolean(tid), [tid]);\n const { navigate } = useEventfullyManaged();\n\n const { data: eventData } = useEventQuery(id);\n // No by-id ticket-type resolver — derive the edit initial state from the list query.\n const { data: ticketsData } = useTicketTypesQuery({ eventId: id, limit: 100, offset: 0 });\n const [createTicketType, { loading: creating }] = useCreateTicketTypeMutation();\n const [updateTicketType, { loading: updating }] = useUpdateTicketTypeMutation();\n const [form, setForm] = useState<TicketTypeFormState>(initialState);\n\n const existingTicket = useMemo(\n () => (ticketsData?.ticketTypes?.items ?? []).find((item) => item.id === tid),\n [ticketsData, tid]\n );\n\n useEffect(() => {\n if (!isEdit || !existingTicket) {\n return;\n }\n const ticket = existingTicket as TicketTypeGql;\n setForm({\n name: ticket.name ?? '',\n description: ticket.description ?? '',\n price: ticket.price ?? '0',\n currency: ticket.currency ?? 'INR',\n capacity: ticket.capacity != null ? String(ticket.capacity) : '',\n salesStartAt: isoToInputValue(ticket.salesStartAt),\n salesEndAt: isoToInputValue(ticket.salesEndAt),\n isActive: ticket.isActive ?? true,\n });\n }, [existingTicket, isEdit]);\n\n const eventName = eventData?.event?.name ?? 'Event';\n\n const onChange = <K extends keyof TicketTypeFormState>(field: K, value: TicketTypeFormState[K]) => {\n setForm((prev) => ({ ...prev, [field]: value }));\n };\n\n const onSubmit = async (event: React.FormEvent<HTMLFormElement>) => {\n event.preventDefault();\n\n if (!form.name.trim()) {\n notify.error('Name required', 'Give the ticket type a name before saving.');\n return;\n }\n const priceValue = Number(form.price);\n if (Number.isNaN(priceValue) || priceValue < 0) {\n notify.error('Invalid price', 'Enter a price of zero or more.');\n return;\n }\n const salesStartIso = inputValueToIso(form.salesStartAt);\n const salesEndIso = inputValueToIso(form.salesEndAt);\n if (salesStartIso && salesEndIso && new Date(salesEndIso).getTime() < new Date(salesStartIso).getTime()) {\n notify.error('Invalid sales window', 'Sales end cannot be before sales start.');\n return;\n }\n\n const input = {\n eventId: id,\n name: form.name.trim(),\n description: form.description || null,\n price: String(priceValue),\n currency: form.currency,\n capacity: form.capacity ? Number(form.capacity) : null,\n salesStartAt: salesStartIso || null,\n salesEndAt: salesEndIso || null,\n isActive: form.isActive,\n metadata: {},\n };\n\n try {\n if (isEdit) {\n const response = await updateTicketType({ variables: { id: tid, input } });\n if (!mutationSucceeded(response, response.data?.updateTicketType?.id, 'Could not update ticket type')) {\n return;\n }\n notify.success('Ticket type updated');\n navigate(`/events/${id}`);\n return;\n }\n const response = await createTicketType({ variables: { input } });\n if (!mutationSucceeded(response, response.data?.createTicketType?.id, 'Could not create ticket type')) {\n return;\n }\n notify.success('Ticket type created');\n navigate(`/events/${id}`);\n } catch (error) {\n notify.error('Could not save ticket type', describeError(error));\n }\n };\n\n const saving = creating || updating;\n\n return (\n <EntityFormShell\n title={isEdit ? 'Edit Ticket Type' : 'Add Ticket Type'}\n description={`Sellable ticket for ${eventName}.`}\n purpose=\"Ticket types are what attendees actually buy — Early Bird, General, VIP. Set a price and currency, cap how many can be sold, and define the sales window so the ticket opens and closes automatically. Toggle it inactive to pull it from sale without deleting the history.\"\n backLabel=\"Back to Event\"\n onBack={() => navigate(`/events/${id}`)}\n onSubmit={onSubmit}\n submitLabel=\"Save Ticket Type\"\n saving={saving}\n >\n <div className=\"grid grid-cols-1 gap-4 md:grid-cols-2\">\n <label className=\"space-y-1 text-sm\">\n <span className=\"font-medium text-text-primary\">Name</span>\n <Input value={form.name} onChange={(e) => onChange('name', e.target.value)} required />\n </label>\n <label className=\"space-y-1 text-sm\">\n <span className=\"font-medium text-text-primary\">Capacity</span>\n <Input\n type=\"number\"\n min=\"0\"\n value={form.capacity}\n onChange={(e) => onChange('capacity', e.target.value)}\n placeholder=\"Unlimited\"\n />\n </label>\n </div>\n\n <div className=\"grid grid-cols-1 gap-4 md:grid-cols-2\">\n <label className=\"space-y-1 text-sm\">\n <span className=\"font-medium text-text-primary\">Price</span>\n <Input\n type=\"number\"\n min=\"0\"\n step=\"0.01\"\n value={form.price}\n onChange={(e) => onChange('price', e.target.value)}\n required\n />\n </label>\n <label className=\"space-y-1 text-sm\">\n <span className=\"font-medium text-text-primary\">Currency</span>\n <Select value={form.currency} onValueChange={(value) => onChange('currency', value)}>\n <SelectTrigger aria-label=\"Currency\">\n <SelectValue />\n </SelectTrigger>\n <SelectContent>\n {CURRENCY_OPTIONS.map((option) => (\n <SelectItem key={option.value} value={option.value}>\n {option.label}\n </SelectItem>\n ))}\n </SelectContent>\n </Select>\n </label>\n </div>\n\n <div className=\"grid grid-cols-1 gap-4 md:grid-cols-2\">\n <label className=\"space-y-1 text-sm\">\n <span className=\"font-medium text-text-primary\">Sales start</span>\n <Input\n type=\"datetime-local\"\n value={form.salesStartAt}\n onChange={(e) => onChange('salesStartAt', e.target.value)}\n />\n </label>\n <label className=\"space-y-1 text-sm\">\n <span className=\"font-medium text-text-primary\">Sales end</span>\n <Input\n type=\"datetime-local\"\n value={form.salesEndAt}\n onChange={(e) => onChange('salesEndAt', e.target.value)}\n />\n </label>\n </div>\n\n <label className=\"block space-y-1 text-sm\">\n <span className=\"font-medium text-text-primary\">Description</span>\n <Textarea value={form.description} onChange={(e) => onChange('description', e.target.value)} rows={3} />\n </label>\n\n <div className=\"flex items-center justify-between rounded-card border border-border-seam p-3\">\n <div>\n <p className=\"text-sm font-medium text-text-primary\">Active</p>\n <p className=\"text-xs text-text-secondary\">Active ticket types are open for sale during their window.</p>\n </div>\n <Switch checked={form.isActive} onCheckedChange={(checked) => onChange('isActive', checked)} />\n </div>\n </EntityFormShell>\n );\n}\n"],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"VirtualSessionPage.d.ts","sourceRoot":"","sources":["../../../../src/eventfullymanaged/pages/events/VirtualSessionPage.tsx"],"names":[],"mappings":"AAyHA,MAAM,CAAC,OAAO,UAAU,kBAAkB,4CAyVzC"}
|