@burdenoff/microfe-eventfullymanaged 2026.703.1 → 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 (75) 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 -68
  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/OperationsHubPage.js.map +1 -1
  43. package/dist/eventfullymanaged/pages/modules/RegistrationPage.js +289 -77
  44. package/dist/eventfullymanaged/pages/modules/RegistrationPage.js.map +1 -1
  45. package/dist/eventfullymanaged/pages/modules/RoadmapPanel.js +95 -0
  46. package/dist/eventfullymanaged/pages/modules/RoadmapPanel.js.map +1 -0
  47. package/dist/eventfullymanaged/pages/modules/SponsorDetailsPage.js +77 -56
  48. package/dist/eventfullymanaged/pages/modules/SponsorDetailsPage.js.map +1 -1
  49. package/dist/eventfullymanaged/pages/modules/SponsorLeadsPage.js +70 -36
  50. package/dist/eventfullymanaged/pages/modules/SponsorLeadsPage.js.map +1 -1
  51. package/dist/eventfullymanaged/pages/networking/MeetingFormPage.js +343 -0
  52. package/dist/eventfullymanaged/pages/networking/MeetingFormPage.js.map +1 -0
  53. package/dist/eventfullymanaged/pages/networking/NetworkingProfileFormPage.js +236 -0
  54. package/dist/eventfullymanaged/pages/networking/NetworkingProfileFormPage.js.map +1 -0
  55. package/dist/eventfullymanaged/pages/registration/AttendeeFormPage.js +181 -0
  56. package/dist/eventfullymanaged/pages/registration/AttendeeFormPage.js.map +1 -0
  57. package/dist/eventfullymanaged/pages/registration/PromoCodeFormPage.js +262 -0
  58. package/dist/eventfullymanaged/pages/registration/PromoCodeFormPage.js.map +1 -0
  59. package/dist/eventfullymanaged/pages/registration/RegisterAttendeePage.js +414 -0
  60. package/dist/eventfullymanaged/pages/registration/RegisterAttendeePage.js.map +1 -0
  61. package/dist/eventfullymanaged/pages/speakers/SpeakerFormPage.js +224 -0
  62. package/dist/eventfullymanaged/pages/speakers/SpeakerFormPage.js.map +1 -0
  63. package/dist/eventfullymanaged/pages/speakers/SpeakersPage.js +96 -0
  64. package/dist/eventfullymanaged/pages/speakers/SpeakersPage.js.map +1 -0
  65. package/dist/eventfullymanaged/pages/sponsors/SponsorFormPage.js +149 -140
  66. package/dist/eventfullymanaged/pages/sponsors/SponsorFormPage.js.map +1 -1
  67. package/dist/eventfullymanaged/pages/sponsors/SponsorsListPage.js +97 -67
  68. package/dist/eventfullymanaged/pages/sponsors/SponsorsListPage.js.map +1 -1
  69. package/dist/eventfullymanaged/pages/venues/VenueDetailPage.js +93 -72
  70. package/dist/eventfullymanaged/pages/venues/VenueDetailPage.js.map +1 -1
  71. package/dist/eventfullymanaged/pages/venues/VenueFormPage.js +91 -89
  72. package/dist/eventfullymanaged/pages/venues/VenueFormPage.js.map +1 -1
  73. package/dist/eventfullymanaged/pages/venues/VenueListPage.js +83 -53
  74. package/dist/eventfullymanaged/pages/venues/VenueListPage.js.map +1 -1
  75. package/package.json +1 -1
@@ -0,0 +1,181 @@
1
+ import { useEventfullyManaged as e } from "../../context/EventfullyManagedContext.js";
2
+ import { notify as t } from "../../lib/toast.js";
3
+ import { useAttendeesQuery as n, useCreateAttendeeMutation as r, useEventsQuery as i, useUpdateAttendeeMutation as a } from "../../api/eventApi.js";
4
+ import { EntityFormShell as o } from "../../components/EntityFormShell.js";
5
+ import { describeError as s, mutationSucceeded as c } from "../../lib/mutationResult.js";
6
+ import { EntityTagsField as l } from "../../components/EntityTagsField.js";
7
+ import { useEffect as u, useMemo as d, useState as f } from "react";
8
+ import { useParams as p } from "react-router-dom";
9
+ import { jsx as m, jsxs as h } from "react/jsx-runtime";
10
+ import { Input as g, Select as _, SelectContent as v, SelectItem as y, SelectTrigger as b, SelectValue as x } from "@burdenoff/fe-libs/ui";
11
+ //#region src/eventfullymanaged/pages/registration/AttendeeFormPage.tsx
12
+ var S = {
13
+ eventId: "",
14
+ fullName: "",
15
+ email: "",
16
+ phone: "",
17
+ company: "",
18
+ role: ""
19
+ };
20
+ function C(e) {
21
+ return {
22
+ eventId: e.eventId ?? "",
23
+ fullName: e.fullName ?? "",
24
+ email: e.email ?? "",
25
+ phone: e.phone ?? "",
26
+ company: e.company ?? "",
27
+ role: e.role ?? ""
28
+ };
29
+ }
30
+ function w() {
31
+ let { id: w = "" } = p(), T = d(() => !!w, [w]), { navigate: E } = e(), { data: D } = i({
32
+ limit: 100,
33
+ offset: 0
34
+ }), { data: O } = n({
35
+ limit: 100,
36
+ offset: 0
37
+ }), k = d(() => (O?.attendees.items ?? []).find((e) => e.id === w), [O, w]), [A, { loading: j }] = r(), [M, { loading: N }] = a(), [P, F] = f(() => k ? C(k) : S);
38
+ u(() => {
39
+ k && F(C(k));
40
+ }, [k]);
41
+ let I = (e, t) => {
42
+ F((n) => ({
43
+ ...n,
44
+ [e]: t
45
+ }));
46
+ }, L = D?.events.items ?? [];
47
+ return /* @__PURE__ */ h(o, {
48
+ title: T ? "Edit Attendee" : "Add Attendee",
49
+ description: "Attendee records are the people behind your registrations.",
50
+ purpose: "Capture the person attending your event — who they are, how to reach them, and the organization they represent. You can tag attendees to segment them later for networking, communications, and analytics.",
51
+ backLabel: "Back to Registration",
52
+ onBack: () => E("/registration"),
53
+ onSubmit: async (e) => {
54
+ if (e.preventDefault(), !P.eventId) {
55
+ t.error("Event required", "Choose the event this attendee belongs to.");
56
+ return;
57
+ }
58
+ if (!P.fullName.trim() || !P.email.trim()) {
59
+ t.error("Missing details", "A full name and email are required.");
60
+ return;
61
+ }
62
+ let n = {
63
+ eventId: P.eventId,
64
+ fullName: P.fullName.trim(),
65
+ email: P.email.trim(),
66
+ phone: P.phone.trim() || null,
67
+ company: P.company.trim() || null,
68
+ role: P.role.trim() || null,
69
+ profile: {},
70
+ metadata: {}
71
+ };
72
+ try {
73
+ if (T) {
74
+ let e = await M({ variables: {
75
+ id: w,
76
+ input: n
77
+ } });
78
+ if (!c(e, e.data?.updateAttendee?.id, "Could not update attendee")) return;
79
+ t.success("Attendee updated", P.fullName.trim()), E("/registration");
80
+ return;
81
+ }
82
+ let e = await A({ variables: { input: n } });
83
+ if (!c(e, e.data?.createAttendee?.id, "Could not create attendee")) return;
84
+ t.success("Attendee added", P.fullName.trim()), E("/registration");
85
+ } catch (e) {
86
+ t.error("Could not save attendee", s(e));
87
+ }
88
+ },
89
+ submitLabel: T ? "Save Attendee" : "Add Attendee",
90
+ saving: j || N,
91
+ children: [
92
+ /* @__PURE__ */ h("div", {
93
+ className: "grid grid-cols-1 gap-4 md:grid-cols-2",
94
+ children: [/* @__PURE__ */ h("label", {
95
+ className: "space-y-1 text-sm",
96
+ children: [/* @__PURE__ */ m("span", {
97
+ className: "font-medium text-text-primary",
98
+ children: "Event"
99
+ }), /* @__PURE__ */ h(_, {
100
+ value: P.eventId,
101
+ onValueChange: (e) => I("eventId", e),
102
+ children: [/* @__PURE__ */ m(b, {
103
+ "aria-label": "Event",
104
+ children: /* @__PURE__ */ m(x, { placeholder: "Select Event" })
105
+ }), /* @__PURE__ */ m(v, { children: L.map((e) => /* @__PURE__ */ m(y, {
106
+ value: e.id,
107
+ children: e.name
108
+ }, e.id)) })]
109
+ })]
110
+ }), /* @__PURE__ */ h("label", {
111
+ className: "space-y-1 text-sm",
112
+ children: [/* @__PURE__ */ m("span", {
113
+ className: "font-medium text-text-primary",
114
+ children: "Full Name"
115
+ }), /* @__PURE__ */ m(g, {
116
+ value: P.fullName,
117
+ onChange: (e) => I("fullName", e.target.value),
118
+ required: !0
119
+ })]
120
+ })]
121
+ }),
122
+ /* @__PURE__ */ h("div", {
123
+ className: "grid grid-cols-1 gap-4 md:grid-cols-2",
124
+ children: [/* @__PURE__ */ h("label", {
125
+ className: "space-y-1 text-sm",
126
+ children: [/* @__PURE__ */ m("span", {
127
+ className: "font-medium text-text-primary",
128
+ children: "Email"
129
+ }), /* @__PURE__ */ m(g, {
130
+ type: "email",
131
+ value: P.email,
132
+ onChange: (e) => I("email", e.target.value),
133
+ required: !0
134
+ })]
135
+ }), /* @__PURE__ */ h("label", {
136
+ className: "space-y-1 text-sm",
137
+ children: [/* @__PURE__ */ m("span", {
138
+ className: "font-medium text-text-primary",
139
+ children: "Phone"
140
+ }), /* @__PURE__ */ m(g, {
141
+ value: P.phone,
142
+ onChange: (e) => I("phone", e.target.value)
143
+ })]
144
+ })]
145
+ }),
146
+ /* @__PURE__ */ h("div", {
147
+ className: "grid grid-cols-1 gap-4 md:grid-cols-2",
148
+ children: [/* @__PURE__ */ h("label", {
149
+ className: "space-y-1 text-sm",
150
+ children: [/* @__PURE__ */ m("span", {
151
+ className: "font-medium text-text-primary",
152
+ children: "Company"
153
+ }), /* @__PURE__ */ m(g, {
154
+ value: P.company,
155
+ onChange: (e) => I("company", e.target.value)
156
+ })]
157
+ }), /* @__PURE__ */ h("label", {
158
+ className: "space-y-1 text-sm",
159
+ children: [/* @__PURE__ */ m("span", {
160
+ className: "font-medium text-text-primary",
161
+ children: "Role"
162
+ }), /* @__PURE__ */ m(g, {
163
+ value: P.role,
164
+ onChange: (e) => I("role", e.target.value)
165
+ })]
166
+ })]
167
+ }),
168
+ T ? /* @__PURE__ */ m(l, {
169
+ entityType: "attendee",
170
+ entityId: w
171
+ }) : /* @__PURE__ */ m(l, {
172
+ entityType: "attendee",
173
+ entityId: ""
174
+ })
175
+ ]
176
+ });
177
+ }
178
+ //#endregion
179
+ export { w as default };
180
+
181
+ //# sourceMappingURL=AttendeeFormPage.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"AttendeeFormPage.js","names":[],"sources":["../../../../src/eventfullymanaged/pages/registration/AttendeeFormPage.tsx"],"sourcesContent":["/* eslint-disable react-hooks/set-state-in-effect */\nimport { useEffect, useMemo, useState } from 'react';\nimport { useParams } from 'react-router-dom';\nimport {\n Input,\n Select,\n SelectContent,\n SelectItem,\n SelectTrigger,\n SelectValue,\n} from '@burdenoff/fe-libs/ui';\n\nimport {\n useAttendeesQuery,\n useCreateAttendeeMutation,\n useEventsQuery,\n useUpdateAttendeeMutation,\n type AttendeeGql,\n} from '../../api/eventApi';\nimport { EntityFormShell } from '../../components/EntityFormShell';\nimport { EntityTagsField } from '../../components/EntityTagsField';\nimport { useEventfullyManaged } from '../../context/EventfullyManagedContext';\nimport { describeError, mutationSucceeded } from '../../lib/mutationResult';\nimport { notify } from '../../lib/toast';\n\ntype AttendeeFormState = {\n eventId: string;\n fullName: string;\n email: string;\n phone: string;\n company: string;\n role: string;\n};\n\nconst emptyState: AttendeeFormState = {\n eventId: '',\n fullName: '',\n email: '',\n phone: '',\n company: '',\n role: '',\n};\n\nfunction toFormState(attendee: AttendeeGql): AttendeeFormState {\n return {\n eventId: attendee.eventId ?? '',\n fullName: attendee.fullName ?? '',\n email: attendee.email ?? '',\n phone: attendee.phone ?? '',\n company: attendee.company ?? '',\n role: attendee.role ?? '',\n };\n}\n\nexport default function AttendeeFormPage() {\n const { id = '' } = useParams<{ id: string }>();\n const isEdit = useMemo(() => Boolean(id), [id]);\n const { navigate } = useEventfullyManaged();\n\n const { data: eventsData } = useEventsQuery({ limit: 100, offset: 0 });\n // No by-id attendee resolver exists — derive edit initial-state from the list.\n const { data: attendeesData } = useAttendeesQuery({ limit: 100, offset: 0 });\n const existing = useMemo(\n () => (attendeesData?.attendees.items ?? []).find((item) => item.id === id),\n [attendeesData, id]\n );\n\n const [createAttendee, { loading: creating }] = useCreateAttendeeMutation();\n const [updateAttendee, { loading: updating }] = useUpdateAttendeeMutation();\n const [form, setForm] = useState<AttendeeFormState>(() => (existing ? toFormState(existing) : emptyState));\n\n useEffect(() => {\n if (existing) {\n setForm(toFormState(existing));\n }\n }, [existing]);\n\n const onChange = (field: keyof AttendeeFormState, value: string) => {\n setForm((prev) => ({ ...prev, [field]: value }));\n };\n\n const events = eventsData?.events.items ?? [];\n const saving = creating || updating;\n\n const onSubmit = async (event: React.FormEvent<HTMLFormElement>) => {\n event.preventDefault();\n\n if (!form.eventId) {\n notify.error('Event required', 'Choose the event this attendee belongs to.');\n return;\n }\n if (!form.fullName.trim() || !form.email.trim()) {\n notify.error('Missing details', 'A full name and email are required.');\n return;\n }\n\n const input = {\n eventId: form.eventId,\n fullName: form.fullName.trim(),\n email: form.email.trim(),\n phone: form.phone.trim() || null,\n company: form.company.trim() || null,\n role: form.role.trim() || null,\n profile: {},\n metadata: {},\n };\n\n try {\n if (isEdit) {\n const response = await updateAttendee({ variables: { id, input } });\n if (!mutationSucceeded(response, response.data?.updateAttendee?.id, 'Could not update attendee')) {\n return;\n }\n notify.success('Attendee updated', form.fullName.trim());\n navigate('/registration');\n return;\n }\n\n const response = await createAttendee({ variables: { input } });\n if (!mutationSucceeded(response, response.data?.createAttendee?.id, 'Could not create attendee')) {\n return;\n }\n notify.success('Attendee added', form.fullName.trim());\n navigate('/registration');\n } catch (error) {\n notify.error('Could not save attendee', describeError(error));\n }\n };\n\n return (\n <EntityFormShell\n title={isEdit ? 'Edit Attendee' : 'Add Attendee'}\n description=\"Attendee records are the people behind your registrations.\"\n purpose=\"Capture the person attending your event — who they are, how to reach them, and the organization they represent. You can tag attendees to segment them later for networking, communications, and analytics.\"\n backLabel=\"Back to Registration\"\n onBack={() => navigate('/registration')}\n onSubmit={onSubmit}\n submitLabel={isEdit ? 'Save Attendee' : 'Add Attendee'}\n saving={saving}\n >\n <div className=\"grid grid-cols-1 gap-4 md:grid-cols-2\">\n <label className=\"space-y-1 text-sm\">\n <span className=\"font-medium text-text-primary\">Event</span>\n <Select value={form.eventId} onValueChange={(value) => onChange('eventId', value)}>\n <SelectTrigger aria-label=\"Event\">\n <SelectValue placeholder=\"Select Event\" />\n </SelectTrigger>\n <SelectContent>\n {events.map((eventItem) => (\n <SelectItem key={eventItem.id} value={eventItem.id}>\n {eventItem.name}\n </SelectItem>\n ))}\n </SelectContent>\n </Select>\n </label>\n <label className=\"space-y-1 text-sm\">\n <span className=\"font-medium text-text-primary\">Full Name</span>\n <Input value={form.fullName} onChange={(e) => onChange('fullName', e.target.value)} required />\n </label>\n </div>\n\n <div className=\"grid grid-cols-1 gap-4 md:grid-cols-2\">\n <label className=\"space-y-1 text-sm\">\n <span className=\"font-medium text-text-primary\">Email</span>\n <Input type=\"email\" value={form.email} onChange={(e) => onChange('email', e.target.value)} required />\n </label>\n <label className=\"space-y-1 text-sm\">\n <span className=\"font-medium text-text-primary\">Phone</span>\n <Input value={form.phone} onChange={(e) => onChange('phone', e.target.value)} />\n </label>\n </div>\n\n <div className=\"grid grid-cols-1 gap-4 md:grid-cols-2\">\n <label className=\"space-y-1 text-sm\">\n <span className=\"font-medium text-text-primary\">Company</span>\n <Input value={form.company} onChange={(e) => onChange('company', e.target.value)} />\n </label>\n <label className=\"space-y-1 text-sm\">\n <span className=\"font-medium text-text-primary\">Role</span>\n <Input value={form.role} onChange={(e) => onChange('role', e.target.value)} />\n </label>\n </div>\n\n {isEdit ? (\n <EntityTagsField entityType=\"attendee\" entityId={id} />\n ) : (\n <EntityTagsField entityType=\"attendee\" entityId=\"\" />\n )}\n </EntityFormShell>\n );\n}\n"],"mappings":"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"}
@@ -0,0 +1,262 @@
1
+ import { useEventfullyManaged as e } from "../../context/EventfullyManagedContext.js";
2
+ import { notify as t } from "../../lib/toast.js";
3
+ import { useCreatePromoCodeMutation as n, useEventsQuery as r, usePromoCodesQuery as i, useUpdatePromoCodeMutation as a } from "../../api/eventApi.js";
4
+ import { EntityFormShell as o } from "../../components/EntityFormShell.js";
5
+ import { describeError as s, mutationSucceeded as c } from "../../lib/mutationResult.js";
6
+ import { useEffect as l, useMemo as u, useState as d } from "react";
7
+ import { useParams as f } from "react-router-dom";
8
+ import { jsx as p, jsxs as m } from "react/jsx-runtime";
9
+ import { Input as h, Select as g, SelectContent as _, SelectItem as v, SelectTrigger as y, SelectValue as b, Switch as x, Textarea as S } from "@burdenoff/fe-libs/ui";
10
+ //#region src/eventfullymanaged/pages/registration/PromoCodeFormPage.tsx
11
+ var C = {
12
+ eventId: "",
13
+ code: "",
14
+ description: "",
15
+ discountType: "PERCENT",
16
+ discountValue: "",
17
+ maxUses: "",
18
+ startsAt: "",
19
+ endsAt: "",
20
+ isActive: !0
21
+ };
22
+ function w(e) {
23
+ if (!e) return "";
24
+ let t = new Date(e);
25
+ if (Number.isNaN(t.getTime())) return "";
26
+ let n = t.getTimezoneOffset() * 6e4;
27
+ return new Date(t.getTime() - n).toISOString().slice(0, 16);
28
+ }
29
+ function T(e) {
30
+ if (!e) return null;
31
+ let t = new Date(e);
32
+ return Number.isNaN(t.getTime()) ? null : t.toISOString();
33
+ }
34
+ function E(e) {
35
+ return {
36
+ eventId: e.eventId ?? "",
37
+ code: e.code ?? "",
38
+ description: e.description ?? "",
39
+ discountType: e.discountType ?? "PERCENT",
40
+ discountValue: e.discountValue ?? "",
41
+ maxUses: e.maxUses == null ? "" : String(e.maxUses),
42
+ startsAt: w(e.startsAt),
43
+ endsAt: w(e.endsAt),
44
+ isActive: e.isActive ?? !0
45
+ };
46
+ }
47
+ function D() {
48
+ let { id: w = "" } = f(), D = u(() => !!w, [w]), { navigate: O } = e(), { data: k } = r({
49
+ limit: 100,
50
+ offset: 0
51
+ }), { data: A } = i({
52
+ limit: 100,
53
+ offset: 0
54
+ }), j = u(() => (A?.promoCodes.items ?? []).find((e) => e.id === w), [A, w]), [M, { loading: N }] = n(), [P, { loading: F }] = a(), [I, L] = d(() => j ? E(j) : C);
55
+ l(() => {
56
+ j && L(E(j));
57
+ }, [j]);
58
+ let R = (e, t) => {
59
+ L((n) => ({
60
+ ...n,
61
+ [e]: t
62
+ }));
63
+ }, z = k?.events.items ?? [], B = N || F, V = async (e) => {
64
+ if (e.preventDefault(), !I.eventId) {
65
+ t.error("Event required", "Choose the event this promo code applies to.");
66
+ return;
67
+ }
68
+ if (!I.code.trim()) {
69
+ t.error("Code required", "Enter the code attendees will type at checkout.");
70
+ return;
71
+ }
72
+ if (!I.discountValue.trim()) {
73
+ t.error("Discount required", "Enter a discount value.");
74
+ return;
75
+ }
76
+ let n = {
77
+ eventId: I.eventId,
78
+ code: I.code.trim().toUpperCase(),
79
+ description: I.description.trim() || null,
80
+ discountType: I.discountType,
81
+ discountValue: I.discountValue.trim(),
82
+ maxUses: I.maxUses.trim() ? Number(I.maxUses) : null,
83
+ startsAt: T(I.startsAt),
84
+ endsAt: T(I.endsAt),
85
+ isActive: I.isActive,
86
+ metadata: {}
87
+ };
88
+ try {
89
+ if (D) {
90
+ let e = await P({ variables: {
91
+ id: w,
92
+ input: n
93
+ } });
94
+ if (!c(e, e.data?.updatePromoCode?.id, "Could not update promo code")) return;
95
+ t.success("Promo code updated", n.code), O("/registration");
96
+ return;
97
+ }
98
+ let e = await M({ variables: { input: n } });
99
+ if (!c(e, e.data?.createPromoCode?.id, "Could not create promo code")) return;
100
+ t.success("Promo code created", n.code), O("/registration");
101
+ } catch (e) {
102
+ t.error("Could not save promo code", s(e));
103
+ }
104
+ }, H = I.discountType === "PERCENT" ? "Percentage off, e.g. 10 for 10%." : "Fixed amount off, e.g. 500.";
105
+ return /* @__PURE__ */ m(o, {
106
+ title: D ? "Edit Promo Code" : "New Promo Code",
107
+ description: "Promo codes let you offer discounts to drive registrations.",
108
+ purpose: "Create a discount attendees can apply at checkout. Choose whether it takes a percentage or a fixed amount off, cap how many times it can be used, and set an active window. Deactivate a code to stop it being redeemed without deleting its history.",
109
+ backLabel: "Back to Registration",
110
+ onBack: () => O("/registration"),
111
+ onSubmit: V,
112
+ submitLabel: D ? "Save Promo Code" : "Create Promo Code",
113
+ saving: B,
114
+ children: [
115
+ /* @__PURE__ */ m("div", {
116
+ className: "grid grid-cols-1 gap-4 md:grid-cols-2",
117
+ children: [/* @__PURE__ */ m("label", {
118
+ className: "space-y-1 text-sm",
119
+ children: [/* @__PURE__ */ p("span", {
120
+ className: "font-medium text-text-primary",
121
+ children: "Event"
122
+ }), /* @__PURE__ */ m(g, {
123
+ value: I.eventId,
124
+ onValueChange: (e) => R("eventId", e),
125
+ children: [/* @__PURE__ */ p(y, {
126
+ "aria-label": "Event",
127
+ children: /* @__PURE__ */ p(b, { placeholder: "Select Event" })
128
+ }), /* @__PURE__ */ p(_, { children: z.map((e) => /* @__PURE__ */ p(v, {
129
+ value: e.id,
130
+ children: e.name
131
+ }, e.id)) })]
132
+ })]
133
+ }), /* @__PURE__ */ m("label", {
134
+ className: "space-y-1 text-sm",
135
+ children: [/* @__PURE__ */ p("span", {
136
+ className: "font-medium text-text-primary",
137
+ children: "Code"
138
+ }), /* @__PURE__ */ p(h, {
139
+ value: I.code,
140
+ onChange: (e) => R("code", e.target.value),
141
+ placeholder: "EARLYBIRD",
142
+ required: !0
143
+ })]
144
+ })]
145
+ }),
146
+ /* @__PURE__ */ m("label", {
147
+ className: "block space-y-1 text-sm",
148
+ children: [/* @__PURE__ */ p("span", {
149
+ className: "font-medium text-text-primary",
150
+ children: "Description"
151
+ }), /* @__PURE__ */ p(S, {
152
+ value: I.description,
153
+ onChange: (e) => R("description", e.target.value),
154
+ rows: 2
155
+ })]
156
+ }),
157
+ /* @__PURE__ */ m("div", {
158
+ className: "grid grid-cols-1 gap-4 md:grid-cols-3",
159
+ children: [
160
+ /* @__PURE__ */ m("label", {
161
+ className: "space-y-1 text-sm",
162
+ children: [/* @__PURE__ */ p("span", {
163
+ className: "font-medium text-text-primary",
164
+ children: "Discount Type"
165
+ }), /* @__PURE__ */ m(g, {
166
+ value: I.discountType,
167
+ onValueChange: (e) => R("discountType", e),
168
+ children: [/* @__PURE__ */ p(y, {
169
+ "aria-label": "Discount Type",
170
+ children: /* @__PURE__ */ p(b, {})
171
+ }), /* @__PURE__ */ m(_, { children: [/* @__PURE__ */ p(v, {
172
+ value: "PERCENT",
173
+ children: "Percentage (%)"
174
+ }), /* @__PURE__ */ p(v, {
175
+ value: "FIXED",
176
+ children: "Fixed amount"
177
+ })] })]
178
+ })]
179
+ }),
180
+ /* @__PURE__ */ m("label", {
181
+ className: "space-y-1 text-sm",
182
+ children: [
183
+ /* @__PURE__ */ p("span", {
184
+ className: "font-medium text-text-primary",
185
+ children: "Discount Value"
186
+ }),
187
+ /* @__PURE__ */ p(h, {
188
+ type: "number",
189
+ min: "0",
190
+ step: "any",
191
+ value: I.discountValue,
192
+ onChange: (e) => R("discountValue", e.target.value),
193
+ required: !0
194
+ }),
195
+ /* @__PURE__ */ p("span", {
196
+ className: "text-xs text-text-secondary",
197
+ children: H
198
+ })
199
+ ]
200
+ }),
201
+ /* @__PURE__ */ m("label", {
202
+ className: "space-y-1 text-sm",
203
+ children: [/* @__PURE__ */ p("span", {
204
+ className: "font-medium text-text-primary",
205
+ children: "Max Uses"
206
+ }), /* @__PURE__ */ p(h, {
207
+ type: "number",
208
+ min: "0",
209
+ value: I.maxUses,
210
+ onChange: (e) => R("maxUses", e.target.value),
211
+ placeholder: "Unlimited"
212
+ })]
213
+ })
214
+ ]
215
+ }),
216
+ /* @__PURE__ */ m("div", {
217
+ className: "grid grid-cols-1 gap-4 md:grid-cols-2",
218
+ children: [/* @__PURE__ */ m("label", {
219
+ className: "space-y-1 text-sm",
220
+ children: [/* @__PURE__ */ p("span", {
221
+ className: "font-medium text-text-primary",
222
+ children: "Starts At"
223
+ }), /* @__PURE__ */ p(h, {
224
+ type: "datetime-local",
225
+ value: I.startsAt,
226
+ onChange: (e) => R("startsAt", e.target.value)
227
+ })]
228
+ }), /* @__PURE__ */ m("label", {
229
+ className: "space-y-1 text-sm",
230
+ children: [/* @__PURE__ */ p("span", {
231
+ className: "font-medium text-text-primary",
232
+ children: "Ends At"
233
+ }), /* @__PURE__ */ p(h, {
234
+ type: "datetime-local",
235
+ value: I.endsAt,
236
+ onChange: (e) => R("endsAt", e.target.value)
237
+ })]
238
+ })]
239
+ }),
240
+ /* @__PURE__ */ m("div", {
241
+ className: "flex items-center justify-between rounded-input border border-border-default bg-bg-surface px-3 py-2",
242
+ children: [/* @__PURE__ */ m("div", {
243
+ className: "text-sm",
244
+ children: [/* @__PURE__ */ p("span", {
245
+ className: "font-medium text-text-primary",
246
+ children: "Active"
247
+ }), /* @__PURE__ */ p("p", {
248
+ className: "text-xs text-text-secondary",
249
+ children: "Only active codes can be redeemed at checkout."
250
+ })]
251
+ }), /* @__PURE__ */ p(x, {
252
+ checked: I.isActive,
253
+ onCheckedChange: (e) => R("isActive", e)
254
+ })]
255
+ })
256
+ ]
257
+ });
258
+ }
259
+ //#endregion
260
+ export { D as default };
261
+
262
+ //# sourceMappingURL=PromoCodeFormPage.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"PromoCodeFormPage.js","names":[],"sources":["../../../../src/eventfullymanaged/pages/registration/PromoCodeFormPage.tsx"],"sourcesContent":["/* eslint-disable react-hooks/set-state-in-effect */\nimport { useEffect, useMemo, useState } from 'react';\nimport { useParams } from 'react-router-dom';\nimport {\n Input,\n Select,\n SelectContent,\n SelectItem,\n SelectTrigger,\n SelectValue,\n Switch,\n Textarea,\n} from '@burdenoff/fe-libs/ui';\n\nimport {\n useCreatePromoCodeMutation,\n useEventsQuery,\n usePromoCodesQuery,\n useUpdatePromoCodeMutation,\n type PromoCodeGql,\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';\n\ntype PromoCodeFormState = {\n eventId: string;\n code: string;\n description: string;\n discountType: string;\n discountValue: string;\n maxUses: string;\n startsAt: string;\n endsAt: string;\n isActive: boolean;\n};\n\nconst emptyState: PromoCodeFormState = {\n eventId: '',\n code: '',\n description: '',\n discountType: 'PERCENT',\n discountValue: '',\n maxUses: '',\n startsAt: '',\n endsAt: '',\n isActive: true,\n};\n\n/** ISO timestamp -> value for <input type=\"datetime-local\"> (local time, minute precision). */\nfunction isoToLocalInput(iso?: string | null): string {\n if (!iso) {\n return '';\n }\n const date = new Date(iso);\n if (Number.isNaN(date.getTime())) {\n return '';\n }\n const offsetMs = date.getTimezoneOffset() * 60_000;\n return new Date(date.getTime() - offsetMs).toISOString().slice(0, 16);\n}\n\n/** datetime-local value -> ISO timestamp for the DateTime scalar. */\nfunction localInputToIso(value: string): string | null {\n if (!value) {\n return null;\n }\n const date = new Date(value);\n if (Number.isNaN(date.getTime())) {\n return null;\n }\n return date.toISOString();\n}\n\nfunction toFormState(promo: PromoCodeGql): PromoCodeFormState {\n return {\n eventId: promo.eventId ?? '',\n code: promo.code ?? '',\n description: promo.description ?? '',\n discountType: promo.discountType ?? 'PERCENT',\n discountValue: promo.discountValue ?? '',\n maxUses: promo.maxUses != null ? String(promo.maxUses) : '',\n startsAt: isoToLocalInput(promo.startsAt),\n endsAt: isoToLocalInput(promo.endsAt),\n isActive: promo.isActive ?? true,\n };\n}\n\nexport default function PromoCodeFormPage() {\n const { id = '' } = useParams<{ id: string }>();\n const isEdit = useMemo(() => Boolean(id), [id]);\n const { navigate } = useEventfullyManaged();\n\n const { data: eventsData } = useEventsQuery({ limit: 100, offset: 0 });\n // No by-id promo resolver exists — derive edit initial-state from the list.\n const { data: promoData } = usePromoCodesQuery({ limit: 100, offset: 0 });\n const existing = useMemo(\n () => (promoData?.promoCodes.items ?? []).find((item) => item.id === id),\n [promoData, id]\n );\n\n const [createPromoCode, { loading: creating }] = useCreatePromoCodeMutation();\n const [updatePromoCode, { loading: updating }] = useUpdatePromoCodeMutation();\n const [form, setForm] = useState<PromoCodeFormState>(() => (existing ? toFormState(existing) : emptyState));\n\n useEffect(() => {\n if (existing) {\n setForm(toFormState(existing));\n }\n }, [existing]);\n\n const onChange = <K extends keyof PromoCodeFormState>(field: K, value: PromoCodeFormState[K]) => {\n setForm((prev) => ({ ...prev, [field]: value }));\n };\n\n const events = eventsData?.events.items ?? [];\n const saving = creating || updating;\n\n const onSubmit = async (event: React.FormEvent<HTMLFormElement>) => {\n event.preventDefault();\n\n if (!form.eventId) {\n notify.error('Event required', 'Choose the event this promo code applies to.');\n return;\n }\n if (!form.code.trim()) {\n notify.error('Code required', 'Enter the code attendees will type at checkout.');\n return;\n }\n if (!form.discountValue.trim()) {\n notify.error('Discount required', 'Enter a discount value.');\n return;\n }\n\n const input = {\n eventId: form.eventId,\n code: form.code.trim().toUpperCase(),\n description: form.description.trim() || null,\n discountType: form.discountType,\n discountValue: form.discountValue.trim(),\n maxUses: form.maxUses.trim() ? Number(form.maxUses) : null,\n startsAt: localInputToIso(form.startsAt),\n endsAt: localInputToIso(form.endsAt),\n isActive: form.isActive,\n metadata: {},\n };\n\n try {\n if (isEdit) {\n const response = await updatePromoCode({ variables: { id, input } });\n if (!mutationSucceeded(response, response.data?.updatePromoCode?.id, 'Could not update promo code')) {\n return;\n }\n notify.success('Promo code updated', input.code);\n navigate('/registration');\n return;\n }\n\n const response = await createPromoCode({ variables: { input } });\n if (!mutationSucceeded(response, response.data?.createPromoCode?.id, 'Could not create promo code')) {\n return;\n }\n notify.success('Promo code created', input.code);\n navigate('/registration');\n } catch (error) {\n notify.error('Could not save promo code', describeError(error));\n }\n };\n\n const valueHint =\n form.discountType === 'PERCENT' ? 'Percentage off, e.g. 10 for 10%.' : 'Fixed amount off, e.g. 500.';\n\n return (\n <EntityFormShell\n title={isEdit ? 'Edit Promo Code' : 'New Promo Code'}\n description=\"Promo codes let you offer discounts to drive registrations.\"\n purpose=\"Create a discount attendees can apply at checkout. Choose whether it takes a percentage or a fixed amount off, cap how many times it can be used, and set an active window. Deactivate a code to stop it being redeemed without deleting its history.\"\n backLabel=\"Back to Registration\"\n onBack={() => navigate('/registration')}\n onSubmit={onSubmit}\n submitLabel={isEdit ? 'Save Promo Code' : 'Create Promo Code'}\n saving={saving}\n >\n <div className=\"grid grid-cols-1 gap-4 md:grid-cols-2\">\n <label className=\"space-y-1 text-sm\">\n <span className=\"font-medium text-text-primary\">Event</span>\n <Select value={form.eventId} onValueChange={(value) => onChange('eventId', value)}>\n <SelectTrigger aria-label=\"Event\">\n <SelectValue placeholder=\"Select Event\" />\n </SelectTrigger>\n <SelectContent>\n {events.map((eventItem) => (\n <SelectItem key={eventItem.id} value={eventItem.id}>\n {eventItem.name}\n </SelectItem>\n ))}\n </SelectContent>\n </Select>\n </label>\n <label className=\"space-y-1 text-sm\">\n <span className=\"font-medium text-text-primary\">Code</span>\n <Input\n value={form.code}\n onChange={(e) => onChange('code', e.target.value)}\n placeholder=\"EARLYBIRD\"\n required\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={2} />\n </label>\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\">Discount Type</span>\n <Select value={form.discountType} onValueChange={(value) => onChange('discountType', value)}>\n <SelectTrigger aria-label=\"Discount Type\">\n <SelectValue />\n </SelectTrigger>\n <SelectContent>\n <SelectItem value=\"PERCENT\">Percentage (%)</SelectItem>\n <SelectItem value=\"FIXED\">Fixed amount</SelectItem>\n </SelectContent>\n </Select>\n </label>\n <label className=\"space-y-1 text-sm\">\n <span className=\"font-medium text-text-primary\">Discount Value</span>\n <Input\n type=\"number\"\n min=\"0\"\n step=\"any\"\n value={form.discountValue}\n onChange={(e) => onChange('discountValue', e.target.value)}\n required\n />\n <span className=\"text-xs text-text-secondary\">{valueHint}</span>\n </label>\n <label className=\"space-y-1 text-sm\">\n <span className=\"font-medium text-text-primary\">Max Uses</span>\n <Input\n type=\"number\"\n min=\"0\"\n value={form.maxUses}\n onChange={(e) => onChange('maxUses', 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\">Starts At</span>\n <Input type=\"datetime-local\" value={form.startsAt} onChange={(e) => onChange('startsAt', e.target.value)} />\n </label>\n <label className=\"space-y-1 text-sm\">\n <span className=\"font-medium text-text-primary\">Ends At</span>\n <Input type=\"datetime-local\" value={form.endsAt} onChange={(e) => onChange('endsAt', e.target.value)} />\n </label>\n </div>\n\n <div className=\"flex items-center justify-between rounded-input border border-border-default bg-bg-surface px-3 py-2\">\n <div className=\"text-sm\">\n <span className=\"font-medium text-text-primary\">Active</span>\n <p className=\"text-xs text-text-secondary\">Only active codes can be redeemed at checkout.</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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"}