@bacano/sdk 1.2.2 → 1.2.4

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
@@ -1122,6 +1122,35 @@ var WEBSITE_GET_PRODUCT_CONTENT = (
1122
1122
  }
1123
1123
  `
1124
1124
  );
1125
+ var WEBSITE_GET_PRODUCT_CONTENT_BATCH = (
1126
+ /* GraphQL */
1127
+ `
1128
+ mutation WebsiteGetProductContentBatch(
1129
+ $websiteId: uuid!
1130
+ $productIds: [uuid!]!
1131
+ $locale: String
1132
+ ) {
1133
+ result: websiteGetProductContentBatch(
1134
+ website_id: $websiteId
1135
+ product_ids: $productIds
1136
+ locale: $locale
1137
+ ) {
1138
+ success
1139
+ errors
1140
+ blocks {
1141
+ id
1142
+ product_id
1143
+ locale
1144
+ content_json
1145
+ resolved_fields
1146
+ sort_order
1147
+ updated_at
1148
+ content_type { id key name kind }
1149
+ }
1150
+ }
1151
+ }
1152
+ `
1153
+ );
1125
1154
  var CHECK_AVAILABILITY = (
1126
1155
  /* GraphQL */
1127
1156
  `
@@ -1998,7 +2027,7 @@ var RECONCILE_PAYMENT_SESSION = (
1998
2027
  $orderId: uuid!
1999
2028
  $publicOrderToken: String!
2000
2029
  $paymentSessionId: uuid!
2001
- $gatewayTransactionId: String!
2030
+ $gatewayTransactionId: String
2002
2031
  ) {
2003
2032
  websiteReconcilePaymentSession(
2004
2033
  order_id: $orderId
@@ -5458,6 +5487,52 @@ function BacanoMarketingProvider({
5458
5487
  // libs/sdk/src/react/provider.tsx
5459
5488
  var import_react6 = require("react");
5460
5489
 
5490
+ // libs/sdk/src/flows.ts
5491
+ function createFlowsClient(config) {
5492
+ const base = config.apiUrl.replace(/\/+$/, "") + "/api/v1/website/flows";
5493
+ async function request(path, body, binary = false) {
5494
+ const token = await config.getAccessToken?.();
5495
+ const response = await fetch(base + path, {
5496
+ method: body === void 0 ? "GET" : "POST",
5497
+ cache: "no-store",
5498
+ headers: {
5499
+ ...token ? { Authorization: `Bearer ${token}` } : {},
5500
+ ...body !== void 0 ? { "Content-Type": "application/json" } : {},
5501
+ ...config.websiteSlug ? { "x-bacano-website-slug": config.websiteSlug } : {}
5502
+ },
5503
+ ...body !== void 0 ? { body: JSON.stringify(body) } : {}
5504
+ });
5505
+ if (!response.ok) {
5506
+ const result = await response.json().catch(() => ({ error: "FLOW_REQUEST_FAILED" }));
5507
+ throw new WebsiteError(
5508
+ result.error ?? "FLOW_REQUEST_FAILED",
5509
+ result.error ?? "FLOW_REQUEST_FAILED"
5510
+ );
5511
+ }
5512
+ return binary ? await response.blob() : await response.json();
5513
+ }
5514
+ const stepPath = (caseId, stepId) => `/cases/${encodeURIComponent(caseId)}/steps/${encodeURIComponent(stepId)}`;
5515
+ return {
5516
+ /** Only the explicitly enabled local demo gateway accepts these credentials. */
5517
+ localDemoLogin: (email, password) => request("/local-session", { email, password }),
5518
+ logout: () => request("/logout", {}),
5519
+ listCases: () => request("/cases"),
5520
+ getJourney: (caseId) => request(`/cases/${encodeURIComponent(caseId)}`),
5521
+ getContent: (caseId, stepId) => request(stepPath(caseId, stepId) + "/content"),
5522
+ getForm: (caseId, stepId) => request(stepPath(caseId, stepId) + "/form"),
5523
+ getResponse: (caseId, stepId) => request(stepPath(caseId, stepId) + "/response"),
5524
+ getFile: (caseId, stepId, fileId) => request(
5525
+ stepPath(caseId, stepId) + `/files/${encodeURIComponent(fileId)}`,
5526
+ void 0,
5527
+ true
5528
+ ),
5529
+ act: (caseId, command) => request(
5530
+ `/cases/${encodeURIComponent(caseId)}/actions`,
5531
+ command
5532
+ )
5533
+ };
5534
+ }
5535
+
5461
5536
  // libs/sdk/src/modules/auth.ts
5462
5537
  function createAuthHeaders(accessToken) {
5463
5538
  return accessToken ? { Authorization: `Bearer ${accessToken}` } : {};
@@ -6768,6 +6843,33 @@ function createContentModule(graphqlUrl, getHeaders, getContext, storageUrl) {
6768
6843
  return (data.result.blocks ?? []).map(
6769
6844
  (block) => mapProductContentBlock(block, storageUrl)
6770
6845
  );
6846
+ },
6847
+ async getForProducts(productIds, opts) {
6848
+ const uniqueIds = [...new Set(productIds.map((id) => id.toLowerCase()))];
6849
+ const blocksByProduct = Object.fromEntries(uniqueIds.map((id) => [id, []]));
6850
+ if (uniqueIds.length === 0) return blocksByProduct;
6851
+ const ctx = getContext();
6852
+ for (let start = 0; start < uniqueIds.length; start += 50) {
6853
+ const batch = uniqueIds.slice(start, start + 50);
6854
+ const data = await executeGraphQL(
6855
+ graphqlUrl,
6856
+ WEBSITE_GET_PRODUCT_CONTENT_BATCH,
6857
+ {
6858
+ websiteId: ctx.websiteId,
6859
+ productIds: batch,
6860
+ locale: opts?.locale ?? DEFAULT_LOCALE
6861
+ },
6862
+ getHeaders
6863
+ );
6864
+ if (!data.result?.success) continue;
6865
+ for (const block of data.result.blocks ?? []) {
6866
+ const id = block.product_id.toLowerCase();
6867
+ if (Object.prototype.hasOwnProperty.call(blocksByProduct, id)) {
6868
+ blocksByProduct[id].push(mapProductContentBlock(block, storageUrl));
6869
+ }
6870
+ }
6871
+ }
6872
+ return blocksByProduct;
6771
6873
  }
6772
6874
  };
6773
6875
  }
@@ -7482,6 +7584,7 @@ function createBacanoClient(config) {
7482
7584
  await initialization;
7483
7585
  },
7484
7586
  getContext,
7587
+ flows: createFlowsClient(config),
7485
7588
  auth: {
7486
7589
  async getAccessToken() {
7487
7590
  return config.getAccessToken ? await config.getAccessToken() : null;
@@ -7514,7 +7617,8 @@ function createBacanoClient(config) {
7514
7617
  getByKey: contentModule.getByKey.bind(contentModule),
7515
7618
  getByCategory: contentModule.getByCategory.bind(contentModule),
7516
7619
  getByTag: contentModule.getByTag.bind(contentModule),
7517
- getForProduct: contentModule.getForProduct.bind(contentModule)
7620
+ getForProduct: contentModule.getForProduct.bind(contentModule),
7621
+ getForProducts: contentModule.getForProducts.bind(contentModule)
7518
7622
  },
7519
7623
  forms: {
7520
7624
  getDefinition: formsModule.getDefinition.bind(formsModule),