@elevasis/ui 2.59.0 → 2.60.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.
@@ -82,6 +82,25 @@ type MessageEvent = {
82
82
  error?: string;
83
83
  };
84
84
 
85
+ /**
86
+ * A single intake field definition stored in agent_access_grants.capture_fields.
87
+ *
88
+ * - key: machine identifier (e.g. "name", "company")
89
+ * - label: display label shown to the visitor
90
+ * - type: input type hint; defaults to "text" when absent
91
+ * - required: whether the visitor must fill this field; defaults to false
92
+ */
93
+ declare const CaptureFieldSchema: z.ZodObject<{
94
+ key: z.ZodString;
95
+ label: z.ZodString;
96
+ type: z.ZodOptional<z.ZodEnum<{
97
+ email: "email";
98
+ text: "text";
99
+ tel: "tel";
100
+ }>>;
101
+ required: z.ZodOptional<z.ZodBoolean>;
102
+ }, z.core.$strict>;
103
+ type CaptureField = z.infer<typeof CaptureFieldSchema>;
85
104
  /**
86
105
  * The public-facing grant shape returned by /authorize and /metadata endpoints.
87
106
  *
@@ -162,7 +181,32 @@ interface PublicAgentChatConnectionState {
162
181
 
163
182
  /**
164
183
  * Context passed to the `renderIntro` render-prop. Provides the start action,
165
- * loading state, resolved display strings, raw branding, and the agent slug.
184
+ * loading state, resolved display strings, raw branding, the agent slug, and the
185
+ * grant's intake (`captureFields`) surface so a custom intro can render the same
186
+ * welcome form the default intro draws.
187
+ *
188
+ * A minimal custom intro that renders the intake form looks like:
189
+ *
190
+ * ```tsx
191
+ * renderIntro={({ start, starting, captureFields, intakeValues, setIntakeValue }) => (
192
+ * <Stack>
193
+ * <Title>Welcome!</Title>
194
+ * {captureFields.map((field) => (
195
+ * <TextInput
196
+ * key={field.key}
197
+ * label={field.label}
198
+ * required={field.required ?? false}
199
+ * value={intakeValues[field.key] ?? ''}
200
+ * onChange={(e) => setIntakeValue(field.key, e.currentTarget.value)}
201
+ * />
202
+ * ))}
203
+ * <Button onClick={start} loading={starting}>Start</Button>
204
+ * </Stack>
205
+ * )}
206
+ * ```
207
+ *
208
+ * Values written via `setIntakeValue` flow into the session `metadata` on `start()`
209
+ * exactly as the default intro's form does (so `name` titles the session).
166
210
  */
167
211
  interface PublicAgentIntroRenderContext {
168
212
  /** Begins the authorize → session flow (the same action the default CTA runs). */
@@ -175,6 +219,12 @@ interface PublicAgentIntroRenderContext {
175
219
  /** Raw grant branding record, so a custom intro can still read intro/instructions/etc. if it wants. */
176
220
  branding: Record<string, unknown>;
177
221
  slug: string;
222
+ /** The grant's resolved, validated intake fields (`[]` when none). Map these to inputs. */
223
+ captureFields: CaptureField[];
224
+ /** Current collected intake values, keyed by `CaptureField.key`. */
225
+ intakeValues: Record<string, string>;
226
+ /** Writes a single intake value; flows into session metadata on `start()`. */
227
+ setIntakeValue: (key: string, value: string) => void;
178
228
  }
179
229
  interface PublicAgentChatProps {
180
230
  apiUrl: string;
@@ -232,4 +282,4 @@ declare const publicAgentChatKeys: {
232
282
  };
233
283
 
234
284
  export { PublicAgentChat, PublicAgentChatRoutePage, publicAgentChatKeys, usePublicAgentChatMessages, usePublicAgentChatWebSocket };
235
- export type { PublicAgentChatAuthorizeResponse, PublicAgentChatConnectionState, PublicAgentChatGrant, PublicAgentChatMessagesResponse, PublicAgentChatMetadataResponse, PublicAgentChatProps, PublicAgentChatRoutePageProps, PublicAgentChatSessionResponse, PublicAgentIntroRenderContext };
285
+ export type { CaptureField, PublicAgentChatAuthorizeResponse, PublicAgentChatConnectionState, PublicAgentChatGrant, PublicAgentChatMessagesResponse, PublicAgentChatMetadataResponse, PublicAgentChatProps, PublicAgentChatRoutePageProps, PublicAgentChatSessionResponse, PublicAgentIntroRenderContext };
@@ -462,6 +462,9 @@ function PublicAgentChatFrame({
462
462
  }
463
463
  );
464
464
  }
465
+ function mergeIntakeMetadata(metadata, intakeValues) {
466
+ return Object.keys(intakeValues).length > 0 ? { ...intakeValues, ...metadata } : metadata;
467
+ }
465
468
  function PublicAgentChat({
466
469
  apiUrl,
467
470
  slug,
@@ -483,6 +486,9 @@ function PublicAgentChat({
483
486
  const [input, setInput] = useState("");
484
487
  const [isCreatingSession, setIsCreatingSession] = useState(false);
485
488
  const [intakeValues, setIntakeValues] = useState({});
489
+ const setIntakeValue = useCallback((key, value) => {
490
+ setIntakeValues((prev) => ({ ...prev, [key]: value }));
491
+ }, []);
486
492
  const autoStartedRef = useRef(false);
487
493
  const greetingTimeRef = useRef(/* @__PURE__ */ new Date());
488
494
  const displayTitle = resolveAgentTitle(title, slug, grant);
@@ -525,7 +531,7 @@ function PublicAgentChat({
525
531
  }, [apiUrl, slug, renderIntro]);
526
532
  const pendingFirstMessageRef = useRef(null);
527
533
  const authorize = useCallback(
528
- async (code) => {
534
+ async (code, onSuccessStatus = "ready") => {
529
535
  if (!grant) return;
530
536
  try {
531
537
  setError(null);
@@ -542,7 +548,7 @@ function PublicAgentChat({
542
548
  }
543
549
  );
544
550
  setCapabilityToken(authorization.capabilityToken);
545
- setStatus("ready");
551
+ setStatus(onSuccessStatus);
546
552
  } catch (requestError) {
547
553
  setError(requestError instanceof Error ? requestError.message : "Unable to start agent chat");
548
554
  setStatus(grant.requiresCode ? "code-required" : "error");
@@ -552,6 +558,7 @@ function PublicAgentChat({
552
558
  );
553
559
  const createSession = useCallback(
554
560
  async (boundCapabilityToken) => {
561
+ const mergedMetadata = mergeIntakeMetadata(metadata, intakeValues);
555
562
  const createdSession = await fetchJson(
556
563
  `${apiUrl}/api/public/agent-chat/${encodeURIComponent(slug)}/sessions`,
557
564
  {
@@ -559,7 +566,7 @@ function PublicAgentChat({
559
566
  headers: { "Content-Type": "application/json" },
560
567
  body: JSON.stringify({
561
568
  capabilityToken: boundCapabilityToken,
562
- ...metadata ? { metadata } : {}
569
+ ...mergedMetadata ? { metadata: mergedMetadata } : {}
563
570
  })
564
571
  }
565
572
  );
@@ -568,7 +575,7 @@ function PublicAgentChat({
568
575
  onSessionReady?.(createdSession);
569
576
  return createdSession;
570
577
  },
571
- [apiUrl, slug, metadata, onSessionReady]
578
+ [apiUrl, slug, metadata, intakeValues, onSessionReady]
572
579
  );
573
580
  const startSession = useCallback(
574
581
  async (code) => {
@@ -588,7 +595,7 @@ function PublicAgentChat({
588
595
  }
589
596
  );
590
597
  setCapabilityToken(authorization.capabilityToken);
591
- const mergedMetadata = Object.keys(intakeValues).length > 0 ? { ...intakeValues, ...metadata } : metadata;
598
+ const mergedMetadata = mergeIntakeMetadata(metadata, intakeValues);
592
599
  const createdSession = await fetchJson(
593
600
  `${apiUrl}/api/public/agent-chat/${encodeURIComponent(slug)}/sessions`,
594
601
  {
@@ -611,6 +618,21 @@ function PublicAgentChat({
611
618
  },
612
619
  [apiUrl, grant, intakeValues, metadata, onSessionReady, slug, visitorId]
613
620
  );
621
+ const beginSession = useCallback(async () => {
622
+ if (capabilityToken) {
623
+ try {
624
+ setError(null);
625
+ setStatus("authorizing");
626
+ await createSession(capabilityToken);
627
+ setStatus("ready");
628
+ } catch (requestError) {
629
+ setError(requestError instanceof Error ? requestError.message : "Unable to start agent chat");
630
+ setStatus(grant?.requiresCode ? "code-required" : "error");
631
+ }
632
+ return;
633
+ }
634
+ await startSession();
635
+ }, [capabilityToken, createSession, startSession, grant]);
614
636
  useEffect(() => {
615
637
  if (!grant || grant.requiresCode || renderIntro || hasIntroContent(grant) || resolveCaptureFields(grant).length > 0 || autoStartedRef.current) {
616
638
  return;
@@ -696,7 +718,9 @@ function PublicAgentChat({
696
718
  {
697
719
  onSubmit: (event) => {
698
720
  event.preventDefault();
699
- void startSession(accessCode);
721
+ if (!grant) return;
722
+ const next = renderIntro || hasIntroContent(grant) || resolveCaptureFields(grant).length > 0 ? "intro" : "ready";
723
+ void authorize(accessCode, next);
700
724
  },
701
725
  children: /* @__PURE__ */ jsxs(Stack, { children: [
702
726
  /* @__PURE__ */ jsxs(Group, { gap: "xs", children: [
@@ -728,13 +752,16 @@ function PublicAgentChat({
728
752
  if (renderIntro) {
729
753
  const ctx = {
730
754
  start: () => {
731
- void startSession();
755
+ void beginSession();
732
756
  },
733
757
  starting: status === "authorizing",
734
758
  title: displayTitle,
735
759
  subtitle: displaySubtitle,
736
760
  branding,
737
- slug
761
+ slug,
762
+ captureFields: resolveCaptureFields(grant),
763
+ intakeValues,
764
+ setIntakeValue
738
765
  };
739
766
  return /* @__PURE__ */ jsx(
740
767
  PublicAgentChatFrame,
@@ -784,10 +811,7 @@ function PublicAgentChat({
784
811
  type: field.type ?? "text",
785
812
  required: field.required ?? false,
786
813
  value: intakeValues[field.key] ?? "",
787
- onChange: (event) => {
788
- const value = event.currentTarget.value;
789
- setIntakeValues((prev) => ({ ...prev, [field.key]: value }));
790
- },
814
+ onChange: (event) => setIntakeValue(field.key, event.currentTarget.value),
791
815
  autoComplete: field.type === "email" ? "email" : field.type === "tel" ? "tel" : "off"
792
816
  },
793
817
  field.key
@@ -796,7 +820,7 @@ function PublicAgentChat({
796
820
  Button,
797
821
  {
798
822
  onClick: () => {
799
- void startSession();
823
+ void beginSession();
800
824
  },
801
825
  leftSection: /* @__PURE__ */ jsx(IconSend, { size: 16 }),
802
826
  children: ctaLabel
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@elevasis/ui",
3
- "version": "2.59.0",
3
+ "version": "2.60.1",
4
4
  "description": "UI components and platform-aware hooks for building custom frontends on the Elevasis platform",
5
5
  "type": "module",
6
6
  "license": "MIT",
@@ -276,8 +276,8 @@
276
276
  "vitest": "^3.2.4",
277
277
  "@elevasis/sdk": "1.36.5",
278
278
  "@repo/core": "0.52.0",
279
- "@repo/elevasis-core": "1.0.0",
280
279
  "@repo/typescript-config": "0.0.0",
280
+ "@repo/elevasis-core": "1.0.0",
281
281
  "@repo/eslint-config": "0.0.0"
282
282
  },
283
283
  "dependencies": {