@burdenoff/microfe-eventfullymanaged 2026.704.3 → 2026.704.4

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.
@@ -296,7 +296,7 @@ function N() {
296
296
  })]
297
297
  }),
298
298
  P ? /* @__PURE__ */ x(g, {
299
- entityType: "session",
299
+ entityType: "EVENTFULLYMANAGED_SESSION",
300
300
  entityId: N
301
301
  }) : null
302
302
  ]
@@ -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 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 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 <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=\"session\" entityId={sid} /> : null}\n </EntityFormShell>\n );\n}\n"],"mappings":"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"}
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 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 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 <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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"}
@@ -3,14 +3,13 @@ import { notify as t } from "../../lib/toast.js";
3
3
  import { useCreateExhibitorMutation as n, useEventsQuery as r, useExhibitorsQuery as i, useUpdateExhibitorMutation as a } from "../../api/eventApi.js";
4
4
  import { EntityFormShell as o } from "../../components/EntityFormShell.js";
5
5
  import { describeError as s, mutationSucceeded as c } from "../../lib/mutationResult.js";
6
- import { EntityTagsField as l } from "../../components/EntityTagsField.js";
7
- import { safeUrl as u } from "../../components/SafeLink.js";
8
- import { useEffect as d, useMemo as f, useState as p } from "react";
9
- import { useParams as m } from "react-router-dom";
10
- import { jsx as h, jsxs as g } from "react/jsx-runtime";
11
- import { Input as _, Select as v, SelectContent as y, SelectItem as b, SelectTrigger as x, SelectValue as S } from "@burdenoff/fe-libs/ui";
6
+ import { safeUrl as l } from "../../components/SafeLink.js";
7
+ import { useEffect as u, useMemo as d, useState as f } from "react";
8
+ import { useParams as p } from "react-router-dom";
9
+ import { jsx as m, jsxs as h } from "react/jsx-runtime";
10
+ import { Input as g, Select as _, SelectContent as v, SelectItem as y, SelectTrigger as b, SelectValue as x } from "@burdenoff/fe-libs/ui";
12
11
  //#region src/eventfullymanaged/pages/exhibitors/ExhibitorFormPage.tsx
13
- var C = {
12
+ var S = {
14
13
  eventId: "",
15
14
  name: "",
16
15
  boothCode: "",
@@ -19,7 +18,7 @@ var C = {
19
18
  website: "",
20
19
  contactName: "",
21
20
  contactEmail: ""
22
- }, w = [
21
+ }, C = [
23
22
  {
24
23
  value: "INVITED",
25
24
  label: "Invited"
@@ -37,148 +36,148 @@ var C = {
37
36
  label: "Cancelled"
38
37
  }
39
38
  ];
40
- function T() {
41
- let { id: T = "" } = m(), E = f(() => !!T, [T]), { navigate: D } = e(), { data: O } = r({
39
+ function w() {
40
+ let { id: w = "" } = p(), T = d(() => !!w, [w]), { navigate: E } = e(), { data: D } = r({
42
41
  limit: 100,
43
42
  offset: 0
44
- }), { data: k } = i({
43
+ }), { data: O } = i({
45
44
  limit: 100,
46
45
  offset: 0
47
- }), A = f(() => (k?.exhibitors?.items ?? []).find((e) => e.id === T), [k, T]), [j, { loading: M }] = n(), [N, { loading: P }] = a(), [F, I] = p(C);
48
- d(() => {
49
- if (!E || !A) return;
50
- let e = A.metadata ?? {}, t = typeof e.website == "string" ? e.website : "";
51
- I({
52
- eventId: A.eventId ?? "",
53
- name: A.name ?? "",
54
- boothCode: A.boothCode ?? "",
55
- category: A.category ?? "",
56
- status: A.status ?? "CONFIRMED",
46
+ }), k = d(() => (O?.exhibitors?.items ?? []).find((e) => e.id === w), [O, w]), [A, { loading: j }] = n(), [M, { loading: N }] = a(), [P, F] = f(S);
47
+ u(() => {
48
+ if (!T || !k) return;
49
+ let e = k.metadata ?? {}, t = typeof e.website == "string" ? e.website : "";
50
+ F({
51
+ eventId: k.eventId ?? "",
52
+ name: k.name ?? "",
53
+ boothCode: k.boothCode ?? "",
54
+ category: k.category ?? "",
55
+ status: k.status ?? "CONFIRMED",
57
56
  website: t,
58
- contactName: A.contactName ?? "",
59
- contactEmail: A.contactEmail ?? ""
57
+ contactName: k.contactName ?? "",
58
+ contactEmail: k.contactEmail ?? ""
60
59
  });
61
- }, [A, E]);
62
- let L = (e, t) => {
63
- I((n) => ({
60
+ }, [k, T]);
61
+ let I = (e, t) => {
62
+ F((n) => ({
64
63
  ...n,
65
64
  [e]: t
66
65
  }));
67
- }, R = async (e) => {
68
- if (e.preventDefault(), !F.eventId) {
66
+ }, L = async (e) => {
67
+ if (e.preventDefault(), !P.eventId) {
69
68
  t.error("Event required", "Choose the event this exhibitor belongs to.");
70
69
  return;
71
70
  }
72
- let n = u(F.website);
73
- if (F.website.trim() && !n) {
71
+ let n = l(P.website);
72
+ if (P.website.trim() && !n) {
74
73
  t.error("Invalid website", "Enter a valid http(s) URL, or leave it blank.");
75
74
  return;
76
75
  }
77
- let r = A?.metadata ?? {}, i = {
78
- eventId: F.eventId,
79
- name: F.name,
80
- boothCode: F.boothCode,
81
- category: F.category,
82
- status: F.status,
83
- contactName: F.contactName,
84
- contactEmail: F.contactEmail,
76
+ let r = k?.metadata ?? {}, i = {
77
+ eventId: P.eventId,
78
+ name: P.name,
79
+ boothCode: P.boothCode,
80
+ category: P.category,
81
+ status: P.status,
82
+ contactName: P.contactName,
83
+ contactEmail: P.contactEmail,
85
84
  metadata: {
86
85
  ...r,
87
86
  website: n ?? ""
88
87
  }
89
88
  };
90
89
  try {
91
- if (E) {
92
- let e = await N({ variables: {
93
- id: T,
90
+ if (T) {
91
+ let e = await M({ variables: {
92
+ id: w,
94
93
  input: i
95
94
  } });
96
95
  if (!c(e, e.data?.updateExhibitor?.id, "Could not update exhibitor")) return;
97
- D("/exhibitors");
96
+ E("/exhibitors");
98
97
  return;
99
98
  }
100
- let e = await j({ variables: { input: i } });
99
+ let e = await A({ variables: { input: i } });
101
100
  if (!c(e, e.data?.createExhibitor?.id, "Could not create exhibitor")) return;
102
- D("/exhibitors");
101
+ E("/exhibitors");
103
102
  } catch (e) {
104
103
  t.error("Could not save exhibitor", s(e));
105
104
  }
106
- }, z = M || P, B = O?.events?.items ?? [];
107
- return /* @__PURE__ */ g(o, {
108
- title: E ? "Edit Exhibitor" : "New Exhibitor",
105
+ }, R = j || N, z = D?.events?.items ?? [];
106
+ return /* @__PURE__ */ h(o, {
107
+ title: T ? "Edit Exhibitor" : "New Exhibitor",
109
108
  description: "Manage a booth exhibitor with live backend persistence.",
110
109
  purpose: "Register a company staffing a booth: link it to the event, give it a booth code and category, and record who to contact. Once saved, capture the leads their booth scans from the exhibitor list.",
111
110
  backLabel: "Back to Exhibitors",
112
- onBack: () => D("/exhibitors"),
113
- onSubmit: R,
114
- submitLabel: E ? "Save Exhibitor" : "Create Exhibitor",
115
- saving: z,
111
+ onBack: () => E("/exhibitors"),
112
+ onSubmit: L,
113
+ submitLabel: T ? "Save Exhibitor" : "Create Exhibitor",
114
+ saving: R,
116
115
  children: [
117
- /* @__PURE__ */ g("div", {
116
+ /* @__PURE__ */ h("div", {
118
117
  className: "grid grid-cols-1 gap-4 md:grid-cols-2",
119
- children: [/* @__PURE__ */ g("label", {
118
+ children: [/* @__PURE__ */ h("label", {
120
119
  className: "space-y-1 text-sm",
121
- children: [/* @__PURE__ */ h("span", {
120
+ children: [/* @__PURE__ */ m("span", {
122
121
  className: "font-medium text-text-primary",
123
122
  children: "Event"
124
- }), /* @__PURE__ */ g(v, {
125
- value: F.eventId,
126
- onValueChange: (e) => L("eventId", e),
127
- children: [/* @__PURE__ */ h(x, {
123
+ }), /* @__PURE__ */ h(_, {
124
+ value: P.eventId,
125
+ onValueChange: (e) => I("eventId", e),
126
+ children: [/* @__PURE__ */ m(b, {
128
127
  "aria-label": "Event",
129
- children: /* @__PURE__ */ h(S, { placeholder: "Select event" })
130
- }), /* @__PURE__ */ h(y, { children: B.map((e) => /* @__PURE__ */ h(b, {
128
+ children: /* @__PURE__ */ m(x, { placeholder: "Select event" })
129
+ }), /* @__PURE__ */ m(v, { children: z.map((e) => /* @__PURE__ */ m(y, {
131
130
  value: e.id,
132
131
  children: e.name
133
132
  }, e.id)) })]
134
133
  })]
135
- }), /* @__PURE__ */ g("label", {
134
+ }), /* @__PURE__ */ h("label", {
136
135
  className: "space-y-1 text-sm",
137
- children: [/* @__PURE__ */ h("span", {
136
+ children: [/* @__PURE__ */ m("span", {
138
137
  className: "font-medium text-text-primary",
139
138
  children: "Name"
140
- }), /* @__PURE__ */ h(_, {
141
- value: F.name,
142
- onChange: (e) => L("name", e.target.value),
139
+ }), /* @__PURE__ */ m(g, {
140
+ value: P.name,
141
+ onChange: (e) => I("name", e.target.value),
143
142
  required: !0
144
143
  })]
145
144
  })]
146
145
  }),
147
- /* @__PURE__ */ g("div", {
146
+ /* @__PURE__ */ h("div", {
148
147
  className: "grid grid-cols-1 gap-4 md:grid-cols-3",
149
148
  children: [
150
- /* @__PURE__ */ g("label", {
149
+ /* @__PURE__ */ h("label", {
151
150
  className: "space-y-1 text-sm",
152
- children: [/* @__PURE__ */ h("span", {
151
+ children: [/* @__PURE__ */ m("span", {
153
152
  className: "font-medium text-text-primary",
154
153
  children: "Booth Code"
155
- }), /* @__PURE__ */ h(_, {
156
- value: F.boothCode,
157
- onChange: (e) => L("boothCode", e.target.value)
154
+ }), /* @__PURE__ */ m(g, {
155
+ value: P.boothCode,
156
+ onChange: (e) => I("boothCode", e.target.value)
158
157
  })]
159
158
  }),
160
- /* @__PURE__ */ g("label", {
159
+ /* @__PURE__ */ h("label", {
161
160
  className: "space-y-1 text-sm",
162
- children: [/* @__PURE__ */ h("span", {
161
+ children: [/* @__PURE__ */ m("span", {
163
162
  className: "font-medium text-text-primary",
164
163
  children: "Category"
165
- }), /* @__PURE__ */ h(_, {
166
- value: F.category,
167
- onChange: (e) => L("category", e.target.value)
164
+ }), /* @__PURE__ */ m(g, {
165
+ value: P.category,
166
+ onChange: (e) => I("category", e.target.value)
168
167
  })]
169
168
  }),
170
- /* @__PURE__ */ g("label", {
169
+ /* @__PURE__ */ h("label", {
171
170
  className: "space-y-1 text-sm",
172
- children: [/* @__PURE__ */ h("span", {
171
+ children: [/* @__PURE__ */ m("span", {
173
172
  className: "font-medium text-text-primary",
174
173
  children: "Status"
175
- }), /* @__PURE__ */ g(v, {
176
- value: F.status,
177
- onValueChange: (e) => L("status", e),
178
- children: [/* @__PURE__ */ h(x, {
174
+ }), /* @__PURE__ */ h(_, {
175
+ value: P.status,
176
+ onValueChange: (e) => I("status", e),
177
+ children: [/* @__PURE__ */ m(b, {
179
178
  "aria-label": "Status",
180
- children: /* @__PURE__ */ h(S, {})
181
- }), /* @__PURE__ */ h(y, { children: w.map((e) => /* @__PURE__ */ h(b, {
179
+ children: /* @__PURE__ */ m(x, {})
180
+ }), /* @__PURE__ */ m(v, { children: C.map((e) => /* @__PURE__ */ m(y, {
182
181
  value: e.value,
183
182
  children: e.label
184
183
  }, e.value)) })]
@@ -186,48 +185,44 @@ function T() {
186
185
  })
187
186
  ]
188
187
  }),
189
- /* @__PURE__ */ g("label", {
188
+ /* @__PURE__ */ h("label", {
190
189
  className: "block space-y-1 text-sm",
191
- children: [/* @__PURE__ */ h("span", {
190
+ children: [/* @__PURE__ */ m("span", {
192
191
  className: "font-medium text-text-primary",
193
192
  children: "Website"
194
- }), /* @__PURE__ */ h(_, {
195
- value: F.website,
196
- onChange: (e) => L("website", e.target.value),
193
+ }), /* @__PURE__ */ m(g, {
194
+ value: P.website,
195
+ onChange: (e) => I("website", e.target.value),
197
196
  placeholder: "https://example.com"
198
197
  })]
199
198
  }),
200
- /* @__PURE__ */ g("div", {
199
+ /* @__PURE__ */ h("div", {
201
200
  className: "grid grid-cols-1 gap-4 md:grid-cols-2",
202
- children: [/* @__PURE__ */ g("label", {
201
+ children: [/* @__PURE__ */ h("label", {
203
202
  className: "space-y-1 text-sm",
204
- children: [/* @__PURE__ */ h("span", {
203
+ children: [/* @__PURE__ */ m("span", {
205
204
  className: "font-medium text-text-primary",
206
205
  children: "Contact Name"
207
- }), /* @__PURE__ */ h(_, {
208
- value: F.contactName,
209
- onChange: (e) => L("contactName", e.target.value)
206
+ }), /* @__PURE__ */ m(g, {
207
+ value: P.contactName,
208
+ onChange: (e) => I("contactName", e.target.value)
210
209
  })]
211
- }), /* @__PURE__ */ g("label", {
210
+ }), /* @__PURE__ */ h("label", {
212
211
  className: "space-y-1 text-sm",
213
- children: [/* @__PURE__ */ h("span", {
212
+ children: [/* @__PURE__ */ m("span", {
214
213
  className: "font-medium text-text-primary",
215
214
  children: "Contact Email"
216
- }), /* @__PURE__ */ h(_, {
215
+ }), /* @__PURE__ */ m(g, {
217
216
  type: "email",
218
- value: F.contactEmail,
219
- onChange: (e) => L("contactEmail", e.target.value)
217
+ value: P.contactEmail,
218
+ onChange: (e) => I("contactEmail", e.target.value)
220
219
  })]
221
220
  })]
222
- }),
223
- E ? /* @__PURE__ */ h(l, {
224
- entityType: "exhibitor",
225
- entityId: T
226
- }) : null
221
+ })
227
222
  ]
228
223
  });
229
224
  }
230
225
  //#endregion
231
- export { T as default };
226
+ export { w as default };
232
227
 
233
228
  //# sourceMappingURL=ExhibitorFormPage.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"ExhibitorFormPage.js","names":[],"sources":["../../../../src/eventfullymanaged/pages/exhibitors/ExhibitorFormPage.tsx"],"sourcesContent":["/* eslint-disable react-hooks/set-state-in-effect */\nimport { Input, Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from '@burdenoff/fe-libs/ui';\nimport { useEffect, useMemo, useState } from 'react';\nimport { useParams } from 'react-router-dom';\n\nimport {\n useCreateExhibitorMutation,\n useEventsQuery,\n useExhibitorsQuery,\n useUpdateExhibitorMutation,\n} from '../../api/eventApi';\nimport { EntityFormShell } from '../../components/EntityFormShell';\nimport { EntityTagsField } from '../../components/EntityTagsField';\nimport { safeUrl } from '../../components/SafeLink';\nimport { useEventfullyManaged } from '../../context/EventfullyManagedContext';\nimport { describeError, mutationSucceeded } from '../../lib/mutationResult';\nimport { notify } from '../../lib/toast';\n\ntype ExhibitorFormState = {\n eventId: string;\n name: string;\n boothCode: string;\n category: string;\n status: string;\n website: string;\n contactName: string;\n contactEmail: string;\n};\n\nconst initialState: ExhibitorFormState = {\n eventId: '',\n name: '',\n boothCode: '',\n category: '',\n status: 'CONFIRMED',\n website: '',\n contactName: '',\n contactEmail: '',\n};\n\nconst STATUS_OPTIONS = [\n { value: 'INVITED', label: 'Invited' },\n { value: 'CONFIRMED', label: 'Confirmed' },\n { value: 'ACTIVE', label: 'Active' },\n { value: 'CANCELLED', label: 'Cancelled' },\n];\n\nexport default function ExhibitorFormPage() {\n const { id = '' } = useParams<{ id: string }>();\n const isEdit = useMemo(() => Boolean(id), [id]);\n const { navigate } = useEventfullyManaged();\n\n const { data: eventsData } = useEventsQuery({ limit: 100, offset: 0 });\n // No by-id resolver exists for exhibitors, so the edit initial-state is derived\n // from the list query row.\n const { data: exhibitorsData } = useExhibitorsQuery({ limit: 100, offset: 0 });\n\n const existing = useMemo(\n () => (exhibitorsData?.exhibitors?.items ?? []).find((item) => item.id === id),\n [exhibitorsData, id]\n );\n\n const [createExhibitor, { loading: creating }] = useCreateExhibitorMutation();\n const [updateExhibitor, { loading: updating }] = useUpdateExhibitorMutation();\n const [form, setForm] = useState<ExhibitorFormState>(initialState);\n\n useEffect(() => {\n if (!isEdit || !existing) {\n return;\n }\n const metadata = existing.metadata ?? {};\n const website = typeof metadata.website === 'string' ? metadata.website : '';\n setForm({\n eventId: existing.eventId ?? '',\n name: existing.name ?? '',\n boothCode: existing.boothCode ?? '',\n category: existing.category ?? '',\n status: existing.status ?? 'CONFIRMED',\n website,\n contactName: existing.contactName ?? '',\n contactEmail: existing.contactEmail ?? '',\n });\n }, [existing, isEdit]);\n\n const onChange = (field: keyof ExhibitorFormState, 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.eventId) {\n notify.error('Event required', 'Choose the event this exhibitor belongs to.');\n return;\n }\n\n // The website is free text persisted verbatim, so normalise/reject it before\n // it is stored and later rendered as a link.\n const normalizedWebsite = safeUrl(form.website);\n if (form.website.trim() && !normalizedWebsite) {\n notify.error('Invalid website', 'Enter a valid http(s) URL, or leave it blank.');\n return;\n }\n\n const existingMetadata = existing?.metadata ?? {};\n const input = {\n eventId: form.eventId,\n name: form.name,\n boothCode: form.boothCode,\n category: form.category,\n status: form.status,\n contactName: form.contactName,\n contactEmail: form.contactEmail,\n metadata: { ...existingMetadata, website: normalizedWebsite ?? '' },\n };\n\n try {\n if (isEdit) {\n const response = await updateExhibitor({ variables: { id, input } });\n if (!mutationSucceeded(response, response.data?.updateExhibitor?.id, 'Could not update exhibitor')) {\n return;\n }\n navigate('/exhibitors');\n return;\n }\n\n const response = await createExhibitor({ variables: { input } });\n if (!mutationSucceeded(response, response.data?.createExhibitor?.id, 'Could not create exhibitor')) {\n return;\n }\n navigate('/exhibitors');\n } catch (error) {\n notify.error('Could not save exhibitor', describeError(error));\n }\n };\n\n const saving = creating || updating;\n const events = eventsData?.events?.items ?? [];\n\n return (\n <EntityFormShell\n title={isEdit ? 'Edit Exhibitor' : 'New Exhibitor'}\n description=\"Manage a booth exhibitor with live backend persistence.\"\n purpose=\"Register a company staffing a booth: link it to the event, give it a booth code and category, and record who to contact. Once saved, capture the leads their booth scans from the exhibitor list.\"\n backLabel=\"Back to Exhibitors\"\n onBack={() => navigate('/exhibitors')}\n onSubmit={onSubmit}\n submitLabel={isEdit ? 'Save Exhibitor' : 'Create Exhibitor'}\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\">Event</span>\n <Select value={form.eventId} onValueChange={(value) => onChange('eventId', value)}>\n <SelectTrigger aria-label=\"Event\">\n <SelectValue placeholder=\"Select event\" />\n </SelectTrigger>\n <SelectContent>\n {events.map((eventItem) => (\n <SelectItem key={eventItem.id} value={eventItem.id}>\n {eventItem.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\">Name</span>\n <Input value={form.name} onChange={(e) => onChange('name', e.target.value)} required />\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\">Booth Code</span>\n <Input value={form.boothCode} onChange={(e) => onChange('boothCode', e.target.value)} />\n </label>\n <label className=\"space-y-1 text-sm\">\n <span className=\"font-medium text-text-primary\">Category</span>\n <Input value={form.category} onChange={(e) => onChange('category', e.target.value)} />\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 {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\">Website</span>\n <Input\n value={form.website}\n onChange={(e) => onChange('website', e.target.value)}\n placeholder=\"https://example.com\"\n />\n </label>\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\">Contact Name</span>\n <Input value={form.contactName} onChange={(e) => onChange('contactName', e.target.value)} />\n </label>\n <label className=\"space-y-1 text-sm\">\n <span className=\"font-medium text-text-primary\">Contact Email</span>\n <Input type=\"email\" value={form.contactEmail} onChange={(e) => onChange('contactEmail', e.target.value)} />\n </label>\n </div>\n\n {isEdit ? <EntityTagsField entityType=\"exhibitor\" entityId={id} /> : null}\n </EntityFormShell>\n );\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"ExhibitorFormPage.js","names":[],"sources":["../../../../src/eventfullymanaged/pages/exhibitors/ExhibitorFormPage.tsx"],"sourcesContent":["/* eslint-disable react-hooks/set-state-in-effect */\nimport { Input, Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from '@burdenoff/fe-libs/ui';\nimport { useEffect, useMemo, useState } from 'react';\nimport { useParams } from 'react-router-dom';\n\nimport {\n useCreateExhibitorMutation,\n useEventsQuery,\n useExhibitorsQuery,\n useUpdateExhibitorMutation,\n} from '../../api/eventApi';\nimport { EntityFormShell } from '../../components/EntityFormShell';\nimport { safeUrl } from '../../components/SafeLink';\nimport { useEventfullyManaged } from '../../context/EventfullyManagedContext';\nimport { describeError, mutationSucceeded } from '../../lib/mutationResult';\nimport { notify } from '../../lib/toast';\n\ntype ExhibitorFormState = {\n eventId: string;\n name: string;\n boothCode: string;\n category: string;\n status: string;\n website: string;\n contactName: string;\n contactEmail: string;\n};\n\nconst initialState: ExhibitorFormState = {\n eventId: '',\n name: '',\n boothCode: '',\n category: '',\n status: 'CONFIRMED',\n website: '',\n contactName: '',\n contactEmail: '',\n};\n\nconst STATUS_OPTIONS = [\n { value: 'INVITED', label: 'Invited' },\n { value: 'CONFIRMED', label: 'Confirmed' },\n { value: 'ACTIVE', label: 'Active' },\n { value: 'CANCELLED', label: 'Cancelled' },\n];\n\nexport default function ExhibitorFormPage() {\n const { id = '' } = useParams<{ id: string }>();\n const isEdit = useMemo(() => Boolean(id), [id]);\n const { navigate } = useEventfullyManaged();\n\n const { data: eventsData } = useEventsQuery({ limit: 100, offset: 0 });\n // No by-id resolver exists for exhibitors, so the edit initial-state is derived\n // from the list query row.\n const { data: exhibitorsData } = useExhibitorsQuery({ limit: 100, offset: 0 });\n\n const existing = useMemo(\n () => (exhibitorsData?.exhibitors?.items ?? []).find((item) => item.id === id),\n [exhibitorsData, id]\n );\n\n const [createExhibitor, { loading: creating }] = useCreateExhibitorMutation();\n const [updateExhibitor, { loading: updating }] = useUpdateExhibitorMutation();\n const [form, setForm] = useState<ExhibitorFormState>(initialState);\n\n useEffect(() => {\n if (!isEdit || !existing) {\n return;\n }\n const metadata = existing.metadata ?? {};\n const website = typeof metadata.website === 'string' ? metadata.website : '';\n setForm({\n eventId: existing.eventId ?? '',\n name: existing.name ?? '',\n boothCode: existing.boothCode ?? '',\n category: existing.category ?? '',\n status: existing.status ?? 'CONFIRMED',\n website,\n contactName: existing.contactName ?? '',\n contactEmail: existing.contactEmail ?? '',\n });\n }, [existing, isEdit]);\n\n const onChange = (field: keyof ExhibitorFormState, 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.eventId) {\n notify.error('Event required', 'Choose the event this exhibitor belongs to.');\n return;\n }\n\n // The website is free text persisted verbatim, so normalise/reject it before\n // it is stored and later rendered as a link.\n const normalizedWebsite = safeUrl(form.website);\n if (form.website.trim() && !normalizedWebsite) {\n notify.error('Invalid website', 'Enter a valid http(s) URL, or leave it blank.');\n return;\n }\n\n const existingMetadata = existing?.metadata ?? {};\n const input = {\n eventId: form.eventId,\n name: form.name,\n boothCode: form.boothCode,\n category: form.category,\n status: form.status,\n contactName: form.contactName,\n contactEmail: form.contactEmail,\n metadata: { ...existingMetadata, website: normalizedWebsite ?? '' },\n };\n\n try {\n if (isEdit) {\n const response = await updateExhibitor({ variables: { id, input } });\n if (!mutationSucceeded(response, response.data?.updateExhibitor?.id, 'Could not update exhibitor')) {\n return;\n }\n navigate('/exhibitors');\n return;\n }\n\n const response = await createExhibitor({ variables: { input } });\n if (!mutationSucceeded(response, response.data?.createExhibitor?.id, 'Could not create exhibitor')) {\n return;\n }\n navigate('/exhibitors');\n } catch (error) {\n notify.error('Could not save exhibitor', describeError(error));\n }\n };\n\n const saving = creating || updating;\n const events = eventsData?.events?.items ?? [];\n\n return (\n <EntityFormShell\n title={isEdit ? 'Edit Exhibitor' : 'New Exhibitor'}\n description=\"Manage a booth exhibitor with live backend persistence.\"\n purpose=\"Register a company staffing a booth: link it to the event, give it a booth code and category, and record who to contact. Once saved, capture the leads their booth scans from the exhibitor list.\"\n backLabel=\"Back to Exhibitors\"\n onBack={() => navigate('/exhibitors')}\n onSubmit={onSubmit}\n submitLabel={isEdit ? 'Save Exhibitor' : 'Create Exhibitor'}\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\">Event</span>\n <Select value={form.eventId} onValueChange={(value) => onChange('eventId', value)}>\n <SelectTrigger aria-label=\"Event\">\n <SelectValue placeholder=\"Select event\" />\n </SelectTrigger>\n <SelectContent>\n {events.map((eventItem) => (\n <SelectItem key={eventItem.id} value={eventItem.id}>\n {eventItem.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\">Name</span>\n <Input value={form.name} onChange={(e) => onChange('name', e.target.value)} required />\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\">Booth Code</span>\n <Input value={form.boothCode} onChange={(e) => onChange('boothCode', e.target.value)} />\n </label>\n <label className=\"space-y-1 text-sm\">\n <span className=\"font-medium text-text-primary\">Category</span>\n <Input value={form.category} onChange={(e) => onChange('category', e.target.value)} />\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 {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\">Website</span>\n <Input\n value={form.website}\n onChange={(e) => onChange('website', e.target.value)}\n placeholder=\"https://example.com\"\n />\n </label>\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\">Contact Name</span>\n <Input value={form.contactName} onChange={(e) => onChange('contactName', e.target.value)} />\n </label>\n <label className=\"space-y-1 text-sm\">\n <span className=\"font-medium text-text-primary\">Contact Email</span>\n <Input type=\"email\" value={form.contactEmail} onChange={(e) => onChange('contactEmail', e.target.value)} />\n </label>\n </div>\n </EntityFormShell>\n );\n}\n"],"mappings":"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"}
@@ -101,7 +101,7 @@ function b() {
101
101
  children: "Label this sponsor so you can segment your portfolio — for example by industry, tier or renewal status."
102
102
  }),
103
103
  /* @__PURE__ */ s(r, {
104
- entityType: "sponsor",
104
+ entityType: "EVENTFULLYMANAGED_SPONSOR",
105
105
  entityId: S.id,
106
106
  label: "Sponsor tags",
107
107
  className: "mt-3"
@@ -1 +1 @@
1
- {"version":3,"file":"SponsorDetailsPage.js","names":[],"sources":["../../../../src/eventfullymanaged/pages/modules/SponsorDetailsPage.tsx"],"sourcesContent":["import { Megaphone, Pencil, TrendingUp } from 'lucide-react';\nimport { ContextConversationWidget } from '@burdenoff/fe-libs/shared';\nimport { useRegisterPageConversationContext } from '@burdenoff/fe-libs/shared/providers/shell';\nimport { EmphasisPanel, GlassCard, GradientText, NextSteps, PagePurpose } from '@burdenoff/fe-libs/ui';\nimport { useMemo } from 'react';\nimport { useParams } from 'react-router-dom';\n\nimport { useCommunicationCampaignsQuery, useSponsorsQuery } from '../../api/eventApi';\nimport { EntityTagsField } from '../../components/EntityTagsField';\nimport { useEventfullyManaged } from '../../context/EventfullyManagedContext';\nimport { EntityListSection } from './EntityListSection';\n\n/**\n * Sponsor ↔ organizer message thread, backed by wspace-conversations-svc and\n * scoped to this sponsor so the conversation follows the partnership.\n */\nfunction SponsorConversation({ sponsorId, sponsorName }: { sponsorId: string; sponsorName: string }) {\n // Stable reference: `useRegisterPageConversationContext` keys its effect on this\n // object's identity, so a fresh inline object every render would loop the shared\n // PageConversationContextProvider (see NetworkingPage for the full rationale).\n const conversationContext = useMemo(\n () => ({\n entityType: 'EVENTFULLYMANAGED_SPONSOR',\n entityId: sponsorId,\n contextTitle: `Sponsor · ${sponsorName}`,\n }),\n [sponsorId, sponsorName]\n );\n useRegisterPageConversationContext(conversationContext);\n return <ContextConversationWidget widgetKey=\"sponsor-message\" />;\n}\n\ntype SponsorRow = {\n id: string;\n name: string;\n level: string;\n website?: string | null;\n contactName?: string | null;\n contactEmail?: string | null;\n};\n\ntype CampaignRow = {\n id: string;\n name?: string;\n title?: string;\n status?: string;\n};\n\nexport default function SponsorDetailsPage() {\n const { id = '' } = useParams();\n const { navigate } = useEventfullyManaged();\n const sponsorsQuery = useSponsorsQuery({ limit: 100, offset: 0 });\n const campaignsQuery = useCommunicationCampaignsQuery({ limit: 100, offset: 0 });\n\n const sponsor = useMemo(\n () => ((sponsorsQuery.data?.sponsors.items ?? []) as SponsorRow[]).find((item) => item.id === id),\n [id, sponsorsQuery.data]\n );\n const campaigns = useMemo(\n () => (campaignsQuery.data?.communicationCampaigns.items ?? []) as CampaignRow[],\n [campaignsQuery.data]\n );\n\n return (\n <div className=\"space-y-6 p-6\">\n <PagePurpose>\n Everything about one sponsor in one place — their tier, contacts, and the campaigns they appear in. Use this to\n prep for a sponsor conversation, confirm their contact details, or check how their branded campaigns are\n performing.\n </PagePurpose>\n\n <EmphasisPanel>\n <h1 className=\"text-2xl font-bold text-text-primary\">\n <GradientText>Sponsor</GradientText> Details\n </h1>\n <p className=\"mt-1 text-sm text-text-secondary\">\n {sponsorsQuery.loading\n ? 'Loading sponsor details...'\n : sponsor\n ? `Live sponsor profile for ${sponsor.name}.`\n : 'Sponsor not found in current query scope.'}\n </p>\n {sponsor ? (\n <div className=\"mt-4 grid grid-cols-1 gap-3 sm:grid-cols-2 lg:grid-cols-4\">\n <GlassCard treatment=\"metric\" className=\"p-3\">\n <p className=\"text-xs uppercase tracking-wide text-text-secondary\">Level</p>\n <p className=\"mt-1 font-medium text-text-primary\">{sponsor.level}</p>\n </GlassCard>\n <GlassCard treatment=\"metric\" className=\"p-3\">\n <p className=\"text-xs uppercase tracking-wide text-text-secondary\">Contact</p>\n <p className=\"mt-1 font-medium text-text-primary\">{sponsor.contactName ?? 'Unassigned'}</p>\n </GlassCard>\n <GlassCard treatment=\"metric\" className=\"p-3\">\n <p className=\"text-xs uppercase tracking-wide text-text-secondary\">Email</p>\n <p className=\"mt-1 font-medium text-text-primary\">{sponsor.contactEmail ?? 'Unavailable'}</p>\n </GlassCard>\n <GlassCard treatment=\"metric\" className=\"p-3\">\n <p className=\"text-xs uppercase tracking-wide text-text-secondary\">Website</p>\n <p className=\"mt-1 truncate font-medium text-text-primary\">{sponsor.website ?? 'Unavailable'}</p>\n </GlassCard>\n </div>\n ) : null}\n </EmphasisPanel>\n\n {sponsor ? (\n <GlassCard treatment=\"plain\" className=\"border-border-seam p-5\">\n <h2 className=\"text-lg font-semibold text-text-primary\">Tags</h2>\n <p className=\"mt-1 text-sm text-text-secondary\">\n Label this sponsor so you can segment your portfolio — for example by industry, tier or renewal status.\n </p>\n <EntityTagsField entityType=\"sponsor\" entityId={sponsor.id} label=\"Sponsor tags\" className=\"mt-3\" />\n </GlassCard>\n ) : null}\n\n <EntityListSection\n heading={campaignsQuery.loading ? 'Loading campaigns...' : `Campaign Activity (${campaigns.length})`}\n emptyLabel=\"No communication campaigns available for sponsor tracking.\"\n rows={campaigns.map((campaign) => ({\n id: campaign.id,\n title: campaign.title ?? campaign.name ?? `Campaign ${campaign.id}`,\n status: campaign.status,\n }))}\n />\n\n {sponsor ? (\n <div className=\"space-y-3\">\n <div>\n <h2 className=\"text-lg font-semibold text-text-primary\">Sponsor messages</h2>\n <p className=\"mt-1 text-sm text-text-secondary\">\n Coordinate with this sponsor in a dedicated thread, backed by the conversations service.\n </p>\n </div>\n <SponsorConversation sponsorId={sponsor.id} sponsorName={sponsor.name} />\n </div>\n ) : null}\n\n <NextSteps\n storageKey=\"eventfullymanaged-sponsor-details\"\n title=\"Grow this partnership\"\n steps={[\n {\n id: 'edit-sponsor',\n label: 'Update sponsor details',\n description: 'Keep tier, contacts and contract value current.',\n icon: <Pencil className=\"h-4 w-4\" />,\n onClick: () => navigate(id ? `/sponsors/${id}/edit` : '/sponsors'),\n },\n {\n id: 'track-leads',\n label: 'Track sponsor leads',\n description: 'See the leads this sponsor is generating at the event.',\n icon: <TrendingUp className=\"h-4 w-4\" />,\n onClick: () => navigate(id ? `/sponsors/${id}/leads` : '/sponsors'),\n },\n {\n id: 'run-campaign',\n label: 'Run a co-branded campaign',\n description: 'Promote this sponsor through a communication campaign.',\n icon: <Megaphone className=\"h-4 w-4\" />,\n onClick: () => navigate('/communication'),\n },\n ]}\n />\n </div>\n );\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"SponsorDetailsPage.js","names":[],"sources":["../../../../src/eventfullymanaged/pages/modules/SponsorDetailsPage.tsx"],"sourcesContent":["import { Megaphone, Pencil, TrendingUp } from 'lucide-react';\nimport { ContextConversationWidget } from '@burdenoff/fe-libs/shared';\nimport { useRegisterPageConversationContext } from '@burdenoff/fe-libs/shared/providers/shell';\nimport { EmphasisPanel, GlassCard, GradientText, NextSteps, PagePurpose } from '@burdenoff/fe-libs/ui';\nimport { useMemo } from 'react';\nimport { useParams } from 'react-router-dom';\n\nimport { useCommunicationCampaignsQuery, useSponsorsQuery } from '../../api/eventApi';\nimport { EntityTagsField } from '../../components/EntityTagsField';\nimport { useEventfullyManaged } from '../../context/EventfullyManagedContext';\nimport { EntityListSection } from './EntityListSection';\n\n/**\n * Sponsor ↔ organizer message thread, backed by wspace-conversations-svc and\n * scoped to this sponsor so the conversation follows the partnership.\n */\nfunction SponsorConversation({ sponsorId, sponsorName }: { sponsorId: string; sponsorName: string }) {\n // Stable reference: `useRegisterPageConversationContext` keys its effect on this\n // object's identity, so a fresh inline object every render would loop the shared\n // PageConversationContextProvider (see NetworkingPage for the full rationale).\n const conversationContext = useMemo(\n () => ({\n entityType: 'EVENTFULLYMANAGED_SPONSOR',\n entityId: sponsorId,\n contextTitle: `Sponsor · ${sponsorName}`,\n }),\n [sponsorId, sponsorName]\n );\n useRegisterPageConversationContext(conversationContext);\n return <ContextConversationWidget widgetKey=\"sponsor-message\" />;\n}\n\ntype SponsorRow = {\n id: string;\n name: string;\n level: string;\n website?: string | null;\n contactName?: string | null;\n contactEmail?: string | null;\n};\n\ntype CampaignRow = {\n id: string;\n name?: string;\n title?: string;\n status?: string;\n};\n\nexport default function SponsorDetailsPage() {\n const { id = '' } = useParams();\n const { navigate } = useEventfullyManaged();\n const sponsorsQuery = useSponsorsQuery({ limit: 100, offset: 0 });\n const campaignsQuery = useCommunicationCampaignsQuery({ limit: 100, offset: 0 });\n\n const sponsor = useMemo(\n () => ((sponsorsQuery.data?.sponsors.items ?? []) as SponsorRow[]).find((item) => item.id === id),\n [id, sponsorsQuery.data]\n );\n const campaigns = useMemo(\n () => (campaignsQuery.data?.communicationCampaigns.items ?? []) as CampaignRow[],\n [campaignsQuery.data]\n );\n\n return (\n <div className=\"space-y-6 p-6\">\n <PagePurpose>\n Everything about one sponsor in one place — their tier, contacts, and the campaigns they appear in. Use this to\n prep for a sponsor conversation, confirm their contact details, or check how their branded campaigns are\n performing.\n </PagePurpose>\n\n <EmphasisPanel>\n <h1 className=\"text-2xl font-bold text-text-primary\">\n <GradientText>Sponsor</GradientText> Details\n </h1>\n <p className=\"mt-1 text-sm text-text-secondary\">\n {sponsorsQuery.loading\n ? 'Loading sponsor details...'\n : sponsor\n ? `Live sponsor profile for ${sponsor.name}.`\n : 'Sponsor not found in current query scope.'}\n </p>\n {sponsor ? (\n <div className=\"mt-4 grid grid-cols-1 gap-3 sm:grid-cols-2 lg:grid-cols-4\">\n <GlassCard treatment=\"metric\" className=\"p-3\">\n <p className=\"text-xs uppercase tracking-wide text-text-secondary\">Level</p>\n <p className=\"mt-1 font-medium text-text-primary\">{sponsor.level}</p>\n </GlassCard>\n <GlassCard treatment=\"metric\" className=\"p-3\">\n <p className=\"text-xs uppercase tracking-wide text-text-secondary\">Contact</p>\n <p className=\"mt-1 font-medium text-text-primary\">{sponsor.contactName ?? 'Unassigned'}</p>\n </GlassCard>\n <GlassCard treatment=\"metric\" className=\"p-3\">\n <p className=\"text-xs uppercase tracking-wide text-text-secondary\">Email</p>\n <p className=\"mt-1 font-medium text-text-primary\">{sponsor.contactEmail ?? 'Unavailable'}</p>\n </GlassCard>\n <GlassCard treatment=\"metric\" className=\"p-3\">\n <p className=\"text-xs uppercase tracking-wide text-text-secondary\">Website</p>\n <p className=\"mt-1 truncate font-medium text-text-primary\">{sponsor.website ?? 'Unavailable'}</p>\n </GlassCard>\n </div>\n ) : null}\n </EmphasisPanel>\n\n {sponsor ? (\n <GlassCard treatment=\"plain\" className=\"border-border-seam p-5\">\n <h2 className=\"text-lg font-semibold text-text-primary\">Tags</h2>\n <p className=\"mt-1 text-sm text-text-secondary\">\n Label this sponsor so you can segment your portfolio — for example by industry, tier or renewal status.\n </p>\n <EntityTagsField entityType=\"EVENTFULLYMANAGED_SPONSOR\" entityId={sponsor.id} label=\"Sponsor tags\" className=\"mt-3\" />\n </GlassCard>\n ) : null}\n\n <EntityListSection\n heading={campaignsQuery.loading ? 'Loading campaigns...' : `Campaign Activity (${campaigns.length})`}\n emptyLabel=\"No communication campaigns available for sponsor tracking.\"\n rows={campaigns.map((campaign) => ({\n id: campaign.id,\n title: campaign.title ?? campaign.name ?? `Campaign ${campaign.id}`,\n status: campaign.status,\n }))}\n />\n\n {sponsor ? (\n <div className=\"space-y-3\">\n <div>\n <h2 className=\"text-lg font-semibold text-text-primary\">Sponsor messages</h2>\n <p className=\"mt-1 text-sm text-text-secondary\">\n Coordinate with this sponsor in a dedicated thread, backed by the conversations service.\n </p>\n </div>\n <SponsorConversation sponsorId={sponsor.id} sponsorName={sponsor.name} />\n </div>\n ) : null}\n\n <NextSteps\n storageKey=\"eventfullymanaged-sponsor-details\"\n title=\"Grow this partnership\"\n steps={[\n {\n id: 'edit-sponsor',\n label: 'Update sponsor details',\n description: 'Keep tier, contacts and contract value current.',\n icon: <Pencil className=\"h-4 w-4\" />,\n onClick: () => navigate(id ? `/sponsors/${id}/edit` : '/sponsors'),\n },\n {\n id: 'track-leads',\n label: 'Track sponsor leads',\n description: 'See the leads this sponsor is generating at the event.',\n icon: <TrendingUp className=\"h-4 w-4\" />,\n onClick: () => navigate(id ? `/sponsors/${id}/leads` : '/sponsors'),\n },\n {\n id: 'run-campaign',\n label: 'Run a co-branded campaign',\n description: 'Promote this sponsor through a communication campaign.',\n icon: <Megaphone className=\"h-4 w-4\" />,\n onClick: () => navigate('/communication'),\n },\n ]}\n />\n </div>\n );\n}\n"],"mappings":"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"}
@@ -166,12 +166,9 @@ function w() {
166
166
  })]
167
167
  }),
168
168
  T ? /* @__PURE__ */ m(l, {
169
- entityType: "attendee",
169
+ entityType: "EVENTFULLYMANAGED_ATTENDEE",
170
170
  entityId: w
171
- }) : /* @__PURE__ */ m(l, {
172
- entityType: "attendee",
173
- entityId: ""
174
- })
171
+ }) : null
175
172
  ]
176
173
  });
177
174
  }
@@ -1 +1 @@
1
- {"version":3,"file":"AttendeeFormPage.js","names":[],"sources":["../../../../src/eventfullymanaged/pages/registration/AttendeeFormPage.tsx"],"sourcesContent":["/* eslint-disable react-hooks/set-state-in-effect */\nimport { useEffect, useMemo, useState } from 'react';\nimport { useParams } from 'react-router-dom';\nimport { Input, Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from '@burdenoff/fe-libs/ui';\n\nimport {\n useAttendeesQuery,\n useCreateAttendeeMutation,\n useEventsQuery,\n useUpdateAttendeeMutation,\n type AttendeeGql,\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';\n\ntype AttendeeFormState = {\n eventId: string;\n fullName: string;\n email: string;\n phone: string;\n company: string;\n role: string;\n};\n\nconst emptyState: AttendeeFormState = {\n eventId: '',\n fullName: '',\n email: '',\n phone: '',\n company: '',\n role: '',\n};\n\nfunction toFormState(attendee: AttendeeGql): AttendeeFormState {\n return {\n eventId: attendee.eventId ?? '',\n fullName: attendee.fullName ?? '',\n email: attendee.email ?? '',\n phone: attendee.phone ?? '',\n company: attendee.company ?? '',\n role: attendee.role ?? '',\n };\n}\n\nexport default function AttendeeFormPage() {\n const { id = '' } = useParams<{ id: string }>();\n const isEdit = useMemo(() => Boolean(id), [id]);\n const { navigate } = useEventfullyManaged();\n\n const { data: eventsData } = useEventsQuery({ limit: 100, offset: 0 });\n // No by-id attendee resolver exists — derive edit initial-state from the list.\n const { data: attendeesData } = useAttendeesQuery({ limit: 100, offset: 0 });\n const existing = useMemo(\n () => (attendeesData?.attendees.items ?? []).find((item) => item.id === id),\n [attendeesData, id]\n );\n\n const [createAttendee, { loading: creating }] = useCreateAttendeeMutation();\n const [updateAttendee, { loading: updating }] = useUpdateAttendeeMutation();\n const [form, setForm] = useState<AttendeeFormState>(() => (existing ? toFormState(existing) : emptyState));\n\n useEffect(() => {\n if (existing) {\n setForm(toFormState(existing));\n }\n }, [existing]);\n\n const onChange = (field: keyof AttendeeFormState, value: string) => {\n setForm((prev) => ({ ...prev, [field]: value }));\n };\n\n const events = eventsData?.events.items ?? [];\n const saving = creating || updating;\n\n const onSubmit = async (event: React.FormEvent<HTMLFormElement>) => {\n event.preventDefault();\n\n if (!form.eventId) {\n notify.error('Event required', 'Choose the event this attendee belongs to.');\n return;\n }\n if (!form.fullName.trim() || !form.email.trim()) {\n notify.error('Missing details', 'A full name and email are required.');\n return;\n }\n\n const input = {\n eventId: form.eventId,\n fullName: form.fullName.trim(),\n email: form.email.trim(),\n phone: form.phone.trim() || null,\n company: form.company.trim() || null,\n role: form.role.trim() || null,\n profile: {},\n metadata: {},\n };\n\n try {\n if (isEdit) {\n const response = await updateAttendee({ variables: { id, input } });\n if (!mutationSucceeded(response, response.data?.updateAttendee?.id, 'Could not update attendee')) {\n return;\n }\n notify.success('Attendee updated', form.fullName.trim());\n navigate('/registration');\n return;\n }\n\n const response = await createAttendee({ variables: { input } });\n if (!mutationSucceeded(response, response.data?.createAttendee?.id, 'Could not create attendee')) {\n return;\n }\n notify.success('Attendee added', form.fullName.trim());\n navigate('/registration');\n } catch (error) {\n notify.error('Could not save attendee', describeError(error));\n }\n };\n\n return (\n <EntityFormShell\n title={isEdit ? 'Edit Attendee' : 'Add Attendee'}\n description=\"Attendee records are the people behind your registrations.\"\n purpose=\"Capture the person attending your event — who they are, how to reach them, and the organization they represent. You can tag attendees to segment them later for networking, communications, and analytics.\"\n backLabel=\"Back to Registration\"\n onBack={() => navigate('/registration')}\n onSubmit={onSubmit}\n submitLabel={isEdit ? 'Save Attendee' : 'Add Attendee'}\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\">Event</span>\n <Select value={form.eventId} onValueChange={(value) => onChange('eventId', value)}>\n <SelectTrigger aria-label=\"Event\">\n <SelectValue placeholder=\"Select Event\" />\n </SelectTrigger>\n <SelectContent>\n {events.map((eventItem) => (\n <SelectItem key={eventItem.id} value={eventItem.id}>\n {eventItem.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\">Full Name</span>\n <Input value={form.fullName} onChange={(e) => onChange('fullName', e.target.value)} required />\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\">Email</span>\n <Input type=\"email\" value={form.email} onChange={(e) => onChange('email', e.target.value)} required />\n </label>\n <label className=\"space-y-1 text-sm\">\n <span className=\"font-medium text-text-primary\">Phone</span>\n <Input value={form.phone} onChange={(e) => onChange('phone', 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\">Company</span>\n <Input value={form.company} onChange={(e) => onChange('company', e.target.value)} />\n </label>\n <label className=\"space-y-1 text-sm\">\n <span className=\"font-medium text-text-primary\">Role</span>\n <Input value={form.role} onChange={(e) => onChange('role', e.target.value)} />\n </label>\n </div>\n\n {isEdit ? (\n <EntityTagsField entityType=\"attendee\" entityId={id} />\n ) : (\n <EntityTagsField entityType=\"attendee\" entityId=\"\" />\n )}\n </EntityFormShell>\n );\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"AttendeeFormPage.js","names":[],"sources":["../../../../src/eventfullymanaged/pages/registration/AttendeeFormPage.tsx"],"sourcesContent":["/* eslint-disable react-hooks/set-state-in-effect */\nimport { useEffect, useMemo, useState } from 'react';\nimport { useParams } from 'react-router-dom';\nimport { Input, Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from '@burdenoff/fe-libs/ui';\n\nimport {\n useAttendeesQuery,\n useCreateAttendeeMutation,\n useEventsQuery,\n useUpdateAttendeeMutation,\n type AttendeeGql,\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';\n\ntype AttendeeFormState = {\n eventId: string;\n fullName: string;\n email: string;\n phone: string;\n company: string;\n role: string;\n};\n\nconst emptyState: AttendeeFormState = {\n eventId: '',\n fullName: '',\n email: '',\n phone: '',\n company: '',\n role: '',\n};\n\nfunction toFormState(attendee: AttendeeGql): AttendeeFormState {\n return {\n eventId: attendee.eventId ?? '',\n fullName: attendee.fullName ?? '',\n email: attendee.email ?? '',\n phone: attendee.phone ?? '',\n company: attendee.company ?? '',\n role: attendee.role ?? '',\n };\n}\n\nexport default function AttendeeFormPage() {\n const { id = '' } = useParams<{ id: string }>();\n const isEdit = useMemo(() => Boolean(id), [id]);\n const { navigate } = useEventfullyManaged();\n\n const { data: eventsData } = useEventsQuery({ limit: 100, offset: 0 });\n // No by-id attendee resolver exists — derive edit initial-state from the list.\n const { data: attendeesData } = useAttendeesQuery({ limit: 100, offset: 0 });\n const existing = useMemo(\n () => (attendeesData?.attendees.items ?? []).find((item) => item.id === id),\n [attendeesData, id]\n );\n\n const [createAttendee, { loading: creating }] = useCreateAttendeeMutation();\n const [updateAttendee, { loading: updating }] = useUpdateAttendeeMutation();\n const [form, setForm] = useState<AttendeeFormState>(() => (existing ? toFormState(existing) : emptyState));\n\n useEffect(() => {\n if (existing) {\n setForm(toFormState(existing));\n }\n }, [existing]);\n\n const onChange = (field: keyof AttendeeFormState, value: string) => {\n setForm((prev) => ({ ...prev, [field]: value }));\n };\n\n const events = eventsData?.events.items ?? [];\n const saving = creating || updating;\n\n const onSubmit = async (event: React.FormEvent<HTMLFormElement>) => {\n event.preventDefault();\n\n if (!form.eventId) {\n notify.error('Event required', 'Choose the event this attendee belongs to.');\n return;\n }\n if (!form.fullName.trim() || !form.email.trim()) {\n notify.error('Missing details', 'A full name and email are required.');\n return;\n }\n\n const input = {\n eventId: form.eventId,\n fullName: form.fullName.trim(),\n email: form.email.trim(),\n phone: form.phone.trim() || null,\n company: form.company.trim() || null,\n role: form.role.trim() || null,\n profile: {},\n metadata: {},\n };\n\n try {\n if (isEdit) {\n const response = await updateAttendee({ variables: { id, input } });\n if (!mutationSucceeded(response, response.data?.updateAttendee?.id, 'Could not update attendee')) {\n return;\n }\n notify.success('Attendee updated', form.fullName.trim());\n navigate('/registration');\n return;\n }\n\n const response = await createAttendee({ variables: { input } });\n if (!mutationSucceeded(response, response.data?.createAttendee?.id, 'Could not create attendee')) {\n return;\n }\n notify.success('Attendee added', form.fullName.trim());\n navigate('/registration');\n } catch (error) {\n notify.error('Could not save attendee', describeError(error));\n }\n };\n\n return (\n <EntityFormShell\n title={isEdit ? 'Edit Attendee' : 'Add Attendee'}\n description=\"Attendee records are the people behind your registrations.\"\n purpose=\"Capture the person attending your event — who they are, how to reach them, and the organization they represent. You can tag attendees to segment them later for networking, communications, and analytics.\"\n backLabel=\"Back to Registration\"\n onBack={() => navigate('/registration')}\n onSubmit={onSubmit}\n submitLabel={isEdit ? 'Save Attendee' : 'Add Attendee'}\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\">Event</span>\n <Select value={form.eventId} onValueChange={(value) => onChange('eventId', value)}>\n <SelectTrigger aria-label=\"Event\">\n <SelectValue placeholder=\"Select Event\" />\n </SelectTrigger>\n <SelectContent>\n {events.map((eventItem) => (\n <SelectItem key={eventItem.id} value={eventItem.id}>\n {eventItem.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\">Full Name</span>\n <Input value={form.fullName} onChange={(e) => onChange('fullName', e.target.value)} required />\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\">Email</span>\n <Input type=\"email\" value={form.email} onChange={(e) => onChange('email', e.target.value)} required />\n </label>\n <label className=\"space-y-1 text-sm\">\n <span className=\"font-medium text-text-primary\">Phone</span>\n <Input value={form.phone} onChange={(e) => onChange('phone', 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\">Company</span>\n <Input value={form.company} onChange={(e) => onChange('company', e.target.value)} />\n </label>\n <label className=\"space-y-1 text-sm\">\n <span className=\"font-medium text-text-primary\">Role</span>\n <Input value={form.role} onChange={(e) => onChange('role', e.target.value)} />\n </label>\n </div>\n\n {isEdit ? <EntityTagsField entityType=\"EVENTFULLYMANAGED_ATTENDEE\" entityId={id} /> : null}\n </EntityFormShell>\n );\n}\n"],"mappings":"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"}
@@ -212,7 +212,7 @@ function C() {
212
212
  })]
213
213
  }),
214
214
  C ? /* @__PURE__ */ g(l, {
215
- entityType: "speaker",
215
+ entityType: "EVENTFULLYMANAGED_SPEAKER",
216
216
  entityId: t
217
217
  }) : null
218
218
  ]
@@ -1 +1 @@
1
- {"version":3,"file":"SpeakerFormPage.js","names":[],"sources":["../../../../src/eventfullymanaged/pages/speakers/SpeakerFormPage.tsx"],"sourcesContent":["/* eslint-disable react-hooks/set-state-in-effect */\nimport { gql } from '@apollo/client';\nimport { useQuery } from '@apollo/client/react';\nimport { Input, Textarea } from '@burdenoff/fe-libs/ui';\nimport { useEffect, useMemo, useState } from 'react';\nimport { useParams } from 'react-router-dom';\n\nimport { useCreateSpeakerMutation, useUpdateSpeakerMutation } from '../../api/eventApi';\nimport { EntityFormShell } from '../../components/EntityFormShell';\nimport { EntityTagsField } from '../../components/EntityTagsField';\nimport { ImageUploadField } from '../../components/ImageUploadField';\nimport { safeUrl } from '../../components/SafeLink';\nimport { useEventfullyManaged } from '../../context/EventfullyManagedContext';\nimport { describeError, mutationSucceeded } from '../../lib/mutationResult';\nimport { notify } from '../../lib/toast';\n\n// The backend `updateSpeaker` resolver is a FULL replace and there is no by-id\n// speaker resolver, so this fuller list query — selecting every editable field,\n// not just the columns the foundation SPEAKERS_QUERY lists — is the edit-hydration\n// source. The form finds its row by id client-side. Without the full selection an\n// edit would blank bio / avatar / phone / socialLinks.\nconst FULL_SPEAKERS_QUERY = gql`\n query EventfullymanagedSpeakerFormSpeakers($limit: Int, $offset: Int) {\n speakers: eventfullyManagedSpeakers(limit: $limit, offset: $offset) {\n total\n items {\n id\n fullName\n title\n organization\n bio\n avatarUrl\n email\n phone\n socialLinks\n }\n }\n }\n`;\n\ntype SpeakerRecord = {\n id: string;\n fullName: string;\n title?: string | null;\n organization?: string | null;\n bio?: string | null;\n avatarUrl?: string | null;\n email?: string | null;\n phone?: string | null;\n socialLinks?: Record<string, unknown> | null;\n};\n\ntype FullSpeakersResponse = { speakers: { total: number; items: SpeakerRecord[] } };\n\ntype SpeakerFormState = {\n fullName: string;\n title: string;\n organization: string;\n email: string;\n phone: string;\n bio: string;\n avatarUrl: string;\n website: string;\n};\n\nconst initialState: SpeakerFormState = {\n fullName: '',\n title: '',\n organization: '',\n email: '',\n phone: '',\n bio: '',\n avatarUrl: '',\n website: '',\n};\n\nfunction readWebsite(socialLinks: Record<string, unknown> | null | undefined): string {\n if (socialLinks && typeof socialLinks.website === 'string') {\n return socialLinks.website;\n }\n return '';\n}\n\nexport default function SpeakerFormPage() {\n const { id = '' } = useParams<{ id: string }>();\n const isEdit = useMemo(() => Boolean(id), [id]);\n const { navigate } = useEventfullyManaged();\n\n const { data } = useQuery<FullSpeakersResponse, { limit?: number; offset?: number }>(FULL_SPEAKERS_QUERY, {\n variables: { limit: 100, offset: 0 },\n skip: !id,\n });\n const [createSpeaker, { loading: creating }] = useCreateSpeakerMutation();\n const [updateSpeaker, { loading: updating }] = useUpdateSpeakerMutation();\n const [form, setForm] = useState<SpeakerFormState>(initialState);\n\n const existingSpeaker = useMemo(() => (data?.speakers?.items ?? []).find((item) => item.id === id), [data, id]);\n\n useEffect(() => {\n if (!isEdit || !existingSpeaker) {\n return;\n }\n setForm({\n fullName: existingSpeaker.fullName ?? '',\n title: existingSpeaker.title ?? '',\n organization: existingSpeaker.organization ?? '',\n email: existingSpeaker.email ?? '',\n phone: existingSpeaker.phone ?? '',\n bio: existingSpeaker.bio ?? '',\n avatarUrl: existingSpeaker.avatarUrl ?? '',\n website: readWebsite(existingSpeaker.socialLinks),\n });\n }, [existingSpeaker, isEdit]);\n\n const onChange = (field: keyof SpeakerFormState, 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.fullName.trim()) {\n notify.error('Name required', 'Give the speaker a name before saving.');\n return;\n }\n\n const normalizedWebsite = safeUrl(form.website);\n if (form.website.trim() && !normalizedWebsite) {\n notify.error('Invalid website', 'Enter a valid http(s) URL, or leave it blank.');\n return;\n }\n\n const input = {\n fullName: form.fullName.trim(),\n title: form.title || null,\n organization: form.organization || null,\n email: form.email || null,\n phone: form.phone || null,\n bio: form.bio || null,\n avatarUrl: form.avatarUrl || null,\n socialLinks: normalizedWebsite ? { website: normalizedWebsite } : {},\n };\n\n try {\n if (isEdit) {\n const response = await updateSpeaker({ variables: { id, input } });\n if (!mutationSucceeded(response, response.data?.updateSpeaker?.id, 'Could not update speaker')) {\n return;\n }\n notify.success('Speaker updated');\n navigate('/speakers');\n return;\n }\n const response = await createSpeaker({ variables: { input } });\n if (!mutationSucceeded(response, response.data?.createSpeaker?.id, 'Could not create speaker')) {\n return;\n }\n notify.success('Speaker created');\n navigate('/speakers');\n } catch (error) {\n notify.error('Could not save speaker', describeError(error));\n }\n };\n\n const saving = creating || updating;\n\n return (\n <EntityFormShell\n title={isEdit ? 'Edit Speaker' : 'Add Speaker'}\n description=\"A person who presents sessions at your events.\"\n purpose=\"A speaker profile is what you attach to sessions and show to attendees. Capture who they are, where they work, and a short bio or talk abstract so the agenda tells a compelling story. The headshot appears anywhere the speaker is listed.\"\n backLabel=\"Back to Speakers\"\n onBack={() => navigate('/speakers')}\n onSubmit={onSubmit}\n submitLabel=\"Save Speaker\"\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\">Full name</span>\n <Input value={form.fullName} onChange={(e) => onChange('fullName', e.target.value)} required />\n </label>\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)} placeholder=\"Head of Product\" />\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\">Company</span>\n <Input value={form.organization} onChange={(e) => onChange('organization', e.target.value)} />\n </label>\n <label className=\"space-y-1 text-sm\">\n <span className=\"font-medium text-text-primary\">Email</span>\n <Input type=\"email\" value={form.email} onChange={(e) => onChange('email', e.target.value)} />\n </label>\n <label className=\"space-y-1 text-sm\">\n <span className=\"font-medium text-text-primary\">Phone</span>\n <Input value={form.phone} onChange={(e) => onChange('phone', e.target.value)} />\n </label>\n </div>\n\n <div className=\"space-y-1 text-sm\">\n <span className=\"font-medium text-text-primary\">Headshot</span>\n <ImageUploadField\n label=\"Upload headshot\"\n folderPath=\"/EventfullyManaged/Speakers\"\n value={form.avatarUrl || null}\n onUploaded={(url) => onChange('avatarUrl', url)}\n hint=\"Stored securely in Files. Shown wherever the speaker is listed.\"\n />\n </div>\n\n <label className=\"block space-y-1 text-sm\">\n <span className=\"font-medium text-text-primary\">Bio &amp; talk abstract</span>\n <Textarea\n value={form.bio}\n onChange={(e) => onChange('bio', e.target.value)}\n rows={5}\n placeholder=\"Short professional bio and a summary of the talk they will give.\"\n />\n </label>\n\n <label className=\"block space-y-1 text-sm\">\n <span className=\"font-medium text-text-primary\">Website / profile URL</span>\n <Input value={form.website} onChange={(e) => onChange('website', e.target.value)} placeholder=\"https://…\" />\n </label>\n\n {isEdit ? <EntityTagsField entityType=\"speaker\" entityId={id} /> : null}\n </EntityFormShell>\n );\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"SpeakerFormPage.js","names":[],"sources":["../../../../src/eventfullymanaged/pages/speakers/SpeakerFormPage.tsx"],"sourcesContent":["/* eslint-disable react-hooks/set-state-in-effect */\nimport { gql } from '@apollo/client';\nimport { useQuery } from '@apollo/client/react';\nimport { Input, Textarea } from '@burdenoff/fe-libs/ui';\nimport { useEffect, useMemo, useState } from 'react';\nimport { useParams } from 'react-router-dom';\n\nimport { useCreateSpeakerMutation, useUpdateSpeakerMutation } from '../../api/eventApi';\nimport { EntityFormShell } from '../../components/EntityFormShell';\nimport { EntityTagsField } from '../../components/EntityTagsField';\nimport { ImageUploadField } from '../../components/ImageUploadField';\nimport { safeUrl } from '../../components/SafeLink';\nimport { useEventfullyManaged } from '../../context/EventfullyManagedContext';\nimport { describeError, mutationSucceeded } from '../../lib/mutationResult';\nimport { notify } from '../../lib/toast';\n\n// The backend `updateSpeaker` resolver is a FULL replace and there is no by-id\n// speaker resolver, so this fuller list query — selecting every editable field,\n// not just the columns the foundation SPEAKERS_QUERY lists — is the edit-hydration\n// source. The form finds its row by id client-side. Without the full selection an\n// edit would blank bio / avatar / phone / socialLinks.\nconst FULL_SPEAKERS_QUERY = gql`\n query EventfullymanagedSpeakerFormSpeakers($limit: Int, $offset: Int) {\n speakers: eventfullyManagedSpeakers(limit: $limit, offset: $offset) {\n total\n items {\n id\n fullName\n title\n organization\n bio\n avatarUrl\n email\n phone\n socialLinks\n }\n }\n }\n`;\n\ntype SpeakerRecord = {\n id: string;\n fullName: string;\n title?: string | null;\n organization?: string | null;\n bio?: string | null;\n avatarUrl?: string | null;\n email?: string | null;\n phone?: string | null;\n socialLinks?: Record<string, unknown> | null;\n};\n\ntype FullSpeakersResponse = { speakers: { total: number; items: SpeakerRecord[] } };\n\ntype SpeakerFormState = {\n fullName: string;\n title: string;\n organization: string;\n email: string;\n phone: string;\n bio: string;\n avatarUrl: string;\n website: string;\n};\n\nconst initialState: SpeakerFormState = {\n fullName: '',\n title: '',\n organization: '',\n email: '',\n phone: '',\n bio: '',\n avatarUrl: '',\n website: '',\n};\n\nfunction readWebsite(socialLinks: Record<string, unknown> | null | undefined): string {\n if (socialLinks && typeof socialLinks.website === 'string') {\n return socialLinks.website;\n }\n return '';\n}\n\nexport default function SpeakerFormPage() {\n const { id = '' } = useParams<{ id: string }>();\n const isEdit = useMemo(() => Boolean(id), [id]);\n const { navigate } = useEventfullyManaged();\n\n const { data } = useQuery<FullSpeakersResponse, { limit?: number; offset?: number }>(FULL_SPEAKERS_QUERY, {\n variables: { limit: 100, offset: 0 },\n skip: !id,\n });\n const [createSpeaker, { loading: creating }] = useCreateSpeakerMutation();\n const [updateSpeaker, { loading: updating }] = useUpdateSpeakerMutation();\n const [form, setForm] = useState<SpeakerFormState>(initialState);\n\n const existingSpeaker = useMemo(() => (data?.speakers?.items ?? []).find((item) => item.id === id), [data, id]);\n\n useEffect(() => {\n if (!isEdit || !existingSpeaker) {\n return;\n }\n setForm({\n fullName: existingSpeaker.fullName ?? '',\n title: existingSpeaker.title ?? '',\n organization: existingSpeaker.organization ?? '',\n email: existingSpeaker.email ?? '',\n phone: existingSpeaker.phone ?? '',\n bio: existingSpeaker.bio ?? '',\n avatarUrl: existingSpeaker.avatarUrl ?? '',\n website: readWebsite(existingSpeaker.socialLinks),\n });\n }, [existingSpeaker, isEdit]);\n\n const onChange = (field: keyof SpeakerFormState, 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.fullName.trim()) {\n notify.error('Name required', 'Give the speaker a name before saving.');\n return;\n }\n\n const normalizedWebsite = safeUrl(form.website);\n if (form.website.trim() && !normalizedWebsite) {\n notify.error('Invalid website', 'Enter a valid http(s) URL, or leave it blank.');\n return;\n }\n\n const input = {\n fullName: form.fullName.trim(),\n title: form.title || null,\n organization: form.organization || null,\n email: form.email || null,\n phone: form.phone || null,\n bio: form.bio || null,\n avatarUrl: form.avatarUrl || null,\n socialLinks: normalizedWebsite ? { website: normalizedWebsite } : {},\n };\n\n try {\n if (isEdit) {\n const response = await updateSpeaker({ variables: { id, input } });\n if (!mutationSucceeded(response, response.data?.updateSpeaker?.id, 'Could not update speaker')) {\n return;\n }\n notify.success('Speaker updated');\n navigate('/speakers');\n return;\n }\n const response = await createSpeaker({ variables: { input } });\n if (!mutationSucceeded(response, response.data?.createSpeaker?.id, 'Could not create speaker')) {\n return;\n }\n notify.success('Speaker created');\n navigate('/speakers');\n } catch (error) {\n notify.error('Could not save speaker', describeError(error));\n }\n };\n\n const saving = creating || updating;\n\n return (\n <EntityFormShell\n title={isEdit ? 'Edit Speaker' : 'Add Speaker'}\n description=\"A person who presents sessions at your events.\"\n purpose=\"A speaker profile is what you attach to sessions and show to attendees. Capture who they are, where they work, and a short bio or talk abstract so the agenda tells a compelling story. The headshot appears anywhere the speaker is listed.\"\n backLabel=\"Back to Speakers\"\n onBack={() => navigate('/speakers')}\n onSubmit={onSubmit}\n submitLabel=\"Save Speaker\"\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\">Full name</span>\n <Input value={form.fullName} onChange={(e) => onChange('fullName', e.target.value)} required />\n </label>\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)} placeholder=\"Head of Product\" />\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\">Company</span>\n <Input value={form.organization} onChange={(e) => onChange('organization', e.target.value)} />\n </label>\n <label className=\"space-y-1 text-sm\">\n <span className=\"font-medium text-text-primary\">Email</span>\n <Input type=\"email\" value={form.email} onChange={(e) => onChange('email', e.target.value)} />\n </label>\n <label className=\"space-y-1 text-sm\">\n <span className=\"font-medium text-text-primary\">Phone</span>\n <Input value={form.phone} onChange={(e) => onChange('phone', e.target.value)} />\n </label>\n </div>\n\n <div className=\"space-y-1 text-sm\">\n <span className=\"font-medium text-text-primary\">Headshot</span>\n <ImageUploadField\n label=\"Upload headshot\"\n folderPath=\"/EventfullyManaged/Speakers\"\n value={form.avatarUrl || null}\n onUploaded={(url) => onChange('avatarUrl', url)}\n hint=\"Stored securely in Files. Shown wherever the speaker is listed.\"\n />\n </div>\n\n <label className=\"block space-y-1 text-sm\">\n <span className=\"font-medium text-text-primary\">Bio &amp; talk abstract</span>\n <Textarea\n value={form.bio}\n onChange={(e) => onChange('bio', e.target.value)}\n rows={5}\n placeholder=\"Short professional bio and a summary of the talk they will give.\"\n />\n </label>\n\n <label className=\"block space-y-1 text-sm\">\n <span className=\"font-medium text-text-primary\">Website / profile URL</span>\n <Input value={form.website} onChange={(e) => onChange('website', e.target.value)} placeholder=\"https://…\" />\n </label>\n\n {isEdit ? <EntityTagsField entityType=\"EVENTFULLYMANAGED_SPEAKER\" entityId={id} /> : null}\n </EntityFormShell>\n );\n}\n"],"mappings":"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"}
@@ -113,7 +113,7 @@ function y() {
113
113
  children: "Label this venue so you can group and filter it — for example by region, tier or amenity profile."
114
114
  }),
115
115
  /* @__PURE__ */ i(n, {
116
- entityType: "venue",
116
+ entityType: "EVENTFULLYMANAGED_VENUE",
117
117
  entityId: C.id,
118
118
  label: "Venue tags",
119
119
  className: "mt-3"
@@ -1 +1 @@
1
- {"version":3,"file":"VenueDetailPage.js","names":[],"sources":["../../../../src/eventfullymanaged/pages/venues/VenueDetailPage.tsx"],"sourcesContent":["import { ArrowLeft, Building2, Edit, MapPin, Megaphone, SquareStack, Users } from 'lucide-react';\nimport { Badge, Button, GlassCard, IllustratedEmptyState, NextSteps, PagePurpose } from '@burdenoff/fe-libs/ui';\nimport { useParams } from 'react-router-dom';\n\nimport { useVenueQuery } from '../../api/eventApi';\nimport { EntityTagsField } from '../../components/EntityTagsField';\nimport { useEventfullyManaged } from '../../context/EventfullyManagedContext';\n\ntype VenueRecord = {\n id: string;\n name: string;\n description?: string | null;\n addressLine1?: string | null;\n city?: string | null;\n state?: string | null;\n country?: string | null;\n postalCode?: string | null;\n capacity?: number | null;\n status: string;\n amenities: string[];\n pricing?: Record<string, unknown> | null;\n openingHours?: Record<string, unknown> | null;\n metadata?: Record<string, unknown> | null;\n};\n\ntype FloorPlanRecord = {\n id: string;\n name: string;\n imageUrl?: string | null;\n dimensions?: Record<string, unknown> | null;\n};\n\nexport default function VenueDetailPage() {\n const { id = '' } = useParams<{ id: string }>();\n const { navigate } = useEventfullyManaged();\n const { data, loading } = useVenueQuery(id);\n\n const venue = (data?.venue ?? null) as VenueRecord | null;\n const floorPlans = (data?.floorPlans?.items ?? []) as FloorPlanRecord[];\n\n if (loading) {\n return <div className=\"p-6 text-sm text-text-secondary\">Loading venue...</div>;\n }\n\n if (!venue) {\n return (\n <div className=\"space-y-4 p-6\">\n <Button type=\"button\" variant=\"ghost\" size=\"sm\" onClick={() => navigate('/venues')}>\n <ArrowLeft className=\"h-4 w-4\" />\n Back to Venues\n </Button>\n <GlassCard treatment=\"plain\" className=\"border-border-seam\">\n <IllustratedEmptyState\n illustration=\"empty-search\"\n title=\"Venue not found.\"\n description=\"This venue 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 <PagePurpose>\n The full record for one venue. Review its location, capacity and status, see what amenities it offers, and\n manage the floor plans attendees will navigate. Come here to confirm a space is ready before you assign an event\n to it.\n </PagePurpose>\n\n <div className=\"flex flex-col gap-4 sm:flex-row sm:items-center sm:justify-between\">\n <div className=\"space-y-3\">\n <Button type=\"button\" variant=\"ghost\" size=\"sm\" onClick={() => navigate('/venues')}>\n <ArrowLeft className=\"h-4 w-4\" />\n Back to Venues\n </Button>\n <div>\n <h1 className=\"text-2xl font-bold text-text-primary\">{venue.name}</h1>\n <p className=\"mt-1 text-sm text-text-secondary\">{venue.description ?? 'No description provided.'}</p>\n </div>\n </div>\n <Button type=\"button\" onClick={() => navigate(`/venues/${venue.id}/edit`)}>\n <Edit className=\"h-4 w-4\" />\n Edit Venue\n </Button>\n </div>\n\n <div className=\"grid grid-cols-1 gap-4 lg:grid-cols-3\">\n <GlassCard treatment=\"metric\" className=\"p-4\">\n <div className=\"mb-2 flex items-center gap-2 text-sm font-medium text-text-primary\">\n <MapPin className=\"h-4 w-4 text-action-primary-bg\" />\n Location\n </div>\n <p className=\"text-sm text-text-secondary\">\n {[venue.addressLine1, venue.city, venue.state, venue.country, venue.postalCode]\n .filter(Boolean)\n .join(', ') || 'Location not configured'}\n </p>\n </GlassCard>\n <GlassCard treatment=\"glass\" glow className=\"p-4\">\n <div className=\"mb-2 flex items-center gap-2 text-sm font-medium text-text-primary\">\n <Users className=\"h-4 w-4 text-action-primary-bg\" />\n Capacity\n </div>\n <p className=\"text-sm tabular-nums text-text-secondary\">{venue.capacity ?? 0} attendees</p>\n </GlassCard>\n <GlassCard treatment=\"metric\" className=\"p-4\">\n <div className=\"mb-2 flex items-center gap-2 text-sm font-medium text-text-primary\">\n <Building2 className=\"h-4 w-4 text-action-primary-bg\" />\n Status\n </div>\n <p className=\"text-sm uppercase text-text-secondary\">{venue.status}</p>\n </GlassCard>\n </div>\n\n <GlassCard treatment=\"plain\" className=\"border-border-seam p-5\">\n <h2 className=\"text-lg font-semibold text-text-primary\">Amenities</h2>\n <div className=\"mt-3 flex flex-wrap gap-2\">\n {venue.amenities.length > 0 ? (\n venue.amenities.map((amenity) => (\n <Badge key={amenity} variant=\"secondary\">\n {amenity}\n </Badge>\n ))\n ) : (\n <p className=\"text-sm text-text-secondary\">No amenities configured.</p>\n )}\n </div>\n </GlassCard>\n\n <GlassCard treatment=\"plain\" className=\"border-border-seam p-5\">\n <h2 className=\"text-lg font-semibold text-text-primary\">Tags</h2>\n <p className=\"mt-1 text-sm text-text-secondary\">\n Label this venue so you can group and filter it — for example by region, tier or amenity profile.\n </p>\n <EntityTagsField entityType=\"venue\" entityId={venue.id} label=\"Venue tags\" className=\"mt-3\" />\n </GlassCard>\n\n <GlassCard treatment=\"plain\" className=\"border-border-seam p-5\">\n <div className=\"flex items-center justify-between gap-3\">\n <h2 className=\"text-lg font-semibold text-text-primary\">Floor Plans</h2>\n <Button type=\"button\" variant=\"outline\" size=\"sm\" onClick={() => navigate(`/venues/${venue.id}/floor-plans`)}>\n <SquareStack className=\"h-4 w-4\" />\n Manage Floor Plans\n </Button>\n </div>\n <div className=\"mt-4\">\n {floorPlans.length > 0 ? (\n <ul className=\"space-y-2.5\">\n {floorPlans.map((plan) => (\n <li\n key={plan.id}\n className=\"rounded-card border border-border-seam p-3 transition-[box-shadow,border-color] duration-200 hover:border-border-strong hover:shadow-elevation-1\"\n >\n <p className=\"font-medium text-text-primary\">{plan.name}</p>\n <p className=\"text-sm text-text-secondary\">\n {plan.imageUrl ? 'Image configured' : 'No image configured'}\n </p>\n </li>\n ))}\n </ul>\n ) : (\n <IllustratedEmptyState\n illustration=\"empty-files\"\n title=\"No floor plans have been added yet.\"\n className=\"py-8\"\n />\n )}\n </div>\n </GlassCard>\n\n <NextSteps\n storageKey=\"eventfullymanaged-venue-detail\"\n title=\"Get this venue ready to host\"\n steps={[\n {\n id: 'edit-venue',\n label: 'Edit venue details',\n description: 'Update capacity, address, status and amenities.',\n icon: <Edit className=\"h-4 w-4\" />,\n onClick: () => navigate(`/venues/${venue.id}/edit`),\n },\n {\n id: 'floor-plans',\n label: 'Add floor plans',\n description: 'Map the layout so attendees and staff can navigate.',\n icon: <SquareStack className=\"h-4 w-4\" />,\n onClick: () => navigate(`/venues/${venue.id}/floor-plans`),\n },\n {\n id: 'resources',\n label: 'Review resource readiness',\n description: 'Check capacity, amenities and plans at a glance.',\n icon: <Megaphone className=\"h-4 w-4\" />,\n onClick: () => navigate(`/venues/${venue.id}/resources`),\n },\n ]}\n />\n </div>\n );\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"VenueDetailPage.js","names":[],"sources":["../../../../src/eventfullymanaged/pages/venues/VenueDetailPage.tsx"],"sourcesContent":["import { ArrowLeft, Building2, Edit, MapPin, Megaphone, SquareStack, Users } from 'lucide-react';\nimport { Badge, Button, GlassCard, IllustratedEmptyState, NextSteps, PagePurpose } from '@burdenoff/fe-libs/ui';\nimport { useParams } from 'react-router-dom';\n\nimport { useVenueQuery } from '../../api/eventApi';\nimport { EntityTagsField } from '../../components/EntityTagsField';\nimport { useEventfullyManaged } from '../../context/EventfullyManagedContext';\n\ntype VenueRecord = {\n id: string;\n name: string;\n description?: string | null;\n addressLine1?: string | null;\n city?: string | null;\n state?: string | null;\n country?: string | null;\n postalCode?: string | null;\n capacity?: number | null;\n status: string;\n amenities: string[];\n pricing?: Record<string, unknown> | null;\n openingHours?: Record<string, unknown> | null;\n metadata?: Record<string, unknown> | null;\n};\n\ntype FloorPlanRecord = {\n id: string;\n name: string;\n imageUrl?: string | null;\n dimensions?: Record<string, unknown> | null;\n};\n\nexport default function VenueDetailPage() {\n const { id = '' } = useParams<{ id: string }>();\n const { navigate } = useEventfullyManaged();\n const { data, loading } = useVenueQuery(id);\n\n const venue = (data?.venue ?? null) as VenueRecord | null;\n const floorPlans = (data?.floorPlans?.items ?? []) as FloorPlanRecord[];\n\n if (loading) {\n return <div className=\"p-6 text-sm text-text-secondary\">Loading venue...</div>;\n }\n\n if (!venue) {\n return (\n <div className=\"space-y-4 p-6\">\n <Button type=\"button\" variant=\"ghost\" size=\"sm\" onClick={() => navigate('/venues')}>\n <ArrowLeft className=\"h-4 w-4\" />\n Back to Venues\n </Button>\n <GlassCard treatment=\"plain\" className=\"border-border-seam\">\n <IllustratedEmptyState\n illustration=\"empty-search\"\n title=\"Venue not found.\"\n description=\"This venue 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 <PagePurpose>\n The full record for one venue. Review its location, capacity and status, see what amenities it offers, and\n manage the floor plans attendees will navigate. Come here to confirm a space is ready before you assign an event\n to it.\n </PagePurpose>\n\n <div className=\"flex flex-col gap-4 sm:flex-row sm:items-center sm:justify-between\">\n <div className=\"space-y-3\">\n <Button type=\"button\" variant=\"ghost\" size=\"sm\" onClick={() => navigate('/venues')}>\n <ArrowLeft className=\"h-4 w-4\" />\n Back to Venues\n </Button>\n <div>\n <h1 className=\"text-2xl font-bold text-text-primary\">{venue.name}</h1>\n <p className=\"mt-1 text-sm text-text-secondary\">{venue.description ?? 'No description provided.'}</p>\n </div>\n </div>\n <Button type=\"button\" onClick={() => navigate(`/venues/${venue.id}/edit`)}>\n <Edit className=\"h-4 w-4\" />\n Edit Venue\n </Button>\n </div>\n\n <div className=\"grid grid-cols-1 gap-4 lg:grid-cols-3\">\n <GlassCard treatment=\"metric\" className=\"p-4\">\n <div className=\"mb-2 flex items-center gap-2 text-sm font-medium text-text-primary\">\n <MapPin className=\"h-4 w-4 text-action-primary-bg\" />\n Location\n </div>\n <p className=\"text-sm text-text-secondary\">\n {[venue.addressLine1, venue.city, venue.state, venue.country, venue.postalCode]\n .filter(Boolean)\n .join(', ') || 'Location not configured'}\n </p>\n </GlassCard>\n <GlassCard treatment=\"glass\" glow className=\"p-4\">\n <div className=\"mb-2 flex items-center gap-2 text-sm font-medium text-text-primary\">\n <Users className=\"h-4 w-4 text-action-primary-bg\" />\n Capacity\n </div>\n <p className=\"text-sm tabular-nums text-text-secondary\">{venue.capacity ?? 0} attendees</p>\n </GlassCard>\n <GlassCard treatment=\"metric\" className=\"p-4\">\n <div className=\"mb-2 flex items-center gap-2 text-sm font-medium text-text-primary\">\n <Building2 className=\"h-4 w-4 text-action-primary-bg\" />\n Status\n </div>\n <p className=\"text-sm uppercase text-text-secondary\">{venue.status}</p>\n </GlassCard>\n </div>\n\n <GlassCard treatment=\"plain\" className=\"border-border-seam p-5\">\n <h2 className=\"text-lg font-semibold text-text-primary\">Amenities</h2>\n <div className=\"mt-3 flex flex-wrap gap-2\">\n {venue.amenities.length > 0 ? (\n venue.amenities.map((amenity) => (\n <Badge key={amenity} variant=\"secondary\">\n {amenity}\n </Badge>\n ))\n ) : (\n <p className=\"text-sm text-text-secondary\">No amenities configured.</p>\n )}\n </div>\n </GlassCard>\n\n <GlassCard treatment=\"plain\" className=\"border-border-seam p-5\">\n <h2 className=\"text-lg font-semibold text-text-primary\">Tags</h2>\n <p className=\"mt-1 text-sm text-text-secondary\">\n Label this venue so you can group and filter it — for example by region, tier or amenity profile.\n </p>\n <EntityTagsField entityType=\"EVENTFULLYMANAGED_VENUE\" entityId={venue.id} label=\"Venue tags\" className=\"mt-3\" />\n </GlassCard>\n\n <GlassCard treatment=\"plain\" className=\"border-border-seam p-5\">\n <div className=\"flex items-center justify-between gap-3\">\n <h2 className=\"text-lg font-semibold text-text-primary\">Floor Plans</h2>\n <Button type=\"button\" variant=\"outline\" size=\"sm\" onClick={() => navigate(`/venues/${venue.id}/floor-plans`)}>\n <SquareStack className=\"h-4 w-4\" />\n Manage Floor Plans\n </Button>\n </div>\n <div className=\"mt-4\">\n {floorPlans.length > 0 ? (\n <ul className=\"space-y-2.5\">\n {floorPlans.map((plan) => (\n <li\n key={plan.id}\n className=\"rounded-card border border-border-seam p-3 transition-[box-shadow,border-color] duration-200 hover:border-border-strong hover:shadow-elevation-1\"\n >\n <p className=\"font-medium text-text-primary\">{plan.name}</p>\n <p className=\"text-sm text-text-secondary\">\n {plan.imageUrl ? 'Image configured' : 'No image configured'}\n </p>\n </li>\n ))}\n </ul>\n ) : (\n <IllustratedEmptyState\n illustration=\"empty-files\"\n title=\"No floor plans have been added yet.\"\n className=\"py-8\"\n />\n )}\n </div>\n </GlassCard>\n\n <NextSteps\n storageKey=\"eventfullymanaged-venue-detail\"\n title=\"Get this venue ready to host\"\n steps={[\n {\n id: 'edit-venue',\n label: 'Edit venue details',\n description: 'Update capacity, address, status and amenities.',\n icon: <Edit className=\"h-4 w-4\" />,\n onClick: () => navigate(`/venues/${venue.id}/edit`),\n },\n {\n id: 'floor-plans',\n label: 'Add floor plans',\n description: 'Map the layout so attendees and staff can navigate.',\n icon: <SquareStack className=\"h-4 w-4\" />,\n onClick: () => navigate(`/venues/${venue.id}/floor-plans`),\n },\n {\n id: 'resources',\n label: 'Review resource readiness',\n description: 'Check capacity, amenities and plans at a glance.',\n icon: <Megaphone className=\"h-4 w-4\" />,\n onClick: () => navigate(`/venues/${venue.id}/resources`),\n },\n ]}\n />\n </div>\n );\n}\n"],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@burdenoff/microfe-eventfullymanaged",
3
- "version": "2026.704.3",
3
+ "version": "2026.704.4",
4
4
  "description": "EventfullyManaged - End-to-end event management platform",
5
5
  "type": "module",
6
6
  "files": [