@burdenoff/microfe-eventfullymanaged 2026.625.2 → 2026.703.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 (39) hide show
  1. package/dist/eventfullymanaged/api/eventApi.js +83 -38
  2. package/dist/eventfullymanaged/api/eventApi.js.map +1 -1
  3. package/dist/eventfullymanaged/components/ImageUploadField.js +98 -0
  4. package/dist/eventfullymanaged/components/ImageUploadField.js.map +1 -0
  5. package/dist/eventfullymanaged/lib/mutationResult.js +12 -0
  6. package/dist/eventfullymanaged/lib/mutationResult.js.map +1 -0
  7. package/dist/eventfullymanaged/lib/toast.js +36 -0
  8. package/dist/eventfullymanaged/lib/toast.js.map +1 -0
  9. package/dist/eventfullymanaged/pages/modules/AnalyticsPage.js +40 -32
  10. package/dist/eventfullymanaged/pages/modules/AnalyticsPage.js.map +1 -1
  11. package/dist/eventfullymanaged/pages/modules/AttendeeProfilePage.js +71 -45
  12. package/dist/eventfullymanaged/pages/modules/AttendeeProfilePage.js.map +1 -1
  13. package/dist/eventfullymanaged/pages/modules/CommunicationPage.js +56 -46
  14. package/dist/eventfullymanaged/pages/modules/CommunicationPage.js.map +1 -1
  15. package/dist/eventfullymanaged/pages/modules/EventsPage.js +75 -67
  16. package/dist/eventfullymanaged/pages/modules/EventsPage.js.map +1 -1
  17. package/dist/eventfullymanaged/pages/modules/NetworkingPage.js +30 -11
  18. package/dist/eventfullymanaged/pages/modules/NetworkingPage.js.map +1 -1
  19. package/dist/eventfullymanaged/pages/modules/SponsorDetailsPage.js +35 -13
  20. package/dist/eventfullymanaged/pages/modules/SponsorDetailsPage.js.map +1 -1
  21. package/dist/eventfullymanaged/pages/modules/WorkflowsPage.js +68 -54
  22. package/dist/eventfullymanaged/pages/modules/WorkflowsPage.js.map +1 -1
  23. package/dist/eventfullymanaged/pages/sponsors/SponsorFormPage.js +153 -125
  24. package/dist/eventfullymanaged/pages/sponsors/SponsorFormPage.js.map +1 -1
  25. package/dist/eventfullymanaged/pages/venues/FloorPlansPage.js +107 -62
  26. package/dist/eventfullymanaged/pages/venues/FloorPlansPage.js.map +1 -1
  27. package/dist/eventfullymanaged/pages/venues/VenueFormPage.js +144 -135
  28. package/dist/eventfullymanaged/pages/venues/VenueFormPage.js.map +1 -1
  29. package/dist/node_modules/@apollo/client/react/context/ApolloContext.js.map +1 -1
  30. package/dist/node_modules/@apollo/client/react/hooks/internal/useDeepMemo.js.map +1 -1
  31. package/dist/node_modules/@apollo/client/react/hooks/internal/useIsomorphicLayoutEffect.js.map +1 -1
  32. package/dist/node_modules/@apollo/client/react/hooks/internal/wrapHook.js.map +1 -1
  33. package/dist/node_modules/@apollo/client/react/hooks/useApolloClient.js.map +1 -1
  34. package/dist/node_modules/@apollo/client/react/hooks/useMutation.js.map +1 -1
  35. package/dist/node_modules/@apollo/client/react/hooks/useQuery.js.map +1 -1
  36. package/dist/node_modules/@apollo/client/react/hooks/useSyncExternalStore.js.map +1 -1
  37. package/dist/node_modules/sonner/dist/index.js +163 -0
  38. package/dist/node_modules/sonner/dist/index.js.map +1 -0
  39. package/package.json +1 -1
@@ -0,0 +1 @@
1
+ {"version":3,"file":"mutationResult.js","names":[],"sources":["../../../src/eventfullymanaged/lib/mutationResult.ts"],"sourcesContent":["import type { ErrorLike } from '@apollo/client';\n\nimport { notify } from './toast';\n\n/**\n * The shared gateway Apollo client runs mutations with `errorPolicy: 'all'`, so a\n * server-side failure is returned IN-BAND on `result.error` rather than thrown —\n * awaiting the mutation resolves successfully even when nothing was persisted.\n * Write forms must therefore inspect the resolved result before treating a call\n * as a success, or they silently report a fake success and drop the user's data.\n *\n * `mutationSucceeded` centralises that check: it returns `true` only when there is\n * no in-band error AND the expected success payload id is present. Otherwise it\n * surfaces the real reason via the platform toast and returns `false` so the\n * caller keeps the form inputs intact and does not navigate away.\n */\nexport function mutationSucceeded(\n result: { error?: ErrorLike } | null | undefined,\n successId: string | null | undefined,\n failureMessage: string\n): boolean {\n if (result?.error) {\n notify.error(failureMessage, result.error.message);\n return false;\n }\n if (!successId) {\n notify.error(failureMessage, 'The server did not confirm the change. Please try again.');\n return false;\n }\n return true;\n}\n\n/**\n * Normalises a thrown error (network failure, or a client configured with\n * `errorPolicy: 'none'`) into a human-readable description for the toast.\n */\nexport function describeError(error: unknown): string | undefined {\n if (error instanceof Error && error.message.length > 0) {\n return error.message;\n }\n return undefined;\n}\n"],"mappings":";;AAgBA,SAAgB,EACd,GACA,GACA,GACS;AAST,QARI,GAAQ,SACV,EAAO,MAAM,GAAgB,EAAO,MAAM,QAAQ,EAC3C,MAEJ,IAIE,MAHL,EAAO,MAAM,GAAgB,2DAA2D,EACjF;;AASX,SAAgB,EAAc,GAAoC;AAChE,KAAI,aAAiB,SAAS,EAAM,QAAQ,SAAS,EACnD,QAAO,EAAM"}
@@ -0,0 +1,36 @@
1
+ import { toast as e } from "../../node_modules/sonner/dist/index.js";
2
+ //#region src/eventfullymanaged/lib/toast.ts
3
+ var t = {
4
+ success(t, n) {
5
+ e.success(t, {
6
+ description: n,
7
+ duration: 4e3
8
+ });
9
+ },
10
+ info(t, n) {
11
+ e.info(t, {
12
+ description: n,
13
+ duration: 4e3
14
+ });
15
+ },
16
+ warning(t, n) {
17
+ e.warning(t, {
18
+ description: n,
19
+ duration: 8e3
20
+ });
21
+ },
22
+ error(t, n) {
23
+ e.error(t, {
24
+ description: n,
25
+ duration: Infinity,
26
+ closeButton: !0
27
+ });
28
+ },
29
+ dismiss(t) {
30
+ e.dismiss(t);
31
+ }
32
+ };
33
+ //#endregion
34
+ export { t as notify };
35
+
36
+ //# sourceMappingURL=toast.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"toast.js","names":[],"sources":["../../../src/eventfullymanaged/lib/toast.ts"],"sourcesContent":["import { toast } from 'sonner';\n\n/**\n * Module-wide toast API. sonner is a global singleton; the matching <Toaster>\n * is owned by the HOST app shell (mounted once via AppShellProvider). Every\n * page/component surfaces feedback through these helpers instead of hand-rolling\n * fixed-position toast markup, so success/error notifications stay consistent\n * with the rest of the platform.\n *\n * Durations follow the notification spec: success/info auto-dismiss, errors stay\n * until the user dismisses them (sonner renders a close button and announces via\n * aria-live).\n */\nexport const notify = {\n success(message: string, description?: string): void {\n toast.success(message, { description, duration: 4000 });\n },\n info(message: string, description?: string): void {\n toast.info(message, { description, duration: 4000 });\n },\n warning(message: string, description?: string): void {\n toast.warning(message, { description, duration: 8000 });\n },\n error(message: string, description?: string): void {\n toast.error(message, { description, duration: Infinity, closeButton: true });\n },\n dismiss(id?: string | number): void {\n toast.dismiss(id);\n },\n};\n\nexport { toast };\n"],"mappings":";;AAaA,IAAa,IAAS;CACpB,QAAQ,GAAiB,GAA4B;AACnD,IAAM,QAAQ,GAAS;GAAE;GAAa,UAAU;GAAM,CAAC;;CAEzD,KAAK,GAAiB,GAA4B;AAChD,IAAM,KAAK,GAAS;GAAE;GAAa,UAAU;GAAM,CAAC;;CAEtD,QAAQ,GAAiB,GAA4B;AACnD,IAAM,QAAQ,GAAS;GAAE;GAAa,UAAU;GAAM,CAAC;;CAEzD,MAAM,GAAiB,GAA4B;AACjD,IAAM,MAAM,GAAS;GAAE;GAAa,UAAU;GAAU,aAAa;GAAM,CAAC;;CAE9E,QAAQ,GAA4B;AAClC,IAAM,QAAQ,EAAG;;CAEpB"}
@@ -1,56 +1,64 @@
1
- import { useAnalyticsSnapshotsQuery as e, useUpsertAnalyticsSnapshotMutation as t } from "../../api/eventApi.js";
2
- import { EntityListSection as n } from "./EntityListSection.js";
3
- import { useMemo as r, useState as i } from "react";
4
- import { jsx as a, jsxs as o } from "react/jsx-runtime";
5
- import { BarChart3 as s } from "lucide-react";
6
- import { Button as c, EmphasisPanel as l, GradientText as u, Input as d, PagePurpose as f } from "@burdenoff/fe-libs/ui";
1
+ import { notify as e } from "../../lib/toast.js";
2
+ import { useAnalyticsSnapshotsQuery as t, useUpsertAnalyticsSnapshotMutation as n } from "../../api/eventApi.js";
3
+ import { describeError as r, mutationSucceeded as i } from "../../lib/mutationResult.js";
4
+ import { EntityListSection as a } from "./EntityListSection.js";
5
+ import { useMemo as o, useState as s } from "react";
6
+ import { jsx as c, jsxs as l } from "react/jsx-runtime";
7
+ import { BarChart3 as u } from "lucide-react";
8
+ import { Button as d, EmphasisPanel as f, GradientText as p, Input as m, PagePurpose as h } from "@burdenoff/fe-libs/ui";
7
9
  //#region src/eventfullymanaged/pages/modules/AnalyticsPage.tsx
8
- function p() {
9
- let p = e({
10
+ function g() {
11
+ let g = t({
10
12
  limit: 100,
11
13
  offset: 0
12
- }), [m, { loading: h }] = t(), [g, _] = i("overview"), v = r(() => p.data?.analyticsSnapshots.items ?? [], [p.data]);
13
- return /* @__PURE__ */ o("div", {
14
+ }), [_, { loading: v }] = n(), [y, b] = s("overview"), x = o(() => g.data?.analyticsSnapshots.items ?? [], [g.data]);
15
+ return /* @__PURE__ */ l("div", {
14
16
  className: "space-y-6 p-6",
15
17
  children: [
16
- /* @__PURE__ */ a(f, { children: "Snapshots capture a point-in-time picture of how your events are performing — registrations, attendance, revenue and engagement frozen at the moment you generate them. Use this page to take a daily or post-event snapshot so you can compare trends over time and report on outcomes." }),
17
- /* @__PURE__ */ o(l, { children: [
18
- /* @__PURE__ */ a("h1", {
18
+ /* @__PURE__ */ c(h, { children: "Snapshots capture a point-in-time picture of how your events are performing — registrations, attendance, revenue and engagement frozen at the moment you generate them. Use this page to take a daily or post-event snapshot so you can compare trends over time and report on outcomes." }),
19
+ /* @__PURE__ */ l(f, { children: [
20
+ /* @__PURE__ */ c("h1", {
19
21
  className: "text-2xl font-bold text-text-primary",
20
- children: /* @__PURE__ */ a(u, { children: "Analytics" })
22
+ children: /* @__PURE__ */ c(p, { children: "Analytics" })
21
23
  }),
22
- /* @__PURE__ */ a("p", {
24
+ /* @__PURE__ */ c("p", {
23
25
  className: "mt-1 text-sm text-text-secondary",
24
26
  children: "Generate and track analytics snapshots from event operations data."
25
27
  }),
26
- /* @__PURE__ */ o("div", {
28
+ /* @__PURE__ */ l("div", {
27
29
  className: "mt-4 flex flex-col gap-3 sm:flex-row",
28
- children: [/* @__PURE__ */ a(d, {
29
- value: g,
30
- onChange: (e) => _(e.target.value),
30
+ children: [/* @__PURE__ */ c(m, {
31
+ value: y,
32
+ onChange: (e) => b(e.target.value),
31
33
  placeholder: "daily-summary",
32
34
  "aria-label": "Snapshot name",
33
35
  className: "flex-1"
34
- }), /* @__PURE__ */ o(c, {
36
+ }), /* @__PURE__ */ l(d, {
35
37
  type: "button",
36
- disabled: h,
38
+ disabled: v,
37
39
  onClick: async () => {
38
- g.trim() && await m({ variables: { input: {
39
- name: g,
40
- title: g,
41
- status: "created",
42
- metadata: {}
43
- } } });
40
+ if (y.trim()) try {
41
+ let t = await _({ variables: { input: {
42
+ name: y,
43
+ title: y,
44
+ status: "created",
45
+ metadata: {}
46
+ } } });
47
+ if (!i(t, t.data?.upsertAnalyticsSnapshot?.id, "Could not generate snapshot")) return;
48
+ e.success("Snapshot generated");
49
+ } catch (t) {
50
+ e.error("Could not generate snapshot", r(t));
51
+ }
44
52
  },
45
- children: [/* @__PURE__ */ a(s, { className: "h-4 w-4" }), h ? "Generating..." : "Generate Snapshot"]
53
+ children: [/* @__PURE__ */ c(u, { className: "h-4 w-4" }), v ? "Generating..." : "Generate Snapshot"]
46
54
  })]
47
55
  })
48
56
  ] }),
49
- /* @__PURE__ */ a(n, {
50
- heading: p.loading ? "Loading snapshots..." : `Snapshots (${v.length})`,
57
+ /* @__PURE__ */ c(a, {
58
+ heading: g.loading ? "Loading snapshots..." : `Snapshots (${x.length})`,
51
59
  emptyLabel: "No analytics snapshots available yet.",
52
60
  emptyIllustration: "empty-data",
53
- rows: v.map((e) => ({
61
+ rows: x.map((e) => ({
54
62
  id: e.id,
55
63
  title: e.title ?? e.name ?? `Snapshot ${e.id}`,
56
64
  status: e.status ?? void 0
@@ -60,6 +68,6 @@ function p() {
60
68
  });
61
69
  }
62
70
  //#endregion
63
- export { p as default };
71
+ export { g as default };
64
72
 
65
73
  //# sourceMappingURL=AnalyticsPage.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"AnalyticsPage.js","names":[],"sources":["../../../../src/eventfullymanaged/pages/modules/AnalyticsPage.tsx"],"sourcesContent":["import { BarChart3 } from 'lucide-react';\nimport { Button, EmphasisPanel, GradientText, Input, PagePurpose } from '@burdenoff/fe-libs/ui';\nimport { useMemo, useState } from 'react';\n\nimport { useAnalyticsSnapshotsQuery, useUpsertAnalyticsSnapshotMutation } from '../../api/eventApi';\nimport { EntityListSection } from './EntityListSection';\n\ntype SnapshotRow = {\n id: string;\n name?: string;\n title?: string;\n status?: string;\n};\n\nexport default function AnalyticsPage() {\n const snapshotsQuery = useAnalyticsSnapshotsQuery({ limit: 100, offset: 0 });\n const [upsertSnapshot, { loading: creating }] = useUpsertAnalyticsSnapshotMutation();\n const [snapshotName, setSnapshotName] = useState('overview');\n\n const snapshots = useMemo(\n () => (snapshotsQuery.data?.analyticsSnapshots.items ?? []) as SnapshotRow[],\n [snapshotsQuery.data]\n );\n\n const onCreate = async () => {\n if (!snapshotName.trim()) return;\n await upsertSnapshot({\n variables: {\n input: {\n name: snapshotName,\n title: snapshotName,\n status: 'created',\n metadata: {},\n },\n },\n });\n };\n\n return (\n <div className=\"space-y-6 p-6\">\n <PagePurpose>\n Snapshots capture a point-in-time picture of how your events are performing — registrations, attendance,\n revenue and engagement frozen at the moment you generate them. Use this page to take a daily or post-event\n snapshot so you can compare trends over time and report on outcomes.\n </PagePurpose>\n\n <EmphasisPanel>\n <h1 className=\"text-2xl font-bold text-text-primary\">\n <GradientText>Analytics</GradientText>\n </h1>\n <p className=\"mt-1 text-sm text-text-secondary\">\n Generate and track analytics snapshots from event operations data.\n </p>\n <div className=\"mt-4 flex flex-col gap-3 sm:flex-row\">\n <Input\n value={snapshotName}\n onChange={(e) => setSnapshotName(e.target.value)}\n placeholder=\"daily-summary\"\n aria-label=\"Snapshot name\"\n className=\"flex-1\"\n />\n <Button type=\"button\" disabled={creating} onClick={onCreate}>\n <BarChart3 className=\"h-4 w-4\" />\n {creating ? 'Generating...' : 'Generate Snapshot'}\n </Button>\n </div>\n </EmphasisPanel>\n\n <EntityListSection\n heading={snapshotsQuery.loading ? 'Loading snapshots...' : `Snapshots (${snapshots.length})`}\n emptyLabel=\"No analytics snapshots available yet.\"\n emptyIllustration=\"empty-data\"\n rows={snapshots.map((row) => ({\n id: row.id,\n title: row.title ?? row.name ?? `Snapshot ${row.id}`,\n status: row.status ?? undefined,\n }))}\n />\n </div>\n );\n}\n"],"mappings":";;;;;;;AAcA,SAAwB,IAAgB;CACtC,IAAM,IAAiB,EAA2B;EAAE,OAAO;EAAK,QAAQ;EAAG,CAAC,EACtE,CAAC,GAAgB,EAAE,SAAS,OAAc,GAAoC,EAC9E,CAAC,GAAc,KAAmB,EAAS,WAAW,EAEtD,IAAY,QACT,EAAe,MAAM,mBAAmB,SAAS,EAAE,EAC1D,CAAC,EAAe,KAAK,CACtB;AAgBD,QACE,kBAAC,OAAD;EAAK,WAAU;YAAf;GACE,kBAAC,GAAD,EAAA,UAAa,4RAIC,CAAA;GAEd,kBAAC,GAAD,EAAA,UAAA;IACE,kBAAC,MAAD;KAAI,WAAU;eACZ,kBAAC,GAAD,EAAA,UAAc,aAAwB,CAAA;KACnC,CAAA;IACL,kBAAC,KAAD;KAAG,WAAU;eAAmC;KAE5C,CAAA;IACJ,kBAAC,OAAD;KAAK,WAAU;eAAf,CACE,kBAAC,GAAD;MACE,OAAO;MACP,WAAW,MAAM,EAAgB,EAAE,OAAO,MAAM;MAChD,aAAY;MACZ,cAAW;MACX,WAAU;MACV,CAAA,EACF,kBAAC,GAAD;MAAQ,MAAK;MAAS,UAAU;MAAU,SArCjC,YAAY;AACtB,SAAa,MAAM,IACxB,MAAM,EAAe,EACnB,WAAW,EACT,OAAO;QACL,MAAM;QACN,OAAO;QACP,QAAQ;QACR,UAAU,EAAE;QACb,EACF,EACF,CAAC;;gBA0BI,CACE,kBAAC,GAAD,EAAW,WAAU,WAAY,CAAA,EAChC,IAAW,kBAAkB,oBACvB;QACL;;IACQ,EAAA,CAAA;GAEhB,kBAAC,GAAD;IACE,SAAS,EAAe,UAAU,yBAAyB,cAAc,EAAU,OAAO;IAC1F,YAAW;IACX,mBAAkB;IAClB,MAAM,EAAU,KAAK,OAAS;KAC5B,IAAI,EAAI;KACR,OAAO,EAAI,SAAS,EAAI,QAAQ,YAAY,EAAI;KAChD,QAAQ,EAAI,UAAU,KAAA;KACvB,EAAE;IACH,CAAA;GACE"}
1
+ {"version":3,"file":"AnalyticsPage.js","names":[],"sources":["../../../../src/eventfullymanaged/pages/modules/AnalyticsPage.tsx"],"sourcesContent":["import { BarChart3 } from 'lucide-react';\nimport { Button, EmphasisPanel, GradientText, Input, PagePurpose } from '@burdenoff/fe-libs/ui';\nimport { useMemo, useState } from 'react';\n\nimport { useAnalyticsSnapshotsQuery, useUpsertAnalyticsSnapshotMutation } from '../../api/eventApi';\nimport { describeError, mutationSucceeded } from '../../lib/mutationResult';\nimport { notify } from '../../lib/toast';\nimport { EntityListSection } from './EntityListSection';\n\ntype SnapshotRow = {\n id: string;\n name?: string;\n title?: string;\n status?: string;\n};\n\nexport default function AnalyticsPage() {\n const snapshotsQuery = useAnalyticsSnapshotsQuery({ limit: 100, offset: 0 });\n const [upsertSnapshot, { loading: creating }] = useUpsertAnalyticsSnapshotMutation();\n const [snapshotName, setSnapshotName] = useState('overview');\n\n const snapshots = useMemo(\n () => (snapshotsQuery.data?.analyticsSnapshots.items ?? []) as SnapshotRow[],\n [snapshotsQuery.data]\n );\n\n const onCreate = async () => {\n if (!snapshotName.trim()) return;\n try {\n const response = await upsertSnapshot({\n variables: {\n input: {\n name: snapshotName,\n title: snapshotName,\n status: 'created',\n metadata: {},\n },\n },\n });\n if (!mutationSucceeded(response, response.data?.upsertAnalyticsSnapshot?.id, 'Could not generate snapshot')) {\n return;\n }\n notify.success('Snapshot generated');\n } catch (error) {\n notify.error('Could not generate snapshot', describeError(error));\n }\n };\n\n return (\n <div className=\"space-y-6 p-6\">\n <PagePurpose>\n Snapshots capture a point-in-time picture of how your events are performing — registrations, attendance, revenue\n and engagement frozen at the moment you generate them. Use this page to take a daily or post-event snapshot so\n you can compare trends over time and report on outcomes.\n </PagePurpose>\n\n <EmphasisPanel>\n <h1 className=\"text-2xl font-bold text-text-primary\">\n <GradientText>Analytics</GradientText>\n </h1>\n <p className=\"mt-1 text-sm text-text-secondary\">\n Generate and track analytics snapshots from event operations data.\n </p>\n <div className=\"mt-4 flex flex-col gap-3 sm:flex-row\">\n <Input\n value={snapshotName}\n onChange={(e) => setSnapshotName(e.target.value)}\n placeholder=\"daily-summary\"\n aria-label=\"Snapshot name\"\n className=\"flex-1\"\n />\n <Button type=\"button\" disabled={creating} onClick={onCreate}>\n <BarChart3 className=\"h-4 w-4\" />\n {creating ? 'Generating...' : 'Generate Snapshot'}\n </Button>\n </div>\n </EmphasisPanel>\n\n <EntityListSection\n heading={snapshotsQuery.loading ? 'Loading snapshots...' : `Snapshots (${snapshots.length})`}\n emptyLabel=\"No analytics snapshots available yet.\"\n emptyIllustration=\"empty-data\"\n rows={snapshots.map((row) => ({\n id: row.id,\n title: row.title ?? row.name ?? `Snapshot ${row.id}`,\n status: row.status ?? undefined,\n }))}\n />\n </div>\n );\n}\n"],"mappings":";;;;;;;;;AAgBA,SAAwB,IAAgB;CACtC,IAAM,IAAiB,EAA2B;EAAE,OAAO;EAAK,QAAQ;EAAG,CAAC,EACtE,CAAC,GAAgB,EAAE,SAAS,OAAc,GAAoC,EAC9E,CAAC,GAAc,KAAmB,EAAS,WAAW,EAEtD,IAAY,QACT,EAAe,MAAM,mBAAmB,SAAS,EAAE,EAC1D,CAAC,EAAe,KAAK,CACtB;AAwBD,QACE,kBAAC,OAAD;EAAK,WAAU;YAAf;GACE,kBAAC,GAAD,EAAA,UAAa,4RAIC,CAAA;GAEd,kBAAC,GAAD,EAAA,UAAA;IACE,kBAAC,MAAD;KAAI,WAAU;eACZ,kBAAC,GAAD,EAAA,UAAc,aAAwB,CAAA;KACnC,CAAA;IACL,kBAAC,KAAD;KAAG,WAAU;eAAmC;KAE5C,CAAA;IACJ,kBAAC,OAAD;KAAK,WAAU;eAAf,CACE,kBAAC,GAAD;MACE,OAAO;MACP,WAAW,MAAM,EAAgB,EAAE,OAAO,MAAM;MAChD,aAAY;MACZ,cAAW;MACX,WAAU;MACV,CAAA,EACF,kBAAC,GAAD;MAAQ,MAAK;MAAS,UAAU;MAAU,SA7CjC,YAAY;AACtB,aAAa,MAAM,CACxB,KAAI;QACF,IAAM,IAAW,MAAM,EAAe,EACpC,WAAW,EACT,OAAO;SACL,MAAM;SACN,OAAO;SACP,QAAQ;SACR,UAAU,EAAE;SACb,EACF,EACF,CAAC;AACF,YAAI,CAAC,EAAkB,GAAU,EAAS,MAAM,yBAAyB,IAAI,8BAA8B,CACzG;AAEF,UAAO,QAAQ,qBAAqB;gBAC7B,GAAO;AACd,UAAO,MAAM,+BAA+B,EAAc,EAAM,CAAC;;;gBA2B7D,CACE,kBAAC,GAAD,EAAW,WAAU,WAAY,CAAA,EAChC,IAAW,kBAAkB,oBACvB;QACL;;IACQ,EAAA,CAAA;GAEhB,kBAAC,GAAD;IACE,SAAS,EAAe,UAAU,yBAAyB,cAAc,EAAU,OAAO;IAC1F,YAAW;IACX,mBAAkB;IAClB,MAAM,EAAU,KAAK,OAAS;KAC5B,IAAI,EAAI;KACR,OAAO,EAAI,SAAS,EAAI,QAAQ,YAAY,EAAI;KAChD,QAAQ,EAAI,UAAU,KAAA;KACvB,EAAE;IACH,CAAA;GACE"}
@@ -1,94 +1,117 @@
1
1
  import { useEventfullyManaged as e } from "../../context/EventfullyManagedContext.js";
2
2
  import { useAttendeesQuery as t, useRegistrationsQuery as n } from "../../api/eventApi.js";
3
3
  import { EntityListSection as r } from "./EntityListSection.js";
4
- import { useMemo as i } from "react";
5
- import { useParams as a } from "react-router-dom";
6
- import { jsx as o, jsxs as s } from "react/jsx-runtime";
7
- import { Megaphone as c, Ticket as l, Users as u } from "lucide-react";
8
- import { EmphasisPanel as d, GlassCard as f, GradientText as p, NextSteps as m, PagePurpose as h } from "@burdenoff/fe-libs/ui";
4
+ import { useMemo as i, useRef as a } from "react";
5
+ import { useParams as o } from "react-router-dom";
6
+ import { jsx as s, jsxs as c } from "react/jsx-runtime";
7
+ import { MessageSquare as l, Ticket as u, Users as d } from "lucide-react";
8
+ import { EmphasisPanel as f, GlassCard as p, GradientText as m, NextSteps as h, PagePurpose as g } from "@burdenoff/fe-libs/ui";
9
+ import { ContextConversationWidget as _ } from "@burdenoff/fe-libs/shared";
10
+ import { useRegisterPageConversationContext as v } from "@burdenoff/fe-libs/shared/providers/shell";
9
11
  //#region src/eventfullymanaged/pages/modules/AttendeeProfilePage.tsx
10
- function g() {
11
- let { id: g = "" } = a(), { navigate: _ } = e(), v = t({
12
+ function y({ attendeeId: e, attendeeName: t }) {
13
+ return v({
14
+ entityType: "EVENTFULLYMANAGED_ATTENDEE",
15
+ entityId: e,
16
+ contextTitle: `Attendee · ${t}`
17
+ }), /* @__PURE__ */ s(_, { widgetKey: "attendee-message" });
18
+ }
19
+ function b() {
20
+ let { id: _ = "" } = o(), { navigate: v } = e(), b = t({
12
21
  limit: 100,
13
22
  offset: 0
14
- }), y = n({
23
+ }), x = n({
15
24
  limit: 100,
16
25
  offset: 0
17
- }), b = i(() => (v.data?.attendees.items ?? []).find((e) => e.id === g), [g, v.data]), x = i(() => (y.data?.registrations.items ?? []).filter((e) => e.attendeeId === g), [g, y.data]);
18
- return /* @__PURE__ */ s("div", {
26
+ }), S = a(null), C = i(() => (b.data?.attendees.items ?? []).find((e) => e.id === _), [_, b.data]), w = i(() => (x.data?.registrations.items ?? []).filter((e) => e.attendeeId === _), [_, x.data]);
27
+ return /* @__PURE__ */ c("div", {
19
28
  className: "space-y-6 p-6",
20
29
  children: [
21
- /* @__PURE__ */ o(h, { children: "A single view of one attendee — who they are and everything they have registered for. Use this when you need to look someone up: verify their ticket, answer a support question, or understand their history with your events before reaching out." }),
22
- /* @__PURE__ */ s(d, { children: [
23
- /* @__PURE__ */ s("h1", {
30
+ /* @__PURE__ */ s(g, { children: "A single view of one attendee — who they are and everything they have registered for. Use this when you need to look someone up: verify their ticket, answer a support question, or understand their history with your events before reaching out." }),
31
+ /* @__PURE__ */ c(f, { children: [
32
+ /* @__PURE__ */ c("h1", {
24
33
  className: "text-2xl font-bold text-text-primary",
25
- children: [/* @__PURE__ */ o(p, { children: "Attendee" }), " Profile"]
34
+ children: [/* @__PURE__ */ s(m, { children: "Attendee" }), " Profile"]
26
35
  }),
27
- /* @__PURE__ */ o("p", {
36
+ /* @__PURE__ */ s("p", {
28
37
  className: "mt-1 text-sm text-text-secondary",
29
- children: v.loading ? "Loading attendee profile..." : b ? `${b.fullName ?? b.id} profile from registration and attendee APIs.` : "Attendee not found in current query scope."
38
+ children: b.loading ? "Loading attendee profile..." : C ? `${C.fullName ?? C.id} profile from registration and attendee APIs.` : "Attendee not found in current query scope."
30
39
  }),
31
- b ? /* @__PURE__ */ s("div", {
40
+ C ? /* @__PURE__ */ c("div", {
32
41
  className: "mt-4 grid grid-cols-1 gap-3 sm:grid-cols-2 lg:grid-cols-4",
33
42
  children: [
34
- /* @__PURE__ */ s(f, {
43
+ /* @__PURE__ */ c(p, {
35
44
  treatment: "metric",
36
45
  className: "p-3",
37
- children: [/* @__PURE__ */ o("p", {
46
+ children: [/* @__PURE__ */ s("p", {
38
47
  className: "text-xs uppercase tracking-wide text-text-secondary",
39
48
  children: "Name"
40
- }), /* @__PURE__ */ o("p", {
49
+ }), /* @__PURE__ */ s("p", {
41
50
  className: "mt-1 font-medium text-text-primary",
42
- children: b.fullName ?? b.id
51
+ children: C.fullName ?? C.id
43
52
  })]
44
53
  }),
45
- /* @__PURE__ */ s(f, {
54
+ /* @__PURE__ */ c(p, {
46
55
  treatment: "metric",
47
56
  className: "p-3",
48
- children: [/* @__PURE__ */ o("p", {
57
+ children: [/* @__PURE__ */ s("p", {
49
58
  className: "text-xs uppercase tracking-wide text-text-secondary",
50
59
  children: "Email"
51
- }), /* @__PURE__ */ o("p", {
60
+ }), /* @__PURE__ */ s("p", {
52
61
  className: "mt-1 font-medium text-text-primary",
53
- children: b.email ?? "Unavailable"
62
+ children: C.email ?? "Unavailable"
54
63
  })]
55
64
  }),
56
- /* @__PURE__ */ s(f, {
65
+ /* @__PURE__ */ c(p, {
57
66
  treatment: "metric",
58
67
  className: "p-3",
59
- children: [/* @__PURE__ */ o("p", {
68
+ children: [/* @__PURE__ */ s("p", {
60
69
  className: "text-xs uppercase tracking-wide text-text-secondary",
61
70
  children: "Company"
62
- }), /* @__PURE__ */ o("p", {
71
+ }), /* @__PURE__ */ s("p", {
63
72
  className: "mt-1 font-medium text-text-primary",
64
- children: b.company ?? "Unavailable"
73
+ children: C.company ?? "Unavailable"
65
74
  })]
66
75
  }),
67
- /* @__PURE__ */ s(f, {
76
+ /* @__PURE__ */ c(p, {
68
77
  treatment: "metric",
69
78
  className: "p-3",
70
- children: [/* @__PURE__ */ o("p", {
79
+ children: [/* @__PURE__ */ s("p", {
71
80
  className: "text-xs uppercase tracking-wide text-text-secondary",
72
81
  children: "Role"
73
- }), /* @__PURE__ */ o("p", {
82
+ }), /* @__PURE__ */ s("p", {
74
83
  className: "mt-1 font-medium text-text-primary",
75
- children: b.role ?? "Unavailable"
84
+ children: C.role ?? "Unavailable"
76
85
  })]
77
86
  })
78
87
  ]
79
88
  }) : null
80
89
  ] }),
81
- /* @__PURE__ */ o(r, {
82
- heading: y.loading ? "Loading attendee registrations..." : `Registrations (${x.length})`,
90
+ /* @__PURE__ */ s(r, {
91
+ heading: x.loading ? "Loading attendee registrations..." : `Registrations (${w.length})`,
83
92
  emptyLabel: "No registrations linked to this attendee.",
84
- rows: x.map((e) => ({
93
+ rows: w.map((e) => ({
85
94
  id: e.id,
86
95
  title: `Registration ${e.id}`,
87
96
  subtitle: [e.quantity ? `Qty ${e.quantity}` : void 0, e.totalAmount ? `${e.totalAmount} ${e.currency ?? ""}` : void 0].filter(Boolean).join(" • "),
88
97
  status: e.status
89
98
  }))
90
99
  }),
91
- /* @__PURE__ */ o(m, {
100
+ _ ? /* @__PURE__ */ c("div", {
101
+ ref: S,
102
+ className: "space-y-3",
103
+ children: [/* @__PURE__ */ c("div", { children: [/* @__PURE__ */ s("h2", {
104
+ className: "text-lg font-semibold text-text-primary",
105
+ children: "Messages"
106
+ }), /* @__PURE__ */ s("p", {
107
+ className: "mt-1 text-sm text-text-secondary",
108
+ children: "Start a direct conversation with this attendee. Threads and attachments are handled by the shared conversations service."
109
+ })] }), /* @__PURE__ */ s(y, {
110
+ attendeeId: _,
111
+ attendeeName: C?.fullName ?? C?.id ?? _
112
+ })]
113
+ }) : null,
114
+ /* @__PURE__ */ s(h, {
92
115
  storageKey: "eventfullymanaged-attendee-profile",
93
116
  title: "Act on this attendee",
94
117
  steps: [
@@ -96,22 +119,25 @@ function g() {
96
119
  id: "view-registrations",
97
120
  label: "Review all registrations",
98
121
  description: "See the full registration list to manage tickets and payments.",
99
- icon: /* @__PURE__ */ o(l, { className: "h-4 w-4" }),
100
- onClick: () => _("/registration")
122
+ icon: /* @__PURE__ */ s(u, { className: "h-4 w-4" }),
123
+ onClick: () => v("/registration")
101
124
  },
102
125
  {
103
126
  id: "networking",
104
127
  label: "Find networking matches",
105
128
  description: "Connect this attendee with relevant people and meetings.",
106
- icon: /* @__PURE__ */ o(u, { className: "h-4 w-4" }),
107
- onClick: () => _("/networking")
129
+ icon: /* @__PURE__ */ s(d, { className: "h-4 w-4" }),
130
+ onClick: () => v("/networking")
108
131
  },
109
132
  {
110
133
  id: "reach-out",
111
134
  label: "Send a message",
112
- description: "Reach this attendee through an announcement or campaign.",
113
- icon: /* @__PURE__ */ o(c, { className: "h-4 w-4" }),
114
- onClick: () => _("/communication")
135
+ description: "Open a direct conversation thread with this attendee.",
136
+ icon: /* @__PURE__ */ s(l, { className: "h-4 w-4" }),
137
+ onClick: () => S.current?.scrollIntoView({
138
+ behavior: "smooth",
139
+ block: "start"
140
+ })
115
141
  }
116
142
  ]
117
143
  })
@@ -119,6 +145,6 @@ function g() {
119
145
  });
120
146
  }
121
147
  //#endregion
122
- export { g as default };
148
+ export { b as default };
123
149
 
124
150
  //# sourceMappingURL=AttendeeProfilePage.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"AttendeeProfilePage.js","names":[],"sources":["../../../../src/eventfullymanaged/pages/modules/AttendeeProfilePage.tsx"],"sourcesContent":["import { Megaphone, Ticket, Users } from 'lucide-react';\nimport { EmphasisPanel, GlassCard, GradientText, NextSteps, PagePurpose } from '@burdenoff/fe-libs/ui';\nimport { useMemo } from 'react';\nimport { useParams } from 'react-router-dom';\n\nimport { useAttendeesQuery, useRegistrationsQuery } from '../../api/eventApi';\nimport { useEventfullyManaged } from '../../context/EventfullyManagedContext';\nimport { EntityListSection } from './EntityListSection';\n\ntype AttendeeRow = {\n id: string;\n fullName?: string;\n email?: string;\n company?: string;\n role?: string;\n};\n\ntype RegistrationRow = {\n id: string;\n attendeeId?: string;\n status?: string;\n quantity?: number;\n currency?: string;\n totalAmount?: string;\n};\n\nexport default function AttendeeProfilePage() {\n const { id = '' } = useParams();\n const { navigate } = useEventfullyManaged();\n const attendeesQuery = useAttendeesQuery({ limit: 100, offset: 0 });\n const registrationsQuery = useRegistrationsQuery({ limit: 100, offset: 0 });\n\n const attendee = useMemo(\n () => ((attendeesQuery.data?.attendees.items ?? []) as AttendeeRow[]).find((item) => item.id === id),\n [id, attendeesQuery.data]\n );\n const registrations = useMemo(\n () =>\n ((registrationsQuery.data?.registrations.items ?? []) as RegistrationRow[]).filter(\n (registration) => registration.attendeeId === id\n ),\n [id, registrationsQuery.data]\n );\n\n return (\n <div className=\"space-y-6 p-6\">\n <PagePurpose>\n A single view of one attendee — who they are and everything they have registered for. Use this when you need\n to look someone up: verify their ticket, answer a support question, or understand their history with your\n events before reaching out.\n </PagePurpose>\n\n <EmphasisPanel>\n <h1 className=\"text-2xl font-bold text-text-primary\">\n <GradientText>Attendee</GradientText> Profile\n </h1>\n <p className=\"mt-1 text-sm text-text-secondary\">\n {attendeesQuery.loading\n ? 'Loading attendee profile...'\n : attendee\n ? `${attendee.fullName ?? attendee.id} profile from registration and attendee APIs.`\n : 'Attendee not found in current query scope.'}\n </p>\n {attendee ? (\n <div className=\"mt-4 grid grid-cols-1 gap-3 sm:grid-cols-2 lg:grid-cols-4\">\n <GlassCard treatment=\"metric\" className=\"p-3\">\n <p className=\"text-xs uppercase tracking-wide text-text-secondary\">Name</p>\n <p className=\"mt-1 font-medium text-text-primary\">{attendee.fullName ?? attendee.id}</p>\n </GlassCard>\n <GlassCard treatment=\"metric\" className=\"p-3\">\n <p className=\"text-xs uppercase tracking-wide text-text-secondary\">Email</p>\n <p className=\"mt-1 font-medium text-text-primary\">{attendee.email ?? 'Unavailable'}</p>\n </GlassCard>\n <GlassCard treatment=\"metric\" className=\"p-3\">\n <p className=\"text-xs uppercase tracking-wide text-text-secondary\">Company</p>\n <p className=\"mt-1 font-medium text-text-primary\">{attendee.company ?? 'Unavailable'}</p>\n </GlassCard>\n <GlassCard treatment=\"metric\" className=\"p-3\">\n <p className=\"text-xs uppercase tracking-wide text-text-secondary\">Role</p>\n <p className=\"mt-1 font-medium text-text-primary\">{attendee.role ?? 'Unavailable'}</p>\n </GlassCard>\n </div>\n ) : null}\n </EmphasisPanel>\n\n <EntityListSection\n heading={\n registrationsQuery.loading ? 'Loading attendee registrations...' : `Registrations (${registrations.length})`\n }\n emptyLabel=\"No registrations linked to this attendee.\"\n rows={registrations.map((registration) => ({\n id: registration.id,\n title: `Registration ${registration.id}`,\n subtitle: [\n registration.quantity ? `Qty ${registration.quantity}` : undefined,\n registration.totalAmount ? `${registration.totalAmount} ${registration.currency ?? ''}` : undefined,\n ]\n .filter(Boolean)\n .join(' • '),\n status: registration.status,\n }))}\n />\n\n <NextSteps\n storageKey=\"eventfullymanaged-attendee-profile\"\n title=\"Act on this attendee\"\n steps={[\n {\n id: 'view-registrations',\n label: 'Review all registrations',\n description: 'See the full registration list to manage tickets and payments.',\n icon: <Ticket className=\"h-4 w-4\" />,\n onClick: () => navigate('/registration'),\n },\n {\n id: 'networking',\n label: 'Find networking matches',\n description: 'Connect this attendee with relevant people and meetings.',\n icon: <Users className=\"h-4 w-4\" />,\n onClick: () => navigate('/networking'),\n },\n {\n id: 'reach-out',\n label: 'Send a message',\n description: 'Reach this attendee through an announcement or campaign.',\n icon: <Megaphone className=\"h-4 w-4\" />,\n onClick: () => navigate('/communication'),\n },\n ]}\n />\n </div>\n );\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"AttendeeProfilePage.js","names":[],"sources":["../../../../src/eventfullymanaged/pages/modules/AttendeeProfilePage.tsx"],"sourcesContent":["import { MessageSquare, Ticket, Users } from 'lucide-react';\nimport { ContextConversationWidget } from '@burdenoff/fe-libs/shared';\nimport { useRegisterPageConversationContext } from '@burdenoff/fe-libs/shared/providers/shell';\nimport { EmphasisPanel, GlassCard, GradientText, NextSteps, PagePurpose } from '@burdenoff/fe-libs/ui';\nimport { useMemo, useRef } from 'react';\nimport { useParams } from 'react-router-dom';\n\nimport { useAttendeesQuery, useRegistrationsQuery } from '../../api/eventApi';\nimport { useEventfullyManaged } from '../../context/EventfullyManagedContext';\nimport { EntityListSection } from './EntityListSection';\n\n/**\n * Direct message thread with a single attendee, backed by wspace-conversations-svc.\n * Kept as a dedicated component so `useRegisterPageConversationContext` scopes the\n * thread to this attendee (entityType + entityId) and unregisters on unmount.\n */\nfunction AttendeeConversation({ attendeeId, attendeeName }: { attendeeId: string; attendeeName: string }) {\n useRegisterPageConversationContext({\n entityType: 'EVENTFULLYMANAGED_ATTENDEE',\n entityId: attendeeId,\n contextTitle: `Attendee · ${attendeeName}`,\n });\n return <ContextConversationWidget widgetKey=\"attendee-message\" />;\n}\n\ntype AttendeeRow = {\n id: string;\n fullName?: string;\n email?: string;\n company?: string;\n role?: string;\n};\n\ntype RegistrationRow = {\n id: string;\n attendeeId?: string;\n status?: string;\n quantity?: number;\n currency?: string;\n totalAmount?: string;\n};\n\nexport default function AttendeeProfilePage() {\n const { id = '' } = useParams();\n const { navigate } = useEventfullyManaged();\n const attendeesQuery = useAttendeesQuery({ limit: 100, offset: 0 });\n const registrationsQuery = useRegistrationsQuery({ limit: 100, offset: 0 });\n const conversationRef = useRef<HTMLDivElement | null>(null);\n\n const attendee = useMemo(\n () => ((attendeesQuery.data?.attendees.items ?? []) as AttendeeRow[]).find((item) => item.id === id),\n [id, attendeesQuery.data]\n );\n const registrations = useMemo(\n () =>\n ((registrationsQuery.data?.registrations.items ?? []) as RegistrationRow[]).filter(\n (registration) => registration.attendeeId === id\n ),\n [id, registrationsQuery.data]\n );\n\n return (\n <div className=\"space-y-6 p-6\">\n <PagePurpose>\n A single view of one attendee — who they are and everything they have registered for. Use this when you need to\n look someone up: verify their ticket, answer a support question, or understand their history with your events\n before reaching out.\n </PagePurpose>\n\n <EmphasisPanel>\n <h1 className=\"text-2xl font-bold text-text-primary\">\n <GradientText>Attendee</GradientText> Profile\n </h1>\n <p className=\"mt-1 text-sm text-text-secondary\">\n {attendeesQuery.loading\n ? 'Loading attendee profile...'\n : attendee\n ? `${attendee.fullName ?? attendee.id} profile from registration and attendee APIs.`\n : 'Attendee not found in current query scope.'}\n </p>\n {attendee ? (\n <div className=\"mt-4 grid grid-cols-1 gap-3 sm:grid-cols-2 lg:grid-cols-4\">\n <GlassCard treatment=\"metric\" className=\"p-3\">\n <p className=\"text-xs uppercase tracking-wide text-text-secondary\">Name</p>\n <p className=\"mt-1 font-medium text-text-primary\">{attendee.fullName ?? attendee.id}</p>\n </GlassCard>\n <GlassCard treatment=\"metric\" className=\"p-3\">\n <p className=\"text-xs uppercase tracking-wide text-text-secondary\">Email</p>\n <p className=\"mt-1 font-medium text-text-primary\">{attendee.email ?? 'Unavailable'}</p>\n </GlassCard>\n <GlassCard treatment=\"metric\" className=\"p-3\">\n <p className=\"text-xs uppercase tracking-wide text-text-secondary\">Company</p>\n <p className=\"mt-1 font-medium text-text-primary\">{attendee.company ?? 'Unavailable'}</p>\n </GlassCard>\n <GlassCard treatment=\"metric\" className=\"p-3\">\n <p className=\"text-xs uppercase tracking-wide text-text-secondary\">Role</p>\n <p className=\"mt-1 font-medium text-text-primary\">{attendee.role ?? 'Unavailable'}</p>\n </GlassCard>\n </div>\n ) : null}\n </EmphasisPanel>\n\n <EntityListSection\n heading={\n registrationsQuery.loading ? 'Loading attendee registrations...' : `Registrations (${registrations.length})`\n }\n emptyLabel=\"No registrations linked to this attendee.\"\n rows={registrations.map((registration) => ({\n id: registration.id,\n title: `Registration ${registration.id}`,\n subtitle: [\n registration.quantity ? `Qty ${registration.quantity}` : undefined,\n registration.totalAmount ? `${registration.totalAmount} ${registration.currency ?? ''}` : undefined,\n ]\n .filter(Boolean)\n .join(' • '),\n status: registration.status,\n }))}\n />\n\n {id ? (\n <div ref={conversationRef} className=\"space-y-3\">\n <div>\n <h2 className=\"text-lg font-semibold text-text-primary\">Messages</h2>\n <p className=\"mt-1 text-sm text-text-secondary\">\n Start a direct conversation with this attendee. Threads and attachments are handled by the shared\n conversations service.\n </p>\n </div>\n <AttendeeConversation attendeeId={id} attendeeName={attendee?.fullName ?? attendee?.id ?? id} />\n </div>\n ) : null}\n\n <NextSteps\n storageKey=\"eventfullymanaged-attendee-profile\"\n title=\"Act on this attendee\"\n steps={[\n {\n id: 'view-registrations',\n label: 'Review all registrations',\n description: 'See the full registration list to manage tickets and payments.',\n icon: <Ticket className=\"h-4 w-4\" />,\n onClick: () => navigate('/registration'),\n },\n {\n id: 'networking',\n label: 'Find networking matches',\n description: 'Connect this attendee with relevant people and meetings.',\n icon: <Users className=\"h-4 w-4\" />,\n onClick: () => navigate('/networking'),\n },\n {\n id: 'reach-out',\n label: 'Send a message',\n description: 'Open a direct conversation thread with this attendee.',\n icon: <MessageSquare className=\"h-4 w-4\" />,\n onClick: () => conversationRef.current?.scrollIntoView({ behavior: 'smooth', block: 'start' }),\n },\n ]}\n />\n </div>\n );\n}\n"],"mappings":"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"}
@@ -1,73 +1,83 @@
1
- import { useAnnouncementsQuery as e, useCommunicationCampaignsQuery as t, useUpsertAnnouncementMutation as n } from "../../api/eventApi.js";
2
- import { EntityListSection as r } from "./EntityListSection.js";
3
- import { useMemo as i, useState as a } from "react";
4
- import { jsx as o, jsxs as s } from "react/jsx-runtime";
5
- import { Megaphone as c } from "lucide-react";
6
- import { useI18n as l } from "@burdenoff/fe-libs/shared/providers/shell/I18nProvider";
7
- import { Button as u, EmphasisPanel as d, GradientText as f, Input as p, PagePurpose as m } from "@burdenoff/fe-libs/ui";
1
+ import { notify as e } from "../../lib/toast.js";
2
+ import { useAnnouncementsQuery as t, useCommunicationCampaignsQuery as n, useUpsertAnnouncementMutation as r } from "../../api/eventApi.js";
3
+ import { describeError as i, mutationSucceeded as a } from "../../lib/mutationResult.js";
4
+ import { EntityListSection as o } from "./EntityListSection.js";
5
+ import { useMemo as s, useState as c } from "react";
6
+ import { jsx as l, jsxs as u } from "react/jsx-runtime";
7
+ import { Megaphone as d } from "lucide-react";
8
+ import { useI18n as f } from "@burdenoff/fe-libs/shared/providers/shell/I18nProvider";
9
+ import { Button as p, EmphasisPanel as m, GradientText as h, Input as g, PagePurpose as _ } from "@burdenoff/fe-libs/ui";
8
10
  //#region src/eventfullymanaged/pages/modules/CommunicationPage.tsx
9
- function h() {
10
- let { t: h } = l(), g = (e, t) => {
11
- let n = h(e);
11
+ function v() {
12
+ let { t: v } = f(), y = (e, t) => {
13
+ let n = v(e);
12
14
  return n === e ? t : n;
13
- }, _ = e({
15
+ }, b = t({
14
16
  limit: 100,
15
17
  offset: 0
16
- }), v = t({
18
+ }), x = n({
17
19
  limit: 100,
18
20
  offset: 0
19
- }), [y, { loading: b }] = n(), [x, S] = a(""), C = i(() => _.data?.announcements.items ?? [], [_.data]), w = i(() => v.data?.communicationCampaigns.items ?? [], [v.data]);
20
- return /* @__PURE__ */ s("div", {
21
+ }), [S, { loading: C }] = r(), [w, T] = c(""), E = s(() => b.data?.announcements.items ?? [], [b.data]), D = s(() => x.data?.communicationCampaigns.items ?? [], [x.data]);
22
+ return /* @__PURE__ */ u("div", {
21
23
  className: "space-y-6 p-6",
22
24
  children: [
23
- /* @__PURE__ */ o(m, { children: g("eventfullymanaged.communication.purpose", "Keep attendees informed and engaged. Announcements are one-off broadcasts (a schedule change, a welcome message); campaigns are scheduled, multi-step outreach (reminder emails, follow-ups). Use this page to draft and publish everything you need to say before, during and after an event.") }),
24
- /* @__PURE__ */ s(d, { children: [
25
- /* @__PURE__ */ o("h1", {
25
+ /* @__PURE__ */ l(_, { children: y("eventfullymanaged.communication.purpose", "Keep attendees informed and engaged. Announcements are one-off broadcasts (a schedule change, a welcome message); campaigns are scheduled, multi-step outreach (reminder emails, follow-ups). Use this page to draft and publish everything you need to say before, during and after an event.") }),
26
+ /* @__PURE__ */ u(m, { children: [
27
+ /* @__PURE__ */ l("h1", {
26
28
  className: "text-2xl font-bold text-text-primary",
27
- children: /* @__PURE__ */ o(f, { children: g("eventfullymanaged.communication.title", "Communication") })
29
+ children: /* @__PURE__ */ l(h, { children: y("eventfullymanaged.communication.title", "Communication") })
28
30
  }),
29
- /* @__PURE__ */ o("p", {
31
+ /* @__PURE__ */ l("p", {
30
32
  className: "mt-1 text-sm text-text-secondary",
31
- children: g("eventfullymanaged.communication.description", "Announcements and campaigns managed via workspace APIs.")
33
+ children: y("eventfullymanaged.communication.description", "Announcements and campaigns managed via workspace APIs.")
32
34
  }),
33
- /* @__PURE__ */ s("div", {
35
+ /* @__PURE__ */ u("div", {
34
36
  className: "mt-4 flex flex-col gap-3 sm:flex-row",
35
- children: [/* @__PURE__ */ o(p, {
36
- value: x,
37
- onChange: (e) => S(e.target.value),
38
- placeholder: g("eventfullymanaged.communication.inputPlaceholder", "Emergency schedule update"),
39
- "aria-label": g("eventfullymanaged.communication.inputPlaceholder", "Emergency schedule update"),
37
+ children: [/* @__PURE__ */ l(g, {
38
+ value: w,
39
+ onChange: (e) => T(e.target.value),
40
+ placeholder: y("eventfullymanaged.communication.inputPlaceholder", "Emergency schedule update"),
41
+ "aria-label": y("eventfullymanaged.communication.inputPlaceholder", "Emergency schedule update"),
40
42
  className: "flex-1"
41
- }), /* @__PURE__ */ s(u, {
43
+ }), /* @__PURE__ */ u(p, {
42
44
  type: "button",
43
- disabled: b,
45
+ disabled: C,
44
46
  onClick: async () => {
45
- x.trim() && (await y({ variables: { input: {
46
- title: x,
47
- name: x,
48
- status: "draft",
49
- metadata: {}
50
- } } }), S(""));
47
+ if (!w.trim()) return;
48
+ let t = y("eventfullymanaged.communication.errors.create", "Could not create announcement");
49
+ try {
50
+ let n = await S({ variables: { input: {
51
+ title: w,
52
+ name: w,
53
+ status: "draft",
54
+ metadata: {}
55
+ } } });
56
+ if (!a(n, n.data?.upsertAnnouncement?.id, t)) return;
57
+ T(""), e.success(y("eventfullymanaged.communication.created", "Announcement created"));
58
+ } catch (n) {
59
+ e.error(t, i(n));
60
+ }
51
61
  },
52
- children: [/* @__PURE__ */ o(c, { className: "h-4 w-4" }), b ? g("eventfullymanaged.communication.actions.creating", "Creating...") : g("eventfullymanaged.communication.actions.createAnnouncement", "Create Announcement")]
62
+ children: [/* @__PURE__ */ l(d, { className: "h-4 w-4" }), C ? y("eventfullymanaged.communication.actions.creating", "Creating...") : y("eventfullymanaged.communication.actions.createAnnouncement", "Create Announcement")]
53
63
  })]
54
64
  })
55
65
  ] }),
56
- /* @__PURE__ */ o(r, {
57
- heading: _.loading ? g("eventfullymanaged.communication.announcements.loading", "Loading announcements...") : `${g("eventfullymanaged.communication.announcements.heading", "Announcements")} (${C.length})`,
58
- emptyLabel: g("eventfullymanaged.communication.announcements.empty", "No announcements published yet."),
59
- rows: C.map((e) => ({
66
+ /* @__PURE__ */ l(o, {
67
+ heading: b.loading ? y("eventfullymanaged.communication.announcements.loading", "Loading announcements...") : `${y("eventfullymanaged.communication.announcements.heading", "Announcements")} (${E.length})`,
68
+ emptyLabel: y("eventfullymanaged.communication.announcements.empty", "No announcements published yet."),
69
+ rows: E.map((e) => ({
60
70
  id: e.id,
61
- title: e.title ?? e.name ?? `${g("eventfullymanaged.communication.announcements.itemPrefix", "Announcement")} ${e.id}`,
71
+ title: e.title ?? e.name ?? `${y("eventfullymanaged.communication.announcements.itemPrefix", "Announcement")} ${e.id}`,
62
72
  status: e.status ?? void 0
63
73
  }))
64
74
  }),
65
- /* @__PURE__ */ o(r, {
66
- heading: v.loading ? g("eventfullymanaged.communication.campaigns.loading", "Loading campaigns...") : `${g("eventfullymanaged.communication.campaigns.heading", "Campaigns")} (${w.length})`,
67
- emptyLabel: g("eventfullymanaged.communication.campaigns.empty", "No communication campaigns created yet."),
68
- rows: w.map((e) => ({
75
+ /* @__PURE__ */ l(o, {
76
+ heading: x.loading ? y("eventfullymanaged.communication.campaigns.loading", "Loading campaigns...") : `${y("eventfullymanaged.communication.campaigns.heading", "Campaigns")} (${D.length})`,
77
+ emptyLabel: y("eventfullymanaged.communication.campaigns.empty", "No communication campaigns created yet."),
78
+ rows: D.map((e) => ({
69
79
  id: e.id,
70
- title: e.title ?? e.name ?? `${g("eventfullymanaged.communication.campaigns.itemPrefix", "Campaign")} ${e.id}`,
80
+ title: e.title ?? e.name ?? `${y("eventfullymanaged.communication.campaigns.itemPrefix", "Campaign")} ${e.id}`,
71
81
  status: e.status ?? void 0
72
82
  }))
73
83
  })
@@ -75,6 +85,6 @@ function h() {
75
85
  });
76
86
  }
77
87
  //#endregion
78
- export { h as default };
88
+ export { v as default };
79
89
 
80
90
  //# sourceMappingURL=CommunicationPage.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"CommunicationPage.js","names":[],"sources":["../../../../src/eventfullymanaged/pages/modules/CommunicationPage.tsx"],"sourcesContent":["import { Megaphone } from 'lucide-react';\nimport { useI18n } from '@burdenoff/fe-libs/shared/providers/shell/I18nProvider';\nimport { Button, EmphasisPanel, GradientText, Input, PagePurpose } from '@burdenoff/fe-libs/ui';\nimport { useMemo, useState } from 'react';\n\nimport {\n useAnnouncementsQuery,\n useCommunicationCampaignsQuery,\n useUpsertAnnouncementMutation,\n} from '../../api/eventApi';\nimport { EntityListSection } from './EntityListSection';\n\ntype SimpleRow = {\n id: string;\n name?: string;\n title?: string;\n status?: string;\n};\n\nexport default function CommunicationPage() {\n const { t: translate } = useI18n();\n const t = (key: string, fallback: string) => {\n const value = translate(key);\n return value === key ? fallback : value;\n };\n const announcementsQuery = useAnnouncementsQuery({ limit: 100, offset: 0 });\n const campaignsQuery = useCommunicationCampaignsQuery({ limit: 100, offset: 0 });\n const [createAnnouncement, { loading: creating }] = useUpsertAnnouncementMutation();\n const [announcementTitle, setAnnouncementTitle] = useState('');\n\n const announcements = useMemo(\n () => (announcementsQuery.data?.announcements.items ?? []) as SimpleRow[],\n [announcementsQuery.data]\n );\n const campaigns = useMemo(\n () => (campaignsQuery.data?.communicationCampaigns.items ?? []) as SimpleRow[],\n [campaignsQuery.data]\n );\n\n const onCreate = async () => {\n if (!announcementTitle.trim()) return;\n await createAnnouncement({\n variables: {\n input: {\n title: announcementTitle,\n name: announcementTitle,\n status: 'draft',\n metadata: {},\n },\n },\n });\n setAnnouncementTitle('');\n };\n\n return (\n <div className=\"space-y-6 p-6\">\n <PagePurpose>\n {t(\n 'eventfullymanaged.communication.purpose',\n 'Keep attendees informed and engaged. Announcements are one-off broadcasts (a schedule change, a welcome message); campaigns are scheduled, multi-step outreach (reminder emails, follow-ups). Use this page to draft and publish everything you need to say before, during and after an event.'\n )}\n </PagePurpose>\n\n <EmphasisPanel>\n <h1 className=\"text-2xl font-bold text-text-primary\">\n <GradientText>{t('eventfullymanaged.communication.title', 'Communication')}</GradientText>\n </h1>\n <p className=\"mt-1 text-sm text-text-secondary\">\n {t('eventfullymanaged.communication.description', 'Announcements and campaigns managed via workspace APIs.')}\n </p>\n <div className=\"mt-4 flex flex-col gap-3 sm:flex-row\">\n <Input\n value={announcementTitle}\n onChange={(e) => setAnnouncementTitle(e.target.value)}\n placeholder={t('eventfullymanaged.communication.inputPlaceholder', 'Emergency schedule update')}\n aria-label={t('eventfullymanaged.communication.inputPlaceholder', 'Emergency schedule update')}\n className=\"flex-1\"\n />\n <Button type=\"button\" disabled={creating} onClick={onCreate}>\n <Megaphone className=\"h-4 w-4\" />\n {creating\n ? t('eventfullymanaged.communication.actions.creating', 'Creating...')\n : t('eventfullymanaged.communication.actions.createAnnouncement', 'Create Announcement')}\n </Button>\n </div>\n </EmphasisPanel>\n\n <EntityListSection\n heading={\n announcementsQuery.loading\n ? t('eventfullymanaged.communication.announcements.loading', 'Loading announcements...')\n : `${t('eventfullymanaged.communication.announcements.heading', 'Announcements')} (${announcements.length})`\n }\n emptyLabel={t('eventfullymanaged.communication.announcements.empty', 'No announcements published yet.')}\n rows={announcements.map((row) => ({\n id: row.id,\n title:\n row.title ??\n row.name ??\n `${t('eventfullymanaged.communication.announcements.itemPrefix', 'Announcement')} ${row.id}`,\n status: row.status ?? undefined,\n }))}\n />\n\n <EntityListSection\n heading={\n campaignsQuery.loading\n ? t('eventfullymanaged.communication.campaigns.loading', 'Loading campaigns...')\n : `${t('eventfullymanaged.communication.campaigns.heading', 'Campaigns')} (${campaigns.length})`\n }\n emptyLabel={t('eventfullymanaged.communication.campaigns.empty', 'No communication campaigns created yet.')}\n rows={campaigns.map((row) => ({\n id: row.id,\n title:\n row.title ??\n row.name ??\n `${t('eventfullymanaged.communication.campaigns.itemPrefix', 'Campaign')} ${row.id}`,\n status: row.status ?? undefined,\n }))}\n />\n </div>\n );\n}\n"],"mappings":";;;;;;;;AAmBA,SAAwB,IAAoB;CAC1C,IAAM,EAAE,GAAG,MAAc,GAAS,EAC5B,KAAK,GAAa,MAAqB;EAC3C,IAAM,IAAQ,EAAU,EAAI;AAC5B,SAAO,MAAU,IAAM,IAAW;IAE9B,IAAqB,EAAsB;EAAE,OAAO;EAAK,QAAQ;EAAG,CAAC,EACrE,IAAiB,EAA+B;EAAE,OAAO;EAAK,QAAQ;EAAG,CAAC,EAC1E,CAAC,GAAoB,EAAE,SAAS,OAAc,GAA+B,EAC7E,CAAC,GAAmB,KAAwB,EAAS,GAAG,EAExD,IAAgB,QACb,EAAmB,MAAM,cAAc,SAAS,EAAE,EACzD,CAAC,EAAmB,KAAK,CAC1B,EACK,IAAY,QACT,EAAe,MAAM,uBAAuB,SAAS,EAAE,EAC9D,CAAC,EAAe,KAAK,CACtB;AAiBD,QACE,kBAAC,OAAD;EAAK,WAAU;YAAf;GACE,kBAAC,GAAD,EAAA,UACG,EACC,2CACA,iSACD,EACW,CAAA;GAEd,kBAAC,GAAD,EAAA,UAAA;IACE,kBAAC,MAAD;KAAI,WAAU;eACZ,kBAAC,GAAD,EAAA,UAAe,EAAE,yCAAyC,gBAAgB,EAAgB,CAAA;KACvF,CAAA;IACL,kBAAC,KAAD;KAAG,WAAU;eACV,EAAE,+CAA+C,0DAA0D;KAC1G,CAAA;IACJ,kBAAC,OAAD;KAAK,WAAU;eAAf,CACE,kBAAC,GAAD;MACE,OAAO;MACP,WAAW,MAAM,EAAqB,EAAE,OAAO,MAAM;MACrD,aAAa,EAAE,oDAAoD,4BAA4B;MAC/F,cAAY,EAAE,oDAAoD,4BAA4B;MAC9F,WAAU;MACV,CAAA,EACF,kBAAC,GAAD;MAAQ,MAAK;MAAS,UAAU;MAAU,SAvCjC,YAAY;AACtB,SAAkB,MAAM,KAC7B,MAAM,EAAmB,EACvB,WAAW,EACT,OAAO;QACL,OAAO;QACP,MAAM;QACN,QAAQ;QACR,UAAU,EAAE;QACb,EACF,EACF,CAAC,EACF,EAAqB,GAAG;;gBA2BlB,CACE,kBAAC,GAAD,EAAW,WAAU,WAAY,CAAA,EAChC,IACG,EAAE,oDAAoD,cAAc,GACpE,EAAE,8DAA8D,sBAAsB,CACnF;QACL;;IACQ,EAAA,CAAA;GAEhB,kBAAC,GAAD;IACE,SACE,EAAmB,UACf,EAAE,yDAAyD,2BAA2B,GACtF,GAAG,EAAE,yDAAyD,gBAAgB,CAAC,IAAI,EAAc,OAAO;IAE9G,YAAY,EAAE,uDAAuD,kCAAkC;IACvG,MAAM,EAAc,KAAK,OAAS;KAChC,IAAI,EAAI;KACR,OACE,EAAI,SACJ,EAAI,QACJ,GAAG,EAAE,4DAA4D,eAAe,CAAC,GAAG,EAAI;KAC1F,QAAQ,EAAI,UAAU,KAAA;KACvB,EAAE;IACH,CAAA;GAEF,kBAAC,GAAD;IACE,SACE,EAAe,UACX,EAAE,qDAAqD,uBAAuB,GAC9E,GAAG,EAAE,qDAAqD,YAAY,CAAC,IAAI,EAAU,OAAO;IAElG,YAAY,EAAE,mDAAmD,0CAA0C;IAC3G,MAAM,EAAU,KAAK,OAAS;KAC5B,IAAI,EAAI;KACR,OACE,EAAI,SACJ,EAAI,QACJ,GAAG,EAAE,wDAAwD,WAAW,CAAC,GAAG,EAAI;KAClF,QAAQ,EAAI,UAAU,KAAA;KACvB,EAAE;IACH,CAAA;GACE"}
1
+ {"version":3,"file":"CommunicationPage.js","names":[],"sources":["../../../../src/eventfullymanaged/pages/modules/CommunicationPage.tsx"],"sourcesContent":["import { Megaphone } from 'lucide-react';\nimport { useI18n } from '@burdenoff/fe-libs/shared/providers/shell/I18nProvider';\nimport { Button, EmphasisPanel, GradientText, Input, PagePurpose } from '@burdenoff/fe-libs/ui';\nimport { useMemo, useState } from 'react';\n\nimport {\n useAnnouncementsQuery,\n useCommunicationCampaignsQuery,\n useUpsertAnnouncementMutation,\n} from '../../api/eventApi';\nimport { describeError, mutationSucceeded } from '../../lib/mutationResult';\nimport { notify } from '../../lib/toast';\nimport { EntityListSection } from './EntityListSection';\n\ntype SimpleRow = {\n id: string;\n name?: string;\n title?: string;\n status?: string;\n};\n\nexport default function CommunicationPage() {\n const { t: translate } = useI18n();\n const t = (key: string, fallback: string) => {\n const value = translate(key);\n return value === key ? fallback : value;\n };\n const announcementsQuery = useAnnouncementsQuery({ limit: 100, offset: 0 });\n const campaignsQuery = useCommunicationCampaignsQuery({ limit: 100, offset: 0 });\n const [createAnnouncement, { loading: creating }] = useUpsertAnnouncementMutation();\n const [announcementTitle, setAnnouncementTitle] = useState('');\n\n const announcements = useMemo(\n () => (announcementsQuery.data?.announcements.items ?? []) as SimpleRow[],\n [announcementsQuery.data]\n );\n const campaigns = useMemo(\n () => (campaignsQuery.data?.communicationCampaigns.items ?? []) as SimpleRow[],\n [campaignsQuery.data]\n );\n\n const onCreate = async () => {\n if (!announcementTitle.trim()) return;\n const failureMessage = t('eventfullymanaged.communication.errors.create', 'Could not create announcement');\n try {\n const response = await createAnnouncement({\n variables: {\n input: {\n title: announcementTitle,\n name: announcementTitle,\n status: 'draft',\n metadata: {},\n },\n },\n });\n if (!mutationSucceeded(response, response.data?.upsertAnnouncement?.id, failureMessage)) {\n return;\n }\n setAnnouncementTitle('');\n notify.success(t('eventfullymanaged.communication.created', 'Announcement created'));\n } catch (error) {\n notify.error(failureMessage, describeError(error));\n }\n };\n\n return (\n <div className=\"space-y-6 p-6\">\n <PagePurpose>\n {t(\n 'eventfullymanaged.communication.purpose',\n 'Keep attendees informed and engaged. Announcements are one-off broadcasts (a schedule change, a welcome message); campaigns are scheduled, multi-step outreach (reminder emails, follow-ups). Use this page to draft and publish everything you need to say before, during and after an event.'\n )}\n </PagePurpose>\n\n <EmphasisPanel>\n <h1 className=\"text-2xl font-bold text-text-primary\">\n <GradientText>{t('eventfullymanaged.communication.title', 'Communication')}</GradientText>\n </h1>\n <p className=\"mt-1 text-sm text-text-secondary\">\n {t('eventfullymanaged.communication.description', 'Announcements and campaigns managed via workspace APIs.')}\n </p>\n <div className=\"mt-4 flex flex-col gap-3 sm:flex-row\">\n <Input\n value={announcementTitle}\n onChange={(e) => setAnnouncementTitle(e.target.value)}\n placeholder={t('eventfullymanaged.communication.inputPlaceholder', 'Emergency schedule update')}\n aria-label={t('eventfullymanaged.communication.inputPlaceholder', 'Emergency schedule update')}\n className=\"flex-1\"\n />\n <Button type=\"button\" disabled={creating} onClick={onCreate}>\n <Megaphone className=\"h-4 w-4\" />\n {creating\n ? t('eventfullymanaged.communication.actions.creating', 'Creating...')\n : t('eventfullymanaged.communication.actions.createAnnouncement', 'Create Announcement')}\n </Button>\n </div>\n </EmphasisPanel>\n\n <EntityListSection\n heading={\n announcementsQuery.loading\n ? t('eventfullymanaged.communication.announcements.loading', 'Loading announcements...')\n : `${t('eventfullymanaged.communication.announcements.heading', 'Announcements')} (${announcements.length})`\n }\n emptyLabel={t('eventfullymanaged.communication.announcements.empty', 'No announcements published yet.')}\n rows={announcements.map((row) => ({\n id: row.id,\n title:\n row.title ??\n row.name ??\n `${t('eventfullymanaged.communication.announcements.itemPrefix', 'Announcement')} ${row.id}`,\n status: row.status ?? undefined,\n }))}\n />\n\n <EntityListSection\n heading={\n campaignsQuery.loading\n ? t('eventfullymanaged.communication.campaigns.loading', 'Loading campaigns...')\n : `${t('eventfullymanaged.communication.campaigns.heading', 'Campaigns')} (${campaigns.length})`\n }\n emptyLabel={t('eventfullymanaged.communication.campaigns.empty', 'No communication campaigns created yet.')}\n rows={campaigns.map((row) => ({\n id: row.id,\n title:\n row.title ??\n row.name ??\n `${t('eventfullymanaged.communication.campaigns.itemPrefix', 'Campaign')} ${row.id}`,\n status: row.status ?? undefined,\n }))}\n />\n </div>\n );\n}\n"],"mappings":"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"}