@burdenoff/microfe-eventfullymanaged 2026.830.1 → 2026.1030.2

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.
@@ -1,117 +1,123 @@
1
1
  import { useEventfullyManaged as e } from "../../context/EventfullyManagedContext.js";
2
2
  import { useAttendeesQuery as t, useRegistrationsQuery as n } from "../../api/eventApi.js";
3
- import { EntityListSection as r } from "./EntityListSection.js";
4
- import { useMemo as i, useRef as a } from "react";
5
- import { useParams as o } from "react-router";
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";
3
+ import { AttendeeRecommendations as r } from "./AttendeeRecommendations.js";
4
+ import { EntityListSection as i } from "./EntityListSection.js";
5
+ import { useMemo as a, useRef as o } from "react";
6
+ import { useParams as s } from "react-router";
7
+ import { jsx as c, jsxs as l } from "react/jsx-runtime";
8
+ import { MessageSquare as u, Ticket as d, Users as f } from "lucide-react";
9
+ import { EmphasisPanel as p, GlassCard as m, GradientText as h, NextSteps as g, PagePurpose as _ } from "@burdenoff/fe-libs/ui";
10
+ import { ContextConversationWidget as v } from "@burdenoff/fe-libs/shared";
11
+ import { useRegisterPageConversationContext as y } from "@burdenoff/fe-libs/shared/providers/shell";
11
12
  //#region src/eventfullymanaged/pages/modules/AttendeeProfilePage.tsx
12
- function y({ attendeeId: e, attendeeName: t }) {
13
- return v(i(() => ({
13
+ function b({ attendeeId: e, attendeeName: t }) {
14
+ return y(a(() => ({
14
15
  entityType: "EVENTFULLYMANAGED_ATTENDEE",
15
16
  entityId: e,
16
17
  contextTitle: `Attendee · ${t}`
17
- }), [e, t])), /* @__PURE__ */ s(_, { widgetKey: "attendee-message" });
18
+ }), [e, t])), /* @__PURE__ */ c(v, { widgetKey: "attendee-message" });
18
19
  }
19
- function b() {
20
- let { id: _ = "" } = o(), { navigate: v } = e(), b = t({
20
+ function x() {
21
+ let { id: v = "" } = s(), { navigate: y } = e(), x = t({
21
22
  limit: 100,
22
23
  offset: 0
23
- }), x = n({
24
+ }), S = n({
24
25
  limit: 100,
25
26
  offset: 0
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", {
27
+ }), C = o(null), w = a(() => (x.data?.attendees.items ?? []).find((e) => e.id === v), [v, x.data]), T = a(() => (S.data?.registrations.items ?? []).filter((e) => e.attendeeId === v), [v, S.data]);
28
+ return /* @__PURE__ */ l("div", {
28
29
  className: "space-y-6 p-6",
29
30
  children: [
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", {
31
+ /* @__PURE__ */ c(_, { 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." }),
32
+ /* @__PURE__ */ l(p, { children: [
33
+ /* @__PURE__ */ l("h1", {
33
34
  className: "text-2xl font-bold text-text-primary",
34
- children: [/* @__PURE__ */ s(m, { children: "Attendee" }), " Profile"]
35
+ children: [/* @__PURE__ */ c(h, { children: "Attendee" }), " Profile"]
35
36
  }),
36
- /* @__PURE__ */ s("p", {
37
+ /* @__PURE__ */ c("p", {
37
38
  className: "mt-1 text-sm text-text-secondary",
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."
39
+ children: x.loading ? "Loading attendee profile..." : w ? `${w.fullName ?? w.id} profile from registration and attendee APIs.` : "Attendee not found in current query scope."
39
40
  }),
40
- C ? /* @__PURE__ */ c("div", {
41
+ w ? /* @__PURE__ */ l("div", {
41
42
  className: "mt-4 grid grid-cols-1 gap-3 sm:grid-cols-2 lg:grid-cols-4",
42
43
  children: [
43
- /* @__PURE__ */ c(p, {
44
+ /* @__PURE__ */ l(m, {
44
45
  treatment: "metric",
45
46
  className: "p-3",
46
- children: [/* @__PURE__ */ s("p", {
47
+ children: [/* @__PURE__ */ c("p", {
47
48
  className: "text-xs uppercase tracking-wide text-text-secondary",
48
49
  children: "Name"
49
- }), /* @__PURE__ */ s("p", {
50
+ }), /* @__PURE__ */ c("p", {
50
51
  className: "mt-1 font-medium text-text-primary",
51
- children: C.fullName ?? C.id
52
+ children: w.fullName ?? w.id
52
53
  })]
53
54
  }),
54
- /* @__PURE__ */ c(p, {
55
+ /* @__PURE__ */ l(m, {
55
56
  treatment: "metric",
56
57
  className: "p-3",
57
- children: [/* @__PURE__ */ s("p", {
58
+ children: [/* @__PURE__ */ c("p", {
58
59
  className: "text-xs uppercase tracking-wide text-text-secondary",
59
60
  children: "Email"
60
- }), /* @__PURE__ */ s("p", {
61
+ }), /* @__PURE__ */ c("p", {
61
62
  className: "mt-1 font-medium text-text-primary",
62
- children: C.email ?? "Unavailable"
63
+ children: w.email ?? "Unavailable"
63
64
  })]
64
65
  }),
65
- /* @__PURE__ */ c(p, {
66
+ /* @__PURE__ */ l(m, {
66
67
  treatment: "metric",
67
68
  className: "p-3",
68
- children: [/* @__PURE__ */ s("p", {
69
+ children: [/* @__PURE__ */ c("p", {
69
70
  className: "text-xs uppercase tracking-wide text-text-secondary",
70
71
  children: "Company"
71
- }), /* @__PURE__ */ s("p", {
72
+ }), /* @__PURE__ */ c("p", {
72
73
  className: "mt-1 font-medium text-text-primary",
73
- children: C.company ?? "Unavailable"
74
+ children: w.company ?? "Unavailable"
74
75
  })]
75
76
  }),
76
- /* @__PURE__ */ c(p, {
77
+ /* @__PURE__ */ l(m, {
77
78
  treatment: "metric",
78
79
  className: "p-3",
79
- children: [/* @__PURE__ */ s("p", {
80
+ children: [/* @__PURE__ */ c("p", {
80
81
  className: "text-xs uppercase tracking-wide text-text-secondary",
81
82
  children: "Role"
82
- }), /* @__PURE__ */ s("p", {
83
+ }), /* @__PURE__ */ c("p", {
83
84
  className: "mt-1 font-medium text-text-primary",
84
- children: C.role ?? "Unavailable"
85
+ children: w.role ?? "Unavailable"
85
86
  })]
86
87
  })
87
88
  ]
88
89
  }) : null
89
90
  ] }),
90
- /* @__PURE__ */ s(r, {
91
- heading: x.loading ? "Loading attendee registrations..." : `Registrations (${w.length})`,
91
+ /* @__PURE__ */ c(i, {
92
+ heading: S.loading ? "Loading attendee registrations..." : `Registrations (${T.length})`,
92
93
  emptyLabel: "No registrations linked to this attendee.",
93
- rows: w.map((e) => ({
94
+ rows: T.map((e) => ({
94
95
  id: e.id,
95
96
  title: `Registration ${e.id}`,
96
97
  subtitle: [e.quantity ? `Qty ${e.quantity}` : void 0, e.totalAmount ? `${e.totalAmount} ${e.currency ?? ""}` : void 0].filter(Boolean).join(" • "),
97
98
  status: e.status
98
99
  }))
99
100
  }),
100
- _ ? /* @__PURE__ */ c("div", {
101
- ref: S,
101
+ w?.eventId ? /* @__PURE__ */ c(r, {
102
+ attendeeId: w.id,
103
+ eventId: w.eventId,
104
+ attendees: x.data?.attendees.items ?? []
105
+ }) : null,
106
+ v ? /* @__PURE__ */ l("div", {
107
+ ref: C,
102
108
  className: "space-y-3",
103
- children: [/* @__PURE__ */ c("div", { children: [/* @__PURE__ */ s("h2", {
109
+ children: [/* @__PURE__ */ l("div", { children: [/* @__PURE__ */ c("h2", {
104
110
  className: "text-lg font-semibold text-text-primary",
105
111
  children: "Messages"
106
- }), /* @__PURE__ */ s("p", {
112
+ }), /* @__PURE__ */ c("p", {
107
113
  className: "mt-1 text-sm text-text-secondary",
108
114
  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 ?? _
115
+ })] }), /* @__PURE__ */ c(b, {
116
+ attendeeId: v,
117
+ attendeeName: w?.fullName ?? w?.id ?? v
112
118
  })]
113
119
  }) : null,
114
- /* @__PURE__ */ s(h, {
120
+ /* @__PURE__ */ c(g, {
115
121
  storageKey: "eventfullymanaged-attendee-profile",
116
122
  title: "Act on this attendee",
117
123
  steps: [
@@ -119,22 +125,22 @@ function b() {
119
125
  id: "view-registrations",
120
126
  label: "Review all registrations",
121
127
  description: "See the full registration list to manage tickets and payments.",
122
- icon: /* @__PURE__ */ s(u, { className: "h-4 w-4" }),
123
- onClick: () => v("/registration")
128
+ icon: /* @__PURE__ */ c(d, { className: "h-4 w-4" }),
129
+ onClick: () => y("/registration")
124
130
  },
125
131
  {
126
132
  id: "networking",
127
133
  label: "Find networking matches",
128
134
  description: "Connect this attendee with relevant people and meetings.",
129
- icon: /* @__PURE__ */ s(d, { className: "h-4 w-4" }),
130
- onClick: () => v("/networking")
135
+ icon: /* @__PURE__ */ c(f, { className: "h-4 w-4" }),
136
+ onClick: () => y("/networking")
131
137
  },
132
138
  {
133
139
  id: "reach-out",
134
140
  label: "Send a message",
135
141
  description: "Open a direct conversation thread with this attendee.",
136
- icon: /* @__PURE__ */ s(l, { className: "h-4 w-4" }),
137
- onClick: () => S.current?.scrollIntoView({
142
+ icon: /* @__PURE__ */ c(u, { className: "h-4 w-4" }),
143
+ onClick: () => C.current?.scrollIntoView({
138
144
  behavior: "smooth",
139
145
  block: "start"
140
146
  })
@@ -145,6 +151,6 @@ function b() {
145
151
  });
146
152
  }
147
153
  //#endregion
148
- export { b as default };
154
+ export { x as default };
149
155
 
150
156
  //# sourceMappingURL=AttendeeProfilePage.js.map
@@ -1 +1 @@
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';\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 // Stable reference: `useRegisterPageConversationContext` keys its effect on this\n // object's identity, so a fresh inline object every render would loop the shared\n // PageConversationContextProvider (see NetworkingPage for the full rationale).\n const conversationContext = useMemo(\n () => ({\n entityType: 'EVENTFULLYMANAGED_ATTENDEE',\n entityId: attendeeId,\n contextTitle: `Attendee · ${attendeeName}`,\n }),\n [attendeeId, attendeeName]\n );\n useRegisterPageConversationContext(conversationContext);\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
+ {"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';\n\nimport { useAttendeesQuery, useRegistrationsQuery } from '../../api/eventApi';\nimport { useEventfullyManaged } from '../../context/EventfullyManagedContext';\nimport { AttendeeRecommendations } from './AttendeeRecommendations';\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 // Stable reference: `useRegisterPageConversationContext` keys its effect on this\n // object's identity, so a fresh inline object every render would loop the shared\n // PageConversationContextProvider (see NetworkingPage for the full rationale).\n const conversationContext = useMemo(\n () => ({\n entityType: 'EVENTFULLYMANAGED_ATTENDEE',\n entityId: attendeeId,\n contextTitle: `Attendee · ${attendeeName}`,\n }),\n [attendeeId, attendeeName]\n );\n useRegisterPageConversationContext(conversationContext);\n return <ContextConversationWidget widgetKey=\"attendee-message\" />;\n}\n\ntype AttendeeRow = {\n id: string;\n eventId?: 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 {attendee?.eventId ? (\n <AttendeeRecommendations\n attendeeId={attendee.id}\n eventId={attendee.eventId}\n attendees={(attendeesQuery.data?.attendees.items ?? []) as AttendeeRow[]}\n />\n ) : null}\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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"}
@@ -0,0 +1,13 @@
1
+ type AttendeeName = {
2
+ id: string;
3
+ fullName?: string;
4
+ };
5
+ interface AttendeeRecommendationsProps {
6
+ attendeeId: string;
7
+ eventId: string;
8
+ /** Attendees already loaded by the page, used to put names on suggested connections. */
9
+ attendees: AttendeeName[];
10
+ }
11
+ export declare function AttendeeRecommendations({ attendeeId, eventId, attendees }: AttendeeRecommendationsProps): import("react/jsx-runtime").JSX.Element;
12
+ export {};
13
+ //# sourceMappingURL=AttendeeRecommendations.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"AttendeeRecommendations.d.ts","sourceRoot":"","sources":["../../../../src/eventfullymanaged/pages/modules/AttendeeRecommendations.tsx"],"names":[],"mappings":"AAmCA,KAAK,YAAY,GAAG;IAAE,EAAE,EAAE,MAAM,CAAC;IAAC,QAAQ,CAAC,EAAE,MAAM,CAAA;CAAE,CAAC;AAEtD,UAAU,4BAA4B;IACpC,UAAU,EAAE,MAAM,CAAC;IACnB,OAAO,EAAE,MAAM,CAAC;IAChB,wFAAwF;IACxF,SAAS,EAAE,YAAY,EAAE,CAAC;CAC3B;AAED,wBAAgB,uBAAuB,CAAC,EAAE,UAAU,EAAE,OAAO,EAAE,SAAS,EAAE,EAAE,4BAA4B,2CAqIvG"}
@@ -0,0 +1,181 @@
1
+ import { useNetworkingProfilesQuery as e, useRecommendedSessionsQuery as t, useSuggestedConnectionsQuery as n } from "../../api/eventApi.js";
2
+ import { useMemo as r } from "react";
3
+ import { jsx as i, jsxs as a } from "react/jsx-runtime";
4
+ import { CalendarClock as o, Sparkles as s, Users as c } from "lucide-react";
5
+ import { Badge as l, GlassCard as u, IllustratedEmptyState as d } from "@burdenoff/fe-libs/ui";
6
+ //#region src/eventfullymanaged/pages/modules/AttendeeRecommendations.tsx
7
+ var f = {
8
+ NO_PROFILE: "Add a networking profile with interests for this attendee to get session recommendations.",
9
+ NO_INTERESTS: "This attendee’s networking profile has no interests yet, so there is nothing to match sessions on.",
10
+ NO_UPCOMING_SESSIONS: "There are no upcoming sessions this attendee has not already attended.",
11
+ NO_SIGNAL: "No upcoming session matches this attendee’s interests yet."
12
+ };
13
+ function p(e, t) {
14
+ let n = new Date(e), r = new Date(t);
15
+ return Number.isNaN(n.getTime()) || Number.isNaN(r.getTime()) ? "" : `${n.toLocaleDateString(void 0, {
16
+ month: "short",
17
+ day: "numeric"
18
+ })}, ${n.toLocaleTimeString(void 0, {
19
+ hour: "2-digit",
20
+ minute: "2-digit"
21
+ })} – ${r.toLocaleTimeString(void 0, {
22
+ hour: "2-digit",
23
+ minute: "2-digit"
24
+ })}`;
25
+ }
26
+ function m({ attendeeId: m, eventId: h, attendees: g }) {
27
+ let _ = t({
28
+ eventId: h,
29
+ attendeeId: m,
30
+ limit: 5
31
+ }), v = n({
32
+ eventId: h,
33
+ attendeeId: m,
34
+ limit: 5
35
+ }), y = e({
36
+ eventId: h,
37
+ limit: 200,
38
+ offset: 0
39
+ }), b = _.data?.recommendedSessions, x = v.data?.suggestedConnections.items ?? [], S = r(() => {
40
+ let e = new Map(g.map((e) => [e.id, e.fullName ?? e.id])), t = /* @__PURE__ */ new Map();
41
+ for (let n of y.data?.networkingProfiles.items ?? []) t.set(n.id, {
42
+ attendeeId: n.attendeeId,
43
+ name: e.get(n.attendeeId) ?? "Attendee"
44
+ });
45
+ return t;
46
+ }, [g, y.data]), C = r(() => [...S.entries()].find(([, e]) => e.attendeeId === m)?.[0], [m, S]);
47
+ return /* @__PURE__ */ a("div", {
48
+ className: "grid grid-cols-1 gap-6 lg:grid-cols-2",
49
+ "data-testid": "attendee-recommendations",
50
+ children: [/* @__PURE__ */ a(u, {
51
+ treatment: "plain",
52
+ className: "border-border-seam p-5",
53
+ children: [
54
+ /* @__PURE__ */ a("div", {
55
+ className: "flex items-center gap-2",
56
+ children: [/* @__PURE__ */ i(o, {
57
+ className: "h-5 w-5 text-accent-primary",
58
+ "aria-hidden": "true"
59
+ }), /* @__PURE__ */ i("h2", {
60
+ className: "text-lg font-semibold text-text-primary",
61
+ children: "Recommended sessions"
62
+ })]
63
+ }),
64
+ /* @__PURE__ */ i("p", {
65
+ className: "mt-1 text-sm text-text-secondary",
66
+ children: "Ranked from this attendee’s networking interests and the sessions they attended before. Each suggestion says why it was picked."
67
+ }),
68
+ /* @__PURE__ */ i("div", {
69
+ className: "mt-4",
70
+ children: _.loading ? /* @__PURE__ */ i("p", {
71
+ className: "text-sm text-text-secondary",
72
+ children: "Finding sessions for this attendee..."
73
+ }) : _.error ? /* @__PURE__ */ i("p", {
74
+ className: "text-sm text-text-secondary",
75
+ children: "Session recommendations are unavailable right now."
76
+ }) : !b || b.items.length === 0 ? /* @__PURE__ */ i(d, {
77
+ illustration: "empty-data",
78
+ title: b?.emptyReason ? f[b.emptyReason] : "No session recommendations yet.",
79
+ className: "py-8"
80
+ }) : /* @__PURE__ */ i("ul", {
81
+ className: "space-y-2.5",
82
+ "data-testid": "recommended-sessions",
83
+ children: b.items.map((e) => /* @__PURE__ */ a("li", {
84
+ className: "rounded-card border border-border-seam bg-bg-surface p-3",
85
+ children: [/* @__PURE__ */ a("div", {
86
+ className: "flex items-start justify-between gap-3",
87
+ children: [/* @__PURE__ */ a("div", {
88
+ className: "min-w-0",
89
+ children: [/* @__PURE__ */ i("p", {
90
+ className: "truncate font-medium text-text-primary",
91
+ children: e.session.title
92
+ }), /* @__PURE__ */ i("p", {
93
+ className: "mt-0.5 text-sm text-text-secondary",
94
+ children: [
95
+ p(e.session.startsAt, e.session.endsAt),
96
+ e.session.track,
97
+ e.seatsRemaining === null || e.seatsRemaining === void 0 ? void 0 : `${e.seatsRemaining} seats left`
98
+ ].filter(Boolean).join(" • ")
99
+ })]
100
+ }), /* @__PURE__ */ a(l, {
101
+ variant: "info",
102
+ className: "shrink-0",
103
+ children: [e.score, "% fit"]
104
+ })]
105
+ }), /* @__PURE__ */ a("p", {
106
+ className: "mt-2 flex items-start gap-1.5 text-sm text-text-secondary",
107
+ children: [/* @__PURE__ */ i(s, {
108
+ className: "mt-0.5 h-3.5 w-3.5 shrink-0",
109
+ "aria-hidden": "true"
110
+ }), /* @__PURE__ */ i("span", { children: e.reason })]
111
+ })]
112
+ }, e.session.id))
113
+ })
114
+ })
115
+ ]
116
+ }), /* @__PURE__ */ a(u, {
117
+ treatment: "plain",
118
+ className: "border-border-seam p-5",
119
+ children: [
120
+ /* @__PURE__ */ a("div", {
121
+ className: "flex items-center gap-2",
122
+ children: [/* @__PURE__ */ i(c, {
123
+ className: "h-5 w-5 text-accent-primary",
124
+ "aria-hidden": "true"
125
+ }), /* @__PURE__ */ i("h2", {
126
+ className: "text-lg font-semibold text-text-primary",
127
+ children: "People to meet"
128
+ })]
129
+ }),
130
+ /* @__PURE__ */ i("p", {
131
+ className: "mt-1 text-sm text-text-secondary",
132
+ children: "Other opted-in attendees with overlapping interests. Suggestions only: nothing is booked until a meeting is created."
133
+ }),
134
+ /* @__PURE__ */ i("div", {
135
+ className: "mt-4",
136
+ children: v.loading ? /* @__PURE__ */ i("p", {
137
+ className: "text-sm text-text-secondary",
138
+ children: "Finding people to meet..."
139
+ }) : v.error ? /* @__PURE__ */ i("p", {
140
+ className: "text-sm text-text-secondary",
141
+ children: "Connection suggestions are unavailable right now."
142
+ }) : x.length === 0 ? /* @__PURE__ */ i(d, {
143
+ illustration: "empty-data",
144
+ title: "No connection suggestions. The attendee needs a public networking profile and others with overlapping interests.",
145
+ className: "py-8"
146
+ }) : /* @__PURE__ */ i("ul", {
147
+ className: "space-y-2.5",
148
+ "data-testid": "suggested-connections",
149
+ children: x.map((e) => {
150
+ let t = e.sourceProfileId === C ? e.targetProfileId : e.sourceProfileId;
151
+ return /* @__PURE__ */ a("li", {
152
+ className: "rounded-card border border-border-seam bg-bg-surface p-3",
153
+ children: [/* @__PURE__ */ a("div", {
154
+ className: "flex items-start justify-between gap-3",
155
+ children: [/* @__PURE__ */ i("p", {
156
+ className: "min-w-0 truncate font-medium text-text-primary",
157
+ children: S.get(t)?.name ?? "Attendee"
158
+ }), /* @__PURE__ */ a(l, {
159
+ variant: "info",
160
+ className: "shrink-0",
161
+ children: [e.score, "% fit"]
162
+ })]
163
+ }), e.reason ? /* @__PURE__ */ a("p", {
164
+ className: "mt-2 flex items-start gap-1.5 text-sm text-text-secondary",
165
+ children: [/* @__PURE__ */ i(s, {
166
+ className: "mt-0.5 h-3.5 w-3.5 shrink-0",
167
+ "aria-hidden": "true"
168
+ }), /* @__PURE__ */ i("span", { children: e.reason })]
169
+ }) : null]
170
+ }, e.id);
171
+ })
172
+ })
173
+ })
174
+ ]
175
+ })]
176
+ });
177
+ }
178
+ //#endregion
179
+ export { m as AttendeeRecommendations };
180
+
181
+ //# sourceMappingURL=AttendeeRecommendations.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"AttendeeRecommendations.js","names":[],"sources":["../../../../src/eventfullymanaged/pages/modules/AttendeeRecommendations.tsx"],"sourcesContent":["import { CalendarClock, Sparkles, Users } from 'lucide-react';\nimport { Badge, GlassCard, IllustratedEmptyState } from '@burdenoff/fe-libs/ui';\nimport { useMemo } from 'react';\n\nimport {\n type SessionRecommendationEmptyReason,\n useNetworkingProfilesQuery,\n useRecommendedSessionsQuery,\n useSuggestedConnectionsQuery,\n} from '../../api/eventApi';\n\n/**\n * Attendee-facing recommendations (BOFF-3764): sessions ranked for this\n * attendee and people worth meeting, each with the reason it was suggested.\n * Both lists come from deterministic, explainable scorers in\n * wspace-eventfullymanaged-svc; nothing is booked or registered from here.\n */\n\nconst EMPTY_SESSION_MESSAGES: Record<SessionRecommendationEmptyReason, string> = {\n NO_PROFILE: 'Add a networking profile with interests for this attendee to get session recommendations.',\n NO_INTERESTS: 'This attendee’s networking profile has no interests yet, so there is nothing to match sessions on.',\n NO_UPCOMING_SESSIONS: 'There are no upcoming sessions this attendee has not already attended.',\n NO_SIGNAL: 'No upcoming session matches this attendee’s interests yet.',\n};\n\nfunction formatSessionTime(startsAt: string, endsAt: string): string {\n const start = new Date(startsAt);\n const end = new Date(endsAt);\n if (Number.isNaN(start.getTime()) || Number.isNaN(end.getTime())) return '';\n const day = start.toLocaleDateString(undefined, { month: 'short', day: 'numeric' });\n const from = start.toLocaleTimeString(undefined, { hour: '2-digit', minute: '2-digit' });\n const to = end.toLocaleTimeString(undefined, { hour: '2-digit', minute: '2-digit' });\n return `${day}, ${from} – ${to}`;\n}\n\ntype AttendeeName = { id: string; fullName?: string };\n\ninterface AttendeeRecommendationsProps {\n attendeeId: string;\n eventId: string;\n /** Attendees already loaded by the page, used to put names on suggested connections. */\n attendees: AttendeeName[];\n}\n\nexport function AttendeeRecommendations({ attendeeId, eventId, attendees }: AttendeeRecommendationsProps) {\n const sessionsQuery = useRecommendedSessionsQuery({ eventId, attendeeId, limit: 5 });\n const connectionsQuery = useSuggestedConnectionsQuery({ eventId, attendeeId, limit: 5 });\n const profilesQuery = useNetworkingProfilesQuery({ eventId, limit: 200, offset: 0 });\n\n const recommendations = sessionsQuery.data?.recommendedSessions;\n const connections = connectionsQuery.data?.suggestedConnections.items ?? [];\n\n const profileToName = useMemo(() => {\n const attendeeNames = new Map(attendees.map((row) => [row.id, row.fullName ?? row.id]));\n const map = new Map<string, { attendeeId: string; name: string }>();\n for (const profile of profilesQuery.data?.networkingProfiles.items ?? []) {\n map.set(profile.id, {\n attendeeId: profile.attendeeId,\n name: attendeeNames.get(profile.attendeeId) ?? 'Attendee',\n });\n }\n return map;\n }, [attendees, profilesQuery.data]);\n const selfProfileId = useMemo(\n () => [...profileToName.entries()].find(([, value]) => value.attendeeId === attendeeId)?.[0],\n [attendeeId, profileToName]\n );\n\n return (\n <div className=\"grid grid-cols-1 gap-6 lg:grid-cols-2\" data-testid=\"attendee-recommendations\">\n <GlassCard treatment=\"plain\" className=\"border-border-seam p-5\">\n <div className=\"flex items-center gap-2\">\n <CalendarClock className=\"h-5 w-5 text-accent-primary\" aria-hidden=\"true\" />\n <h2 className=\"text-lg font-semibold text-text-primary\">Recommended sessions</h2>\n </div>\n <p className=\"mt-1 text-sm text-text-secondary\">\n Ranked from this attendee’s networking interests and the sessions they attended before. Each suggestion says\n why it was picked.\n </p>\n <div className=\"mt-4\">\n {sessionsQuery.loading ? (\n <p className=\"text-sm text-text-secondary\">Finding sessions for this attendee...</p>\n ) : sessionsQuery.error ? (\n <p className=\"text-sm text-text-secondary\">Session recommendations are unavailable right now.</p>\n ) : !recommendations || recommendations.items.length === 0 ? (\n <IllustratedEmptyState\n illustration=\"empty-data\"\n title={\n recommendations?.emptyReason\n ? EMPTY_SESSION_MESSAGES[recommendations.emptyReason]\n : 'No session recommendations yet.'\n }\n className=\"py-8\"\n />\n ) : (\n <ul className=\"space-y-2.5\" data-testid=\"recommended-sessions\">\n {recommendations.items.map((item) => (\n <li key={item.session.id} className=\"rounded-card border border-border-seam bg-bg-surface p-3\">\n <div className=\"flex items-start justify-between gap-3\">\n <div className=\"min-w-0\">\n <p className=\"truncate font-medium text-text-primary\">{item.session.title}</p>\n <p className=\"mt-0.5 text-sm text-text-secondary\">\n {[\n formatSessionTime(item.session.startsAt, item.session.endsAt),\n item.session.track,\n item.seatsRemaining === null || item.seatsRemaining === undefined\n ? undefined\n : `${item.seatsRemaining} seats left`,\n ]\n .filter(Boolean)\n .join(' • ')}\n </p>\n </div>\n <Badge variant=\"info\" className=\"shrink-0\">\n {item.score}% fit\n </Badge>\n </div>\n <p className=\"mt-2 flex items-start gap-1.5 text-sm text-text-secondary\">\n <Sparkles className=\"mt-0.5 h-3.5 w-3.5 shrink-0\" aria-hidden=\"true\" />\n <span>{item.reason}</span>\n </p>\n </li>\n ))}\n </ul>\n )}\n </div>\n </GlassCard>\n\n <GlassCard treatment=\"plain\" className=\"border-border-seam p-5\">\n <div className=\"flex items-center gap-2\">\n <Users className=\"h-5 w-5 text-accent-primary\" aria-hidden=\"true\" />\n <h2 className=\"text-lg font-semibold text-text-primary\">People to meet</h2>\n </div>\n <p className=\"mt-1 text-sm text-text-secondary\">\n Other opted-in attendees with overlapping interests. Suggestions only: nothing is booked until a meeting is\n created.\n </p>\n <div className=\"mt-4\">\n {connectionsQuery.loading ? (\n <p className=\"text-sm text-text-secondary\">Finding people to meet...</p>\n ) : connectionsQuery.error ? (\n <p className=\"text-sm text-text-secondary\">Connection suggestions are unavailable right now.</p>\n ) : connections.length === 0 ? (\n <IllustratedEmptyState\n illustration=\"empty-data\"\n title=\"No connection suggestions. The attendee needs a public networking profile and others with overlapping interests.\"\n className=\"py-8\"\n />\n ) : (\n <ul className=\"space-y-2.5\" data-testid=\"suggested-connections\">\n {connections.map((match) => {\n const otherProfileId =\n match.sourceProfileId === selfProfileId ? match.targetProfileId : match.sourceProfileId;\n const other = profileToName.get(otherProfileId);\n return (\n <li key={match.id} className=\"rounded-card border border-border-seam bg-bg-surface p-3\">\n <div className=\"flex items-start justify-between gap-3\">\n <p className=\"min-w-0 truncate font-medium text-text-primary\">{other?.name ?? 'Attendee'}</p>\n <Badge variant=\"info\" className=\"shrink-0\">\n {match.score}% fit\n </Badge>\n </div>\n {match.reason ? (\n <p className=\"mt-2 flex items-start gap-1.5 text-sm text-text-secondary\">\n <Sparkles className=\"mt-0.5 h-3.5 w-3.5 shrink-0\" aria-hidden=\"true\" />\n <span>{match.reason}</span>\n </p>\n ) : null}\n </li>\n );\n })}\n </ul>\n )}\n </div>\n </GlassCard>\n </div>\n );\n}\n"],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"createSandboxAssistantTransport.d.ts","sourceRoot":"","sources":["../../src/services/createSandboxAssistantTransport.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAIH,UAAU,iBAAiB;IACzB,MAAM,EAAE,MAAM,CAAC;IACf,UAAU,EAAE,CAAC,WAAW,EAAE,MAAM,KAAK,IAAI,CAAC;IAC1C,MAAM,EAAE,WAAW,CAAC;CACrB;AAED,MAAM,WAAW,kBAAkB;IACjC,UAAU,EAAE,CAAC,IAAI,EAAE,iBAAiB,KAAK,OAAO,CAAC;QAAE,IAAI,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;IACnE,OAAO,CAAC,EAAE,CAAC,KAAK,CAAC,EAAE,OAAO,GAAG,MAAM,KAAK,IAAI,CAAC;IAC7C,OAAO,CAAC,EAAE,MAAM,IAAI,CAAC;CACtB;AAED,wBAAgB,4BAA4B,IAAI,kBAAkB,CAMjE"}
1
+ {"version":3,"file":"createSandboxAssistantTransport.d.ts","sourceRoot":"","sources":["../../src/services/createSandboxAssistantTransport.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAKH,UAAU,iBAAiB;IACzB,MAAM,EAAE,MAAM,CAAC;IACf,UAAU,EAAE,CAAC,WAAW,EAAE,MAAM,KAAK,IAAI,CAAC;IAC1C,MAAM,EAAE,WAAW,CAAC;CACrB;AAED,MAAM,WAAW,kBAAkB;IACjC,UAAU,EAAE,CAAC,IAAI,EAAE,iBAAiB,KAAK,OAAO,CAAC;QAAE,IAAI,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;IACnE,OAAO,CAAC,EAAE,CAAC,KAAK,CAAC,EAAE,OAAO,GAAG,MAAM,KAAK,IAAI,CAAC;IAC7C,OAAO,CAAC,EAAE,MAAM,IAAI,CAAC;CACtB;AAED,wBAAgB,4BAA4B,IAAI,kBAAkB,CAQjE"}
@@ -1,12 +1,14 @@
1
- import { useSandboxAssistantTransport as e } from "@burdenoff/fe-libs/shared/assistant";
1
+ import { gatherPageContext as e } from "../utils/pageContext.js";
2
+ import { useSandboxAssistantTransport as t } from "@burdenoff/fe-libs/shared/assistant";
2
3
  //#region src/services/createSandboxAssistantTransport.ts
3
- function t() {
4
- return e({
4
+ function n() {
5
+ return t({
5
6
  product: "eventfullymanaged",
7
+ gatherPageContext: e,
6
8
  enableCredits: !0
7
9
  });
8
10
  }
9
11
  //#endregion
10
- export { t as useSandboxAssistantTransport };
12
+ export { n as useSandboxAssistantTransport };
11
13
 
12
14
  //# sourceMappingURL=createSandboxAssistantTransport.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"createSandboxAssistantTransport.js","names":[],"sources":["../../src/services/createSandboxAssistantTransport.ts"],"sourcesContent":["/**\n * eventfullymanaged adapter for the shared fe-libs AssistantWidget transport.\n *\n * The sandbox lifecycle + poll/stream loop now lives in\n * `@burdenoff/fe-libs/shared/assistant` (one implementation for all products);\n * this is a thin wrapper. The exported name/shape are unchanged so app-shell\n * imports don't move. The public return type is a LOCALLY-declared structural\n * mirror — never re-export fe-libs types (vite-plugin-dts path rewriting).\n */\n\nimport { useSandboxAssistantTransport as useSharedTransport } from '@burdenoff/fe-libs/shared/assistant';\n\ninterface AssistantSendArgs {\n prompt: string;\n onProgress: (partialText: string) => void;\n signal: AbortSignal;\n}\n\nexport interface AssistantTransport {\n sendPrompt: (args: AssistantSendArgs) => Promise<{ text: string }>;\n prewarm?: (level?: 'probe' | 'full') => void;\n suspend?: () => void;\n}\n\nexport function useSandboxAssistantTransport(): AssistantTransport {\n return useSharedTransport({\n product: 'eventfullymanaged',\n\n enableCredits: true,\n });\n}\n"],"mappings":";;AAwBA,SAAgB,IAAmD;AACjE,QAAO,EAAmB;EACxB,SAAS;EAET,eAAe;EAChB,CAAC"}
1
+ {"version":3,"file":"createSandboxAssistantTransport.js","names":[],"sources":["../../src/services/createSandboxAssistantTransport.ts"],"sourcesContent":["/**\n * eventfullymanaged adapter for the shared fe-libs AssistantWidget transport.\n *\n * The sandbox lifecycle + poll/stream loop now lives in\n * `@burdenoff/fe-libs/shared/assistant` (one implementation for all products);\n * this is a thin wrapper. The exported name/shape are unchanged so app-shell\n * imports don't move. The public return type is a LOCALLY-declared structural\n * mirror — never re-export fe-libs types (vite-plugin-dts path rewriting).\n */\n\nimport { useSandboxAssistantTransport as useSharedTransport } from '@burdenoff/fe-libs/shared/assistant';\nimport { gatherPageContext } from '../utils/pageContext';\n\ninterface AssistantSendArgs {\n prompt: string;\n onProgress: (partialText: string) => void;\n signal: AbortSignal;\n}\n\nexport interface AssistantTransport {\n sendPrompt: (args: AssistantSendArgs) => Promise<{ text: string }>;\n prewarm?: (level?: 'probe' | 'full') => void;\n suspend?: () => void;\n}\n\nexport function useSandboxAssistantTransport(): AssistantTransport {\n return useSharedTransport({\n product: 'eventfullymanaged',\n // Route-derived ids only (BOFF-3741): lets the assistant act on \"this\n // event / session / sponsor\" without a clarifying question.\n gatherPageContext,\n enableCredits: true,\n });\n}\n"],"mappings":";;;AAyBA,SAAgB,IAAmD;AACjE,QAAO,EAAmB;EACxB,SAAS;EAGT;EACA,eAAe;EAChB,CAAC"}
@@ -0,0 +1,66 @@
1
+ /**
2
+ * Page context for the EventfullyManaged AI assistant (BOFF-3741).
3
+ *
4
+ * Captures where the organizer is in the app — the URL, the page and the record
5
+ * on screen — so the floating assistant can act on "this event", "this session"
6
+ * or "here" without asking which record the user means.
7
+ *
8
+ * Data boundary: this module emits ids and route names only. It never reads the
9
+ * DOM, the Apollo cache or any form state, so attendee names and emails, lead
10
+ * contact details, speaker bios and networking-profile free text cannot reach
11
+ * the assistant through it. The query string and fragment are dropped from
12
+ * `currentUrl` as well: they can carry search text or tokens, and the sandbox's
13
+ * key allow-list only filters keys after the payload has left the browser.
14
+ */
15
+ /**
16
+ * Entity types the assistant understands for EventfullyManaged. The api-calls
17
+ * guide in algoshred/ai-assistant (`context/api-calls/products/eventfullymanaged.md`)
18
+ * documents each one with the query that reads it; keep the two in step.
19
+ */
20
+ export declare const EVENTFULLYMANAGED_ENTITY_TYPES: readonly ["EVENTFULLYMANAGED_EVENT", "EVENTFULLYMANAGED_SESSION", "EVENTFULLYMANAGED_TICKET_TYPE", "EVENTFULLYMANAGED_VENUE", "EVENTFULLYMANAGED_SPONSOR", "EVENTFULLYMANAGED_SPEAKER", "EVENTFULLYMANAGED_ATTENDEE", "EVENTFULLYMANAGED_PROMO_CODE", "EVENTFULLYMANAGED_NETWORKING_PROFILE", "EVENTFULLYMANAGED_MEETING", "EVENTFULLYMANAGED_EXHIBITOR"];
21
+ export type EventfullyManagedEntityType = (typeof EVENTFULLYMANAGED_ENTITY_TYPES)[number];
22
+ /**
23
+ * A `type` alias rather than an `interface`, on purpose: TypeScript gives a type
24
+ * alias an implicit index signature, which keeps it assignable to the shared
25
+ * transport's `gatherPageContext: () => object` seam without a cast.
26
+ *
27
+ * Every key here is on the sandbox agent's allow-list
28
+ * (`ALLOWED_CONTEXT_KEYS` in ai-assistant `src/context-loader.ts`). In particular
29
+ * the URL key is `currentUrl`: an `activeUrl` key would be silently dropped.
30
+ */
31
+ export type PageContext = {
32
+ currentUrl: string;
33
+ currentPage: string;
34
+ entityType?: EventfullyManagedEntityType;
35
+ entityId?: string;
36
+ activeTab?: string;
37
+ platform: string;
38
+ version: string;
39
+ viewportSize: string;
40
+ };
41
+ export interface ResolvedEntity {
42
+ entityType?: EventfullyManagedEntityType;
43
+ entityId?: string;
44
+ activeTab?: string;
45
+ }
46
+ /**
47
+ * Strip the mount prefix and any trailing slash, so the table above matches
48
+ * the same page whether the MFE runs inside the app shell
49
+ * (`/eventfullymanaged/events/abc`) or standalone in dev (`/events/abc`).
50
+ */
51
+ export declare function toProductPath(pathname: string): string;
52
+ /**
53
+ * Resolve the record the user is looking at from the route alone.
54
+ *
55
+ * Returns an empty object on list, dashboard and create pages: there is no
56
+ * "this" there, and a guessed entity is worse than none because the assistant
57
+ * would act on it confidently.
58
+ */
59
+ export declare function resolveEventfullyManagedEntity(pathname: string): ResolvedEntity;
60
+ /**
61
+ * Up to three path segments under the product, e.g. `events/<id>/sessions`.
62
+ * Matches the shape every other product sends, so the agent can name the page.
63
+ */
64
+ export declare function toCurrentPage(pathname: string): string;
65
+ export declare function gatherPageContext(): PageContext;
66
+ //# sourceMappingURL=pageContext.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"pageContext.d.ts","sourceRoot":"","sources":["../../src/utils/pageContext.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG;AAEH;;;;GAIG;AACH,eAAO,MAAM,8BAA8B,2VAYjC,CAAC;AAEX,MAAM,MAAM,2BAA2B,GAAG,CAAC,OAAO,8BAA8B,CAAC,CAAC,MAAM,CAAC,CAAC;AAE1F;;;;;;;;GAQG;AACH,MAAM,MAAM,WAAW,GAAG;IACxB,UAAU,EAAE,MAAM,CAAC;IACnB,WAAW,EAAE,MAAM,CAAC;IACpB,UAAU,CAAC,EAAE,2BAA2B,CAAC;IACzC,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,QAAQ,EAAE,MAAM,CAAC;IACjB,OAAO,EAAE,MAAM,CAAC;IAChB,YAAY,EAAE,MAAM,CAAC;CACtB,CAAC;AAEF,MAAM,WAAW,cAAc;IAC7B,UAAU,CAAC,EAAE,2BAA2B,CAAC;IACzC,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB;AA+FD;;;;GAIG;AACH,wBAAgB,aAAa,CAAC,QAAQ,EAAE,MAAM,GAAG,MAAM,CAOtD;AAUD;;;;;;GAMG;AACH,wBAAgB,8BAA8B,CAAC,QAAQ,EAAE,MAAM,GAAG,cAAc,CAc/E;AAED;;;GAGG;AACH,wBAAgB,aAAa,CAAC,QAAQ,EAAE,MAAM,GAAG,MAAM,CAGtD;AAcD,wBAAgB,iBAAiB,IAAI,WAAW,CAY/C"}