@burdenoff/microfe-eventfullymanaged 2026.703.2 → 2026.704.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 (74) hide show
  1. package/dist/eventfullymanaged/EventfullyManagedRoutes.js +154 -66
  2. package/dist/eventfullymanaged/EventfullyManagedRoutes.js.map +1 -1
  3. package/dist/eventfullymanaged/api/eventApi.js +546 -67
  4. package/dist/eventfullymanaged/api/eventApi.js.map +1 -1
  5. package/dist/eventfullymanaged/components/ActionMenu.js +113 -0
  6. package/dist/eventfullymanaged/components/ActionMenu.js.map +1 -0
  7. package/dist/eventfullymanaged/components/EntityFormShell.js +51 -0
  8. package/dist/eventfullymanaged/components/EntityFormShell.js.map +1 -0
  9. package/dist/eventfullymanaged/components/EntityTagsField.js +146 -0
  10. package/dist/eventfullymanaged/components/EntityTagsField.js.map +1 -0
  11. package/dist/eventfullymanaged/components/Pagination.js +84 -0
  12. package/dist/eventfullymanaged/components/Pagination.js.map +1 -0
  13. package/dist/eventfullymanaged/components/SafeLink.js +32 -0
  14. package/dist/eventfullymanaged/components/SafeLink.js.map +1 -0
  15. package/dist/eventfullymanaged/components/index.js +7 -0
  16. package/dist/eventfullymanaged/pages/events/EventDetailPage.js +458 -0
  17. package/dist/eventfullymanaged/pages/events/EventDetailPage.js.map +1 -0
  18. package/dist/eventfullymanaged/pages/events/EventFormPage.js +267 -0
  19. package/dist/eventfullymanaged/pages/events/EventFormPage.js.map +1 -0
  20. package/dist/eventfullymanaged/pages/events/SessionFormPage.js +308 -0
  21. package/dist/eventfullymanaged/pages/events/SessionFormPage.js.map +1 -0
  22. package/dist/eventfullymanaged/pages/events/TicketTypeFormPage.js +214 -0
  23. package/dist/eventfullymanaged/pages/events/TicketTypeFormPage.js.map +1 -0
  24. package/dist/eventfullymanaged/pages/events/eventFormUtils.js +128 -0
  25. package/dist/eventfullymanaged/pages/events/eventFormUtils.js.map +1 -0
  26. package/dist/eventfullymanaged/pages/exhibitors/ExhibitorFormPage.js +233 -0
  27. package/dist/eventfullymanaged/pages/exhibitors/ExhibitorFormPage.js.map +1 -0
  28. package/dist/eventfullymanaged/pages/exhibitors/ExhibitorsPage.js +165 -0
  29. package/dist/eventfullymanaged/pages/exhibitors/ExhibitorsPage.js.map +1 -0
  30. package/dist/eventfullymanaged/pages/leads/LeadCaptureDialog.js +141 -0
  31. package/dist/eventfullymanaged/pages/leads/LeadCaptureDialog.js.map +1 -0
  32. package/dist/eventfullymanaged/pages/modules/AnalyticsPage.js +178 -45
  33. package/dist/eventfullymanaged/pages/modules/AnalyticsPage.js.map +1 -1
  34. package/dist/eventfullymanaged/pages/modules/CommunicationPage.js +240 -70
  35. package/dist/eventfullymanaged/pages/modules/CommunicationPage.js.map +1 -1
  36. package/dist/eventfullymanaged/pages/modules/EventsPage.js +133 -97
  37. package/dist/eventfullymanaged/pages/modules/EventsPage.js.map +1 -1
  38. package/dist/eventfullymanaged/pages/modules/KioskPage.js +746 -36
  39. package/dist/eventfullymanaged/pages/modules/KioskPage.js.map +1 -1
  40. package/dist/eventfullymanaged/pages/modules/NetworkingPage.js +210 -69
  41. package/dist/eventfullymanaged/pages/modules/NetworkingPage.js.map +1 -1
  42. package/dist/eventfullymanaged/pages/modules/RegistrationPage.js +289 -77
  43. package/dist/eventfullymanaged/pages/modules/RegistrationPage.js.map +1 -1
  44. package/dist/eventfullymanaged/pages/modules/RoadmapPanel.js +95 -0
  45. package/dist/eventfullymanaged/pages/modules/RoadmapPanel.js.map +1 -0
  46. package/dist/eventfullymanaged/pages/modules/SponsorDetailsPage.js +77 -56
  47. package/dist/eventfullymanaged/pages/modules/SponsorDetailsPage.js.map +1 -1
  48. package/dist/eventfullymanaged/pages/modules/SponsorLeadsPage.js +70 -36
  49. package/dist/eventfullymanaged/pages/modules/SponsorLeadsPage.js.map +1 -1
  50. package/dist/eventfullymanaged/pages/networking/MeetingFormPage.js +343 -0
  51. package/dist/eventfullymanaged/pages/networking/MeetingFormPage.js.map +1 -0
  52. package/dist/eventfullymanaged/pages/networking/NetworkingProfileFormPage.js +236 -0
  53. package/dist/eventfullymanaged/pages/networking/NetworkingProfileFormPage.js.map +1 -0
  54. package/dist/eventfullymanaged/pages/registration/AttendeeFormPage.js +181 -0
  55. package/dist/eventfullymanaged/pages/registration/AttendeeFormPage.js.map +1 -0
  56. package/dist/eventfullymanaged/pages/registration/PromoCodeFormPage.js +262 -0
  57. package/dist/eventfullymanaged/pages/registration/PromoCodeFormPage.js.map +1 -0
  58. package/dist/eventfullymanaged/pages/registration/RegisterAttendeePage.js +414 -0
  59. package/dist/eventfullymanaged/pages/registration/RegisterAttendeePage.js.map +1 -0
  60. package/dist/eventfullymanaged/pages/speakers/SpeakerFormPage.js +224 -0
  61. package/dist/eventfullymanaged/pages/speakers/SpeakerFormPage.js.map +1 -0
  62. package/dist/eventfullymanaged/pages/speakers/SpeakersPage.js +96 -0
  63. package/dist/eventfullymanaged/pages/speakers/SpeakersPage.js.map +1 -0
  64. package/dist/eventfullymanaged/pages/sponsors/SponsorFormPage.js +149 -140
  65. package/dist/eventfullymanaged/pages/sponsors/SponsorFormPage.js.map +1 -1
  66. package/dist/eventfullymanaged/pages/sponsors/SponsorsListPage.js +97 -67
  67. package/dist/eventfullymanaged/pages/sponsors/SponsorsListPage.js.map +1 -1
  68. package/dist/eventfullymanaged/pages/venues/VenueDetailPage.js +93 -72
  69. package/dist/eventfullymanaged/pages/venues/VenueDetailPage.js.map +1 -1
  70. package/dist/eventfullymanaged/pages/venues/VenueFormPage.js +91 -89
  71. package/dist/eventfullymanaged/pages/venues/VenueFormPage.js.map +1 -1
  72. package/dist/eventfullymanaged/pages/venues/VenueListPage.js +83 -53
  73. package/dist/eventfullymanaged/pages/venues/VenueListPage.js.map +1 -1
  74. package/package.json +1 -1
@@ -0,0 +1,308 @@
1
+ import { useEventfullyManaged as e } from "../../context/EventfullyManagedContext.js";
2
+ import { gql as t } from "../../../node_modules/graphql-tag/lib/index.js";
3
+ import { useQuery as n } from "../../../node_modules/@apollo/client/react/hooks/useQuery.js";
4
+ import { notify as r } from "../../lib/toast.js";
5
+ import { useCreateEventSessionMutation as i, useEventQuery as a, useSpeakersQuery as o, useUpdateEventSessionMutation as s, useVenuesQuery as c } from "../../api/eventApi.js";
6
+ import { SESSION_STATUS_OPTIONS as l, SESSION_TYPE_OPTIONS as u, inputValueToIso as d, isoToInputValue as f } from "./eventFormUtils.js";
7
+ import { EntityFormShell as p } from "../../components/EntityFormShell.js";
8
+ import { describeError as m, mutationSucceeded as h } from "../../lib/mutationResult.js";
9
+ import { EntityTagsField as g } from "../../components/EntityTagsField.js";
10
+ import { useEffect as _, useMemo as v, useState as y } from "react";
11
+ import { useParams as b } from "react-router-dom";
12
+ import { jsx as x, jsxs as S } from "react/jsx-runtime";
13
+ import { Input as C, Select as w, SelectContent as T, SelectItem as E, SelectTrigger as D, SelectValue as O, Textarea as k } from "@burdenoff/fe-libs/ui";
14
+ //#region src/eventfullymanaged/pages/events/SessionFormPage.tsx
15
+ var A = "speakerId", j = t`
16
+ query EventfullymanagedSessionFormSessions($eventId: ID, $limit: Int, $offset: Int) {
17
+ eventSessions: eventfullyManagedEventSessions(eventId: $eventId, limit: $limit, offset: $offset) {
18
+ total
19
+ items {
20
+ id
21
+ eventId
22
+ venueId
23
+ title
24
+ description
25
+ sessionType
26
+ track
27
+ startsAt
28
+ endsAt
29
+ capacity
30
+ status
31
+ metadata
32
+ }
33
+ }
34
+ }
35
+ `;
36
+ function M() {
37
+ let e = /* @__PURE__ */ new Date(), t = new Date(e.getTime() + 3600 * 1e3);
38
+ return {
39
+ title: "",
40
+ sessionType: "TALK",
41
+ track: "",
42
+ venueId: "",
43
+ speakerId: "",
44
+ startsAt: f(e.toISOString()),
45
+ endsAt: f(t.toISOString()),
46
+ capacity: "",
47
+ status: "SCHEDULED",
48
+ description: ""
49
+ };
50
+ }
51
+ function N() {
52
+ let { id: t = "", sid: N = "" } = b(), P = v(() => !!N, [N]), { navigate: F } = e(), { data: I } = a(t), { data: L } = n(j, {
53
+ variables: {
54
+ eventId: t,
55
+ limit: 100,
56
+ offset: 0
57
+ },
58
+ skip: !N
59
+ }), { data: R } = c({
60
+ limit: 100,
61
+ offset: 0
62
+ }), { data: z } = o({
63
+ limit: 100,
64
+ offset: 0
65
+ }), [B, { loading: V }] = i(), [H, { loading: U }] = s(), [W, G] = y(M), K = v(() => (L?.eventSessions?.items ?? []).find((e) => e.id === N), [L, N]);
66
+ _(() => {
67
+ if (!P || !K) return;
68
+ let e = K.metadata ?? {};
69
+ G({
70
+ title: K.title ?? "",
71
+ sessionType: K.sessionType ?? "TALK",
72
+ track: K.track ?? "",
73
+ venueId: K.venueId ?? "",
74
+ speakerId: typeof e[A] == "string" ? e[A] : "",
75
+ startsAt: f(K.startsAt),
76
+ endsAt: f(K.endsAt),
77
+ capacity: K.capacity == null ? "" : String(K.capacity),
78
+ status: K.status ?? "SCHEDULED",
79
+ description: K.description ?? ""
80
+ });
81
+ }, [K, P]);
82
+ let q = R?.venues?.items ?? [], J = z?.speakers?.items ?? [], Y = I?.event?.name ?? "Event", X = (e, t) => {
83
+ G((n) => ({
84
+ ...n,
85
+ [e]: t
86
+ }));
87
+ }, Z = async (e) => {
88
+ if (e.preventDefault(), !W.title.trim()) {
89
+ r.error("Title required", "Give the session a title before saving.");
90
+ return;
91
+ }
92
+ let n = d(W.startsAt), i = d(W.endsAt);
93
+ if (!n || !i) {
94
+ r.error("Times required", "Set both a start and an end time.");
95
+ return;
96
+ }
97
+ if (new Date(i).getTime() < new Date(n).getTime()) {
98
+ r.error("Invalid times", "The end time cannot be before the start time.");
99
+ return;
100
+ }
101
+ let a = K?.metadata ?? {}, o = {
102
+ eventId: t,
103
+ title: W.title.trim(),
104
+ sessionType: W.sessionType,
105
+ track: W.track || null,
106
+ venueId: W.venueId || null,
107
+ startsAt: n,
108
+ endsAt: i,
109
+ capacity: W.capacity ? Number(W.capacity) : null,
110
+ status: W.status,
111
+ description: W.description || null,
112
+ metadata: {
113
+ ...a,
114
+ [A]: W.speakerId || null
115
+ }
116
+ };
117
+ try {
118
+ if (P) {
119
+ let e = await H({ variables: {
120
+ id: N,
121
+ input: o
122
+ } });
123
+ if (!h(e, e.data?.updateEventSession?.id, "Could not update session")) return;
124
+ r.success("Session updated"), F(`/events/${t}`);
125
+ return;
126
+ }
127
+ let e = await B({ variables: { input: o } });
128
+ if (!h(e, e.data?.createEventSession?.id, "Could not create session")) return;
129
+ r.success("Session created"), F(`/events/${t}`);
130
+ } catch (e) {
131
+ r.error("Could not save session", m(e));
132
+ }
133
+ }, Q = V || U;
134
+ return /* @__PURE__ */ S(p, {
135
+ title: P ? "Edit Session" : "Add Session",
136
+ description: `Agenda item for ${Y}.`,
137
+ purpose: "Sessions make up the agenda attendees follow on the day — a talk, workshop or panel with a time, a room and a speaker. Set the slot and assign a room so two sessions never clash, and pick the speaker who will present it.",
138
+ backLabel: "Back to Event",
139
+ onBack: () => F(`/events/${t}`),
140
+ onSubmit: Z,
141
+ submitLabel: "Save Session",
142
+ saving: Q,
143
+ children: [
144
+ /* @__PURE__ */ S("div", {
145
+ className: "grid grid-cols-1 gap-4 md:grid-cols-2",
146
+ children: [/* @__PURE__ */ S("label", {
147
+ className: "space-y-1 text-sm",
148
+ children: [/* @__PURE__ */ x("span", {
149
+ className: "font-medium text-text-primary",
150
+ children: "Title"
151
+ }), /* @__PURE__ */ x(C, {
152
+ value: W.title,
153
+ onChange: (e) => X("title", e.target.value),
154
+ required: !0
155
+ })]
156
+ }), /* @__PURE__ */ S("label", {
157
+ className: "space-y-1 text-sm",
158
+ children: [/* @__PURE__ */ x("span", {
159
+ className: "font-medium text-text-primary",
160
+ children: "Track"
161
+ }), /* @__PURE__ */ x(C, {
162
+ value: W.track,
163
+ onChange: (e) => X("track", e.target.value),
164
+ placeholder: "Main stage, Product, Engineering…"
165
+ })]
166
+ })]
167
+ }),
168
+ /* @__PURE__ */ S("div", {
169
+ className: "grid grid-cols-1 gap-4 md:grid-cols-3",
170
+ children: [
171
+ /* @__PURE__ */ S("label", {
172
+ className: "space-y-1 text-sm",
173
+ children: [/* @__PURE__ */ x("span", {
174
+ className: "font-medium text-text-primary",
175
+ children: "Room / Venue"
176
+ }), /* @__PURE__ */ S(w, {
177
+ value: W.venueId || "none",
178
+ onValueChange: (e) => X("venueId", e === "none" ? "" : e),
179
+ children: [/* @__PURE__ */ x(D, {
180
+ "aria-label": "Room or venue",
181
+ children: /* @__PURE__ */ x(O, { placeholder: "Select a room" })
182
+ }), /* @__PURE__ */ S(T, { children: [/* @__PURE__ */ x(E, {
183
+ value: "none",
184
+ children: "No room"
185
+ }), q.map((e) => /* @__PURE__ */ x(E, {
186
+ value: e.id,
187
+ children: e.name
188
+ }, e.id))] })]
189
+ })]
190
+ }),
191
+ /* @__PURE__ */ S("label", {
192
+ className: "space-y-1 text-sm",
193
+ children: [/* @__PURE__ */ x("span", {
194
+ className: "font-medium text-text-primary",
195
+ children: "Speaker"
196
+ }), /* @__PURE__ */ S(w, {
197
+ value: W.speakerId || "none",
198
+ onValueChange: (e) => X("speakerId", e === "none" ? "" : e),
199
+ children: [/* @__PURE__ */ x(D, {
200
+ "aria-label": "Speaker",
201
+ children: /* @__PURE__ */ x(O, { placeholder: "Select a speaker" })
202
+ }), /* @__PURE__ */ S(T, { children: [/* @__PURE__ */ x(E, {
203
+ value: "none",
204
+ children: "No speaker"
205
+ }), J.map((e) => /* @__PURE__ */ x(E, {
206
+ value: e.id,
207
+ children: e.fullName
208
+ }, e.id))] })]
209
+ })]
210
+ }),
211
+ /* @__PURE__ */ S("label", {
212
+ className: "space-y-1 text-sm",
213
+ children: [/* @__PURE__ */ x("span", {
214
+ className: "font-medium text-text-primary",
215
+ children: "Capacity"
216
+ }), /* @__PURE__ */ x(C, {
217
+ type: "number",
218
+ min: "0",
219
+ value: W.capacity,
220
+ onChange: (e) => X("capacity", e.target.value)
221
+ })]
222
+ })
223
+ ]
224
+ }),
225
+ /* @__PURE__ */ S("div", {
226
+ className: "grid grid-cols-1 gap-4 md:grid-cols-2",
227
+ children: [/* @__PURE__ */ S("label", {
228
+ className: "space-y-1 text-sm",
229
+ children: [/* @__PURE__ */ x("span", {
230
+ className: "font-medium text-text-primary",
231
+ children: "Start"
232
+ }), /* @__PURE__ */ x(C, {
233
+ type: "datetime-local",
234
+ value: W.startsAt,
235
+ onChange: (e) => X("startsAt", e.target.value),
236
+ required: !0
237
+ })]
238
+ }), /* @__PURE__ */ S("label", {
239
+ className: "space-y-1 text-sm",
240
+ children: [/* @__PURE__ */ x("span", {
241
+ className: "font-medium text-text-primary",
242
+ children: "End"
243
+ }), /* @__PURE__ */ x(C, {
244
+ type: "datetime-local",
245
+ value: W.endsAt,
246
+ onChange: (e) => X("endsAt", e.target.value),
247
+ required: !0
248
+ })]
249
+ })]
250
+ }),
251
+ /* @__PURE__ */ S("div", {
252
+ className: "grid grid-cols-1 gap-4 md:grid-cols-2",
253
+ children: [/* @__PURE__ */ S("label", {
254
+ className: "space-y-1 text-sm",
255
+ children: [/* @__PURE__ */ x("span", {
256
+ className: "font-medium text-text-primary",
257
+ children: "Type"
258
+ }), /* @__PURE__ */ S(w, {
259
+ value: W.sessionType,
260
+ onValueChange: (e) => X("sessionType", e),
261
+ children: [/* @__PURE__ */ x(D, {
262
+ "aria-label": "Session type",
263
+ children: /* @__PURE__ */ x(O, {})
264
+ }), /* @__PURE__ */ x(T, { children: u.map((e) => /* @__PURE__ */ x(E, {
265
+ value: e.value,
266
+ children: e.label
267
+ }, e.value)) })]
268
+ })]
269
+ }), /* @__PURE__ */ S("label", {
270
+ className: "space-y-1 text-sm",
271
+ children: [/* @__PURE__ */ x("span", {
272
+ className: "font-medium text-text-primary",
273
+ children: "Status"
274
+ }), /* @__PURE__ */ S(w, {
275
+ value: W.status,
276
+ onValueChange: (e) => X("status", e),
277
+ children: [/* @__PURE__ */ x(D, {
278
+ "aria-label": "Status",
279
+ children: /* @__PURE__ */ x(O, {})
280
+ }), /* @__PURE__ */ x(T, { children: l.map((e) => /* @__PURE__ */ x(E, {
281
+ value: e.value,
282
+ children: e.label
283
+ }, e.value)) })]
284
+ })]
285
+ })]
286
+ }),
287
+ /* @__PURE__ */ S("label", {
288
+ className: "block space-y-1 text-sm",
289
+ children: [/* @__PURE__ */ x("span", {
290
+ className: "font-medium text-text-primary",
291
+ children: "Description"
292
+ }), /* @__PURE__ */ x(k, {
293
+ value: W.description,
294
+ onChange: (e) => X("description", e.target.value),
295
+ rows: 3
296
+ })]
297
+ }),
298
+ P ? /* @__PURE__ */ x(g, {
299
+ entityType: "session",
300
+ entityId: N
301
+ }) : null
302
+ ]
303
+ });
304
+ }
305
+ //#endregion
306
+ export { N as default };
307
+
308
+ //# sourceMappingURL=SessionFormPage.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"SessionFormPage.js","names":[],"sources":["../../../../src/eventfullymanaged/pages/events/SessionFormPage.tsx"],"sourcesContent":["/* eslint-disable react-hooks/set-state-in-effect */\nimport { gql } from '@apollo/client';\nimport { useQuery } from '@apollo/client/react';\nimport {\n Input,\n Select,\n SelectContent,\n SelectItem,\n SelectTrigger,\n SelectValue,\n Textarea,\n} from '@burdenoff/fe-libs/ui';\nimport { useEffect, useMemo, useState } from 'react';\nimport { useParams } from 'react-router-dom';\n\nimport {\n useCreateEventSessionMutation,\n useEventQuery,\n useSpeakersQuery,\n useUpdateEventSessionMutation,\n useVenuesQuery,\n} from '../../api/eventApi';\nimport { EntityFormShell } from '../../components/EntityFormShell';\nimport { EntityTagsField } from '../../components/EntityTagsField';\nimport { useEventfullyManaged } from '../../context/EventfullyManagedContext';\nimport { describeError, mutationSucceeded } from '../../lib/mutationResult';\nimport { notify } from '../../lib/toast';\nimport {\n inputValueToIso,\n isoToInputValue,\n SESSION_STATUS_OPTIONS,\n SESSION_TYPE_OPTIONS,\n} from './eventFormUtils';\n\nconst SPEAKER_METADATA_KEY = 'speakerId';\n\n// The foundation EVENT_SESSIONS_QUERY omits description/capacity/metadata, but the\n// backend `updateEventSession` resolver is a FULL replace — editing with a partial\n// initial state would blank those columns. There is no by-id session resolver, so\n// this fuller list query is the edit-hydration source: it selects every editable\n// field and the form finds its row by id client-side.\nconst FULL_EVENT_SESSIONS_QUERY = gql`\n query EventfullymanagedSessionFormSessions($eventId: ID, $limit: Int, $offset: Int) {\n eventSessions: eventfullyManagedEventSessions(eventId: $eventId, limit: $limit, offset: $offset) {\n total\n items {\n id\n eventId\n venueId\n title\n description\n sessionType\n track\n startsAt\n endsAt\n capacity\n status\n metadata\n }\n }\n }\n`;\n\ntype FullSessionsResponse = {\n eventSessions: { total: number; items: SessionListItem[] };\n};\n\ntype SessionListItem = {\n id: string;\n eventId: string;\n venueId?: string | null;\n title: string;\n sessionType: string;\n track?: string | null;\n startsAt: string;\n endsAt: string;\n status: string;\n capacity?: number | null;\n description?: string | null;\n metadata?: Record<string, unknown> | null;\n};\n\ntype SessionFormState = {\n title: string;\n sessionType: string;\n track: string;\n venueId: string;\n speakerId: string;\n startsAt: string;\n endsAt: string;\n capacity: string;\n status: string;\n description: string;\n};\n\nfunction defaultState(): SessionFormState {\n const now = new Date();\n const later = new Date(now.getTime() + 60 * 60 * 1000);\n return {\n title: '',\n sessionType: 'TALK',\n track: '',\n venueId: '',\n speakerId: '',\n startsAt: isoToInputValue(now.toISOString()),\n endsAt: isoToInputValue(later.toISOString()),\n capacity: '',\n status: 'SCHEDULED',\n description: '',\n };\n}\n\nexport default function SessionFormPage() {\n const { id = '', sid = '' } = useParams<{ id: string; sid: string }>();\n const isEdit = useMemo(() => Boolean(sid), [sid]);\n const { navigate } = useEventfullyManaged();\n\n const { data: eventData } = useEventQuery(id);\n // No by-id session resolver — derive the edit initial state from the (fuller) list query.\n const { data: sessionsData } = useQuery<FullSessionsResponse, { eventId?: string; limit?: number; offset?: number }>(\n FULL_EVENT_SESSIONS_QUERY,\n { variables: { eventId: id, limit: 100, offset: 0 }, skip: !sid }\n );\n const { data: venuesData } = useVenuesQuery({ limit: 100, offset: 0 });\n const { data: speakersData } = useSpeakersQuery({ limit: 100, offset: 0 });\n const [createSession, { loading: creating }] = useCreateEventSessionMutation();\n const [updateSession, { loading: updating }] = useUpdateEventSessionMutation();\n const [form, setForm] = useState<SessionFormState>(defaultState);\n\n const existingSession = useMemo(\n () => (sessionsData?.eventSessions?.items ?? []).find((item) => item.id === sid),\n [sessionsData, sid]\n );\n\n useEffect(() => {\n if (!isEdit || !existingSession) {\n return;\n }\n const metadata = (existingSession.metadata ?? {}) as Record<string, unknown>;\n setForm({\n title: existingSession.title ?? '',\n sessionType: existingSession.sessionType ?? 'TALK',\n track: existingSession.track ?? '',\n venueId: existingSession.venueId ?? '',\n speakerId: typeof metadata[SPEAKER_METADATA_KEY] === 'string' ? (metadata[SPEAKER_METADATA_KEY] as string) : '',\n startsAt: isoToInputValue(existingSession.startsAt),\n endsAt: isoToInputValue(existingSession.endsAt),\n capacity: existingSession.capacity != null ? String(existingSession.capacity) : '',\n status: existingSession.status ?? 'SCHEDULED',\n description: existingSession.description ?? '',\n });\n }, [existingSession, isEdit]);\n\n const venues = venuesData?.venues?.items ?? [];\n const speakers = (speakersData?.speakers?.items ?? []) as Array<{ id: string; fullName: string }>;\n const eventName = eventData?.event?.name ?? 'Event';\n\n const onChange = (field: keyof SessionFormState, value: string) => {\n setForm((prev) => ({ ...prev, [field]: value }));\n };\n\n const onSubmit = async (event: React.FormEvent<HTMLFormElement>) => {\n event.preventDefault();\n\n if (!form.title.trim()) {\n notify.error('Title required', 'Give the session a title before saving.');\n return;\n }\n const startIso = inputValueToIso(form.startsAt);\n const endIso = inputValueToIso(form.endsAt);\n if (!startIso || !endIso) {\n notify.error('Times required', 'Set both a start and an end time.');\n return;\n }\n if (new Date(endIso).getTime() < new Date(startIso).getTime()) {\n notify.error('Invalid times', 'The end time cannot be before the start time.');\n return;\n }\n\n const existingMetadata = (existingSession?.metadata ?? {}) as Record<string, unknown>;\n const input = {\n eventId: id,\n title: form.title.trim(),\n sessionType: form.sessionType,\n track: form.track || null,\n venueId: form.venueId || null,\n startsAt: startIso,\n endsAt: endIso,\n capacity: form.capacity ? Number(form.capacity) : null,\n status: form.status,\n description: form.description || null,\n metadata: { ...existingMetadata, [SPEAKER_METADATA_KEY]: form.speakerId || null },\n };\n\n try {\n if (isEdit) {\n const response = await updateSession({ variables: { id: sid, input } });\n if (!mutationSucceeded(response, response.data?.updateEventSession?.id, 'Could not update session')) {\n return;\n }\n notify.success('Session updated');\n navigate(`/events/${id}`);\n return;\n }\n const response = await createSession({ variables: { input } });\n if (!mutationSucceeded(response, response.data?.createEventSession?.id, 'Could not create session')) {\n return;\n }\n notify.success('Session created');\n navigate(`/events/${id}`);\n } catch (error) {\n notify.error('Could not save session', describeError(error));\n }\n };\n\n const saving = creating || updating;\n\n return (\n <EntityFormShell\n title={isEdit ? 'Edit Session' : 'Add Session'}\n description={`Agenda item for ${eventName}.`}\n purpose=\"Sessions make up the agenda attendees follow on the day — a talk, workshop or panel with a time, a room and a speaker. Set the slot and assign a room so two sessions never clash, and pick the speaker who will present it.\"\n backLabel=\"Back to Event\"\n onBack={() => navigate(`/events/${id}`)}\n onSubmit={onSubmit}\n submitLabel=\"Save Session\"\n saving={saving}\n >\n <div className=\"grid grid-cols-1 gap-4 md:grid-cols-2\">\n <label className=\"space-y-1 text-sm\">\n <span className=\"font-medium text-text-primary\">Title</span>\n <Input value={form.title} onChange={(e) => onChange('title', e.target.value)} required />\n </label>\n <label className=\"space-y-1 text-sm\">\n <span className=\"font-medium text-text-primary\">Track</span>\n <Input\n value={form.track}\n onChange={(e) => onChange('track', e.target.value)}\n placeholder=\"Main stage, Product, Engineering…\"\n />\n </label>\n </div>\n\n <div className=\"grid grid-cols-1 gap-4 md:grid-cols-3\">\n <label className=\"space-y-1 text-sm\">\n <span className=\"font-medium text-text-primary\">Room / Venue</span>\n <Select\n value={form.venueId || 'none'}\n onValueChange={(value) => onChange('venueId', value === 'none' ? '' : value)}\n >\n <SelectTrigger aria-label=\"Room or venue\">\n <SelectValue placeholder=\"Select a room\" />\n </SelectTrigger>\n <SelectContent>\n <SelectItem value=\"none\">No room</SelectItem>\n {venues.map((venue: { id: string; name: string }) => (\n <SelectItem key={venue.id} value={venue.id}>\n {venue.name}\n </SelectItem>\n ))}\n </SelectContent>\n </Select>\n </label>\n <label className=\"space-y-1 text-sm\">\n <span className=\"font-medium text-text-primary\">Speaker</span>\n <Select\n value={form.speakerId || 'none'}\n onValueChange={(value) => onChange('speakerId', value === 'none' ? '' : value)}\n >\n <SelectTrigger aria-label=\"Speaker\">\n <SelectValue placeholder=\"Select a speaker\" />\n </SelectTrigger>\n <SelectContent>\n <SelectItem value=\"none\">No speaker</SelectItem>\n {speakers.map((speaker) => (\n <SelectItem key={speaker.id} value={speaker.id}>\n {speaker.fullName}\n </SelectItem>\n ))}\n </SelectContent>\n </Select>\n </label>\n <label className=\"space-y-1 text-sm\">\n <span className=\"font-medium text-text-primary\">Capacity</span>\n <Input\n type=\"number\"\n min=\"0\"\n value={form.capacity}\n onChange={(e) => onChange('capacity', e.target.value)}\n />\n </label>\n </div>\n\n <div className=\"grid grid-cols-1 gap-4 md:grid-cols-2\">\n <label className=\"space-y-1 text-sm\">\n <span className=\"font-medium text-text-primary\">Start</span>\n <Input\n type=\"datetime-local\"\n value={form.startsAt}\n onChange={(e) => onChange('startsAt', e.target.value)}\n required\n />\n </label>\n <label className=\"space-y-1 text-sm\">\n <span className=\"font-medium text-text-primary\">End</span>\n <Input\n type=\"datetime-local\"\n value={form.endsAt}\n onChange={(e) => onChange('endsAt', e.target.value)}\n required\n />\n </label>\n </div>\n\n <div className=\"grid grid-cols-1 gap-4 md:grid-cols-2\">\n <label className=\"space-y-1 text-sm\">\n <span className=\"font-medium text-text-primary\">Type</span>\n <Select value={form.sessionType} onValueChange={(value) => onChange('sessionType', value)}>\n <SelectTrigger aria-label=\"Session type\">\n <SelectValue />\n </SelectTrigger>\n <SelectContent>\n {SESSION_TYPE_OPTIONS.map((option) => (\n <SelectItem key={option.value} value={option.value}>\n {option.label}\n </SelectItem>\n ))}\n </SelectContent>\n </Select>\n </label>\n <label className=\"space-y-1 text-sm\">\n <span className=\"font-medium text-text-primary\">Status</span>\n <Select value={form.status} onValueChange={(value) => onChange('status', value)}>\n <SelectTrigger aria-label=\"Status\">\n <SelectValue />\n </SelectTrigger>\n <SelectContent>\n {SESSION_STATUS_OPTIONS.map((option) => (\n <SelectItem key={option.value} value={option.value}>\n {option.label}\n </SelectItem>\n ))}\n </SelectContent>\n </Select>\n </label>\n </div>\n\n <label className=\"block space-y-1 text-sm\">\n <span className=\"font-medium text-text-primary\">Description</span>\n <Textarea value={form.description} onChange={(e) => onChange('description', e.target.value)} rows={3} />\n </label>\n\n {isEdit ? <EntityTagsField entityType=\"session\" entityId={sid} /> : null}\n </EntityFormShell>\n );\n}\n"],"mappings":"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"}
@@ -0,0 +1,214 @@
1
+ import { useEventfullyManaged as e } from "../../context/EventfullyManagedContext.js";
2
+ import { notify as t } from "../../lib/toast.js";
3
+ import { useCreateTicketTypeMutation as n, useEventQuery as r, useTicketTypesQuery as i, useUpdateTicketTypeMutation as a } from "../../api/eventApi.js";
4
+ import { CURRENCY_OPTIONS as o, inputValueToIso as s, isoToInputValue as c } from "./eventFormUtils.js";
5
+ import { EntityFormShell as l } from "../../components/EntityFormShell.js";
6
+ import { describeError as u, mutationSucceeded as d } from "../../lib/mutationResult.js";
7
+ import { useEffect as f, useMemo as p, useState as m } from "react";
8
+ import { useParams as h } from "react-router-dom";
9
+ import { jsx as g, jsxs as _ } from "react/jsx-runtime";
10
+ import { Input as v, Select as y, SelectContent as b, SelectItem as x, SelectTrigger as S, SelectValue as C, Switch as w, Textarea as T } from "@burdenoff/fe-libs/ui";
11
+ //#region src/eventfullymanaged/pages/events/TicketTypeFormPage.tsx
12
+ var E = {
13
+ name: "",
14
+ description: "",
15
+ price: "0",
16
+ currency: "INR",
17
+ capacity: "",
18
+ salesStartAt: "",
19
+ salesEndAt: "",
20
+ isActive: !0
21
+ };
22
+ function D() {
23
+ let { id: D = "", tid: O = "" } = h(), k = p(() => !!O, [O]), { navigate: A } = e(), { data: j } = r(D), { data: M } = i({
24
+ eventId: D,
25
+ limit: 100,
26
+ offset: 0
27
+ }), [N, { loading: P }] = n(), [F, { loading: I }] = a(), [L, R] = m(E), z = p(() => (M?.ticketTypes?.items ?? []).find((e) => e.id === O), [M, O]);
28
+ f(() => {
29
+ if (!k || !z) return;
30
+ let e = z;
31
+ R({
32
+ name: e.name ?? "",
33
+ description: e.description ?? "",
34
+ price: e.price ?? "0",
35
+ currency: e.currency ?? "INR",
36
+ capacity: e.capacity == null ? "" : String(e.capacity),
37
+ salesStartAt: c(e.salesStartAt),
38
+ salesEndAt: c(e.salesEndAt),
39
+ isActive: e.isActive ?? !0
40
+ });
41
+ }, [z, k]);
42
+ let B = j?.event?.name ?? "Event", V = (e, t) => {
43
+ R((n) => ({
44
+ ...n,
45
+ [e]: t
46
+ }));
47
+ }, H = async (e) => {
48
+ if (e.preventDefault(), !L.name.trim()) {
49
+ t.error("Name required", "Give the ticket type a name before saving.");
50
+ return;
51
+ }
52
+ let n = Number(L.price);
53
+ if (Number.isNaN(n) || n < 0) {
54
+ t.error("Invalid price", "Enter a price of zero or more.");
55
+ return;
56
+ }
57
+ let r = s(L.salesStartAt), i = s(L.salesEndAt);
58
+ if (r && i && new Date(i).getTime() < new Date(r).getTime()) {
59
+ t.error("Invalid sales window", "Sales end cannot be before sales start.");
60
+ return;
61
+ }
62
+ let a = {
63
+ eventId: D,
64
+ name: L.name.trim(),
65
+ description: L.description || null,
66
+ price: String(n),
67
+ currency: L.currency,
68
+ capacity: L.capacity ? Number(L.capacity) : null,
69
+ salesStartAt: r || null,
70
+ salesEndAt: i || null,
71
+ isActive: L.isActive,
72
+ metadata: {}
73
+ };
74
+ try {
75
+ if (k) {
76
+ let e = await F({ variables: {
77
+ id: O,
78
+ input: a
79
+ } });
80
+ if (!d(e, e.data?.updateTicketType?.id, "Could not update ticket type")) return;
81
+ t.success("Ticket type updated"), A(`/events/${D}`);
82
+ return;
83
+ }
84
+ let e = await N({ variables: { input: a } });
85
+ if (!d(e, e.data?.createTicketType?.id, "Could not create ticket type")) return;
86
+ t.success("Ticket type created"), A(`/events/${D}`);
87
+ } catch (e) {
88
+ t.error("Could not save ticket type", u(e));
89
+ }
90
+ }, U = P || I;
91
+ return /* @__PURE__ */ _(l, {
92
+ title: k ? "Edit Ticket Type" : "Add Ticket Type",
93
+ description: `Sellable ticket for ${B}.`,
94
+ purpose: "Ticket types are what attendees actually buy — Early Bird, General, VIP. Set a price and currency, cap how many can be sold, and define the sales window so the ticket opens and closes automatically. Toggle it inactive to pull it from sale without deleting the history.",
95
+ backLabel: "Back to Event",
96
+ onBack: () => A(`/events/${D}`),
97
+ onSubmit: H,
98
+ submitLabel: "Save Ticket Type",
99
+ saving: U,
100
+ children: [
101
+ /* @__PURE__ */ _("div", {
102
+ className: "grid grid-cols-1 gap-4 md:grid-cols-2",
103
+ children: [/* @__PURE__ */ _("label", {
104
+ className: "space-y-1 text-sm",
105
+ children: [/* @__PURE__ */ g("span", {
106
+ className: "font-medium text-text-primary",
107
+ children: "Name"
108
+ }), /* @__PURE__ */ g(v, {
109
+ value: L.name,
110
+ onChange: (e) => V("name", e.target.value),
111
+ required: !0
112
+ })]
113
+ }), /* @__PURE__ */ _("label", {
114
+ className: "space-y-1 text-sm",
115
+ children: [/* @__PURE__ */ g("span", {
116
+ className: "font-medium text-text-primary",
117
+ children: "Capacity"
118
+ }), /* @__PURE__ */ g(v, {
119
+ type: "number",
120
+ min: "0",
121
+ value: L.capacity,
122
+ onChange: (e) => V("capacity", e.target.value),
123
+ placeholder: "Unlimited"
124
+ })]
125
+ })]
126
+ }),
127
+ /* @__PURE__ */ _("div", {
128
+ className: "grid grid-cols-1 gap-4 md:grid-cols-2",
129
+ children: [/* @__PURE__ */ _("label", {
130
+ className: "space-y-1 text-sm",
131
+ children: [/* @__PURE__ */ g("span", {
132
+ className: "font-medium text-text-primary",
133
+ children: "Price"
134
+ }), /* @__PURE__ */ g(v, {
135
+ type: "number",
136
+ min: "0",
137
+ step: "0.01",
138
+ value: L.price,
139
+ onChange: (e) => V("price", e.target.value),
140
+ required: !0
141
+ })]
142
+ }), /* @__PURE__ */ _("label", {
143
+ className: "space-y-1 text-sm",
144
+ children: [/* @__PURE__ */ g("span", {
145
+ className: "font-medium text-text-primary",
146
+ children: "Currency"
147
+ }), /* @__PURE__ */ _(y, {
148
+ value: L.currency,
149
+ onValueChange: (e) => V("currency", e),
150
+ children: [/* @__PURE__ */ g(S, {
151
+ "aria-label": "Currency",
152
+ children: /* @__PURE__ */ g(C, {})
153
+ }), /* @__PURE__ */ g(b, { children: o.map((e) => /* @__PURE__ */ g(x, {
154
+ value: e.value,
155
+ children: e.label
156
+ }, e.value)) })]
157
+ })]
158
+ })]
159
+ }),
160
+ /* @__PURE__ */ _("div", {
161
+ className: "grid grid-cols-1 gap-4 md:grid-cols-2",
162
+ children: [/* @__PURE__ */ _("label", {
163
+ className: "space-y-1 text-sm",
164
+ children: [/* @__PURE__ */ g("span", {
165
+ className: "font-medium text-text-primary",
166
+ children: "Sales start"
167
+ }), /* @__PURE__ */ g(v, {
168
+ type: "datetime-local",
169
+ value: L.salesStartAt,
170
+ onChange: (e) => V("salesStartAt", e.target.value)
171
+ })]
172
+ }), /* @__PURE__ */ _("label", {
173
+ className: "space-y-1 text-sm",
174
+ children: [/* @__PURE__ */ g("span", {
175
+ className: "font-medium text-text-primary",
176
+ children: "Sales end"
177
+ }), /* @__PURE__ */ g(v, {
178
+ type: "datetime-local",
179
+ value: L.salesEndAt,
180
+ onChange: (e) => V("salesEndAt", e.target.value)
181
+ })]
182
+ })]
183
+ }),
184
+ /* @__PURE__ */ _("label", {
185
+ className: "block space-y-1 text-sm",
186
+ children: [/* @__PURE__ */ g("span", {
187
+ className: "font-medium text-text-primary",
188
+ children: "Description"
189
+ }), /* @__PURE__ */ g(T, {
190
+ value: L.description,
191
+ onChange: (e) => V("description", e.target.value),
192
+ rows: 3
193
+ })]
194
+ }),
195
+ /* @__PURE__ */ _("div", {
196
+ className: "flex items-center justify-between rounded-card border border-border-seam p-3",
197
+ children: [/* @__PURE__ */ _("div", { children: [/* @__PURE__ */ g("p", {
198
+ className: "text-sm font-medium text-text-primary",
199
+ children: "Active"
200
+ }), /* @__PURE__ */ g("p", {
201
+ className: "text-xs text-text-secondary",
202
+ children: "Active ticket types are open for sale during their window."
203
+ })] }), /* @__PURE__ */ g(w, {
204
+ checked: L.isActive,
205
+ onCheckedChange: (e) => V("isActive", e)
206
+ })]
207
+ })
208
+ ]
209
+ });
210
+ }
211
+ //#endregion
212
+ export { D as default };
213
+
214
+ //# sourceMappingURL=TicketTypeFormPage.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"TicketTypeFormPage.js","names":[],"sources":["../../../../src/eventfullymanaged/pages/events/TicketTypeFormPage.tsx"],"sourcesContent":["/* eslint-disable react-hooks/set-state-in-effect */\nimport {\n Input,\n Select,\n SelectContent,\n SelectItem,\n SelectTrigger,\n SelectValue,\n Switch,\n Textarea,\n} from '@burdenoff/fe-libs/ui';\nimport { useEffect, useMemo, useState } from 'react';\nimport { useParams } from 'react-router-dom';\n\nimport {\n useCreateTicketTypeMutation,\n useEventQuery,\n useTicketTypesQuery,\n useUpdateTicketTypeMutation,\n type TicketTypeGql,\n} from '../../api/eventApi';\nimport { EntityFormShell } from '../../components/EntityFormShell';\nimport { useEventfullyManaged } from '../../context/EventfullyManagedContext';\nimport { describeError, mutationSucceeded } from '../../lib/mutationResult';\nimport { notify } from '../../lib/toast';\nimport { CURRENCY_OPTIONS, inputValueToIso, isoToInputValue } from './eventFormUtils';\n\ntype TicketTypeFormState = {\n name: string;\n description: string;\n price: string;\n currency: string;\n capacity: string;\n salesStartAt: string;\n salesEndAt: string;\n isActive: boolean;\n};\n\nconst initialState: TicketTypeFormState = {\n name: '',\n description: '',\n price: '0',\n currency: 'INR',\n capacity: '',\n salesStartAt: '',\n salesEndAt: '',\n isActive: true,\n};\n\nexport default function TicketTypeFormPage() {\n const { id = '', tid = '' } = useParams<{ id: string; tid: string }>();\n const isEdit = useMemo(() => Boolean(tid), [tid]);\n const { navigate } = useEventfullyManaged();\n\n const { data: eventData } = useEventQuery(id);\n // No by-id ticket-type resolver — derive the edit initial state from the list query.\n const { data: ticketsData } = useTicketTypesQuery({ eventId: id, limit: 100, offset: 0 });\n const [createTicketType, { loading: creating }] = useCreateTicketTypeMutation();\n const [updateTicketType, { loading: updating }] = useUpdateTicketTypeMutation();\n const [form, setForm] = useState<TicketTypeFormState>(initialState);\n\n const existingTicket = useMemo(\n () => (ticketsData?.ticketTypes?.items ?? []).find((item) => item.id === tid),\n [ticketsData, tid]\n );\n\n useEffect(() => {\n if (!isEdit || !existingTicket) {\n return;\n }\n const ticket = existingTicket as TicketTypeGql;\n setForm({\n name: ticket.name ?? '',\n description: ticket.description ?? '',\n price: ticket.price ?? '0',\n currency: ticket.currency ?? 'INR',\n capacity: ticket.capacity != null ? String(ticket.capacity) : '',\n salesStartAt: isoToInputValue(ticket.salesStartAt),\n salesEndAt: isoToInputValue(ticket.salesEndAt),\n isActive: ticket.isActive ?? true,\n });\n }, [existingTicket, isEdit]);\n\n const eventName = eventData?.event?.name ?? 'Event';\n\n const onChange = <K extends keyof TicketTypeFormState>(field: K, value: TicketTypeFormState[K]) => {\n setForm((prev) => ({ ...prev, [field]: value }));\n };\n\n const onSubmit = async (event: React.FormEvent<HTMLFormElement>) => {\n event.preventDefault();\n\n if (!form.name.trim()) {\n notify.error('Name required', 'Give the ticket type a name before saving.');\n return;\n }\n const priceValue = Number(form.price);\n if (Number.isNaN(priceValue) || priceValue < 0) {\n notify.error('Invalid price', 'Enter a price of zero or more.');\n return;\n }\n const salesStartIso = inputValueToIso(form.salesStartAt);\n const salesEndIso = inputValueToIso(form.salesEndAt);\n if (salesStartIso && salesEndIso && new Date(salesEndIso).getTime() < new Date(salesStartIso).getTime()) {\n notify.error('Invalid sales window', 'Sales end cannot be before sales start.');\n return;\n }\n\n const input = {\n eventId: id,\n name: form.name.trim(),\n description: form.description || null,\n price: String(priceValue),\n currency: form.currency,\n capacity: form.capacity ? Number(form.capacity) : null,\n salesStartAt: salesStartIso || null,\n salesEndAt: salesEndIso || null,\n isActive: form.isActive,\n metadata: {},\n };\n\n try {\n if (isEdit) {\n const response = await updateTicketType({ variables: { id: tid, input } });\n if (!mutationSucceeded(response, response.data?.updateTicketType?.id, 'Could not update ticket type')) {\n return;\n }\n notify.success('Ticket type updated');\n navigate(`/events/${id}`);\n return;\n }\n const response = await createTicketType({ variables: { input } });\n if (!mutationSucceeded(response, response.data?.createTicketType?.id, 'Could not create ticket type')) {\n return;\n }\n notify.success('Ticket type created');\n navigate(`/events/${id}`);\n } catch (error) {\n notify.error('Could not save ticket type', describeError(error));\n }\n };\n\n const saving = creating || updating;\n\n return (\n <EntityFormShell\n title={isEdit ? 'Edit Ticket Type' : 'Add Ticket Type'}\n description={`Sellable ticket for ${eventName}.`}\n purpose=\"Ticket types are what attendees actually buy — Early Bird, General, VIP. Set a price and currency, cap how many can be sold, and define the sales window so the ticket opens and closes automatically. Toggle it inactive to pull it from sale without deleting the history.\"\n backLabel=\"Back to Event\"\n onBack={() => navigate(`/events/${id}`)}\n onSubmit={onSubmit}\n submitLabel=\"Save Ticket Type\"\n saving={saving}\n >\n <div className=\"grid grid-cols-1 gap-4 md:grid-cols-2\">\n <label className=\"space-y-1 text-sm\">\n <span className=\"font-medium text-text-primary\">Name</span>\n <Input value={form.name} onChange={(e) => onChange('name', e.target.value)} required />\n </label>\n <label className=\"space-y-1 text-sm\">\n <span className=\"font-medium text-text-primary\">Capacity</span>\n <Input\n type=\"number\"\n min=\"0\"\n value={form.capacity}\n onChange={(e) => onChange('capacity', e.target.value)}\n placeholder=\"Unlimited\"\n />\n </label>\n </div>\n\n <div className=\"grid grid-cols-1 gap-4 md:grid-cols-2\">\n <label className=\"space-y-1 text-sm\">\n <span className=\"font-medium text-text-primary\">Price</span>\n <Input\n type=\"number\"\n min=\"0\"\n step=\"0.01\"\n value={form.price}\n onChange={(e) => onChange('price', e.target.value)}\n required\n />\n </label>\n <label className=\"space-y-1 text-sm\">\n <span className=\"font-medium text-text-primary\">Currency</span>\n <Select value={form.currency} onValueChange={(value) => onChange('currency', value)}>\n <SelectTrigger aria-label=\"Currency\">\n <SelectValue />\n </SelectTrigger>\n <SelectContent>\n {CURRENCY_OPTIONS.map((option) => (\n <SelectItem key={option.value} value={option.value}>\n {option.label}\n </SelectItem>\n ))}\n </SelectContent>\n </Select>\n </label>\n </div>\n\n <div className=\"grid grid-cols-1 gap-4 md:grid-cols-2\">\n <label className=\"space-y-1 text-sm\">\n <span className=\"font-medium text-text-primary\">Sales start</span>\n <Input\n type=\"datetime-local\"\n value={form.salesStartAt}\n onChange={(e) => onChange('salesStartAt', e.target.value)}\n />\n </label>\n <label className=\"space-y-1 text-sm\">\n <span className=\"font-medium text-text-primary\">Sales end</span>\n <Input\n type=\"datetime-local\"\n value={form.salesEndAt}\n onChange={(e) => onChange('salesEndAt', e.target.value)}\n />\n </label>\n </div>\n\n <label className=\"block space-y-1 text-sm\">\n <span className=\"font-medium text-text-primary\">Description</span>\n <Textarea value={form.description} onChange={(e) => onChange('description', e.target.value)} rows={3} />\n </label>\n\n <div className=\"flex items-center justify-between rounded-card border border-border-seam p-3\">\n <div>\n <p className=\"text-sm font-medium text-text-primary\">Active</p>\n <p className=\"text-xs text-text-secondary\">Active ticket types are open for sale during their window.</p>\n </div>\n <Switch checked={form.isActive} onCheckedChange={(checked) => onChange('isActive', checked)} />\n </div>\n </EntityFormShell>\n );\n}\n"],"mappings":"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"}