@bacano/sdk 1.2.2 → 1.2.3

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.
package/react/forms.js CHANGED
@@ -2,10 +2,10 @@
2
2
  import {
3
3
  useCallback as useCallback2,
4
4
  useEffect as useEffect3,
5
- useId,
5
+ useId as useId2,
6
6
  useMemo as useMemo2,
7
7
  useRef as useRef3,
8
- useState as useState2
8
+ useState as useState3
9
9
  } from "react";
10
10
 
11
11
  // libs/sdk/src/errors.ts
@@ -22,11 +22,124 @@ var WebsiteError = class extends Error {
22
22
  }
23
23
  };
24
24
 
25
- // libs/sdk/src/react/form-field-control.tsx
25
+ // libs/sdk/src/form-conditions.ts
26
+ function formRootFields(definition) {
27
+ return [
28
+ ...definition.fields,
29
+ ...definition.sections.flatMap((section) => section.fields),
30
+ ...definition.steps.flatMap((step) => [
31
+ ...step.fields,
32
+ ...step.sections.flatMap((section) => section.fields)
33
+ ])
34
+ ];
35
+ }
36
+ function visibleFormFields(fields, values) {
37
+ return fields.flatMap((field) => [
38
+ field,
39
+ ...visibleFormFields(
40
+ field.branches?.find((branch) => branch.value === values[field.key])?.fields ?? [],
41
+ values
42
+ )
43
+ ]);
44
+ }
45
+ function allFormFields(fields) {
46
+ return fields.flatMap((field) => [
47
+ field,
48
+ ...allFormFields((field.branches ?? []).flatMap((branch) => branch.fields))
49
+ ]);
50
+ }
51
+ function activeFormValues(definition, values) {
52
+ const keys = new Set(
53
+ visibleFormFields(formRootFields(definition), values).map(
54
+ (field) => field.key
55
+ )
56
+ );
57
+ return Object.fromEntries(
58
+ Object.entries(values).filter(([key]) => keys.has(key))
59
+ );
60
+ }
61
+
62
+ // libs/sdk/src/react/form-scale-control.tsx
26
63
  import { jsx, jsxs } from "react/jsx-runtime";
64
+ function FormScaleControl({
65
+ field,
66
+ id,
67
+ value,
68
+ onChange,
69
+ className
70
+ }) {
71
+ const scale = field.scale;
72
+ if (!scale) return null;
73
+ const options = Array.from(
74
+ { length: Math.max(0, Math.min(11, scale.max - scale.min + 1)) },
75
+ (_, index) => scale.min + index
76
+ );
77
+ return /* @__PURE__ */ jsxs("div", { children: [
78
+ /* @__PURE__ */ jsx(
79
+ "div",
80
+ {
81
+ role: "radiogroup",
82
+ "aria-labelledby": `${id}-label`,
83
+ style: { display: "flex", flexWrap: "wrap", gap: 12 },
84
+ children: options.map((number) => /* @__PURE__ */ jsxs(
85
+ "label",
86
+ {
87
+ style: { display: "inline-flex", alignItems: "center", gap: 4 },
88
+ children: [
89
+ /* @__PURE__ */ jsx(
90
+ "input",
91
+ {
92
+ className,
93
+ type: "radio",
94
+ name: id,
95
+ value: String(number),
96
+ checked: value === String(number),
97
+ required: field.required,
98
+ onChange: () => onChange(String(number)),
99
+ "aria-label": `${number} de ${scale.max}`
100
+ }
101
+ ),
102
+ scale.display === "stars" ? /* @__PURE__ */ jsx(
103
+ "span",
104
+ {
105
+ "aria-hidden": "true",
106
+ style: {
107
+ fontSize: 24,
108
+ color: Number(value) >= number ? "var(--accent, #b45309)" : "var(--muted, #6b7280)"
109
+ },
110
+ children: "\u2605"
111
+ }
112
+ ) : number
113
+ ]
114
+ },
115
+ number
116
+ ))
117
+ }
118
+ ),
119
+ (scale.minLabel || scale.maxLabel) && /* @__PURE__ */ jsxs("p", { children: [
120
+ scale.minLabel,
121
+ " \u2014 ",
122
+ scale.maxLabel
123
+ ] })
124
+ ] });
125
+ }
126
+
127
+ // libs/sdk/src/react/form-field-control.tsx
128
+ import { jsx as jsx2, jsxs as jsxs2 } from "react/jsx-runtime";
27
129
  function BacanoFormFieldControl(props) {
28
130
  const { field, className, value, onChange } = props;
29
131
  const controlId = props.id ?? field.key;
132
+ if (field.type === "scale")
133
+ return /* @__PURE__ */ jsx2(
134
+ FormScaleControl,
135
+ {
136
+ field,
137
+ id: controlId,
138
+ value,
139
+ onChange,
140
+ className
141
+ }
142
+ );
30
143
  const common = {
31
144
  id: controlId,
32
145
  name: field.key,
@@ -34,7 +147,7 @@ function BacanoFormFieldControl(props) {
34
147
  className
35
148
  };
36
149
  if (field.type === "textarea") {
37
- return /* @__PURE__ */ jsx(
150
+ return /* @__PURE__ */ jsx2(
38
151
  "textarea",
39
152
  {
40
153
  ...common,
@@ -48,7 +161,7 @@ function BacanoFormFieldControl(props) {
48
161
  }
49
162
  if (field.type === "select" || field.type === "multi_select") {
50
163
  const isMultiple = field.type === "multi_select";
51
- return /* @__PURE__ */ jsxs(
164
+ return /* @__PURE__ */ jsxs2(
52
165
  "select",
53
166
  {
54
167
  ...common,
@@ -61,15 +174,15 @@ function BacanoFormFieldControl(props) {
61
174
  ) : event.target.value
62
175
  ),
63
176
  children: [
64
- !isMultiple ? /* @__PURE__ */ jsx("option", { value: "", children: field.placeholder ?? "Selecciona una opci\xF3n" }) : null,
65
- (field.options ?? []).map((option) => /* @__PURE__ */ jsx("option", { value: option.value, children: option.label }, option.value))
177
+ !isMultiple ? /* @__PURE__ */ jsx2("option", { value: "", children: field.placeholder ?? "Selecciona una opci\xF3n" }) : null,
178
+ (field.options ?? []).map((option) => /* @__PURE__ */ jsx2("option", { value: option.value, children: option.label }, option.value))
66
179
  ]
67
180
  }
68
181
  );
69
182
  }
70
183
  if (field.type === "radio") {
71
- return /* @__PURE__ */ jsx("div", { role: "radiogroup", "aria-labelledby": `${controlId}-label`, children: (field.options ?? []).map((option) => /* @__PURE__ */ jsxs("label", { children: [
72
- /* @__PURE__ */ jsx(
184
+ return /* @__PURE__ */ jsx2("div", { role: "radiogroup", "aria-labelledby": `${controlId}-label`, children: (field.options ?? []).map((option) => /* @__PURE__ */ jsxs2("label", { children: [
185
+ /* @__PURE__ */ jsx2(
73
186
  "input",
74
187
  {
75
188
  type: "radio",
@@ -84,7 +197,7 @@ function BacanoFormFieldControl(props) {
84
197
  ] }, option.value)) });
85
198
  }
86
199
  if (field.type === "checkbox") {
87
- return /* @__PURE__ */ jsx(
200
+ return /* @__PURE__ */ jsx2(
88
201
  "input",
89
202
  {
90
203
  ...common,
@@ -94,7 +207,7 @@ function BacanoFormFieldControl(props) {
94
207
  }
95
208
  );
96
209
  }
97
- return /* @__PURE__ */ jsx(
210
+ return /* @__PURE__ */ jsx2(
98
211
  "input",
99
212
  {
100
213
  ...common,
@@ -111,13 +224,13 @@ function BacanoFormFieldControl(props) {
111
224
  }
112
225
 
113
226
  // libs/sdk/src/react/form-sections.tsx
114
- import { jsx as jsx2, jsxs as jsxs2 } from "react/jsx-runtime";
227
+ import { jsx as jsx3, jsxs as jsxs3 } from "react/jsx-runtime";
115
228
  function FormFields(props) {
116
229
  return props.fields.map((field) => {
117
230
  const controlId = `${props.idPrefix}-${field.key}`;
118
231
  const value = props.values[field.key] ?? (field.type === "checkbox" ? false : field.type === "multi_select" ? [] : "");
119
- return /* @__PURE__ */ jsxs2("div", { className: props.classNames?.field, children: [
120
- /* @__PURE__ */ jsx2(
232
+ return /* @__PURE__ */ jsxs3("div", { className: props.classNames?.field, children: [
233
+ /* @__PURE__ */ jsx3(
121
234
  "label",
122
235
  {
123
236
  id: `${controlId}-label`,
@@ -126,7 +239,7 @@ function FormFields(props) {
126
239
  children: field.label
127
240
  }
128
241
  ),
129
- /* @__PURE__ */ jsx2(
242
+ /* @__PURE__ */ jsx3(
130
243
  BacanoFormFieldControl,
131
244
  {
132
245
  field,
@@ -136,15 +249,18 @@ function FormFields(props) {
136
249
  onChange: (nextValue) => props.onChange(field.key, nextValue)
137
250
  }
138
251
  ),
139
- field.helpText ? /* @__PURE__ */ jsx2("p", { className: props.classNames?.helpText, children: field.helpText }) : null
252
+ field.helpText ? /* @__PURE__ */ jsx3("p", { className: props.classNames?.helpText, children: field.helpText }) : null,
253
+ field.branches?.map(
254
+ (branch) => branch.value === props.values[field.key] ? /* @__PURE__ */ jsx3("div", { "data-bacano-form-branch": branch.value, children: /* @__PURE__ */ jsx3(FormFields, { ...props, fields: branch.fields }) }, branch.value) : null
255
+ )
140
256
  ] }, field.key);
141
257
  });
142
258
  }
143
259
  function FormSections(props) {
144
- return props.sections.map((section) => /* @__PURE__ */ jsxs2("section", { className: props.classNames?.section, children: [
145
- /* @__PURE__ */ jsx2("h3", { className: props.classNames?.sectionTitle, children: section.title }),
146
- section.description ? /* @__PURE__ */ jsx2("p", { className: props.classNames?.sectionDescription, children: section.description }) : null,
147
- /* @__PURE__ */ jsx2(
260
+ return props.sections.map((section) => /* @__PURE__ */ jsxs3("section", { className: props.classNames?.section, children: [
261
+ /* @__PURE__ */ jsx3("h3", { className: props.classNames?.sectionTitle, children: section.title }),
262
+ section.description ? /* @__PURE__ */ jsx3("p", { className: props.classNames?.sectionDescription, children: section.description }) : null,
263
+ /* @__PURE__ */ jsx3(
148
264
  FormFields,
149
265
  {
150
266
  fields: section.fields,
@@ -284,8 +400,82 @@ function useBacanoState() {
284
400
  return useContext2(BacanoReactContext);
285
401
  }
286
402
 
403
+ // libs/sdk/src/react/flow-form.tsx
404
+ import { useId, useState as useState2 } from "react";
405
+ import { jsx as jsx4, jsxs as jsxs4 } from "react/jsx-runtime";
406
+ function BacanoFlowForm({
407
+ form,
408
+ onSubmit,
409
+ onRefresh,
410
+ classNames
411
+ }) {
412
+ const id = useId();
413
+ const [values, setValues] = useState2(
414
+ () => activeFormValues(form.definition, form.initialValues ?? {})
415
+ );
416
+ const [error, setError] = useState2("");
417
+ const [busy, setBusy] = useState2(false);
418
+ const common = {
419
+ idPrefix: id,
420
+ values,
421
+ classNames,
422
+ onChange: (key, value) => setValues(
423
+ (current) => activeFormValues(form.definition, { ...current, [key]: value })
424
+ )
425
+ };
426
+ return /* @__PURE__ */ jsxs4(
427
+ "form",
428
+ {
429
+ className: classNames?.root,
430
+ onSubmit: async (event) => {
431
+ event.preventDefault();
432
+ if (busy) return;
433
+ setBusy(true);
434
+ setError("");
435
+ try {
436
+ await onSubmit(activeFormValues(form.definition, values), form);
437
+ } catch (reason) {
438
+ setError(
439
+ reason instanceof Error ? reason.message : "No fue posible enviar la respuesta."
440
+ );
441
+ } finally {
442
+ setBusy(false);
443
+ }
444
+ },
445
+ children: [
446
+ form.correction && /* @__PURE__ */ jsxs4("p", { role: "status", children: [
447
+ "Cargamos tus respuestas anteriores para que puedas corregirlas.",
448
+ form.correction.formChanged && " Las preguntas cambiaron; revisa los campos antes de enviar."
449
+ ] }),
450
+ /* @__PURE__ */ jsx4(FormFields, { ...common, fields: form.definition.fields }),
451
+ /* @__PURE__ */ jsx4(FormSections, { ...common, sections: form.definition.sections }),
452
+ form.definition.steps.map((step) => /* @__PURE__ */ jsxs4("section", { className: classNames?.section, children: [
453
+ /* @__PURE__ */ jsx4("h3", { className: classNames?.sectionTitle, children: step.title }),
454
+ /* @__PURE__ */ jsx4("p", { children: step.description }),
455
+ /* @__PURE__ */ jsx4(FormFields, { ...common, fields: step.fields }),
456
+ /* @__PURE__ */ jsx4(FormSections, { ...common, sections: step.sections })
457
+ ] }, step.key)),
458
+ error && /* @__PURE__ */ jsxs4("div", { role: "alert", children: [
459
+ /* @__PURE__ */ jsx4("p", { children: error === "FLOW_FORM_CHANGED" ? "El formulario cambi\xF3. Actualiza las preguntas y revisa tus respuestas antes de enviarlas." : error }),
460
+ /* @__PURE__ */ jsx4(
461
+ "button",
462
+ {
463
+ type: "button",
464
+ onClick: () => void onRefresh().catch(
465
+ () => setError("No se pudo actualizar el formulario.")
466
+ ),
467
+ children: "Actualizar preguntas sin borrar mis respuestas"
468
+ }
469
+ )
470
+ ] }),
471
+ /* @__PURE__ */ jsx4("button", { type: "submit", disabled: busy, className: classNames?.button, children: busy ? "Guardando\u2026" : form.definition.submitLabel })
472
+ ]
473
+ }
474
+ );
475
+ }
476
+
287
477
  // libs/sdk/src/react/forms.tsx
288
- import { Fragment, jsx as jsx3, jsxs as jsxs3 } from "react/jsx-runtime";
478
+ import { Fragment, jsx as jsx5, jsxs as jsxs5 } from "react/jsx-runtime";
289
479
  var EMPTY_FORM_FIELDS = [];
290
480
  var EMPTY_FORM_SECTIONS = [];
291
481
  var EMPTY_FORM_STEPS = [];
@@ -297,11 +487,11 @@ function useBacanoForm(formKey, options) {
297
487
  } = useBacanoState();
298
488
  const enabled = options?.enabled ?? true;
299
489
  const initialForm = options?.initialForm?.key === formKey ? options.initialForm : null;
300
- const [form, setForm] = useState2(initialForm);
490
+ const [form, setForm] = useState3(initialForm);
301
491
  const formRef = useRef3(initialForm);
302
492
  const requestVersion = useRef3(0);
303
- const [loading, setLoading] = useState2(enabled && !initialForm);
304
- const [error, setError] = useState2(null);
493
+ const [loading, setLoading] = useState3(enabled && !initialForm);
494
+ const [error, setError] = useState3(null);
305
495
  const load = useCallback2(
306
496
  async (force = false) => {
307
497
  if (!enabled || !client) return;
@@ -346,18 +536,18 @@ function useBacanoForm(formKey, options) {
346
536
  };
347
537
  }
348
538
  function BacanoFormInstance(props) {
349
- const reactId = useId();
539
+ const reactId = useId2();
350
540
  const idPrefix = `${props.formKey}-${reactId.replace(/:/g, "")}`;
351
541
  const { client } = useBacanoState();
352
542
  const { form, loading, error } = useBacanoForm(props.formKey, {
353
543
  initialForm: props.initialForm
354
544
  });
355
- const [internalValues, setValues] = useState2({});
356
- const [step, setStep] = useState2(0);
357
- const [submitting, setSubmitting] = useState2(false);
358
- const [submitted, setSubmitted] = useState2(false);
359
- const [submissionError, setSubmissionError] = useState2(null);
360
- const [honeypot, setHoneypot] = useState2("");
545
+ const [internalValues, setValues] = useState3({});
546
+ const [step, setStep] = useState3(0);
547
+ const [submitting, setSubmitting] = useState3(false);
548
+ const [submitted, setSubmitted] = useState3(false);
549
+ const [submissionError, setSubmissionError] = useState3(null);
550
+ const [honeypot, setHoneypot] = useState3("");
361
551
  const tracking = useBacanoFormTracking(props.formKey, {
362
552
  visible: (props.visible ?? true) && !loading && form?.status === "ACTIVE" && !submitted
363
553
  });
@@ -367,22 +557,23 @@ function BacanoFormInstance(props) {
367
557
  const steps = form?.definition.steps ?? EMPTY_FORM_STEPS;
368
558
  const fieldKeys = useMemo2(
369
559
  () => new Set(
370
- [
560
+ allFormFields([
371
561
  ...fields,
372
562
  ...sections.flatMap((section) => section.fields),
373
563
  ...steps.flatMap((formStep) => [
374
564
  ...formStep.fields,
375
565
  ...formStep.sections.flatMap((section) => section.fields)
376
566
  ])
377
- ].map((field) => field.key)
567
+ ]).map((field) => field.key)
378
568
  ),
379
569
  [fields, sections, steps]
380
570
  );
381
- const values = Object.fromEntries(
571
+ const rawValues = Object.fromEntries(
382
572
  Object.entries(props.values ?? internalValues).filter(
383
573
  ([key]) => fieldKeys.has(key)
384
574
  )
385
575
  );
576
+ const values = form ? activeFormValues(form.definition, props.values ?? internalValues) : rawValues;
386
577
  useEffect3(() => {
387
578
  setStep(0);
388
579
  setValues(
@@ -395,15 +586,15 @@ function BacanoFormInstance(props) {
395
586
  setHoneypot("");
396
587
  idempotencyKey.current = crypto.randomUUID();
397
588
  }, [fieldKeys, form?.version]);
398
- if (loading) return /* @__PURE__ */ jsx3(Fragment, { children: props.loadingFallback ?? null });
589
+ if (loading) return /* @__PURE__ */ jsx5(Fragment, { children: props.loadingFallback ?? null });
399
590
  if (error && !form)
400
- return /* @__PURE__ */ jsx3(Fragment, { children: props.errorFallback?.(error) ?? props.unavailableFallback ?? null });
401
- if (!form) return /* @__PURE__ */ jsx3(Fragment, { children: props.unavailableFallback ?? null });
591
+ return /* @__PURE__ */ jsx5(Fragment, { children: props.errorFallback?.(error) ?? props.unavailableFallback ?? null });
592
+ if (!form) return /* @__PURE__ */ jsx5(Fragment, { children: props.unavailableFallback ?? null });
402
593
  if (form.status === "PAUSED") {
403
- return /* @__PURE__ */ jsx3("div", { className: props.classNames?.message, "data-bacano-form-unavailable": true, children: props.unavailableFallback ?? form.definition.unavailableMessage });
594
+ return /* @__PURE__ */ jsx5("div", { className: props.classNames?.message, "data-bacano-form-unavailable": true, children: props.unavailableFallback ?? form.definition.unavailableMessage });
404
595
  }
405
596
  if (submitted) {
406
- return /* @__PURE__ */ jsx3("div", { className: props.classNames?.message, "data-bacano-form-success": true, children: form.definition.successMessage });
597
+ return /* @__PURE__ */ jsx5("div", { className: props.classNames?.message, "data-bacano-form-success": true, children: form.definition.successMessage });
407
598
  }
408
599
  const isMultiStep = steps.length > 0;
409
600
  const activeStep = isMultiStep ? steps[step] : null;
@@ -411,7 +602,10 @@ function BacanoFormInstance(props) {
411
602
  const setFieldValue = (key, value) => {
412
603
  if (value !== "" && value !== false && (!Array.isArray(value) || value.length > 0))
413
604
  tracking.start();
414
- const nextValues = { ...values, [key]: value };
605
+ const nextValues = activeFormValues(form.definition, {
606
+ ...values,
607
+ [key]: value
608
+ });
415
609
  setValues(nextValues);
416
610
  props.onValuesChange?.(nextValues);
417
611
  };
@@ -467,7 +661,7 @@ function BacanoFormInstance(props) {
467
661
  setSubmitting(false);
468
662
  }
469
663
  };
470
- return /* @__PURE__ */ jsxs3(
664
+ return /* @__PURE__ */ jsxs5(
471
665
  "form",
472
666
  {
473
667
  noValidate: true,
@@ -476,7 +670,7 @@ function BacanoFormInstance(props) {
476
670
  "data-bacano-form-version": form.version,
477
671
  onSubmit: (event) => void submit(event),
478
672
  children: [
479
- /* @__PURE__ */ jsxs3(
673
+ /* @__PURE__ */ jsxs5(
480
674
  "div",
481
675
  {
482
676
  "aria-hidden": "true",
@@ -488,8 +682,8 @@ function BacanoFormInstance(props) {
488
682
  clip: "rect(0 0 0 0)"
489
683
  },
490
684
  children: [
491
- /* @__PURE__ */ jsx3("label", { htmlFor: `${form.key}-company-website`, children: "Empresa" }),
492
- /* @__PURE__ */ jsx3(
685
+ /* @__PURE__ */ jsx5("label", { htmlFor: `${form.key}-company-website`, children: "Empresa" }),
686
+ /* @__PURE__ */ jsx5(
493
687
  "input",
494
688
  {
495
689
  id: `${form.key}-company-website`,
@@ -503,12 +697,12 @@ function BacanoFormInstance(props) {
503
697
  ]
504
698
  }
505
699
  ),
506
- /* @__PURE__ */ jsx3("h2", { className: props.classNames?.title, children: form.name }),
507
- form.description ? /* @__PURE__ */ jsx3("p", { className: props.classNames?.description, children: form.description }) : null,
508
- activeStep ? /* @__PURE__ */ jsxs3("section", { className: props.classNames?.step, "data-bacano-form-step": true, children: [
509
- /* @__PURE__ */ jsx3("h3", { className: props.classNames?.stepTitle, children: activeStep.title }),
510
- activeStep.description ? /* @__PURE__ */ jsx3("p", { className: props.classNames?.stepDescription, children: activeStep.description }) : null,
511
- /* @__PURE__ */ jsx3(
700
+ /* @__PURE__ */ jsx5("h2", { className: props.classNames?.title, children: form.name }),
701
+ form.description ? /* @__PURE__ */ jsx5("p", { className: props.classNames?.description, children: form.description }) : null,
702
+ activeStep ? /* @__PURE__ */ jsxs5("section", { className: props.classNames?.step, "data-bacano-form-step": true, children: [
703
+ /* @__PURE__ */ jsx5("h3", { className: props.classNames?.stepTitle, children: activeStep.title }),
704
+ activeStep.description ? /* @__PURE__ */ jsx5("p", { className: props.classNames?.stepDescription, children: activeStep.description }) : null,
705
+ /* @__PURE__ */ jsx5(
512
706
  FormFields,
513
707
  {
514
708
  fields: activeStep.fields,
@@ -518,7 +712,7 @@ function BacanoFormInstance(props) {
518
712
  onChange: setFieldValue
519
713
  }
520
714
  ),
521
- /* @__PURE__ */ jsx3(
715
+ /* @__PURE__ */ jsx5(
522
716
  FormSections,
523
717
  {
524
718
  sections: activeStep.sections,
@@ -528,8 +722,8 @@ function BacanoFormInstance(props) {
528
722
  onChange: setFieldValue
529
723
  }
530
724
  )
531
- ] }) : /* @__PURE__ */ jsxs3(Fragment, { children: [
532
- /* @__PURE__ */ jsx3(
725
+ ] }) : /* @__PURE__ */ jsxs5(Fragment, { children: [
726
+ /* @__PURE__ */ jsx5(
533
727
  FormFields,
534
728
  {
535
729
  fields,
@@ -539,7 +733,7 @@ function BacanoFormInstance(props) {
539
733
  onChange: setFieldValue
540
734
  }
541
735
  ),
542
- /* @__PURE__ */ jsx3(
736
+ /* @__PURE__ */ jsx5(
543
737
  FormSections,
544
738
  {
545
739
  sections,
@@ -550,8 +744,8 @@ function BacanoFormInstance(props) {
550
744
  }
551
745
  )
552
746
  ] }),
553
- /* @__PURE__ */ jsxs3("div", { className: props.classNames?.actions, children: [
554
- isMultiStep && step > 0 ? /* @__PURE__ */ jsx3(
747
+ /* @__PURE__ */ jsxs5("div", { className: props.classNames?.actions, children: [
748
+ isMultiStep && step > 0 ? /* @__PURE__ */ jsx5(
555
749
  "button",
556
750
  {
557
751
  type: "button",
@@ -559,7 +753,7 @@ function BacanoFormInstance(props) {
559
753
  children: "Anterior"
560
754
  }
561
755
  ) : null,
562
- /* @__PURE__ */ jsx3(
756
+ /* @__PURE__ */ jsx5(
563
757
  "button",
564
758
  {
565
759
  type: "submit",
@@ -569,7 +763,7 @@ function BacanoFormInstance(props) {
569
763
  }
570
764
  )
571
765
  ] }),
572
- submissionError ? /* @__PURE__ */ jsx3(
766
+ submissionError ? /* @__PURE__ */ jsx5(
573
767
  "div",
574
768
  {
575
769
  role: "alert",
@@ -583,9 +777,10 @@ function BacanoFormInstance(props) {
583
777
  );
584
778
  }
585
779
  function BacanoForm(props) {
586
- return /* @__PURE__ */ jsx3(BacanoFormInstance, { ...props }, props.formKey);
780
+ return /* @__PURE__ */ jsx5(BacanoFormInstance, { ...props }, props.formKey);
587
781
  }
588
782
  export {
783
+ BacanoFlowForm,
589
784
  BacanoForm,
590
785
  useBacanoForm,
591
786
  useBacanoFormTracking
package/react.cjs CHANGED
@@ -1998,7 +1998,7 @@ var RECONCILE_PAYMENT_SESSION = (
1998
1998
  $orderId: uuid!
1999
1999
  $publicOrderToken: String!
2000
2000
  $paymentSessionId: uuid!
2001
- $gatewayTransactionId: String!
2001
+ $gatewayTransactionId: String
2002
2002
  ) {
2003
2003
  websiteReconcilePaymentSession(
2004
2004
  order_id: $orderId
@@ -5458,6 +5458,52 @@ function BacanoMarketingProvider({
5458
5458
  // libs/sdk/src/react/provider.tsx
5459
5459
  var import_react6 = require("react");
5460
5460
 
5461
+ // libs/sdk/src/flows.ts
5462
+ function createFlowsClient(config) {
5463
+ const base = config.apiUrl.replace(/\/+$/, "") + "/api/v1/website/flows";
5464
+ async function request(path, body, binary = false) {
5465
+ const token = await config.getAccessToken?.();
5466
+ const response = await fetch(base + path, {
5467
+ method: body === void 0 ? "GET" : "POST",
5468
+ cache: "no-store",
5469
+ headers: {
5470
+ ...token ? { Authorization: `Bearer ${token}` } : {},
5471
+ ...body !== void 0 ? { "Content-Type": "application/json" } : {},
5472
+ ...config.websiteSlug ? { "x-bacano-website-slug": config.websiteSlug } : {}
5473
+ },
5474
+ ...body !== void 0 ? { body: JSON.stringify(body) } : {}
5475
+ });
5476
+ if (!response.ok) {
5477
+ const result = await response.json().catch(() => ({ error: "FLOW_REQUEST_FAILED" }));
5478
+ throw new WebsiteError(
5479
+ result.error ?? "FLOW_REQUEST_FAILED",
5480
+ result.error ?? "FLOW_REQUEST_FAILED"
5481
+ );
5482
+ }
5483
+ return binary ? await response.blob() : await response.json();
5484
+ }
5485
+ const stepPath = (caseId, stepId) => `/cases/${encodeURIComponent(caseId)}/steps/${encodeURIComponent(stepId)}`;
5486
+ return {
5487
+ /** Only the explicitly enabled local demo gateway accepts these credentials. */
5488
+ localDemoLogin: (email, password) => request("/local-session", { email, password }),
5489
+ logout: () => request("/logout", {}),
5490
+ listCases: () => request("/cases"),
5491
+ getJourney: (caseId) => request(`/cases/${encodeURIComponent(caseId)}`),
5492
+ getContent: (caseId, stepId) => request(stepPath(caseId, stepId) + "/content"),
5493
+ getForm: (caseId, stepId) => request(stepPath(caseId, stepId) + "/form"),
5494
+ getResponse: (caseId, stepId) => request(stepPath(caseId, stepId) + "/response"),
5495
+ getFile: (caseId, stepId, fileId) => request(
5496
+ stepPath(caseId, stepId) + `/files/${encodeURIComponent(fileId)}`,
5497
+ void 0,
5498
+ true
5499
+ ),
5500
+ act: (caseId, command) => request(
5501
+ `/cases/${encodeURIComponent(caseId)}/actions`,
5502
+ command
5503
+ )
5504
+ };
5505
+ }
5506
+
5461
5507
  // libs/sdk/src/modules/auth.ts
5462
5508
  function createAuthHeaders(accessToken) {
5463
5509
  return accessToken ? { Authorization: `Bearer ${accessToken}` } : {};
@@ -7482,6 +7528,7 @@ function createBacanoClient(config) {
7482
7528
  await initialization;
7483
7529
  },
7484
7530
  getContext,
7531
+ flows: createFlowsClient(config),
7485
7532
  auth: {
7486
7533
  async getAccessToken() {
7487
7534
  return config.getAccessToken ? await config.getAccessToken() : null;
package/react.js CHANGED
@@ -1941,7 +1941,7 @@ var RECONCILE_PAYMENT_SESSION = (
1941
1941
  $orderId: uuid!
1942
1942
  $publicOrderToken: String!
1943
1943
  $paymentSessionId: uuid!
1944
- $gatewayTransactionId: String!
1944
+ $gatewayTransactionId: String
1945
1945
  ) {
1946
1946
  websiteReconcilePaymentSession(
1947
1947
  order_id: $orderId
@@ -5405,6 +5405,52 @@ function BacanoMarketingProvider({
5405
5405
  // libs/sdk/src/react/provider.tsx
5406
5406
  import { useEffect as useEffect3, useRef as useRef3, useState as useState3 } from "react";
5407
5407
 
5408
+ // libs/sdk/src/flows.ts
5409
+ function createFlowsClient(config) {
5410
+ const base = config.apiUrl.replace(/\/+$/, "") + "/api/v1/website/flows";
5411
+ async function request(path, body, binary = false) {
5412
+ const token = await config.getAccessToken?.();
5413
+ const response = await fetch(base + path, {
5414
+ method: body === void 0 ? "GET" : "POST",
5415
+ cache: "no-store",
5416
+ headers: {
5417
+ ...token ? { Authorization: `Bearer ${token}` } : {},
5418
+ ...body !== void 0 ? { "Content-Type": "application/json" } : {},
5419
+ ...config.websiteSlug ? { "x-bacano-website-slug": config.websiteSlug } : {}
5420
+ },
5421
+ ...body !== void 0 ? { body: JSON.stringify(body) } : {}
5422
+ });
5423
+ if (!response.ok) {
5424
+ const result = await response.json().catch(() => ({ error: "FLOW_REQUEST_FAILED" }));
5425
+ throw new WebsiteError(
5426
+ result.error ?? "FLOW_REQUEST_FAILED",
5427
+ result.error ?? "FLOW_REQUEST_FAILED"
5428
+ );
5429
+ }
5430
+ return binary ? await response.blob() : await response.json();
5431
+ }
5432
+ const stepPath = (caseId, stepId) => `/cases/${encodeURIComponent(caseId)}/steps/${encodeURIComponent(stepId)}`;
5433
+ return {
5434
+ /** Only the explicitly enabled local demo gateway accepts these credentials. */
5435
+ localDemoLogin: (email, password) => request("/local-session", { email, password }),
5436
+ logout: () => request("/logout", {}),
5437
+ listCases: () => request("/cases"),
5438
+ getJourney: (caseId) => request(`/cases/${encodeURIComponent(caseId)}`),
5439
+ getContent: (caseId, stepId) => request(stepPath(caseId, stepId) + "/content"),
5440
+ getForm: (caseId, stepId) => request(stepPath(caseId, stepId) + "/form"),
5441
+ getResponse: (caseId, stepId) => request(stepPath(caseId, stepId) + "/response"),
5442
+ getFile: (caseId, stepId, fileId) => request(
5443
+ stepPath(caseId, stepId) + `/files/${encodeURIComponent(fileId)}`,
5444
+ void 0,
5445
+ true
5446
+ ),
5447
+ act: (caseId, command) => request(
5448
+ `/cases/${encodeURIComponent(caseId)}/actions`,
5449
+ command
5450
+ )
5451
+ };
5452
+ }
5453
+
5408
5454
  // libs/sdk/src/modules/auth.ts
5409
5455
  function createAuthHeaders(accessToken) {
5410
5456
  return accessToken ? { Authorization: `Bearer ${accessToken}` } : {};
@@ -7429,6 +7475,7 @@ function createBacanoClient(config) {
7429
7475
  await initialization;
7430
7476
  },
7431
7477
  getContext,
7478
+ flows: createFlowsClient(config),
7432
7479
  auth: {
7433
7480
  async getAccessToken() {
7434
7481
  return config.getAccessToken ? await config.getAccessToken() : null;