@burdenoff/microfe-eventfullymanaged 2026.704.7 → 2026.710.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (152) hide show
  1. package/dist/dev/main.d.ts +2 -0
  2. package/dist/dev/main.d.ts.map +1 -0
  3. package/dist/eventfullymanaged/EventfullyManagedRoot.d.ts +4 -0
  4. package/dist/eventfullymanaged/EventfullyManagedRoot.d.ts.map +1 -0
  5. package/dist/eventfullymanaged/EventfullyManagedRoutes.d.ts +2 -0
  6. package/dist/eventfullymanaged/EventfullyManagedRoutes.d.ts.map +1 -0
  7. package/dist/eventfullymanaged/api/eventApi.d.ts +1185 -0
  8. package/dist/eventfullymanaged/api/eventApi.d.ts.map +1 -0
  9. package/dist/eventfullymanaged/components/ActionMenu.d.ts +45 -0
  10. package/dist/eventfullymanaged/components/ActionMenu.d.ts.map +1 -0
  11. package/dist/eventfullymanaged/components/EntityFormShell.d.ts +27 -0
  12. package/dist/eventfullymanaged/components/EntityFormShell.d.ts.map +1 -0
  13. package/dist/eventfullymanaged/components/EntityTagsField.d.ts +19 -0
  14. package/dist/eventfullymanaged/components/EntityTagsField.d.ts.map +1 -0
  15. package/dist/eventfullymanaged/components/EventCharts.d.ts +44 -0
  16. package/dist/eventfullymanaged/components/EventCharts.d.ts.map +1 -0
  17. package/dist/eventfullymanaged/components/EventCharts.js.map +1 -1
  18. package/dist/eventfullymanaged/components/ImageUploadField.d.ts +24 -0
  19. package/dist/eventfullymanaged/components/ImageUploadField.d.ts.map +1 -0
  20. package/dist/eventfullymanaged/components/PageLayout.d.ts +14 -0
  21. package/dist/eventfullymanaged/components/PageLayout.d.ts.map +1 -0
  22. package/dist/eventfullymanaged/components/Pagination.d.ts +41 -0
  23. package/dist/eventfullymanaged/components/Pagination.d.ts.map +1 -0
  24. package/dist/eventfullymanaged/components/SafeLink.d.ts +30 -0
  25. package/dist/eventfullymanaged/components/SafeLink.d.ts.map +1 -0
  26. package/dist/eventfullymanaged/components/index.d.ts +13 -0
  27. package/dist/eventfullymanaged/components/index.d.ts.map +1 -0
  28. package/dist/eventfullymanaged/context/EventfullyManagedContext.d.ts +25 -0
  29. package/dist/eventfullymanaged/context/EventfullyManagedContext.d.ts.map +1 -0
  30. package/dist/eventfullymanaged/hooks/index.d.ts +3 -0
  31. package/dist/eventfullymanaged/hooks/index.d.ts.map +1 -0
  32. package/dist/eventfullymanaged/index.d.ts +27 -0
  33. package/dist/eventfullymanaged/index.d.ts.map +1 -0
  34. package/dist/eventfullymanaged/lib/format.d.ts +18 -0
  35. package/dist/eventfullymanaged/lib/format.d.ts.map +1 -0
  36. package/dist/eventfullymanaged/lib/mutationResult.d.ts +23 -0
  37. package/dist/eventfullymanaged/lib/mutationResult.d.ts.map +1 -0
  38. package/dist/eventfullymanaged/lib/toast.d.ts +22 -0
  39. package/dist/eventfullymanaged/lib/toast.d.ts.map +1 -0
  40. package/dist/eventfullymanaged/pages/dashboard/DashboardPage.d.ts +2 -0
  41. package/dist/eventfullymanaged/pages/dashboard/DashboardPage.d.ts.map +1 -0
  42. package/dist/eventfullymanaged/pages/dashboard/DashboardPage.js.map +1 -1
  43. package/dist/eventfullymanaged/pages/events/EventDetailPage.d.ts +2 -0
  44. package/dist/eventfullymanaged/pages/events/EventDetailPage.d.ts.map +1 -0
  45. package/dist/eventfullymanaged/pages/events/EventDetailPage.js +1 -2
  46. package/dist/eventfullymanaged/pages/events/EventDetailPage.js.map +1 -1
  47. package/dist/eventfullymanaged/pages/events/EventFormPage.d.ts +2 -0
  48. package/dist/eventfullymanaged/pages/events/EventFormPage.d.ts.map +1 -0
  49. package/dist/eventfullymanaged/pages/events/SessionFormPage.d.ts +2 -0
  50. package/dist/eventfullymanaged/pages/events/SessionFormPage.d.ts.map +1 -0
  51. package/dist/eventfullymanaged/pages/events/TicketTypeFormPage.d.ts +2 -0
  52. package/dist/eventfullymanaged/pages/events/TicketTypeFormPage.d.ts.map +1 -0
  53. package/dist/eventfullymanaged/pages/events/eventFormUtils.d.ts +40 -0
  54. package/dist/eventfullymanaged/pages/events/eventFormUtils.d.ts.map +1 -0
  55. package/dist/eventfullymanaged/pages/exhibitors/ExhibitorFormPage.d.ts +2 -0
  56. package/dist/eventfullymanaged/pages/exhibitors/ExhibitorFormPage.d.ts.map +1 -0
  57. package/dist/eventfullymanaged/pages/exhibitors/ExhibitorsPage.d.ts +2 -0
  58. package/dist/eventfullymanaged/pages/exhibitors/ExhibitorsPage.d.ts.map +1 -0
  59. package/dist/eventfullymanaged/pages/leads/LeadCaptureDialog.d.ts +23 -0
  60. package/dist/eventfullymanaged/pages/leads/LeadCaptureDialog.d.ts.map +1 -0
  61. package/dist/eventfullymanaged/pages/modules/AnalyticsPage.d.ts +2 -0
  62. package/dist/eventfullymanaged/pages/modules/AnalyticsPage.d.ts.map +1 -0
  63. package/dist/eventfullymanaged/pages/modules/AnalyticsPage.js.map +1 -1
  64. package/dist/eventfullymanaged/pages/modules/AttendeeProfilePage.d.ts +2 -0
  65. package/dist/eventfullymanaged/pages/modules/AttendeeProfilePage.d.ts.map +1 -0
  66. package/dist/eventfullymanaged/pages/modules/CommunicationPage.d.ts +2 -0
  67. package/dist/eventfullymanaged/pages/modules/CommunicationPage.d.ts.map +1 -0
  68. package/dist/eventfullymanaged/pages/modules/CommunicationPage.js.map +1 -1
  69. package/dist/eventfullymanaged/pages/modules/EntityListSection.d.ts +16 -0
  70. package/dist/eventfullymanaged/pages/modules/EntityListSection.d.ts.map +1 -0
  71. package/dist/eventfullymanaged/pages/modules/EventsPage.d.ts +2 -0
  72. package/dist/eventfullymanaged/pages/modules/EventsPage.d.ts.map +1 -0
  73. package/dist/eventfullymanaged/pages/modules/EventsPage.js +223 -118
  74. package/dist/eventfullymanaged/pages/modules/EventsPage.js.map +1 -1
  75. package/dist/eventfullymanaged/pages/modules/KioskPage.d.ts +19 -0
  76. package/dist/eventfullymanaged/pages/modules/KioskPage.d.ts.map +1 -0
  77. package/dist/eventfullymanaged/pages/modules/NetworkingPage.d.ts +2 -0
  78. package/dist/eventfullymanaged/pages/modules/NetworkingPage.d.ts.map +1 -0
  79. package/dist/eventfullymanaged/pages/modules/OperationsHubPage.d.ts +9 -0
  80. package/dist/eventfullymanaged/pages/modules/OperationsHubPage.d.ts.map +1 -0
  81. package/dist/eventfullymanaged/pages/modules/RegistrationPage.d.ts +2 -0
  82. package/dist/eventfullymanaged/pages/modules/RegistrationPage.d.ts.map +1 -0
  83. package/dist/eventfullymanaged/pages/modules/RegistrationPage.js.map +1 -1
  84. package/dist/eventfullymanaged/pages/modules/RoadmapPanel.d.ts +24 -0
  85. package/dist/eventfullymanaged/pages/modules/RoadmapPanel.d.ts.map +1 -0
  86. package/dist/eventfullymanaged/pages/modules/SponsorDetailsPage.d.ts +2 -0
  87. package/dist/eventfullymanaged/pages/modules/SponsorDetailsPage.d.ts.map +1 -0
  88. package/dist/eventfullymanaged/pages/modules/SponsorDetailsPage.js.map +1 -1
  89. package/dist/eventfullymanaged/pages/modules/SponsorLeadsPage.d.ts +2 -0
  90. package/dist/eventfullymanaged/pages/modules/SponsorLeadsPage.d.ts.map +1 -0
  91. package/dist/eventfullymanaged/pages/modules/VenueResourcesPage.d.ts +2 -0
  92. package/dist/eventfullymanaged/pages/modules/VenueResourcesPage.d.ts.map +1 -0
  93. package/dist/eventfullymanaged/pages/modules/WorkflowsPage.d.ts +2 -0
  94. package/dist/eventfullymanaged/pages/modules/WorkflowsPage.d.ts.map +1 -0
  95. package/dist/eventfullymanaged/pages/networking/MeetingFormPage.d.ts +2 -0
  96. package/dist/eventfullymanaged/pages/networking/MeetingFormPage.d.ts.map +1 -0
  97. package/dist/eventfullymanaged/pages/networking/NetworkingProfileFormPage.d.ts +2 -0
  98. package/dist/eventfullymanaged/pages/networking/NetworkingProfileFormPage.d.ts.map +1 -0
  99. package/dist/eventfullymanaged/pages/registration/AttendeeFormPage.d.ts +2 -0
  100. package/dist/eventfullymanaged/pages/registration/AttendeeFormPage.d.ts.map +1 -0
  101. package/dist/eventfullymanaged/pages/registration/AttendeeFormPage.js.map +1 -1
  102. package/dist/eventfullymanaged/pages/registration/PromoCodeFormPage.d.ts +2 -0
  103. package/dist/eventfullymanaged/pages/registration/PromoCodeFormPage.d.ts.map +1 -0
  104. package/dist/eventfullymanaged/pages/registration/RegisterAttendeePage.d.ts +2 -0
  105. package/dist/eventfullymanaged/pages/registration/RegisterAttendeePage.d.ts.map +1 -0
  106. package/dist/eventfullymanaged/pages/registration/RegisterAttendeePage.js.map +1 -1
  107. package/dist/eventfullymanaged/pages/speakers/SpeakerFormPage.d.ts +2 -0
  108. package/dist/eventfullymanaged/pages/speakers/SpeakerFormPage.d.ts.map +1 -0
  109. package/dist/eventfullymanaged/pages/speakers/SpeakersPage.d.ts +2 -0
  110. package/dist/eventfullymanaged/pages/speakers/SpeakersPage.d.ts.map +1 -0
  111. package/dist/eventfullymanaged/pages/speakers/SpeakersPage.js.map +1 -1
  112. package/dist/eventfullymanaged/pages/sponsors/SponsorFormPage.d.ts +2 -0
  113. package/dist/eventfullymanaged/pages/sponsors/SponsorFormPage.d.ts.map +1 -0
  114. package/dist/eventfullymanaged/pages/sponsors/SponsorsListPage.d.ts +2 -0
  115. package/dist/eventfullymanaged/pages/sponsors/SponsorsListPage.d.ts.map +1 -0
  116. package/dist/eventfullymanaged/pages/sponsors/SponsorsListPage.js.map +1 -1
  117. package/dist/eventfullymanaged/pages/venues/FloorPlansPage.d.ts +2 -0
  118. package/dist/eventfullymanaged/pages/venues/FloorPlansPage.d.ts.map +1 -0
  119. package/dist/eventfullymanaged/pages/venues/FloorPlansPage.js.map +1 -1
  120. package/dist/eventfullymanaged/pages/venues/VenueDetailPage.d.ts +2 -0
  121. package/dist/eventfullymanaged/pages/venues/VenueDetailPage.d.ts.map +1 -0
  122. package/dist/eventfullymanaged/pages/venues/VenueDetailPage.js.map +1 -1
  123. package/dist/eventfullymanaged/pages/venues/VenueFormPage.d.ts +2 -0
  124. package/dist/eventfullymanaged/pages/venues/VenueFormPage.d.ts.map +1 -0
  125. package/dist/eventfullymanaged/pages/venues/VenueListPage.d.ts +2 -0
  126. package/dist/eventfullymanaged/pages/venues/VenueListPage.d.ts.map +1 -0
  127. package/dist/eventfullymanaged/store/eventfullyManagedStore.d.ts +47 -0
  128. package/dist/eventfullymanaged/store/eventfullyManagedStore.d.ts.map +1 -0
  129. package/dist/eventfullymanaged/types/index.d.ts +3649 -0
  130. package/dist/eventfullymanaged/types/index.d.ts.map +1 -0
  131. package/dist/eventfullymanaged/utils/cn.d.ts +2 -0
  132. package/dist/eventfullymanaged/utils/cn.d.ts.map +1 -0
  133. package/dist/eventfullymanaged/utils/constants.d.ts +3 -0
  134. package/dist/eventfullymanaged/utils/constants.d.ts.map +1 -0
  135. package/dist/eventfullymanaged/utils/navigation.d.ts +2 -0
  136. package/dist/eventfullymanaged/utils/navigation.d.ts.map +1 -0
  137. package/dist/generated/global-operations.d.ts +2 -0
  138. package/dist/generated/global-operations.d.ts.map +1 -0
  139. package/dist/generated/global-types.d.ts +23527 -0
  140. package/dist/generated/global-types.d.ts.map +1 -0
  141. package/dist/generated/wspace-operations.d.ts +2 -0
  142. package/dist/generated/wspace-operations.d.ts.map +1 -0
  143. package/dist/generated/wspace-types.d.ts +57851 -0
  144. package/dist/generated/wspace-types.d.ts.map +1 -0
  145. package/dist/index.d.ts +9 -0
  146. package/dist/index.d.ts.map +1 -0
  147. package/dist/index.js +2 -1
  148. package/dist/services/createSandboxAssistantTransport.d.ts +33 -0
  149. package/dist/services/createSandboxAssistantTransport.d.ts.map +1 -0
  150. package/dist/services/createSandboxAssistantTransport.js +19 -0
  151. package/dist/services/createSandboxAssistantTransport.js.map +1 -0
  152. package/package.json +7 -3
@@ -1 +1 @@
1
- {"version":3,"file":"RegisterAttendeePage.js","names":[],"sources":["../../../../src/eventfullymanaged/pages/registration/RegisterAttendeePage.tsx"],"sourcesContent":["import { useMemo, useState } from 'react';\nimport {\n ArrowLeft,\n ArrowRight,\n BadgePercent,\n CalendarDays,\n Check,\n CheckCircle2,\n Minus,\n Plus,\n Ticket,\n UserRound,\n} from 'lucide-react';\nimport {\n Button,\n GlassCard,\n GradientText,\n Input,\n PagePurpose,\n Select,\n SelectContent,\n SelectItem,\n SelectTrigger,\n SelectValue,\n Stepper,\n} from '@burdenoff/fe-libs/ui';\n\nimport {\n useAttendeesQuery,\n useCreateAttendeeMutation,\n useCreateRegistrationMutation,\n useEventsQuery,\n usePromoCodesQuery,\n useTicketTypesQuery,\n type PromoCodeGql,\n} from '../../api/eventApi';\nimport { useEventfullyManaged } from '../../context/EventfullyManagedContext';\nimport { computeDiscount, describeDiscount, formatMoney } from '../../lib/format';\nimport { describeError, mutationSucceeded } from '../../lib/mutationResult';\nimport { notify } from '../../lib/toast';\n\ntype Confirmation = {\n attendeeName: string;\n eventName: string;\n ticketName: string;\n quantity: number;\n status: string;\n subtotal: number;\n discount: number;\n total: number;\n currency: string;\n promoCode: string | null;\n free: boolean;\n};\n\nconst STEPS = [\n { key: 'event', label: 'Event', description: 'Choose the event' },\n { key: 'ticket', label: 'Ticket', description: 'Pick a ticket type' },\n { key: 'attendee', label: 'Attendee', description: 'Who is coming' },\n { key: 'confirm', label: 'Confirm', description: 'Review & submit' },\n];\n\nexport default function RegisterAttendeePage() {\n const { navigate } = useEventfullyManaged();\n\n const [step, setStep] = useState(0);\n const [eventId, setEventId] = useState('');\n const [ticketTypeId, setTicketTypeId] = useState('');\n const [attendeeMode, setAttendeeMode] = useState<'new' | 'existing'>('new');\n const [attendeeId, setAttendeeId] = useState('');\n const [fullName, setFullName] = useState('');\n const [email, setEmail] = useState('');\n const [company, setCompany] = useState('');\n const [phone, setPhone] = useState('');\n const [quantity, setQuantity] = useState(1);\n const [promoInput, setPromoInput] = useState('');\n const [appliedPromo, setAppliedPromo] = useState<PromoCodeGql | null>(null);\n const [promoError, setPromoError] = useState('');\n const [billingAccountId, setBillingAccountId] = useState('');\n const [confirmation, setConfirmation] = useState<Confirmation | null>(null);\n\n const { data: eventsData, loading: eventsLoading } = useEventsQuery({ limit: 100, offset: 0 });\n const { data: ticketData, loading: ticketsLoading } = useTicketTypesQuery({\n eventId: eventId || undefined,\n limit: 100,\n offset: 0,\n });\n const { data: attendeesData } = useAttendeesQuery({ eventId: eventId || undefined, limit: 100, offset: 0 });\n const { data: promoData } = usePromoCodesQuery({ eventId: eventId || undefined, limit: 100, offset: 0 });\n\n const events = useMemo(() => eventsData?.events.items ?? [], [eventsData]);\n const tickets = useMemo(\n () =>\n (ticketData?.ticketTypes.items ?? []).filter((ticket) => (!eventId || ticket.eventId === eventId) && ticket.isActive),\n [ticketData, eventId]\n );\n const attendees = useMemo(\n () => (attendeesData?.attendees.items ?? []).filter((attendee) => !eventId || attendee.eventId === eventId),\n [attendeesData, eventId]\n );\n const activePromos = useMemo(\n () =>\n (promoData?.promoCodes.items ?? []).filter((promo) => promo.isActive && (!eventId || promo.eventId === eventId)),\n [promoData, eventId]\n );\n\n const [createAttendee, { loading: creatingAttendee }] = useCreateAttendeeMutation();\n const [createRegistration, { loading: creatingRegistration }] = useCreateRegistrationMutation();\n const saving = creatingAttendee || creatingRegistration;\n\n const selectedEvent = useMemo(() => events.find((item) => item.id === eventId), [events, eventId]);\n const selectedTicket = useMemo(() => tickets.find((ticket) => ticket.id === ticketTypeId), [tickets, ticketTypeId]);\n const selectedAttendee = useMemo(\n () => attendees.find((attendee) => attendee.id === attendeeId),\n [attendees, attendeeId]\n );\n\n const qty = Math.max(1, quantity);\n const unitPrice = selectedTicket ? Number(selectedTicket.price) : 0;\n const currency = selectedTicket?.currency ?? 'USD';\n const subtotal = Number.isFinite(unitPrice) ? unitPrice * qty : 0;\n const discount = appliedPromo\n ? computeDiscount(subtotal, appliedPromo.discountType, Number(appliedPromo.discountValue))\n : 0;\n const total = Math.max(subtotal - discount, 0);\n const hasPositiveAmount = subtotal > 0;\n\n const attendeeName =\n attendeeMode === 'existing' ? (selectedAttendee?.fullName ?? '') : fullName.trim();\n\n const onEventChange = (value: string) => {\n setEventId(value);\n setTicketTypeId('');\n setAttendeeMode('new');\n setAttendeeId('');\n setAppliedPromo(null);\n setPromoInput('');\n setPromoError('');\n };\n\n const isStepValid = (index: number): boolean => {\n switch (index) {\n case 0:\n return Boolean(eventId);\n case 1:\n return Boolean(ticketTypeId);\n case 2:\n return attendeeMode === 'existing' ? Boolean(attendeeId) : Boolean(fullName.trim() && email.trim());\n default:\n return true;\n }\n };\n\n const canGoTo = (index: number): boolean => {\n if (index <= step) return true;\n for (let i = 0; i < index; i += 1) {\n if (!isStepValid(i)) return false;\n }\n return true;\n };\n\n const goToStep = (index: number) => {\n if (canGoTo(index)) {\n setStep(index);\n }\n };\n\n const goNext = () => {\n if (!isStepValid(step)) {\n const messages = [\n 'Choose an event to continue.',\n 'Pick a ticket type to continue.',\n 'Select or enter an attendee to continue.',\n ];\n notify.error('One more thing', messages[step] ?? 'Complete this step to continue.');\n return;\n }\n setStep((current) => Math.min(current + 1, STEPS.length - 1));\n };\n\n const goBack = () => setStep((current) => Math.max(current - 1, 0));\n\n const applyPromo = () => {\n const code = promoInput.trim().toUpperCase();\n if (!code) {\n setAppliedPromo(null);\n setPromoError('');\n return;\n }\n const match = activePromos.find((promo) => promo.code.toUpperCase() === code);\n if (!match) {\n setAppliedPromo(null);\n setPromoError('That code is not active for this event.');\n return;\n }\n setAppliedPromo(match);\n setPromoError('');\n notify.success('Promo applied', describeDiscount(match.discountType, match.discountValue, currency));\n };\n\n const clearPromo = () => {\n setAppliedPromo(null);\n setPromoInput('');\n setPromoError('');\n };\n\n const resetAll = () => {\n setStep(0);\n setEventId('');\n setTicketTypeId('');\n setAttendeeMode('new');\n setAttendeeId('');\n setFullName('');\n setEmail('');\n setCompany('');\n setPhone('');\n setQuantity(1);\n clearPromo();\n setBillingAccountId('');\n };\n\n const onConfirm = async () => {\n if (!eventId || !ticketTypeId) {\n notify.error('Incomplete', 'Pick an event and ticket first.');\n return;\n }\n if (attendeeMode === 'new' && (!fullName.trim() || !email.trim())) {\n notify.error('Attendee required', 'Enter a name and email, or pick an existing attendee.');\n return;\n }\n if (attendeeMode === 'existing' && !attendeeId) {\n notify.error('Attendee required', 'Select an existing attendee.');\n return;\n }\n\n try {\n let resolvedAttendeeId = attendeeId;\n let resolvedAttendeeName = selectedAttendee?.fullName ?? '';\n\n if (attendeeMode === 'new') {\n const attendeeResponse = await createAttendee({\n variables: {\n input: {\n eventId,\n fullName: fullName.trim(),\n email: email.trim(),\n company: company.trim() || null,\n phone: phone.trim() || null,\n profile: {},\n metadata: {},\n },\n },\n });\n const createdId = attendeeResponse.data?.createAttendee?.id;\n if (!mutationSucceeded(attendeeResponse, createdId, 'Could not create attendee') || !createdId) {\n return;\n }\n resolvedAttendeeId = createdId;\n resolvedAttendeeName = fullName.trim();\n }\n\n // The money leg is owned by global-billing-svc — never price locally. We\n // forward the promo code + optional billing-account reference via metadata\n // so the backend can attach the charge; totalAmount is left for the backend.\n const metadata: Record<string, unknown> = {};\n if (appliedPromo) {\n metadata.promoCode = appliedPromo.code;\n }\n if (hasPositiveAmount && billingAccountId.trim()) {\n metadata.billingAccountId = billingAccountId.trim();\n }\n\n const registrationResponse = await createRegistration({\n variables: {\n input: {\n eventId,\n attendeeId: resolvedAttendeeId,\n ticketTypeId,\n quantity: qty,\n currency,\n status: 'CONFIRMED',\n metadata,\n },\n },\n });\n if (\n !mutationSucceeded(\n registrationResponse,\n registrationResponse.data?.createRegistration?.id,\n 'Could not create registration'\n )\n ) {\n return;\n }\n\n const registration = registrationResponse.data?.createRegistration;\n setConfirmation({\n attendeeName: resolvedAttendeeName || 'Attendee',\n eventName: selectedEvent?.name ?? 'Event',\n ticketName: selectedTicket?.name ?? 'Ticket',\n quantity: qty,\n status: registration?.status ?? 'CONFIRMED',\n subtotal,\n discount,\n total,\n currency,\n promoCode: appliedPromo?.code ?? null,\n free: !hasPositiveAmount,\n });\n notify.success('Registration confirmed', resolvedAttendeeName || undefined);\n } catch (error) {\n notify.error('Could not register attendee', describeError(error));\n }\n };\n\n if (confirmation) {\n return (\n <div className=\"mx-auto max-w-2xl space-y-6 p-6\">\n <div>\n <Button type=\"button\" variant=\"ghost\" size=\"sm\" onClick={() => navigate('/registration')}>\n <ArrowLeft className=\"h-4 w-4\" />\n Back to Registration\n </Button>\n </div>\n <GlassCard treatment=\"glass\" glow className=\"p-8 text-center\">\n <div className=\"mx-auto flex h-14 w-14 items-center justify-center rounded-full bg-status-success-bg-subtle\">\n <CheckCircle2 className=\"h-8 w-8 text-status-success-text\" aria-hidden=\"true\" />\n </div>\n <h1 className=\"mt-4 text-2xl font-bold text-text-primary\">Registration confirmed</h1>\n <p className=\"mt-2 text-sm text-text-secondary\">\n {confirmation.attendeeName} is registered for {confirmation.eventName}.\n </p>\n\n <dl className=\"mx-auto mt-6 max-w-sm space-y-2 rounded-card border border-border-seam bg-bg-surface p-4 text-left text-sm\">\n <div className=\"flex justify-between\">\n <dt className=\"text-text-secondary\">Ticket</dt>\n <dd className=\"font-medium text-text-primary\">\n {confirmation.ticketName}\n {confirmation.quantity > 1 ? ` × ${confirmation.quantity}` : ''}\n </dd>\n </div>\n {!confirmation.free ? (\n <>\n <div className=\"flex justify-between\">\n <dt className=\"text-text-secondary\">Subtotal</dt>\n <dd className=\"tabular-nums text-text-primary\">\n {formatMoney(confirmation.subtotal, confirmation.currency)}\n </dd>\n </div>\n {confirmation.discount > 0 ? (\n <div className=\"flex justify-between\">\n <dt className=\"text-text-secondary\">\n Promo{confirmation.promoCode ? ` · ${confirmation.promoCode}` : ''}\n </dt>\n <dd className=\"tabular-nums text-status-success-text\">\n −{formatMoney(confirmation.discount, confirmation.currency)}\n </dd>\n </div>\n ) : null}\n </>\n ) : null}\n <div className=\"flex justify-between border-t border-border-seam pt-2\">\n <dt className=\"font-semibold text-text-primary\">Total</dt>\n <dd className=\"font-bold tabular-nums text-text-primary\">\n {confirmation.free ? 'Free' : formatMoney(confirmation.total, confirmation.currency)}\n </dd>\n </div>\n <div className=\"flex justify-between\">\n <dt className=\"text-text-secondary\">Status</dt>\n <dd className=\"font-medium text-text-primary\">{confirmation.status}</dd>\n </div>\n </dl>\n {!confirmation.free ? (\n <p className=\"mt-3 text-xs text-text-muted\">Final pricing and payment are settled by the billing service.</p>\n ) : null}\n\n <div className=\"mt-8 flex flex-col justify-center gap-3 sm:flex-row\">\n <Button type=\"button\" variant=\"outline\" onClick={() => navigate('/registration')}>\n Back to Registrations\n </Button>\n <Button\n type=\"button\"\n onClick={() => {\n setConfirmation(null);\n resetAll();\n }}\n >\n Register another\n </Button>\n </div>\n </GlassCard>\n </div>\n );\n }\n\n return (\n <div className=\"space-y-6 p-6\">\n <div>\n <Button type=\"button\" variant=\"ghost\" size=\"sm\" onClick={() => navigate('/registration')}>\n <ArrowLeft className=\"h-4 w-4\" />\n Back to Registration\n </Button>\n <h1 className=\"mt-3 text-2xl font-bold text-text-primary\">\n <GradientText>Register Attendee</GradientText>\n </h1>\n <p className=\"mt-1 text-sm text-text-secondary\">\n Walk through the event, ticket and attendee in a few quick steps.\n </p>\n </div>\n\n <PagePurpose>\n Register a person for an event against a ticket type. Pick the event and ticket, choose an existing attendee or\n add a new one, apply a promo code if they have one, then confirm. The order summary previews the price — final\n pricing and payment are handled by the billing service.\n </PagePurpose>\n\n <Stepper steps={STEPS} active={step} onStepClick={goToStep} />\n\n <div className=\"grid grid-cols-1 gap-6 lg:grid-cols-[minmax(0,1fr)_20rem]\">\n <GlassCard treatment=\"plain\" className=\"border-border-seam p-5 sm:p-6\">\n {/* STEP 0 — EVENT */}\n {step === 0 ? (\n <div className=\"space-y-4\">\n <div>\n <h2 className=\"text-lg font-semibold text-text-primary\">Which event?</h2>\n <p className=\"mt-1 text-sm text-text-secondary\">Choose the event this attendee is signing up for.</p>\n </div>\n {eventsLoading ? (\n <p className=\"text-sm text-text-muted\">Loading events…</p>\n ) : events.length === 0 ? (\n <div className=\"rounded-card border border-dashed border-border-default p-6 text-center text-sm text-text-secondary\">\n No events yet.{' '}\n <button\n type=\"button\"\n className=\"font-medium text-text-link underline-offset-2 hover:underline\"\n onClick={() => navigate('/events/create')}\n >\n Create an event\n </button>{' '}\n first.\n </div>\n ) : (\n <div className=\"grid grid-cols-1 gap-3 sm:grid-cols-2\">\n {events.map((eventItem) => {\n const selected = eventItem.id === eventId;\n return (\n <button\n key={eventItem.id}\n type=\"button\"\n onClick={() => onEventChange(eventItem.id)}\n aria-pressed={selected}\n className={`flex items-center gap-3 rounded-card border p-4 text-left transition-colors ${\n selected\n ? 'border-action-primary-border bg-accent-wash'\n : 'border-border-seam bg-bg-surface hover:border-border-strong'\n }`}\n >\n <span\n className={`flex h-10 w-10 shrink-0 items-center justify-center rounded-input ${\n selected ? 'bg-action-primary-bg text-action-primary-text' : 'bg-bg-sunken text-text-muted'\n }`}\n >\n <CalendarDays className=\"h-5 w-5\" aria-hidden=\"true\" />\n </span>\n <span className=\"min-w-0 flex-1 truncate text-sm font-medium text-text-primary\">\n {eventItem.name}\n </span>\n {selected ? <Check className=\"h-4 w-4 shrink-0 text-action-primary-bg\" aria-hidden=\"true\" /> : null}\n </button>\n );\n })}\n </div>\n )}\n </div>\n ) : null}\n\n {/* STEP 1 — TICKET */}\n {step === 1 ? (\n <div className=\"space-y-4\">\n <div>\n <h2 className=\"text-lg font-semibold text-text-primary\">Ticket type</h2>\n <p className=\"mt-1 text-sm text-text-secondary\">Pick the ticket and how many they need.</p>\n </div>\n {ticketsLoading ? (\n <p className=\"text-sm text-text-muted\">Loading tickets…</p>\n ) : tickets.length === 0 ? (\n <div className=\"rounded-card border border-dashed border-border-default p-6 text-center text-sm text-text-secondary\">\n This event has no active ticket types.\n </div>\n ) : (\n <div className=\"grid grid-cols-1 gap-3 sm:grid-cols-2 xl:grid-cols-3\">\n {tickets.map((ticket) => {\n const selected = ticket.id === ticketTypeId;\n const cap = ticket.capacity ?? null;\n const sold = ticket.soldCount ?? 0;\n const pct = cap && cap > 0 ? Math.min(100, Math.round((sold / cap) * 100)) : null;\n const soldOut = cap != null && sold >= cap;\n return (\n <button\n key={ticket.id}\n type=\"button\"\n disabled={soldOut && !selected}\n onClick={() => setTicketTypeId(ticket.id)}\n aria-pressed={selected}\n className={`flex flex-col gap-3 rounded-card border p-4 text-left transition-colors disabled:cursor-not-allowed disabled:opacity-60 ${\n selected\n ? 'border-action-primary-border bg-accent-wash ring-1 ring-border-focus'\n : 'border-border-seam bg-bg-surface hover:border-border-strong'\n }`}\n >\n <div className=\"flex items-start justify-between gap-2\">\n <span className=\"text-sm font-semibold text-text-primary\">{ticket.name}</span>\n {selected ? (\n <Check className=\"h-4 w-4 shrink-0 text-action-primary-bg\" aria-hidden=\"true\" />\n ) : (\n <Ticket className=\"h-4 w-4 shrink-0 text-text-muted\" aria-hidden=\"true\" />\n )}\n </div>\n <span className=\"text-lg font-bold tabular-nums text-text-primary\">\n {Number(ticket.price) > 0 ? formatMoney(Number(ticket.price), ticket.currency) : 'Free'}\n </span>\n {cap != null ? (\n <div className=\"space-y-1\">\n <div className=\"h-1.5 w-full overflow-hidden rounded-full bg-bg-sunken\">\n <div\n className=\"h-full rounded-full bg-action-primary-bg\"\n style={{ width: `${pct ?? 0}%` }}\n />\n </div>\n <span className=\"text-xs text-text-muted\">\n {soldOut ? 'Sold out' : `${sold} / ${cap} sold`}\n </span>\n </div>\n ) : (\n <span className=\"text-xs text-text-muted\">{sold} sold</span>\n )}\n </button>\n );\n })}\n </div>\n )}\n\n <div className=\"flex flex-wrap items-end gap-4 border-t border-border-seam pt-4\">\n <div className=\"space-y-1\">\n <span className=\"text-sm font-medium text-text-primary\">Quantity</span>\n <div className=\"flex items-center gap-1\">\n <Button\n type=\"button\"\n variant=\"outline\"\n size=\"icon\"\n aria-label=\"Decrease quantity\"\n disabled={qty <= 1}\n onClick={() => setQuantity((value) => Math.max(1, value - 1))}\n >\n <Minus className=\"h-4 w-4\" />\n </Button>\n <span className=\"w-10 text-center text-base font-semibold tabular-nums text-text-primary\">{qty}</span>\n <Button\n type=\"button\"\n variant=\"outline\"\n size=\"icon\"\n aria-label=\"Increase quantity\"\n onClick={() => setQuantity((value) => value + 1)}\n >\n <Plus className=\"h-4 w-4\" />\n </Button>\n </div>\n </div>\n </div>\n </div>\n ) : null}\n\n {/* STEP 2 — ATTENDEE */}\n {step === 2 ? (\n <div className=\"space-y-4\">\n <div>\n <h2 className=\"text-lg font-semibold text-text-primary\">Attendee information</h2>\n <p className=\"mt-1 text-sm text-text-secondary\">Add someone new or pick an existing attendee.</p>\n </div>\n\n <div className=\"inline-flex rounded-input border border-border-seam bg-bg-sunken p-1\">\n <button\n type=\"button\"\n onClick={() => setAttendeeMode('new')}\n className={`rounded-[calc(var(--radius-input)-2px)] px-3 py-1.5 text-sm font-medium transition-colors ${\n attendeeMode === 'new'\n ? 'bg-bg-surface text-text-primary shadow-sm'\n : 'text-text-secondary hover:text-text-primary'\n }`}\n >\n New attendee\n </button>\n <button\n type=\"button\"\n onClick={() => setAttendeeMode('existing')}\n disabled={attendees.length === 0}\n className={`rounded-[calc(var(--radius-input)-2px)] px-3 py-1.5 text-sm font-medium transition-colors disabled:cursor-not-allowed disabled:opacity-50 ${\n attendeeMode === 'existing'\n ? 'bg-bg-surface text-text-primary shadow-sm'\n : 'text-text-secondary hover:text-text-primary'\n }`}\n >\n Existing attendee\n </button>\n </div>\n\n {attendeeMode === 'existing' ? (\n <label className=\"block space-y-1 text-sm\">\n <span className=\"font-medium text-text-primary\">Attendee</span>\n <Select value={attendeeId} onValueChange={setAttendeeId}>\n <SelectTrigger aria-label=\"Attendee\">\n <SelectValue placeholder=\"Select an attendee\" />\n </SelectTrigger>\n <SelectContent>\n {attendees.map((attendee) => (\n <SelectItem key={attendee.id} value={attendee.id}>\n {attendee.fullName} · {attendee.email}\n </SelectItem>\n ))}\n </SelectContent>\n </Select>\n </label>\n ) : (\n <div className=\"grid grid-cols-1 gap-4 sm: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={fullName} onChange={(e) => setFullName(e.target.value)} placeholder=\"Enter full name\" />\n </label>\n <label className=\"space-y-1 text-sm\">\n <span className=\"font-medium text-text-primary\">Email</span>\n <Input\n type=\"email\"\n value={email}\n onChange={(e) => setEmail(e.target.value)}\n placeholder=\"Enter email address\"\n />\n </label>\n <label className=\"space-y-1 text-sm\">\n <span className=\"font-medium text-text-primary\">Company</span>\n <Input\n value={company}\n onChange={(e) => setCompany(e.target.value)}\n placeholder=\"Enter company name\"\n />\n </label>\n <label className=\"space-y-1 text-sm\">\n <span className=\"font-medium text-text-primary\">Phone</span>\n <Input value={phone} onChange={(e) => setPhone(e.target.value)} placeholder=\"Optional\" />\n </label>\n </div>\n )}\n\n {/* Promo code */}\n <div className=\"space-y-2 border-t border-border-seam pt-4\">\n <span className=\"text-sm font-medium text-text-primary\">Promo code (optional)</span>\n <div className=\"flex flex-col gap-2 sm:flex-row\">\n <Input\n value={promoInput}\n onChange={(e) => {\n setPromoInput(e.target.value);\n setPromoError('');\n }}\n placeholder=\"Enter promo code\"\n className=\"sm:flex-1\"\n onKeyDown={(e) => {\n if (e.key === 'Enter') {\n e.preventDefault();\n applyPromo();\n }\n }}\n />\n {appliedPromo ? (\n <Button type=\"button\" variant=\"outline\" onClick={clearPromo}>\n Remove\n </Button>\n ) : (\n <Button type=\"button\" variant=\"outline\" onClick={applyPromo} disabled={!promoInput.trim()}>\n Apply\n </Button>\n )}\n </div>\n {appliedPromo ? (\n <span className=\"inline-flex items-center gap-1 rounded-full bg-status-success-bg-subtle px-2.5 py-0.5 text-xs font-medium text-status-success-text\">\n <BadgePercent className=\"h-3.5 w-3.5\" aria-hidden=\"true\" />\n {describeDiscount(appliedPromo.discountType, appliedPromo.discountValue, currency)} applied\n </span>\n ) : promoError ? (\n <span className=\"text-xs text-status-error-text\">{promoError}</span>\n ) : activePromos.length > 0 ? (\n <div className=\"flex flex-wrap items-center gap-1.5 text-xs text-text-muted\">\n <span>Available:</span>\n {activePromos.slice(0, 4).map((promo) => (\n <button\n key={promo.id}\n type=\"button\"\n onClick={() => {\n setPromoInput(promo.code);\n setAppliedPromo(promo);\n setPromoError('');\n }}\n className=\"rounded-full border border-border-seam bg-bg-surface px-2 py-0.5 font-medium text-text-secondary hover:border-border-strong hover:text-text-primary\"\n >\n {promo.code}\n </button>\n ))}\n </div>\n ) : null}\n </div>\n </div>\n ) : null}\n\n {/* STEP 3 — CONFIRM */}\n {step === 3 ? (\n <div className=\"space-y-4\">\n <div>\n <h2 className=\"text-lg font-semibold text-text-primary\">Review &amp; confirm</h2>\n <p className=\"mt-1 text-sm text-text-secondary\">Double-check the details before registering.</p>\n </div>\n <dl className=\"divide-y divide-border-seam rounded-card border border-border-seam bg-bg-surface text-sm\">\n <div className=\"flex items-center gap-3 p-3\">\n <CalendarDays className=\"h-4 w-4 text-text-muted\" aria-hidden=\"true\" />\n <dt className=\"text-text-secondary\">Event</dt>\n <dd className=\"ml-auto font-medium text-text-primary\">{selectedEvent?.name ?? '—'}</dd>\n </div>\n <div className=\"flex items-center gap-3 p-3\">\n <Ticket className=\"h-4 w-4 text-text-muted\" aria-hidden=\"true\" />\n <dt className=\"text-text-secondary\">Ticket</dt>\n <dd className=\"ml-auto font-medium text-text-primary\">\n {selectedTicket?.name ?? '—'}\n {qty > 1 ? ` × ${qty}` : ''}\n </dd>\n </div>\n <div className=\"flex items-center gap-3 p-3\">\n <UserRound className=\"h-4 w-4 text-text-muted\" aria-hidden=\"true\" />\n <dt className=\"text-text-secondary\">Attendee</dt>\n <dd className=\"ml-auto text-right font-medium text-text-primary\">\n {attendeeName || '—'}\n <span className=\"block text-xs font-normal text-text-secondary\">\n {attendeeMode === 'existing' ? selectedAttendee?.email : email.trim()}\n </span>\n </dd>\n </div>\n </dl>\n\n {hasPositiveAmount ? (\n <label className=\"block space-y-1 text-sm\">\n <span className=\"font-medium text-text-primary\">Billing account ID (optional)</span>\n <Input\n value={billingAccountId}\n onChange={(e) => setBillingAccountId(e.target.value)}\n placeholder=\"Attach an existing billing account\"\n />\n <span className=\"text-xs text-text-secondary\">\n When provided, the charge is attached to this billing account.\n </span>\n </label>\n ) : null}\n </div>\n ) : null}\n\n {/* Wizard controls */}\n <div className=\"mt-6 flex items-center justify-between gap-3 border-t border-border-seam pt-4\">\n <Button type=\"button\" variant=\"ghost\" onClick={goBack} disabled={step === 0 || saving}>\n <ArrowLeft className=\"h-4 w-4\" />\n Back\n </Button>\n {step < STEPS.length - 1 ? (\n <Button type=\"button\" onClick={goNext}>\n Continue\n <ArrowRight className=\"h-4 w-4\" />\n </Button>\n ) : (\n <Button type=\"button\" onClick={onConfirm} disabled={saving}>\n <Ticket className=\"h-4 w-4\" />\n {saving ? 'Registering…' : 'Confirm registration'}\n </Button>\n )}\n </div>\n </GlassCard>\n\n {/* ORDER SUMMARY */}\n <GlassCard treatment=\"glass\" className=\"h-fit p-5 lg:sticky lg:top-6\">\n <h2 className=\"text-base font-semibold text-text-primary\">Order summary</h2>\n {selectedTicket ? (\n <div className=\"mt-4 space-y-3 text-sm\">\n <div className=\"flex items-start justify-between gap-3\">\n <span className=\"min-w-0 text-text-secondary\">\n {selectedEvent?.name ? `${selectedEvent.name} — ` : ''}\n {selectedTicket.name}\n {qty > 1 ? <span className=\"text-text-muted\"> × {qty}</span> : null}\n </span>\n <span className=\"shrink-0 tabular-nums text-text-primary\">\n {subtotal > 0 ? formatMoney(subtotal, currency) : 'Free'}\n </span>\n </div>\n {appliedPromo && discount > 0 ? (\n <div className=\"flex items-start justify-between gap-3\">\n <span className=\"text-text-secondary\">Promo · {appliedPromo.code}</span>\n <span className=\"shrink-0 tabular-nums text-status-success-text\">\n −{formatMoney(discount, currency)}\n </span>\n </div>\n ) : null}\n <div className=\"flex items-center justify-between border-t border-border-seam pt-3\">\n <span className=\"font-semibold text-text-primary\">Total</span>\n <span className=\"text-lg font-bold tabular-nums text-text-primary\">\n {subtotal > 0 ? formatMoney(total, currency) : 'Free'}\n </span>\n </div>\n {subtotal > 0 ? (\n <p className=\"text-xs text-text-muted\">Final pricing &amp; payment handled by the billing service.</p>\n ) : null}\n </div>\n ) : (\n <p className=\"mt-4 text-sm text-text-muted\">\n Pick a ticket to see the price breakdown. Your order total updates as you go.\n </p>\n )}\n </GlassCard>\n </div>\n </div>\n );\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"RegisterAttendeePage.js","names":[],"sources":["../../../../src/eventfullymanaged/pages/registration/RegisterAttendeePage.tsx"],"sourcesContent":["import { useMemo, useState } from 'react';\nimport {\n ArrowLeft,\n ArrowRight,\n BadgePercent,\n CalendarDays,\n Check,\n CheckCircle2,\n Minus,\n Plus,\n Ticket,\n UserRound,\n} from 'lucide-react';\nimport {\n Button,\n GlassCard,\n GradientText,\n Input,\n PagePurpose,\n Select,\n SelectContent,\n SelectItem,\n SelectTrigger,\n SelectValue,\n Stepper,\n} from '@burdenoff/fe-libs/ui';\n\nimport {\n useAttendeesQuery,\n useCreateAttendeeMutation,\n useCreateRegistrationMutation,\n useEventsQuery,\n usePromoCodesQuery,\n useTicketTypesQuery,\n type PromoCodeGql,\n} from '../../api/eventApi';\nimport { useEventfullyManaged } from '../../context/EventfullyManagedContext';\nimport { computeDiscount, describeDiscount, formatMoney } from '../../lib/format';\nimport { describeError, mutationSucceeded } from '../../lib/mutationResult';\nimport { notify } from '../../lib/toast';\n\ntype Confirmation = {\n attendeeName: string;\n eventName: string;\n ticketName: string;\n quantity: number;\n status: string;\n subtotal: number;\n discount: number;\n total: number;\n currency: string;\n promoCode: string | null;\n free: boolean;\n};\n\nconst STEPS = [\n { key: 'event', label: 'Event', description: 'Choose the event' },\n { key: 'ticket', label: 'Ticket', description: 'Pick a ticket type' },\n { key: 'attendee', label: 'Attendee', description: 'Who is coming' },\n { key: 'confirm', label: 'Confirm', description: 'Review & submit' },\n];\n\nexport default function RegisterAttendeePage() {\n const { navigate } = useEventfullyManaged();\n\n const [step, setStep] = useState(0);\n const [eventId, setEventId] = useState('');\n const [ticketTypeId, setTicketTypeId] = useState('');\n const [attendeeMode, setAttendeeMode] = useState<'new' | 'existing'>('new');\n const [attendeeId, setAttendeeId] = useState('');\n const [fullName, setFullName] = useState('');\n const [email, setEmail] = useState('');\n const [company, setCompany] = useState('');\n const [phone, setPhone] = useState('');\n const [quantity, setQuantity] = useState(1);\n const [promoInput, setPromoInput] = useState('');\n const [appliedPromo, setAppliedPromo] = useState<PromoCodeGql | null>(null);\n const [promoError, setPromoError] = useState('');\n const [billingAccountId, setBillingAccountId] = useState('');\n const [confirmation, setConfirmation] = useState<Confirmation | null>(null);\n\n const { data: eventsData, loading: eventsLoading } = useEventsQuery({ limit: 100, offset: 0 });\n const { data: ticketData, loading: ticketsLoading } = useTicketTypesQuery({\n eventId: eventId || undefined,\n limit: 100,\n offset: 0,\n });\n const { data: attendeesData } = useAttendeesQuery({ eventId: eventId || undefined, limit: 100, offset: 0 });\n const { data: promoData } = usePromoCodesQuery({ eventId: eventId || undefined, limit: 100, offset: 0 });\n\n const events = useMemo(() => eventsData?.events.items ?? [], [eventsData]);\n const tickets = useMemo(\n () =>\n (ticketData?.ticketTypes.items ?? []).filter(\n (ticket) => (!eventId || ticket.eventId === eventId) && ticket.isActive\n ),\n [ticketData, eventId]\n );\n const attendees = useMemo(\n () => (attendeesData?.attendees.items ?? []).filter((attendee) => !eventId || attendee.eventId === eventId),\n [attendeesData, eventId]\n );\n const activePromos = useMemo(\n () =>\n (promoData?.promoCodes.items ?? []).filter((promo) => promo.isActive && (!eventId || promo.eventId === eventId)),\n [promoData, eventId]\n );\n\n const [createAttendee, { loading: creatingAttendee }] = useCreateAttendeeMutation();\n const [createRegistration, { loading: creatingRegistration }] = useCreateRegistrationMutation();\n const saving = creatingAttendee || creatingRegistration;\n\n const selectedEvent = useMemo(() => events.find((item) => item.id === eventId), [events, eventId]);\n const selectedTicket = useMemo(() => tickets.find((ticket) => ticket.id === ticketTypeId), [tickets, ticketTypeId]);\n const selectedAttendee = useMemo(\n () => attendees.find((attendee) => attendee.id === attendeeId),\n [attendees, attendeeId]\n );\n\n const qty = Math.max(1, quantity);\n const unitPrice = selectedTicket ? Number(selectedTicket.price) : 0;\n const currency = selectedTicket?.currency ?? 'USD';\n const subtotal = Number.isFinite(unitPrice) ? unitPrice * qty : 0;\n const discount = appliedPromo\n ? computeDiscount(subtotal, appliedPromo.discountType, Number(appliedPromo.discountValue))\n : 0;\n const total = Math.max(subtotal - discount, 0);\n const hasPositiveAmount = subtotal > 0;\n\n const attendeeName = attendeeMode === 'existing' ? (selectedAttendee?.fullName ?? '') : fullName.trim();\n\n const onEventChange = (value: string) => {\n setEventId(value);\n setTicketTypeId('');\n setAttendeeMode('new');\n setAttendeeId('');\n setAppliedPromo(null);\n setPromoInput('');\n setPromoError('');\n };\n\n const isStepValid = (index: number): boolean => {\n switch (index) {\n case 0:\n return Boolean(eventId);\n case 1:\n return Boolean(ticketTypeId);\n case 2:\n return attendeeMode === 'existing' ? Boolean(attendeeId) : Boolean(fullName.trim() && email.trim());\n default:\n return true;\n }\n };\n\n const canGoTo = (index: number): boolean => {\n if (index <= step) return true;\n for (let i = 0; i < index; i += 1) {\n if (!isStepValid(i)) return false;\n }\n return true;\n };\n\n const goToStep = (index: number) => {\n if (canGoTo(index)) {\n setStep(index);\n }\n };\n\n const goNext = () => {\n if (!isStepValid(step)) {\n const messages = [\n 'Choose an event to continue.',\n 'Pick a ticket type to continue.',\n 'Select or enter an attendee to continue.',\n ];\n notify.error('One more thing', messages[step] ?? 'Complete this step to continue.');\n return;\n }\n setStep((current) => Math.min(current + 1, STEPS.length - 1));\n };\n\n const goBack = () => setStep((current) => Math.max(current - 1, 0));\n\n const applyPromo = () => {\n const code = promoInput.trim().toUpperCase();\n if (!code) {\n setAppliedPromo(null);\n setPromoError('');\n return;\n }\n const match = activePromos.find((promo) => promo.code.toUpperCase() === code);\n if (!match) {\n setAppliedPromo(null);\n setPromoError('That code is not active for this event.');\n return;\n }\n setAppliedPromo(match);\n setPromoError('');\n notify.success('Promo applied', describeDiscount(match.discountType, match.discountValue, currency));\n };\n\n const clearPromo = () => {\n setAppliedPromo(null);\n setPromoInput('');\n setPromoError('');\n };\n\n const resetAll = () => {\n setStep(0);\n setEventId('');\n setTicketTypeId('');\n setAttendeeMode('new');\n setAttendeeId('');\n setFullName('');\n setEmail('');\n setCompany('');\n setPhone('');\n setQuantity(1);\n clearPromo();\n setBillingAccountId('');\n };\n\n const onConfirm = async () => {\n if (!eventId || !ticketTypeId) {\n notify.error('Incomplete', 'Pick an event and ticket first.');\n return;\n }\n if (attendeeMode === 'new' && (!fullName.trim() || !email.trim())) {\n notify.error('Attendee required', 'Enter a name and email, or pick an existing attendee.');\n return;\n }\n if (attendeeMode === 'existing' && !attendeeId) {\n notify.error('Attendee required', 'Select an existing attendee.');\n return;\n }\n\n try {\n let resolvedAttendeeId = attendeeId;\n let resolvedAttendeeName = selectedAttendee?.fullName ?? '';\n\n if (attendeeMode === 'new') {\n const attendeeResponse = await createAttendee({\n variables: {\n input: {\n eventId,\n fullName: fullName.trim(),\n email: email.trim(),\n company: company.trim() || null,\n phone: phone.trim() || null,\n profile: {},\n metadata: {},\n },\n },\n });\n const createdId = attendeeResponse.data?.createAttendee?.id;\n if (!mutationSucceeded(attendeeResponse, createdId, 'Could not create attendee') || !createdId) {\n return;\n }\n resolvedAttendeeId = createdId;\n resolvedAttendeeName = fullName.trim();\n }\n\n // The money leg is owned by global-billing-svc — never price locally. We\n // forward the promo code + optional billing-account reference via metadata\n // so the backend can attach the charge; totalAmount is left for the backend.\n const metadata: Record<string, unknown> = {};\n if (appliedPromo) {\n metadata.promoCode = appliedPromo.code;\n }\n if (hasPositiveAmount && billingAccountId.trim()) {\n metadata.billingAccountId = billingAccountId.trim();\n }\n\n const registrationResponse = await createRegistration({\n variables: {\n input: {\n eventId,\n attendeeId: resolvedAttendeeId,\n ticketTypeId,\n quantity: qty,\n currency,\n status: 'CONFIRMED',\n metadata,\n },\n },\n });\n if (\n !mutationSucceeded(\n registrationResponse,\n registrationResponse.data?.createRegistration?.id,\n 'Could not create registration'\n )\n ) {\n return;\n }\n\n const registration = registrationResponse.data?.createRegistration;\n setConfirmation({\n attendeeName: resolvedAttendeeName || 'Attendee',\n eventName: selectedEvent?.name ?? 'Event',\n ticketName: selectedTicket?.name ?? 'Ticket',\n quantity: qty,\n status: registration?.status ?? 'CONFIRMED',\n subtotal,\n discount,\n total,\n currency,\n promoCode: appliedPromo?.code ?? null,\n free: !hasPositiveAmount,\n });\n notify.success('Registration confirmed', resolvedAttendeeName || undefined);\n } catch (error) {\n notify.error('Could not register attendee', describeError(error));\n }\n };\n\n if (confirmation) {\n return (\n <div className=\"mx-auto max-w-2xl space-y-6 p-6\">\n <div>\n <Button type=\"button\" variant=\"ghost\" size=\"sm\" onClick={() => navigate('/registration')}>\n <ArrowLeft className=\"h-4 w-4\" />\n Back to Registration\n </Button>\n </div>\n <GlassCard treatment=\"glass\" glow className=\"p-8 text-center\">\n <div className=\"mx-auto flex h-14 w-14 items-center justify-center rounded-full bg-status-success-bg-subtle\">\n <CheckCircle2 className=\"h-8 w-8 text-status-success-text\" aria-hidden=\"true\" />\n </div>\n <h1 className=\"mt-4 text-2xl font-bold text-text-primary\">Registration confirmed</h1>\n <p className=\"mt-2 text-sm text-text-secondary\">\n {confirmation.attendeeName} is registered for {confirmation.eventName}.\n </p>\n\n <dl className=\"mx-auto mt-6 max-w-sm space-y-2 rounded-card border border-border-seam bg-bg-surface p-4 text-left text-sm\">\n <div className=\"flex justify-between\">\n <dt className=\"text-text-secondary\">Ticket</dt>\n <dd className=\"font-medium text-text-primary\">\n {confirmation.ticketName}\n {confirmation.quantity > 1 ? ` × ${confirmation.quantity}` : ''}\n </dd>\n </div>\n {!confirmation.free ? (\n <>\n <div className=\"flex justify-between\">\n <dt className=\"text-text-secondary\">Subtotal</dt>\n <dd className=\"tabular-nums text-text-primary\">\n {formatMoney(confirmation.subtotal, confirmation.currency)}\n </dd>\n </div>\n {confirmation.discount > 0 ? (\n <div className=\"flex justify-between\">\n <dt className=\"text-text-secondary\">\n Promo{confirmation.promoCode ? ` · ${confirmation.promoCode}` : ''}\n </dt>\n <dd className=\"tabular-nums text-status-success-text\">\n −{formatMoney(confirmation.discount, confirmation.currency)}\n </dd>\n </div>\n ) : null}\n </>\n ) : null}\n <div className=\"flex justify-between border-t border-border-seam pt-2\">\n <dt className=\"font-semibold text-text-primary\">Total</dt>\n <dd className=\"font-bold tabular-nums text-text-primary\">\n {confirmation.free ? 'Free' : formatMoney(confirmation.total, confirmation.currency)}\n </dd>\n </div>\n <div className=\"flex justify-between\">\n <dt className=\"text-text-secondary\">Status</dt>\n <dd className=\"font-medium text-text-primary\">{confirmation.status}</dd>\n </div>\n </dl>\n {!confirmation.free ? (\n <p className=\"mt-3 text-xs text-text-muted\">\n Final pricing and payment are settled by the billing service.\n </p>\n ) : null}\n\n <div className=\"mt-8 flex flex-col justify-center gap-3 sm:flex-row\">\n <Button type=\"button\" variant=\"outline\" onClick={() => navigate('/registration')}>\n Back to Registrations\n </Button>\n <Button\n type=\"button\"\n onClick={() => {\n setConfirmation(null);\n resetAll();\n }}\n >\n Register another\n </Button>\n </div>\n </GlassCard>\n </div>\n );\n }\n\n return (\n <div className=\"space-y-6 p-6\">\n <div>\n <Button type=\"button\" variant=\"ghost\" size=\"sm\" onClick={() => navigate('/registration')}>\n <ArrowLeft className=\"h-4 w-4\" />\n Back to Registration\n </Button>\n <h1 className=\"mt-3 text-2xl font-bold text-text-primary\">\n <GradientText>Register Attendee</GradientText>\n </h1>\n <p className=\"mt-1 text-sm text-text-secondary\">\n Walk through the event, ticket and attendee in a few quick steps.\n </p>\n </div>\n\n <PagePurpose>\n Register a person for an event against a ticket type. Pick the event and ticket, choose an existing attendee or\n add a new one, apply a promo code if they have one, then confirm. The order summary previews the price — final\n pricing and payment are handled by the billing service.\n </PagePurpose>\n\n <Stepper steps={STEPS} active={step} onStepClick={goToStep} />\n\n <div className=\"grid grid-cols-1 gap-6 lg:grid-cols-[minmax(0,1fr)_20rem]\">\n <GlassCard treatment=\"plain\" className=\"border-border-seam p-5 sm:p-6\">\n {/* STEP 0 — EVENT */}\n {step === 0 ? (\n <div className=\"space-y-4\">\n <div>\n <h2 className=\"text-lg font-semibold text-text-primary\">Which event?</h2>\n <p className=\"mt-1 text-sm text-text-secondary\">Choose the event this attendee is signing up for.</p>\n </div>\n {eventsLoading ? (\n <p className=\"text-sm text-text-muted\">Loading events…</p>\n ) : events.length === 0 ? (\n <div className=\"rounded-card border border-dashed border-border-default p-6 text-center text-sm text-text-secondary\">\n No events yet.{' '}\n <button\n type=\"button\"\n className=\"font-medium text-text-link underline-offset-2 hover:underline\"\n onClick={() => navigate('/events/create')}\n >\n Create an event\n </button>{' '}\n first.\n </div>\n ) : (\n <div className=\"grid grid-cols-1 gap-3 sm:grid-cols-2\">\n {events.map((eventItem) => {\n const selected = eventItem.id === eventId;\n return (\n <button\n key={eventItem.id}\n type=\"button\"\n onClick={() => onEventChange(eventItem.id)}\n aria-pressed={selected}\n className={`flex items-center gap-3 rounded-card border p-4 text-left transition-colors ${\n selected\n ? 'border-action-primary-border bg-accent-wash'\n : 'border-border-seam bg-bg-surface hover:border-border-strong'\n }`}\n >\n <span\n className={`flex h-10 w-10 shrink-0 items-center justify-center rounded-input ${\n selected ? 'bg-action-primary-bg text-action-primary-text' : 'bg-bg-sunken text-text-muted'\n }`}\n >\n <CalendarDays className=\"h-5 w-5\" aria-hidden=\"true\" />\n </span>\n <span className=\"min-w-0 flex-1 truncate text-sm font-medium text-text-primary\">\n {eventItem.name}\n </span>\n {selected ? (\n <Check className=\"h-4 w-4 shrink-0 text-action-primary-bg\" aria-hidden=\"true\" />\n ) : null}\n </button>\n );\n })}\n </div>\n )}\n </div>\n ) : null}\n\n {/* STEP 1 — TICKET */}\n {step === 1 ? (\n <div className=\"space-y-4\">\n <div>\n <h2 className=\"text-lg font-semibold text-text-primary\">Ticket type</h2>\n <p className=\"mt-1 text-sm text-text-secondary\">Pick the ticket and how many they need.</p>\n </div>\n {ticketsLoading ? (\n <p className=\"text-sm text-text-muted\">Loading tickets…</p>\n ) : tickets.length === 0 ? (\n <div className=\"rounded-card border border-dashed border-border-default p-6 text-center text-sm text-text-secondary\">\n This event has no active ticket types.\n </div>\n ) : (\n <div className=\"grid grid-cols-1 gap-3 sm:grid-cols-2 xl:grid-cols-3\">\n {tickets.map((ticket) => {\n const selected = ticket.id === ticketTypeId;\n const cap = ticket.capacity ?? null;\n const sold = ticket.soldCount ?? 0;\n const pct = cap && cap > 0 ? Math.min(100, Math.round((sold / cap) * 100)) : null;\n const soldOut = cap != null && sold >= cap;\n return (\n <button\n key={ticket.id}\n type=\"button\"\n disabled={soldOut && !selected}\n onClick={() => setTicketTypeId(ticket.id)}\n aria-pressed={selected}\n className={`flex flex-col gap-3 rounded-card border p-4 text-left transition-colors disabled:cursor-not-allowed disabled:opacity-60 ${\n selected\n ? 'border-action-primary-border bg-accent-wash ring-1 ring-border-focus'\n : 'border-border-seam bg-bg-surface hover:border-border-strong'\n }`}\n >\n <div className=\"flex items-start justify-between gap-2\">\n <span className=\"text-sm font-semibold text-text-primary\">{ticket.name}</span>\n {selected ? (\n <Check className=\"h-4 w-4 shrink-0 text-action-primary-bg\" aria-hidden=\"true\" />\n ) : (\n <Ticket className=\"h-4 w-4 shrink-0 text-text-muted\" aria-hidden=\"true\" />\n )}\n </div>\n <span className=\"text-lg font-bold tabular-nums text-text-primary\">\n {Number(ticket.price) > 0 ? formatMoney(Number(ticket.price), ticket.currency) : 'Free'}\n </span>\n {cap != null ? (\n <div className=\"space-y-1\">\n <div className=\"h-1.5 w-full overflow-hidden rounded-full bg-bg-sunken\">\n <div\n className=\"h-full rounded-full bg-action-primary-bg\"\n style={{ width: `${pct ?? 0}%` }}\n />\n </div>\n <span className=\"text-xs text-text-muted\">\n {soldOut ? 'Sold out' : `${sold} / ${cap} sold`}\n </span>\n </div>\n ) : (\n <span className=\"text-xs text-text-muted\">{sold} sold</span>\n )}\n </button>\n );\n })}\n </div>\n )}\n\n <div className=\"flex flex-wrap items-end gap-4 border-t border-border-seam pt-4\">\n <div className=\"space-y-1\">\n <span className=\"text-sm font-medium text-text-primary\">Quantity</span>\n <div className=\"flex items-center gap-1\">\n <Button\n type=\"button\"\n variant=\"outline\"\n size=\"icon\"\n aria-label=\"Decrease quantity\"\n disabled={qty <= 1}\n onClick={() => setQuantity((value) => Math.max(1, value - 1))}\n >\n <Minus className=\"h-4 w-4\" />\n </Button>\n <span className=\"w-10 text-center text-base font-semibold tabular-nums text-text-primary\">\n {qty}\n </span>\n <Button\n type=\"button\"\n variant=\"outline\"\n size=\"icon\"\n aria-label=\"Increase quantity\"\n onClick={() => setQuantity((value) => value + 1)}\n >\n <Plus className=\"h-4 w-4\" />\n </Button>\n </div>\n </div>\n </div>\n </div>\n ) : null}\n\n {/* STEP 2 — ATTENDEE */}\n {step === 2 ? (\n <div className=\"space-y-4\">\n <div>\n <h2 className=\"text-lg font-semibold text-text-primary\">Attendee information</h2>\n <p className=\"mt-1 text-sm text-text-secondary\">Add someone new or pick an existing attendee.</p>\n </div>\n\n <div className=\"inline-flex rounded-input border border-border-seam bg-bg-sunken p-1\">\n <button\n type=\"button\"\n onClick={() => setAttendeeMode('new')}\n className={`rounded-[calc(var(--radius-input)-2px)] px-3 py-1.5 text-sm font-medium transition-colors ${\n attendeeMode === 'new'\n ? 'bg-bg-surface text-text-primary shadow-sm'\n : 'text-text-secondary hover:text-text-primary'\n }`}\n >\n New attendee\n </button>\n <button\n type=\"button\"\n onClick={() => setAttendeeMode('existing')}\n disabled={attendees.length === 0}\n className={`rounded-[calc(var(--radius-input)-2px)] px-3 py-1.5 text-sm font-medium transition-colors disabled:cursor-not-allowed disabled:opacity-50 ${\n attendeeMode === 'existing'\n ? 'bg-bg-surface text-text-primary shadow-sm'\n : 'text-text-secondary hover:text-text-primary'\n }`}\n >\n Existing attendee\n </button>\n </div>\n\n {attendeeMode === 'existing' ? (\n <label className=\"block space-y-1 text-sm\">\n <span className=\"font-medium text-text-primary\">Attendee</span>\n <Select value={attendeeId} onValueChange={setAttendeeId}>\n <SelectTrigger aria-label=\"Attendee\">\n <SelectValue placeholder=\"Select an attendee\" />\n </SelectTrigger>\n <SelectContent>\n {attendees.map((attendee) => (\n <SelectItem key={attendee.id} value={attendee.id}>\n {attendee.fullName} · {attendee.email}\n </SelectItem>\n ))}\n </SelectContent>\n </Select>\n </label>\n ) : (\n <div className=\"grid grid-cols-1 gap-4 sm:grid-cols-2\">\n <label className=\"space-y-1 text-sm\">\n <span className=\"font-medium text-text-primary\">Full name</span>\n <Input\n value={fullName}\n onChange={(e) => setFullName(e.target.value)}\n placeholder=\"Enter full name\"\n />\n </label>\n <label className=\"space-y-1 text-sm\">\n <span className=\"font-medium text-text-primary\">Email</span>\n <Input\n type=\"email\"\n value={email}\n onChange={(e) => setEmail(e.target.value)}\n placeholder=\"Enter email address\"\n />\n </label>\n <label className=\"space-y-1 text-sm\">\n <span className=\"font-medium text-text-primary\">Company</span>\n <Input\n value={company}\n onChange={(e) => setCompany(e.target.value)}\n placeholder=\"Enter company name\"\n />\n </label>\n <label className=\"space-y-1 text-sm\">\n <span className=\"font-medium text-text-primary\">Phone</span>\n <Input value={phone} onChange={(e) => setPhone(e.target.value)} placeholder=\"Optional\" />\n </label>\n </div>\n )}\n\n {/* Promo code */}\n <div className=\"space-y-2 border-t border-border-seam pt-4\">\n <span className=\"text-sm font-medium text-text-primary\">Promo code (optional)</span>\n <div className=\"flex flex-col gap-2 sm:flex-row\">\n <Input\n value={promoInput}\n onChange={(e) => {\n setPromoInput(e.target.value);\n setPromoError('');\n }}\n placeholder=\"Enter promo code\"\n className=\"sm:flex-1\"\n onKeyDown={(e) => {\n if (e.key === 'Enter') {\n e.preventDefault();\n applyPromo();\n }\n }}\n />\n {appliedPromo ? (\n <Button type=\"button\" variant=\"outline\" onClick={clearPromo}>\n Remove\n </Button>\n ) : (\n <Button type=\"button\" variant=\"outline\" onClick={applyPromo} disabled={!promoInput.trim()}>\n Apply\n </Button>\n )}\n </div>\n {appliedPromo ? (\n <span className=\"inline-flex items-center gap-1 rounded-full bg-status-success-bg-subtle px-2.5 py-0.5 text-xs font-medium text-status-success-text\">\n <BadgePercent className=\"h-3.5 w-3.5\" aria-hidden=\"true\" />\n {describeDiscount(appliedPromo.discountType, appliedPromo.discountValue, currency)} applied\n </span>\n ) : promoError ? (\n <span className=\"text-xs text-status-error-text\">{promoError}</span>\n ) : activePromos.length > 0 ? (\n <div className=\"flex flex-wrap items-center gap-1.5 text-xs text-text-muted\">\n <span>Available:</span>\n {activePromos.slice(0, 4).map((promo) => (\n <button\n key={promo.id}\n type=\"button\"\n onClick={() => {\n setPromoInput(promo.code);\n setAppliedPromo(promo);\n setPromoError('');\n }}\n className=\"rounded-full border border-border-seam bg-bg-surface px-2 py-0.5 font-medium text-text-secondary hover:border-border-strong hover:text-text-primary\"\n >\n {promo.code}\n </button>\n ))}\n </div>\n ) : null}\n </div>\n </div>\n ) : null}\n\n {/* STEP 3 — CONFIRM */}\n {step === 3 ? (\n <div className=\"space-y-4\">\n <div>\n <h2 className=\"text-lg font-semibold text-text-primary\">Review &amp; confirm</h2>\n <p className=\"mt-1 text-sm text-text-secondary\">Double-check the details before registering.</p>\n </div>\n <dl className=\"divide-y divide-border-seam rounded-card border border-border-seam bg-bg-surface text-sm\">\n <div className=\"flex items-center gap-3 p-3\">\n <CalendarDays className=\"h-4 w-4 text-text-muted\" aria-hidden=\"true\" />\n <dt className=\"text-text-secondary\">Event</dt>\n <dd className=\"ml-auto font-medium text-text-primary\">{selectedEvent?.name ?? '—'}</dd>\n </div>\n <div className=\"flex items-center gap-3 p-3\">\n <Ticket className=\"h-4 w-4 text-text-muted\" aria-hidden=\"true\" />\n <dt className=\"text-text-secondary\">Ticket</dt>\n <dd className=\"ml-auto font-medium text-text-primary\">\n {selectedTicket?.name ?? '—'}\n {qty > 1 ? ` × ${qty}` : ''}\n </dd>\n </div>\n <div className=\"flex items-center gap-3 p-3\">\n <UserRound className=\"h-4 w-4 text-text-muted\" aria-hidden=\"true\" />\n <dt className=\"text-text-secondary\">Attendee</dt>\n <dd className=\"ml-auto text-right font-medium text-text-primary\">\n {attendeeName || '—'}\n <span className=\"block text-xs font-normal text-text-secondary\">\n {attendeeMode === 'existing' ? selectedAttendee?.email : email.trim()}\n </span>\n </dd>\n </div>\n </dl>\n\n {hasPositiveAmount ? (\n <label className=\"block space-y-1 text-sm\">\n <span className=\"font-medium text-text-primary\">Billing account ID (optional)</span>\n <Input\n value={billingAccountId}\n onChange={(e) => setBillingAccountId(e.target.value)}\n placeholder=\"Attach an existing billing account\"\n />\n <span className=\"text-xs text-text-secondary\">\n When provided, the charge is attached to this billing account.\n </span>\n </label>\n ) : null}\n </div>\n ) : null}\n\n {/* Wizard controls */}\n <div className=\"mt-6 flex items-center justify-between gap-3 border-t border-border-seam pt-4\">\n <Button type=\"button\" variant=\"ghost\" onClick={goBack} disabled={step === 0 || saving}>\n <ArrowLeft className=\"h-4 w-4\" />\n Back\n </Button>\n {step < STEPS.length - 1 ? (\n <Button type=\"button\" onClick={goNext}>\n Continue\n <ArrowRight className=\"h-4 w-4\" />\n </Button>\n ) : (\n <Button type=\"button\" onClick={onConfirm} disabled={saving}>\n <Ticket className=\"h-4 w-4\" />\n {saving ? 'Registering…' : 'Confirm registration'}\n </Button>\n )}\n </div>\n </GlassCard>\n\n {/* ORDER SUMMARY */}\n <GlassCard treatment=\"glass\" className=\"h-fit p-5 lg:sticky lg:top-6\">\n <h2 className=\"text-base font-semibold text-text-primary\">Order summary</h2>\n {selectedTicket ? (\n <div className=\"mt-4 space-y-3 text-sm\">\n <div className=\"flex items-start justify-between gap-3\">\n <span className=\"min-w-0 text-text-secondary\">\n {selectedEvent?.name ? `${selectedEvent.name} — ` : ''}\n {selectedTicket.name}\n {qty > 1 ? <span className=\"text-text-muted\"> × {qty}</span> : null}\n </span>\n <span className=\"shrink-0 tabular-nums text-text-primary\">\n {subtotal > 0 ? formatMoney(subtotal, currency) : 'Free'}\n </span>\n </div>\n {appliedPromo && discount > 0 ? (\n <div className=\"flex items-start justify-between gap-3\">\n <span className=\"text-text-secondary\">Promo · {appliedPromo.code}</span>\n <span className=\"shrink-0 tabular-nums text-status-success-text\">\n −{formatMoney(discount, currency)}\n </span>\n </div>\n ) : null}\n <div className=\"flex items-center justify-between border-t border-border-seam pt-3\">\n <span className=\"font-semibold text-text-primary\">Total</span>\n <span className=\"text-lg font-bold tabular-nums text-text-primary\">\n {subtotal > 0 ? formatMoney(total, currency) : 'Free'}\n </span>\n </div>\n {subtotal > 0 ? (\n <p className=\"text-xs text-text-muted\">Final pricing &amp; payment handled by the billing service.</p>\n ) : null}\n </div>\n ) : (\n <p className=\"mt-4 text-sm text-text-muted\">\n Pick a ticket to see the price breakdown. Your order total updates as you go.\n </p>\n )}\n </GlassCard>\n </div>\n </div>\n );\n}\n"],"mappings":"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"}
@@ -0,0 +1,2 @@
1
+ export default function SpeakerFormPage(): import("react/jsx-runtime").JSX.Element;
2
+ //# sourceMappingURL=SpeakerFormPage.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"SpeakerFormPage.d.ts","sourceRoot":"","sources":["../../../../src/eventfullymanaged/pages/speakers/SpeakerFormPage.tsx"],"names":[],"mappings":"AAmFA,MAAM,CAAC,OAAO,UAAU,eAAe,4CAqJtC"}
@@ -0,0 +1,2 @@
1
+ export default function SpeakersPage(): import("react/jsx-runtime").JSX.Element;
2
+ //# sourceMappingURL=SpeakersPage.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"SpeakersPage.d.ts","sourceRoot":"","sources":["../../../../src/eventfullymanaged/pages/speakers/SpeakersPage.tsx"],"names":[],"mappings":"AAgIA,MAAM,CAAC,OAAO,UAAU,YAAY,4CA+JnC"}
@@ -1 +1 @@
1
- {"version":3,"file":"SpeakersPage.js","names":[],"sources":["../../../../src/eventfullymanaged/pages/speakers/SpeakersPage.tsx"],"sourcesContent":["import { gql } from '@apollo/client';\nimport { useQuery } from '@apollo/client/react';\nimport { Plus, Search } from 'lucide-react';\nimport {\n Badge,\n Button,\n GlassCard,\n GradientText,\n IllustratedEmptyState,\n Input,\n PagePurpose,\n Skeleton,\n} from '@burdenoff/fe-libs/ui';\nimport { useMemo, useState } from 'react';\n\nimport { useDeleteSpeakerMutation, useEntityTagsQuery } from '../../api/eventApi';\nimport { ActionMenu } from '../../components/ActionMenu';\nimport { Pagination, usePagination } from '../../components/Pagination';\nimport { useEventfullyManaged } from '../../context/EventfullyManagedContext';\nimport { notify } from '../../lib/toast';\n\n// The foundation SPEAKERS_QUERY only selects the columns other screens need\n// (id/name/title/org/email). The roster grid additionally shows the headshot\n// and the one-line talk abstract, so it reads from this fuller selection rather\n// than mutating the shared hook that Event/Session pages also consume.\nconst SPEAKER_GRID_QUERY = gql`\n query EventfullymanagedSpeakerGrid($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 }\n }\n }\n`;\n\ntype SpeakerRow = {\n id: string;\n fullName: string;\n title?: string | null;\n organization?: string | null;\n bio?: string | null;\n avatarUrl?: string | null;\n};\n\ntype SpeakerGridResponse = { speakers: { total: number; items: SpeakerRow[] } };\n\nfunction initials(name: string): string {\n const parts = name.trim().split(/\\s+/).filter(Boolean);\n if (parts.length === 0) {\n return '?';\n }\n if (parts.length === 1) {\n return parts[0].slice(0, 2).toUpperCase();\n }\n return (parts[0][0] + parts[parts.length - 1][0]).toUpperCase();\n}\n\nfunction SpeakerAvatar({ speaker }: { speaker: SpeakerRow }) {\n const [failed, setFailed] = useState(false);\n const showImage = Boolean(speaker.avatarUrl) && !failed;\n return (\n <span className=\"relative flex h-16 w-16 shrink-0 items-center justify-center overflow-hidden rounded-full bg-bg-sunken ring-1 ring-border-subtle\">\n {showImage ? (\n <img\n src={speaker.avatarUrl ?? ''}\n alt={speaker.fullName}\n loading=\"lazy\"\n onError={() => setFailed(true)}\n className=\"h-full w-full object-cover\"\n />\n ) : (\n <span className=\"text-base font-semibold text-text-secondary\">{initials(speaker.fullName)}</span>\n )}\n </span>\n );\n}\n\n// Read-only topic chips sourced from the platform tags service for this speaker.\n// The mock shows two topic tags per speaker; these are the real assignments.\nfunction SpeakerTopics({ speakerId }: { speakerId: string }) {\n const { data } = useEntityTagsQuery({ input: { entityType: 'EVENTFULLYMANAGED_SPEAKER', entityId: speakerId } });\n const assignments = data?.eventfullyManagedEntityTags ?? [];\n if (assignments.length === 0) {\n return null;\n }\n return (\n <div className=\"mt-3 flex flex-wrap gap-1.5\" role=\"list\" aria-label=\"Topics\">\n {assignments.slice(0, 4).map((assignment) => {\n const info = assignment.tagInfo;\n const color = info?.color ?? undefined;\n return (\n <Badge key={assignment.id} variant=\"secondary\" className=\"gap-1.5\" role=\"listitem\">\n {color ? (\n <span className=\"h-2 w-2 rounded-full\" style={{ backgroundColor: color }} aria-hidden=\"true\" />\n ) : null}\n {info?.label ?? info?.key ?? 'Topic'}\n </Badge>\n );\n })}\n </div>\n );\n}\n\nfunction SpeakerCardSkeleton() {\n return (\n <GlassCard treatment=\"plain\" className=\"border-border-seam p-5\">\n <div className=\"flex items-start gap-4\">\n <Skeleton className=\"h-16 w-16 shrink-0 rounded-full\" />\n <div className=\"min-w-0 flex-1 space-y-2\">\n <Skeleton className=\"h-5 w-32\" />\n <Skeleton className=\"h-4 w-40\" />\n <Skeleton className=\"h-4 w-full\" />\n <div className=\"flex gap-1.5 pt-1\">\n <Skeleton className=\"h-5 w-12 rounded-full\" />\n <Skeleton className=\"h-5 w-16 rounded-full\" />\n </div>\n </div>\n </div>\n </GlassCard>\n );\n}\n\nexport default function SpeakersPage() {\n const { navigate } = useEventfullyManaged();\n const pagination = usePagination({ pageSize: 12 });\n const [search, setSearch] = useState('');\n const { data, loading, refetch } = useQuery<SpeakerGridResponse, { limit?: number; offset?: number }>(\n SPEAKER_GRID_QUERY,\n { variables: { limit: pagination.limit, offset: pagination.offset } }\n );\n const [deleteSpeaker] = useDeleteSpeakerMutation();\n\n const total = data?.speakers?.total ?? 0;\n const speakers = useMemo(() => data?.speakers?.items ?? [], [data]);\n\n const filtered = useMemo(() => {\n const term = search.trim().toLowerCase();\n if (!term) {\n return speakers;\n }\n return speakers.filter((speaker) =>\n [speaker.fullName, speaker.title, speaker.organization, speaker.bio]\n .filter(Boolean)\n .some((field) => (field as string).toLowerCase().includes(term))\n );\n }, [speakers, search]);\n\n const onDelete = async (speaker: SpeakerRow) => {\n const response = await deleteSpeaker({ variables: { id: speaker.id } });\n if (response.error) {\n notify.error('Could not delete speaker', response.error.message);\n throw response.error;\n }\n if (!response.data?.deleteSpeaker) {\n notify.error('Could not delete speaker', 'The server did not confirm the change.');\n throw new Error('delete-unconfirmed');\n }\n notify.success('Speaker deleted', speaker.fullName);\n await refetch();\n };\n\n return (\n <div className=\"space-y-6 p-6\">\n <PagePurpose>\n Speakers are the people who present your sessions. Keep a roster here — their name, role, company and bio — so\n that when you build the agenda you can assign a speaker to each session in a couple of clicks. A good speaker\n profile also gives attendees a reason to attend.\n </PagePurpose>\n\n <div className=\"flex flex-col gap-4 sm:flex-row sm:items-end sm:justify-between\">\n <div>\n <GradientText as=\"h1\" className=\"text-2xl font-bold\">\n Speakers\n </GradientText>\n <p className=\"mt-1 text-sm text-text-secondary\">Manage your speaker roster with live API data.</p>\n </div>\n <div className=\"flex flex-col gap-2 sm:flex-row sm:items-center\">\n <div className=\"relative\">\n <Search className=\"pointer-events-none absolute left-3 top-1/2 h-4 w-4 -translate-y-1/2 text-text-muted\" />\n <Input\n type=\"search\"\n value={search}\n onChange={(event) => setSearch(event.target.value)}\n placeholder=\"Search speakers…\"\n aria-label=\"Search speakers on this page\"\n className=\"pl-10 sm:w-64\"\n />\n </div>\n <Button type=\"button\" onClick={() => navigate('/speakers/create')}>\n <Plus className=\"h-4 w-4\" />\n Add Speaker\n </Button>\n </div>\n </div>\n\n {loading ? (\n <div className=\"grid grid-cols-1 gap-4 sm:grid-cols-2 xl:grid-cols-3\">\n {Array.from({ length: 6 }).map((_, index) => (\n <SpeakerCardSkeleton key={index} />\n ))}\n </div>\n ) : speakers.length === 0 ? (\n <GlassCard treatment=\"plain\" className=\"border-border-seam\">\n <IllustratedEmptyState\n illustration=\"empty-people\"\n title=\"No speakers yet.\"\n description=\"Add your first speaker to start building the agenda.\"\n action={\n <Button type=\"button\" onClick={() => navigate('/speakers/create')}>\n <Plus className=\"h-4 w-4\" />\n Add Speaker\n </Button>\n }\n />\n </GlassCard>\n ) : filtered.length === 0 ? (\n <GlassCard treatment=\"plain\" className=\"border-border-seam\">\n <IllustratedEmptyState\n illustration=\"empty-search\"\n title=\"No speakers match your search.\"\n description=\"Try a different name, title or company.\"\n />\n </GlassCard>\n ) : (\n <div className=\"space-y-4\">\n <div className=\"grid grid-cols-1 gap-4 sm:grid-cols-2 xl:grid-cols-3\">\n {filtered.map((speaker) => {\n const roleLine = [speaker.title, speaker.organization].filter(Boolean).join(', ');\n return (\n <GlassCard\n key={speaker.id}\n treatment=\"plain\"\n className=\"group flex flex-col border-border-seam p-5 transition-shadow duration-200 hover:shadow-elevation-1\"\n >\n <div className=\"flex items-start gap-4\">\n <SpeakerAvatar speaker={speaker} />\n <div className=\"min-w-0 flex-1\">\n <div className=\"flex items-start justify-between gap-2\">\n <button\n type=\"button\"\n onClick={() => navigate(`/speakers/${speaker.id}/edit`)}\n className=\"rounded-button text-left text-base font-semibold text-text-primary transition-colors hover:text-text-link focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-border-focus\"\n >\n {speaker.fullName}\n </button>\n <ActionMenu\n ariaLabel={`Speaker actions for ${speaker.fullName}`}\n entityName={speaker.fullName}\n deleteLabel=\"Delete speaker\"\n onEdit={() => navigate(`/speakers/${speaker.id}/edit`)}\n onDelete={() => onDelete(speaker)}\n />\n </div>\n <p className=\"mt-0.5 truncate text-sm text-text-secondary\">{roleLine || 'Speaker'}</p>\n </div>\n </div>\n\n {speaker.bio ? (\n <p className=\"mt-3 line-clamp-2 text-sm text-text-secondary\">{speaker.bio}</p>\n ) : null}\n\n <SpeakerTopics speakerId={speaker.id} />\n </GlassCard>\n );\n })}\n </div>\n\n {search.trim() ? null : (\n <Pagination\n offset={pagination.offset}\n limit={pagination.limit}\n loadedCount={speakers.length}\n total={total}\n onPrev={pagination.prevPage}\n onNext={pagination.nextPage}\n itemLabel=\"speakers\"\n loading={loading}\n />\n )}\n </div>\n )}\n </div>\n );\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"SpeakersPage.js","names":[],"sources":["../../../../src/eventfullymanaged/pages/speakers/SpeakersPage.tsx"],"sourcesContent":["import { gql } from '@apollo/client';\nimport { useQuery } from '@apollo/client/react';\nimport { Plus, Search } from 'lucide-react';\nimport {\n Badge,\n Button,\n GlassCard,\n GradientText,\n IllustratedEmptyState,\n Input,\n PagePurpose,\n Skeleton,\n} from '@burdenoff/fe-libs/ui';\nimport { useMemo, useState } from 'react';\n\nimport { useDeleteSpeakerMutation, useEntityTagsQuery } from '../../api/eventApi';\nimport { ActionMenu } from '../../components/ActionMenu';\nimport { Pagination, usePagination } from '../../components/Pagination';\nimport { useEventfullyManaged } from '../../context/EventfullyManagedContext';\nimport { notify } from '../../lib/toast';\n\n// The foundation SPEAKERS_QUERY only selects the columns other screens need\n// (id/name/title/org/email). The roster grid additionally shows the headshot\n// and the one-line talk abstract, so it reads from this fuller selection rather\n// than mutating the shared hook that Event/Session pages also consume.\nconst SPEAKER_GRID_QUERY = gql`\n query EventfullymanagedSpeakerGrid($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 }\n }\n }\n`;\n\ntype SpeakerRow = {\n id: string;\n fullName: string;\n title?: string | null;\n organization?: string | null;\n bio?: string | null;\n avatarUrl?: string | null;\n};\n\ntype SpeakerGridResponse = { speakers: { total: number; items: SpeakerRow[] } };\n\nfunction initials(name: string): string {\n const parts = name.trim().split(/\\s+/).filter(Boolean);\n if (parts.length === 0) {\n return '?';\n }\n if (parts.length === 1) {\n return parts[0].slice(0, 2).toUpperCase();\n }\n return (parts[0][0] + parts[parts.length - 1][0]).toUpperCase();\n}\n\nfunction SpeakerAvatar({ speaker }: { speaker: SpeakerRow }) {\n const [failed, setFailed] = useState(false);\n const showImage = Boolean(speaker.avatarUrl) && !failed;\n return (\n <span className=\"relative flex h-16 w-16 shrink-0 items-center justify-center overflow-hidden rounded-full bg-bg-sunken ring-1 ring-border-subtle\">\n {showImage ? (\n <img\n src={speaker.avatarUrl ?? ''}\n alt={speaker.fullName}\n loading=\"lazy\"\n onError={() => setFailed(true)}\n className=\"h-full w-full object-cover\"\n />\n ) : (\n <span className=\"text-base font-semibold text-text-secondary\">{initials(speaker.fullName)}</span>\n )}\n </span>\n );\n}\n\n// Read-only topic chips sourced from the platform tags service for this speaker.\n// The mock shows two topic tags per speaker; these are the real assignments.\nfunction SpeakerTopics({ speakerId }: { speakerId: string }) {\n const { data } = useEntityTagsQuery({ input: { entityType: 'EVENTFULLYMANAGED_SPEAKER', entityId: speakerId } });\n const assignments = data?.eventfullyManagedEntityTags ?? [];\n if (assignments.length === 0) {\n return null;\n }\n return (\n <div className=\"mt-3 flex flex-wrap gap-1.5\" role=\"list\" aria-label=\"Topics\">\n {assignments.slice(0, 4).map((assignment) => {\n const info = assignment.tagInfo;\n const color = info?.color ?? undefined;\n return (\n <Badge key={assignment.id} variant=\"secondary\" className=\"gap-1.5\" role=\"listitem\">\n {color ? (\n <span className=\"h-2 w-2 rounded-full\" style={{ backgroundColor: color }} aria-hidden=\"true\" />\n ) : null}\n {info?.label ?? info?.key ?? 'Topic'}\n </Badge>\n );\n })}\n </div>\n );\n}\n\nfunction SpeakerCardSkeleton() {\n return (\n <GlassCard treatment=\"plain\" className=\"border-border-seam p-5\">\n <div className=\"flex items-start gap-4\">\n <Skeleton className=\"h-16 w-16 shrink-0 rounded-full\" />\n <div className=\"min-w-0 flex-1 space-y-2\">\n <Skeleton className=\"h-5 w-32\" />\n <Skeleton className=\"h-4 w-40\" />\n <Skeleton className=\"h-4 w-full\" />\n <div className=\"flex gap-1.5 pt-1\">\n <Skeleton className=\"h-5 w-12 rounded-full\" />\n <Skeleton className=\"h-5 w-16 rounded-full\" />\n </div>\n </div>\n </div>\n </GlassCard>\n );\n}\n\nexport default function SpeakersPage() {\n const { navigate } = useEventfullyManaged();\n const pagination = usePagination({ pageSize: 12 });\n const [search, setSearch] = useState('');\n const { data, loading, refetch } = useQuery<SpeakerGridResponse, { limit?: number; offset?: number }>(\n SPEAKER_GRID_QUERY,\n { variables: { limit: pagination.limit, offset: pagination.offset } }\n );\n const [deleteSpeaker] = useDeleteSpeakerMutation();\n\n const total = data?.speakers?.total ?? 0;\n const speakers = useMemo(() => data?.speakers?.items ?? [], [data]);\n\n const filtered = useMemo(() => {\n const term = search.trim().toLowerCase();\n if (!term) {\n return speakers;\n }\n return speakers.filter((speaker) =>\n [speaker.fullName, speaker.title, speaker.organization, speaker.bio]\n .filter(Boolean)\n .some((field) => (field as string).toLowerCase().includes(term))\n );\n }, [speakers, search]);\n\n const onDelete = async (speaker: SpeakerRow) => {\n const response = await deleteSpeaker({ variables: { id: speaker.id } });\n if (response.error) {\n notify.error('Could not delete speaker', response.error.message);\n throw response.error;\n }\n if (!response.data?.deleteSpeaker) {\n notify.error('Could not delete speaker', 'The server did not confirm the change.');\n throw new Error('delete-unconfirmed');\n }\n notify.success('Speaker deleted', speaker.fullName);\n await refetch();\n };\n\n return (\n <div className=\"space-y-6 p-6\">\n <PagePurpose>\n Speakers are the people who present your sessions. Keep a roster here — their name, role, company and bio — so\n that when you build the agenda you can assign a speaker to each session in a couple of clicks. A good speaker\n profile also gives attendees a reason to attend.\n </PagePurpose>\n\n <div className=\"flex flex-col gap-4 sm:flex-row sm:items-end sm:justify-between\">\n <div>\n <GradientText as=\"h1\" className=\"text-2xl font-bold\">\n Speakers\n </GradientText>\n <p className=\"mt-1 text-sm text-text-secondary\">Manage your speaker roster with live API data.</p>\n </div>\n <div className=\"flex flex-col gap-2 sm:flex-row sm:items-center\">\n <div className=\"relative\">\n <Search className=\"pointer-events-none absolute left-3 top-1/2 h-4 w-4 -translate-y-1/2 text-text-muted\" />\n <Input\n type=\"search\"\n value={search}\n onChange={(event) => setSearch(event.target.value)}\n placeholder=\"Search speakers…\"\n aria-label=\"Search speakers on this page\"\n className=\"pl-10 sm:w-64\"\n />\n </div>\n <Button type=\"button\" onClick={() => navigate('/speakers/create')}>\n <Plus className=\"h-4 w-4\" />\n Add Speaker\n </Button>\n </div>\n </div>\n\n {loading ? (\n <div className=\"grid grid-cols-1 gap-4 sm:grid-cols-2 xl:grid-cols-3\">\n {Array.from({ length: 6 }).map((_, index) => (\n <SpeakerCardSkeleton key={index} />\n ))}\n </div>\n ) : speakers.length === 0 ? (\n <GlassCard treatment=\"plain\" className=\"border-border-seam\">\n <IllustratedEmptyState\n illustration=\"empty-people\"\n title=\"No speakers yet.\"\n description=\"Add your first speaker to start building the agenda.\"\n action={\n <Button type=\"button\" onClick={() => navigate('/speakers/create')}>\n <Plus className=\"h-4 w-4\" />\n Add Speaker\n </Button>\n }\n />\n </GlassCard>\n ) : filtered.length === 0 ? (\n <GlassCard treatment=\"plain\" className=\"border-border-seam\">\n <IllustratedEmptyState\n illustration=\"empty-search\"\n title=\"No speakers match your search.\"\n description=\"Try a different name, title or company.\"\n />\n </GlassCard>\n ) : (\n <div className=\"space-y-4\">\n <div className=\"grid grid-cols-1 gap-4 sm:grid-cols-2 xl:grid-cols-3\">\n {filtered.map((speaker) => {\n const roleLine = [speaker.title, speaker.organization].filter(Boolean).join(', ');\n return (\n <GlassCard\n key={speaker.id}\n treatment=\"plain\"\n className=\"group flex flex-col border-border-seam p-5 transition-shadow duration-200 hover:shadow-elevation-1\"\n >\n <div className=\"flex items-start gap-4\">\n <SpeakerAvatar speaker={speaker} />\n <div className=\"min-w-0 flex-1\">\n <div className=\"flex items-start justify-between gap-2\">\n <button\n type=\"button\"\n onClick={() => navigate(`/speakers/${speaker.id}/edit`)}\n className=\"rounded-button text-left text-base font-semibold text-text-primary transition-colors hover:text-text-link focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-border-focus\"\n >\n {speaker.fullName}\n </button>\n <ActionMenu\n ariaLabel={`Speaker actions for ${speaker.fullName}`}\n entityName={speaker.fullName}\n deleteLabel=\"Delete speaker\"\n onEdit={() => navigate(`/speakers/${speaker.id}/edit`)}\n onDelete={() => onDelete(speaker)}\n />\n </div>\n <p className=\"mt-0.5 truncate text-sm text-text-secondary\">{roleLine || 'Speaker'}</p>\n </div>\n </div>\n\n {speaker.bio ? <p className=\"mt-3 line-clamp-2 text-sm text-text-secondary\">{speaker.bio}</p> : null}\n\n <SpeakerTopics speakerId={speaker.id} />\n </GlassCard>\n );\n })}\n </div>\n\n {search.trim() ? null : (\n <Pagination\n offset={pagination.offset}\n limit={pagination.limit}\n loadedCount={speakers.length}\n total={total}\n onPrev={pagination.prevPage}\n onNext={pagination.nextPage}\n itemLabel=\"speakers\"\n loading={loading}\n />\n )}\n </div>\n )}\n </div>\n );\n}\n"],"mappings":"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"}
@@ -0,0 +1,2 @@
1
+ export default function SponsorFormPage(): import("react/jsx-runtime").JSX.Element;
2
+ //# sourceMappingURL=SponsorFormPage.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"SponsorFormPage.d.ts","sourceRoot":"","sources":["../../../../src/eventfullymanaged/pages/sponsors/SponsorFormPage.tsx"],"names":[],"mappings":"AAsDA,MAAM,CAAC,OAAO,UAAU,eAAe,4CA8NtC"}
@@ -0,0 +1,2 @@
1
+ export default function SponsorsListPage(): import("react/jsx-runtime").JSX.Element;
2
+ //# sourceMappingURL=SponsorsListPage.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"SponsorsListPage.d.ts","sourceRoot":"","sources":["../../../../src/eventfullymanaged/pages/sponsors/SponsorsListPage.tsx"],"names":[],"mappings":"AA0KA,MAAM,CAAC,OAAO,UAAU,gBAAgB,4CAgJvC"}
@@ -1 +1 @@
1
- {"version":3,"file":"SponsorsListPage.js","names":[],"sources":["../../../../src/eventfullymanaged/pages/sponsors/SponsorsListPage.tsx"],"sourcesContent":["import { Building2, Coins, Magnet, Plus, Search, TrendingUp, Users } from 'lucide-react';\nimport type { LucideIcon } from 'lucide-react';\nimport { Badge, Button, GlassCard, IllustratedEmptyState, Input, PagePurpose } from '@burdenoff/fe-libs/ui';\nimport { useMemo, useState } from 'react';\n\nimport { useDeleteSponsorMutation, useSponsorsQuery } from '../../api/eventApi';\nimport { ActionMenu } from '../../components/ActionMenu';\nimport { SafeLink } from '../../components/SafeLink';\nimport { useEventfullyManaged } from '../../context/EventfullyManagedContext';\nimport { notify } from '../../lib/toast';\n\ntype SponsorMetrics = { value?: number; leads?: number; leadsTarget?: number };\n\ntype SponsorItem = {\n id: string;\n eventId: string;\n name: string;\n level: string;\n logoUrl?: string | null;\n website?: string | null;\n contactName?: string | null;\n contactEmail?: string | null;\n contactPhone?: string | null;\n metrics?: SponsorMetrics | null;\n};\n\n/** Tier order + accent token. Colours resolve to semantic chart tokens only. */\nconst TIERS: { key: string; label: string; accent: string }[] = [\n { key: 'PLATINUM', label: 'Platinum', accent: 'var(--color-chart-2)' },\n { key: 'GOLD', label: 'Gold', accent: 'var(--color-chart-4)' },\n { key: 'SILVER', label: 'Silver', accent: 'var(--color-chart-3)' },\n { key: 'BRONZE', label: 'Bronze', accent: 'var(--color-chart-5)' },\n { key: 'COMMUNITY', label: 'Community', accent: 'var(--color-chart-1)' },\n];\nconst OTHER_TIER = { key: 'OTHER', label: 'Other', accent: 'var(--color-chart-1)' };\n\nconst compactNumber = new Intl.NumberFormat(undefined, { notation: 'compact', maximumFractionDigits: 1 });\n\nfunction StatTile({ icon: Icon, label, value }: { icon: LucideIcon; label: string; value: string }) {\n return (\n <GlassCard treatment=\"metric\" className=\"p-4\">\n <div className=\"flex items-center gap-3\">\n <span className=\"flex h-10 w-10 shrink-0 items-center justify-center rounded-full bg-bg-sunken text-accent-primary\">\n <Icon className=\"h-5 w-5\" />\n </span>\n <div className=\"min-w-0\">\n <p className=\"text-xs font-medium uppercase tracking-wide text-text-secondary\">{label}</p>\n <p className=\"mt-0.5 text-2xl font-bold tabular-nums text-text-primary\">{value}</p>\n </div>\n </div>\n </GlassCard>\n );\n}\n\nfunction LeadsMeter({ leads, target }: { leads: number; target: number }) {\n const pct = target > 0 ? Math.min(100, Math.round((leads / target) * 100)) : 0;\n return (\n <div>\n <div\n className=\"h-2 w-full overflow-hidden rounded-full bg-bg-sunken\"\n role=\"progressbar\"\n aria-valuenow={pct}\n aria-valuemin={0}\n aria-valuemax={100}\n aria-label=\"Leads progress\"\n >\n <div\n className=\"h-full rounded-full transition-[width] duration-500\"\n style={{\n width: `${pct}%`,\n background: 'linear-gradient(90deg, var(--color-chart-1), var(--color-chart-2))',\n }}\n />\n </div>\n <div className=\"mt-1.5 flex items-center justify-between text-xs text-text-secondary\">\n <span>{pct}%</span>\n <span className=\"tabular-nums\">\n {leads.toLocaleString()}\n {target > 0 ? ` / ${target.toLocaleString()} leads target` : ' leads'}\n </span>\n </div>\n </div>\n );\n}\n\nfunction SponsorCard({\n sponsor,\n maxLeads,\n onView,\n onEdit,\n onDelete,\n onViewLeads,\n}: {\n sponsor: SponsorItem;\n maxLeads: number;\n onView: () => void;\n onEdit: () => void;\n onDelete: () => void | Promise<void>;\n onViewLeads: () => void;\n}) {\n const value = Number(sponsor.metrics?.value ?? 0);\n const leads = Number(sponsor.metrics?.leads ?? 0);\n const target = Number(sponsor.metrics?.leadsTarget ?? 0) || maxLeads;\n\n return (\n <GlassCard treatment=\"plain\" className=\"flex flex-col gap-3 border-border-seam p-4\">\n <div className=\"flex items-start justify-between gap-3\">\n <div className=\"flex min-w-0 items-center gap-3\">\n <span className=\"flex h-11 w-11 shrink-0 items-center justify-center overflow-hidden rounded-card border border-border-subtle bg-bg-sunken\">\n {sponsor.logoUrl ? (\n <img src={sponsor.logoUrl} alt=\"\" className=\"h-full w-full object-cover\" />\n ) : (\n <Building2 className=\"h-5 w-5 text-text-muted\" />\n )}\n </span>\n <div className=\"min-w-0\">\n <button\n type=\"button\"\n onClick={onView}\n className=\"rounded-button text-left text-base font-semibold text-text-primary transition-colors hover:text-text-link focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-[var(--color-focus-ring)]\"\n >\n {sponsor.name}\n </button>\n <p className=\"mt-0.5 text-xs text-text-secondary\">\n Contract value: <span className=\"font-semibold text-text-primary\">${compactNumber.format(value)}</span>\n </p>\n </div>\n </div>\n <div className=\"flex shrink-0 items-center gap-1\">\n <Badge variant=\"secondary\" className=\"uppercase\">\n {sponsor.level}\n </Badge>\n <ActionMenu\n ariaLabel={`Actions for ${sponsor.name}`}\n entityName={sponsor.name}\n onView={onView}\n onEdit={onEdit}\n onDelete={onDelete}\n deleteLabel=\"Delete sponsor\"\n />\n </div>\n </div>\n\n <LeadsMeter leads={leads} target={target} />\n\n <div className=\"flex items-center justify-between border-t border-border-seam pt-3 text-xs\">\n {sponsor.website ? (\n <SafeLink\n href={sponsor.website}\n className=\"truncate text-text-link hover:underline\"\n fallbackClassName=\"truncate text-text-secondary\"\n >\n {sponsor.website}\n </SafeLink>\n ) : (\n <span className=\"text-text-muted\">No website</span>\n )}\n <button\n type=\"button\"\n onClick={onViewLeads}\n className=\"ml-2 flex shrink-0 items-center gap-1 rounded-button font-medium text-text-link transition-colors hover:text-accent-primary focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-[var(--color-focus-ring)]\"\n >\n <TrendingUp className=\"h-3.5 w-3.5\" />\n View leads\n </button>\n </div>\n </GlassCard>\n );\n}\n\nexport default function SponsorsListPage() {\n const { navigate } = useEventfullyManaged();\n const [search, setSearch] = useState('');\n // Load the full portfolio (not paginated) so tier grouping + rollups are\n // accurate; the sponsor roster is a small, curated list per workspace.\n const { data, loading, refetch } = useSponsorsQuery({ search, limit: 200, offset: 0 });\n const [deleteSponsor] = useDeleteSponsorMutation();\n\n const sponsors = useMemo(() => (data?.sponsors?.items ?? []) as SponsorItem[], [data]);\n const total = data?.sponsors?.total ?? 0;\n\n const portfolioValue = sponsors.reduce((sum, sponsor) => sum + Number(sponsor.metrics?.value ?? 0), 0);\n const portfolioLeads = sponsors.reduce((sum, sponsor) => sum + Number(sponsor.metrics?.leads ?? 0), 0);\n const maxLeads = sponsors.reduce((max, sponsor) => Math.max(max, Number(sponsor.metrics?.leads ?? 0)), 0);\n\n const grouped = useMemo(() => {\n const buckets = new Map<string, SponsorItem[]>();\n for (const sponsor of sponsors) {\n const tier = TIERS.find((t) => t.key === sponsor.level.toUpperCase()) ? sponsor.level.toUpperCase() : OTHER_TIER.key;\n const list = buckets.get(tier) ?? [];\n list.push(sponsor);\n buckets.set(tier, list);\n }\n const ordered = [...TIERS, OTHER_TIER]\n .map((tier) => ({ tier, items: buckets.get(tier.key) ?? [] }))\n .filter((group) => group.items.length > 0);\n return ordered;\n }, [sponsors]);\n\n const handleDelete = async (sponsor: SponsorItem) => {\n const response = await deleteSponsor({ variables: { id: sponsor.id } });\n if (response.error) {\n throw new Error(response.error.message);\n }\n if (!response.data?.deleteSponsor) {\n notify.error('Could not delete sponsor', 'The server did not confirm the deletion. Please try again.');\n throw new Error('Deletion not confirmed');\n }\n await refetch();\n notify.success('Sponsor deleted');\n };\n\n return (\n <div className=\"space-y-6 p-6\">\n <PagePurpose>\n Sponsors fund your event in exchange for visibility and leads. Grouped by tier, each card shows the contract\n value they bring and their progress toward a leads target — so you can prove ROI and keep the relationship warm\n for next time. The portfolio tiles summarise the whole roster at a glance.\n </PagePurpose>\n\n <div className=\"flex flex-col gap-4 sm:flex-row sm:items-center sm:justify-between\">\n <div>\n <h1 className=\"text-2xl font-bold text-text-primary\">Sponsors</h1>\n <p className=\"mt-1 text-sm text-text-secondary\">Manage your event partners by category.</p>\n </div>\n <Button type=\"button\" onClick={() => navigate('/sponsors/create')}>\n <Plus className=\"h-4 w-4\" />\n Add Sponsor\n </Button>\n </div>\n\n <div>\n <h2 className=\"text-sm font-semibold uppercase tracking-wide text-text-secondary\">Portfolio</h2>\n <div className=\"mt-3 grid grid-cols-1 gap-4 md:grid-cols-3\">\n <StatTile icon={Users} label=\"Total Sponsors\" value={total.toLocaleString()} />\n <StatTile icon={Coins} label=\"Contract Value\" value={`$${compactNumber.format(portfolioValue)}`} />\n <StatTile icon={Magnet} label=\"Leads\" value={portfolioLeads.toLocaleString()} />\n </div>\n </div>\n\n <div className=\"relative\">\n <Search className=\"pointer-events-none absolute left-3 top-1/2 h-4 w-4 -translate-y-1/2 text-text-muted\" />\n <Input\n type=\"text\"\n value={search}\n onChange={(event) => setSearch(event.target.value)}\n placeholder=\"Search sponsors\"\n aria-label=\"Search sponsors\"\n className=\"pl-10\"\n />\n </div>\n\n {loading && sponsors.length === 0 ? (\n <GlassCard treatment=\"plain\" className=\"border-border-seam p-8 text-center text-sm text-text-secondary\">\n Loading sponsors...\n </GlassCard>\n ) : sponsors.length === 0 ? (\n <GlassCard treatment=\"plain\" className=\"border-border-seam\">\n <IllustratedEmptyState\n illustration={search ? 'empty-search' : 'empty-people'}\n title={search ? 'No sponsors match your search.' : 'No sponsors yet.'}\n description={search ? 'Try a different name.' : 'Add your first sponsor to start tracking value and leads.'}\n action={\n !search ? (\n <Button type=\"button\" onClick={() => navigate('/sponsors/create')}>\n <Plus className=\"h-4 w-4\" />\n Add Sponsor\n </Button>\n ) : undefined\n }\n />\n </GlassCard>\n ) : (\n <div className=\"space-y-8\">\n <div>\n <h2 className=\"text-lg font-semibold text-text-primary\">Sponsor Tiers</h2>\n <p className=\"text-sm text-text-secondary\">Manage your event partners by category.</p>\n </div>\n {grouped.map(({ tier, items }) => (\n <section key={tier.key} className=\"space-y-3\">\n <div\n className=\"flex items-center justify-between rounded-card border border-border-seam px-4 py-2.5 text-sm font-semibold uppercase tracking-wide\"\n style={{\n background: `color-mix(in oklab, ${tier.accent} 14%, var(--color-bg-surface))`,\n color: `color-mix(in oklab, ${tier.accent} 72%, var(--color-text-primary))`,\n }}\n >\n <span className=\"flex items-center gap-2.5\">\n <span aria-hidden className=\"h-4 w-1.5 rounded-full\" style={{ backgroundColor: tier.accent }} />\n {tier.label}\n </span>\n <span className=\"tabular-nums\">{items.length}</span>\n </div>\n <div className=\"grid grid-cols-1 gap-4 md:grid-cols-2 xl:grid-cols-3\">\n {items.map((sponsor) => (\n <SponsorCard\n key={sponsor.id}\n sponsor={sponsor}\n maxLeads={maxLeads}\n onView={() => navigate(`/sponsors/${sponsor.id}`)}\n onEdit={() => navigate(`/sponsors/${sponsor.id}/edit`)}\n onDelete={() => handleDelete(sponsor)}\n onViewLeads={() => navigate(`/sponsors/${sponsor.id}/leads`)}\n />\n ))}\n </div>\n </section>\n ))}\n </div>\n )}\n </div>\n );\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"SponsorsListPage.js","names":[],"sources":["../../../../src/eventfullymanaged/pages/sponsors/SponsorsListPage.tsx"],"sourcesContent":["import { Building2, Coins, Magnet, Plus, Search, TrendingUp, Users } from 'lucide-react';\nimport type { LucideIcon } from 'lucide-react';\nimport { Badge, Button, GlassCard, IllustratedEmptyState, Input, PagePurpose } from '@burdenoff/fe-libs/ui';\nimport { useMemo, useState } from 'react';\n\nimport { useDeleteSponsorMutation, useSponsorsQuery } from '../../api/eventApi';\nimport { ActionMenu } from '../../components/ActionMenu';\nimport { SafeLink } from '../../components/SafeLink';\nimport { useEventfullyManaged } from '../../context/EventfullyManagedContext';\nimport { notify } from '../../lib/toast';\n\ntype SponsorMetrics = { value?: number; leads?: number; leadsTarget?: number };\n\ntype SponsorItem = {\n id: string;\n eventId: string;\n name: string;\n level: string;\n logoUrl?: string | null;\n website?: string | null;\n contactName?: string | null;\n contactEmail?: string | null;\n contactPhone?: string | null;\n metrics?: SponsorMetrics | null;\n};\n\n/** Tier order + accent token. Colours resolve to semantic chart tokens only. */\nconst TIERS: { key: string; label: string; accent: string }[] = [\n { key: 'PLATINUM', label: 'Platinum', accent: 'var(--color-chart-2)' },\n { key: 'GOLD', label: 'Gold', accent: 'var(--color-chart-4)' },\n { key: 'SILVER', label: 'Silver', accent: 'var(--color-chart-3)' },\n { key: 'BRONZE', label: 'Bronze', accent: 'var(--color-chart-5)' },\n { key: 'COMMUNITY', label: 'Community', accent: 'var(--color-chart-1)' },\n];\nconst OTHER_TIER = { key: 'OTHER', label: 'Other', accent: 'var(--color-chart-1)' };\n\nconst compactNumber = new Intl.NumberFormat(undefined, { notation: 'compact', maximumFractionDigits: 1 });\n\nfunction StatTile({ icon: Icon, label, value }: { icon: LucideIcon; label: string; value: string }) {\n return (\n <GlassCard treatment=\"metric\" className=\"p-4\">\n <div className=\"flex items-center gap-3\">\n <span className=\"flex h-10 w-10 shrink-0 items-center justify-center rounded-full bg-bg-sunken text-accent-primary\">\n <Icon className=\"h-5 w-5\" />\n </span>\n <div className=\"min-w-0\">\n <p className=\"text-xs font-medium uppercase tracking-wide text-text-secondary\">{label}</p>\n <p className=\"mt-0.5 text-2xl font-bold tabular-nums text-text-primary\">{value}</p>\n </div>\n </div>\n </GlassCard>\n );\n}\n\nfunction LeadsMeter({ leads, target }: { leads: number; target: number }) {\n const pct = target > 0 ? Math.min(100, Math.round((leads / target) * 100)) : 0;\n return (\n <div>\n <div\n className=\"h-2 w-full overflow-hidden rounded-full bg-bg-sunken\"\n role=\"progressbar\"\n aria-valuenow={pct}\n aria-valuemin={0}\n aria-valuemax={100}\n aria-label=\"Leads progress\"\n >\n <div\n className=\"h-full rounded-full transition-[width] duration-500\"\n style={{\n width: `${pct}%`,\n background: 'linear-gradient(90deg, var(--color-chart-1), var(--color-chart-2))',\n }}\n />\n </div>\n <div className=\"mt-1.5 flex items-center justify-between text-xs text-text-secondary\">\n <span>{pct}%</span>\n <span className=\"tabular-nums\">\n {leads.toLocaleString()}\n {target > 0 ? ` / ${target.toLocaleString()} leads target` : ' leads'}\n </span>\n </div>\n </div>\n );\n}\n\nfunction SponsorCard({\n sponsor,\n maxLeads,\n onView,\n onEdit,\n onDelete,\n onViewLeads,\n}: {\n sponsor: SponsorItem;\n maxLeads: number;\n onView: () => void;\n onEdit: () => void;\n onDelete: () => void | Promise<void>;\n onViewLeads: () => void;\n}) {\n const value = Number(sponsor.metrics?.value ?? 0);\n const leads = Number(sponsor.metrics?.leads ?? 0);\n const target = Number(sponsor.metrics?.leadsTarget ?? 0) || maxLeads;\n\n return (\n <GlassCard treatment=\"plain\" className=\"flex flex-col gap-3 border-border-seam p-4\">\n <div className=\"flex items-start justify-between gap-3\">\n <div className=\"flex min-w-0 items-center gap-3\">\n <span className=\"flex h-11 w-11 shrink-0 items-center justify-center overflow-hidden rounded-card border border-border-subtle bg-bg-sunken\">\n {sponsor.logoUrl ? (\n <img src={sponsor.logoUrl} alt=\"\" className=\"h-full w-full object-cover\" />\n ) : (\n <Building2 className=\"h-5 w-5 text-text-muted\" />\n )}\n </span>\n <div className=\"min-w-0\">\n <button\n type=\"button\"\n onClick={onView}\n className=\"rounded-button text-left text-base font-semibold text-text-primary transition-colors hover:text-text-link focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-[var(--color-focus-ring)]\"\n >\n {sponsor.name}\n </button>\n <p className=\"mt-0.5 text-xs text-text-secondary\">\n Contract value: <span className=\"font-semibold text-text-primary\">${compactNumber.format(value)}</span>\n </p>\n </div>\n </div>\n <div className=\"flex shrink-0 items-center gap-1\">\n <Badge variant=\"secondary\" className=\"uppercase\">\n {sponsor.level}\n </Badge>\n <ActionMenu\n ariaLabel={`Actions for ${sponsor.name}`}\n entityName={sponsor.name}\n onView={onView}\n onEdit={onEdit}\n onDelete={onDelete}\n deleteLabel=\"Delete sponsor\"\n />\n </div>\n </div>\n\n <LeadsMeter leads={leads} target={target} />\n\n <div className=\"flex items-center justify-between border-t border-border-seam pt-3 text-xs\">\n {sponsor.website ? (\n <SafeLink\n href={sponsor.website}\n className=\"truncate text-text-link hover:underline\"\n fallbackClassName=\"truncate text-text-secondary\"\n >\n {sponsor.website}\n </SafeLink>\n ) : (\n <span className=\"text-text-muted\">No website</span>\n )}\n <button\n type=\"button\"\n onClick={onViewLeads}\n className=\"ml-2 flex shrink-0 items-center gap-1 rounded-button font-medium text-text-link transition-colors hover:text-accent-primary focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-[var(--color-focus-ring)]\"\n >\n <TrendingUp className=\"h-3.5 w-3.5\" />\n View leads\n </button>\n </div>\n </GlassCard>\n );\n}\n\nexport default function SponsorsListPage() {\n const { navigate } = useEventfullyManaged();\n const [search, setSearch] = useState('');\n // Load the full portfolio (not paginated) so tier grouping + rollups are\n // accurate; the sponsor roster is a small, curated list per workspace.\n const { data, loading, refetch } = useSponsorsQuery({ search, limit: 200, offset: 0 });\n const [deleteSponsor] = useDeleteSponsorMutation();\n\n const sponsors = useMemo(() => (data?.sponsors?.items ?? []) as SponsorItem[], [data]);\n const total = data?.sponsors?.total ?? 0;\n\n const portfolioValue = sponsors.reduce((sum, sponsor) => sum + Number(sponsor.metrics?.value ?? 0), 0);\n const portfolioLeads = sponsors.reduce((sum, sponsor) => sum + Number(sponsor.metrics?.leads ?? 0), 0);\n const maxLeads = sponsors.reduce((max, sponsor) => Math.max(max, Number(sponsor.metrics?.leads ?? 0)), 0);\n\n const grouped = useMemo(() => {\n const buckets = new Map<string, SponsorItem[]>();\n for (const sponsor of sponsors) {\n const tier = TIERS.find((t) => t.key === sponsor.level.toUpperCase())\n ? sponsor.level.toUpperCase()\n : OTHER_TIER.key;\n const list = buckets.get(tier) ?? [];\n list.push(sponsor);\n buckets.set(tier, list);\n }\n const ordered = [...TIERS, OTHER_TIER]\n .map((tier) => ({ tier, items: buckets.get(tier.key) ?? [] }))\n .filter((group) => group.items.length > 0);\n return ordered;\n }, [sponsors]);\n\n const handleDelete = async (sponsor: SponsorItem) => {\n const response = await deleteSponsor({ variables: { id: sponsor.id } });\n if (response.error) {\n throw new Error(response.error.message);\n }\n if (!response.data?.deleteSponsor) {\n notify.error('Could not delete sponsor', 'The server did not confirm the deletion. Please try again.');\n throw new Error('Deletion not confirmed');\n }\n await refetch();\n notify.success('Sponsor deleted');\n };\n\n return (\n <div className=\"space-y-6 p-6\">\n <PagePurpose>\n Sponsors fund your event in exchange for visibility and leads. Grouped by tier, each card shows the contract\n value they bring and their progress toward a leads target — so you can prove ROI and keep the relationship warm\n for next time. The portfolio tiles summarise the whole roster at a glance.\n </PagePurpose>\n\n <div className=\"flex flex-col gap-4 sm:flex-row sm:items-center sm:justify-between\">\n <div>\n <h1 className=\"text-2xl font-bold text-text-primary\">Sponsors</h1>\n <p className=\"mt-1 text-sm text-text-secondary\">Manage your event partners by category.</p>\n </div>\n <Button type=\"button\" onClick={() => navigate('/sponsors/create')}>\n <Plus className=\"h-4 w-4\" />\n Add Sponsor\n </Button>\n </div>\n\n <div>\n <h2 className=\"text-sm font-semibold uppercase tracking-wide text-text-secondary\">Portfolio</h2>\n <div className=\"mt-3 grid grid-cols-1 gap-4 md:grid-cols-3\">\n <StatTile icon={Users} label=\"Total Sponsors\" value={total.toLocaleString()} />\n <StatTile icon={Coins} label=\"Contract Value\" value={`$${compactNumber.format(portfolioValue)}`} />\n <StatTile icon={Magnet} label=\"Leads\" value={portfolioLeads.toLocaleString()} />\n </div>\n </div>\n\n <div className=\"relative\">\n <Search className=\"pointer-events-none absolute left-3 top-1/2 h-4 w-4 -translate-y-1/2 text-text-muted\" />\n <Input\n type=\"text\"\n value={search}\n onChange={(event) => setSearch(event.target.value)}\n placeholder=\"Search sponsors\"\n aria-label=\"Search sponsors\"\n className=\"pl-10\"\n />\n </div>\n\n {loading && sponsors.length === 0 ? (\n <GlassCard treatment=\"plain\" className=\"border-border-seam p-8 text-center text-sm text-text-secondary\">\n Loading sponsors...\n </GlassCard>\n ) : sponsors.length === 0 ? (\n <GlassCard treatment=\"plain\" className=\"border-border-seam\">\n <IllustratedEmptyState\n illustration={search ? 'empty-search' : 'empty-people'}\n title={search ? 'No sponsors match your search.' : 'No sponsors yet.'}\n description={search ? 'Try a different name.' : 'Add your first sponsor to start tracking value and leads.'}\n action={\n !search ? (\n <Button type=\"button\" onClick={() => navigate('/sponsors/create')}>\n <Plus className=\"h-4 w-4\" />\n Add Sponsor\n </Button>\n ) : undefined\n }\n />\n </GlassCard>\n ) : (\n <div className=\"space-y-8\">\n <div>\n <h2 className=\"text-lg font-semibold text-text-primary\">Sponsor Tiers</h2>\n <p className=\"text-sm text-text-secondary\">Manage your event partners by category.</p>\n </div>\n {grouped.map(({ tier, items }) => (\n <section key={tier.key} className=\"space-y-3\">\n <div\n className=\"flex items-center justify-between rounded-card border border-border-seam px-4 py-2.5 text-sm font-semibold uppercase tracking-wide\"\n style={{\n background: `color-mix(in oklab, ${tier.accent} 14%, var(--color-bg-surface))`,\n color: `color-mix(in oklab, ${tier.accent} 72%, var(--color-text-primary))`,\n }}\n >\n <span className=\"flex items-center gap-2.5\">\n <span aria-hidden className=\"h-4 w-1.5 rounded-full\" style={{ backgroundColor: tier.accent }} />\n {tier.label}\n </span>\n <span className=\"tabular-nums\">{items.length}</span>\n </div>\n <div className=\"grid grid-cols-1 gap-4 md:grid-cols-2 xl:grid-cols-3\">\n {items.map((sponsor) => (\n <SponsorCard\n key={sponsor.id}\n sponsor={sponsor}\n maxLeads={maxLeads}\n onView={() => navigate(`/sponsors/${sponsor.id}`)}\n onEdit={() => navigate(`/sponsors/${sponsor.id}/edit`)}\n onDelete={() => handleDelete(sponsor)}\n onViewLeads={() => navigate(`/sponsors/${sponsor.id}/leads`)}\n />\n ))}\n </div>\n </section>\n ))}\n </div>\n )}\n </div>\n );\n}\n"],"mappings":"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"}
@@ -0,0 +1,2 @@
1
+ export default function FloorPlansPage(): import("react/jsx-runtime").JSX.Element;
2
+ //# sourceMappingURL=FloorPlansPage.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"FloorPlansPage.d.ts","sourceRoot":"","sources":["../../../../src/eventfullymanaged/pages/venues/FloorPlansPage.tsx"],"names":[],"mappings":"AA+NA,MAAM,CAAC,OAAO,UAAU,cAAc,4CA8PrC"}