@resira/ui 0.3.2 → 0.4.0

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/dist/index.cjs CHANGED
@@ -224,6 +224,12 @@ function ResiraProvider({
224
224
  const visibleServiceCount = config?.visibleServiceCount ?? 4;
225
225
  const groupServicesByCategory = config?.groupServicesByCategory ?? true;
226
226
  const renderServiceCard = config?.renderServiceCard;
227
+ const showStepIndicator = config?.showStepIndicator ?? true;
228
+ const deeplink = config?.deeplink;
229
+ const deeplinkGuest = config?.deeplinkGuest;
230
+ const onStepChange = config?.onStepChange;
231
+ const onBookingComplete = config?.onBookingComplete;
232
+ const onError = config?.onError;
227
233
  const cssVars = react.useMemo(() => themeToCSS(theme), [theme]);
228
234
  const value = react.useMemo(
229
235
  () => ({
@@ -250,9 +256,15 @@ function ResiraProvider({
250
256
  serviceLayout,
251
257
  visibleServiceCount,
252
258
  groupServicesByCategory,
253
- renderServiceCard
259
+ renderServiceCard,
260
+ showStepIndicator,
261
+ deeplink,
262
+ deeplinkGuest,
263
+ onStepChange,
264
+ onBookingComplete,
265
+ onError
254
266
  }),
255
- [client, resourceId, activeResourceId, setActiveResourceId, catalogMode, allowMultiSelect, domain, theme, locale, domainConfig, stripePublishableKey, termsText, waiverText, showWaiver, showTerms, showRemainingSpots, depositPercent, refundPolicy, onClose, classNames, serviceLayout, visibleServiceCount, groupServicesByCategory, renderServiceCard]
267
+ [client, resourceId, activeResourceId, setActiveResourceId, catalogMode, allowMultiSelect, domain, theme, locale, domainConfig, stripePublishableKey, termsText, waiverText, showWaiver, showTerms, showRemainingSpots, depositPercent, refundPolicy, onClose, classNames, serviceLayout, visibleServiceCount, groupServicesByCategory, renderServiceCard, showStepIndicator, deeplink, deeplinkGuest, onStepChange, onBookingComplete, onError]
256
268
  );
257
269
  return /* @__PURE__ */ jsxRuntime.jsx(ResiraContext.Provider, { value, children: /* @__PURE__ */ jsxRuntime.jsx("div", { className: "resira-root", style: cssVars, children }) });
258
270
  }
@@ -1632,39 +1644,90 @@ function formatCategoryLabel(resourceType) {
1632
1644
  return resourceType.trim().split(/[_\-\s]+/).filter(Boolean).map((segment) => segment.charAt(0).toUpperCase() + segment.slice(1).toLowerCase()).join(" ");
1633
1645
  }
1634
1646
  function groupProductsByCategory(products, resources) {
1635
- const resourceTypeById = new Map(
1636
- resources.map((resource) => [resource.id, resource.resourceType])
1647
+ const resourceById = new Map(
1648
+ resources.map((resource) => [resource.id, resource])
1637
1649
  );
1638
1650
  const groups = /* @__PURE__ */ new Map();
1639
1651
  products.forEach((product) => {
1640
- const categoryType = product.equipmentIds.map((equipmentId) => resourceTypeById.get(equipmentId)?.trim()).find((resourceType) => Boolean(resourceType));
1652
+ let categoryResource;
1653
+ const categoryType = product.equipmentIds.map((equipmentId) => {
1654
+ const res = resourceById.get(equipmentId);
1655
+ if (res?.resourceType?.trim() && !categoryResource) {
1656
+ categoryResource = res;
1657
+ }
1658
+ return res?.resourceType?.trim();
1659
+ }).find((resourceType) => Boolean(resourceType));
1641
1660
  const groupId = categoryType?.toLowerCase() ?? UNCATEGORIZED_CATEGORY_KEY;
1642
1661
  const label = categoryType ? formatCategoryLabel(categoryType) : UNCATEGORIZED_CATEGORY_LABEL;
1643
1662
  if (!groups.has(groupId)) {
1644
1663
  groups.set(groupId, {
1645
1664
  id: groupId,
1646
1665
  label,
1666
+ imageUrl: categoryResource?.imageUrl ?? void 0,
1647
1667
  products: []
1648
1668
  });
1649
1669
  }
1650
- groups.get(groupId)?.products.push(product);
1670
+ const group = groups.get(groupId);
1671
+ group.products.push(product);
1672
+ if (!group.imageUrl && categoryResource?.imageUrl) {
1673
+ group.imageUrl = categoryResource.imageUrl;
1674
+ }
1651
1675
  });
1652
1676
  return Array.from(groups.values());
1653
1677
  }
1654
- function DefaultServiceCard({
1678
+ function BackArrow() {
1679
+ return /* @__PURE__ */ jsxRuntime.jsxs("svg", { width: "18", height: "18", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", children: [
1680
+ /* @__PURE__ */ jsxRuntime.jsx("path", { d: "M19 12H5" }),
1681
+ /* @__PURE__ */ jsxRuntime.jsx("path", { d: "M12 19l-7-7 7-7" })
1682
+ ] });
1683
+ }
1684
+ function CategoryTile({
1685
+ group,
1686
+ onClick
1687
+ }) {
1688
+ return /* @__PURE__ */ jsxRuntime.jsxs(
1689
+ "button",
1690
+ {
1691
+ type: "button",
1692
+ className: "resira-category-tile",
1693
+ onClick,
1694
+ children: [
1695
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "resira-category-tile-image", children: [
1696
+ /* @__PURE__ */ jsxRuntime.jsx(
1697
+ "img",
1698
+ {
1699
+ src: group.imageUrl || PLACEHOLDER_IMG2,
1700
+ alt: group.label,
1701
+ loading: "lazy"
1702
+ }
1703
+ ),
1704
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "resira-category-tile-overlay" })
1705
+ ] }),
1706
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "resira-category-tile-content", children: [
1707
+ /* @__PURE__ */ jsxRuntime.jsx("h3", { className: "resira-category-tile-name", children: group.label }),
1708
+ /* @__PURE__ */ jsxRuntime.jsxs("span", { className: "resira-category-tile-count", children: [
1709
+ group.products.length,
1710
+ " ",
1711
+ group.products.length === 1 ? "service" : "services"
1712
+ ] })
1713
+ ] })
1714
+ ]
1715
+ }
1716
+ );
1717
+ }
1718
+ function ServiceOverlayCard({
1655
1719
  product,
1656
1720
  isSelected,
1657
- layout,
1658
1721
  locale,
1659
1722
  cardClassName
1660
1723
  }) {
1661
1724
  const currency = product.currency ?? "EUR";
1662
1725
  const priceLabel = product.pricingModel === "per_rider" ? "per rider" : product.pricingModel === "per_person" ? locale.perPerson : locale.perSession;
1663
- let className = `resira-service-card resira-service-card--${layout}`;
1664
- if (isSelected) className += " resira-service-card--selected";
1726
+ let className = "resira-service-overlay-card";
1727
+ if (isSelected) className += " resira-service-overlay-card--selected";
1665
1728
  if (cardClassName) className += ` ${cardClassName}`;
1666
1729
  return /* @__PURE__ */ jsxRuntime.jsxs("div", { className, children: [
1667
- /* @__PURE__ */ jsxRuntime.jsx("div", { className: "resira-service-card-image", children: /* @__PURE__ */ jsxRuntime.jsx(
1730
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "resira-service-overlay-card-bg", children: /* @__PURE__ */ jsxRuntime.jsx(
1668
1731
  "img",
1669
1732
  {
1670
1733
  src: product.imageUrl ?? PLACEHOLDER_IMG2,
@@ -1672,30 +1735,27 @@ function DefaultServiceCard({
1672
1735
  loading: "lazy"
1673
1736
  }
1674
1737
  ) }),
1675
- /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "resira-service-card-body", children: [
1676
- /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "resira-service-card-top", children: [
1677
- /* @__PURE__ */ jsxRuntime.jsx("h3", { className: "resira-service-card-name", children: product.name }),
1678
- product.description && /* @__PURE__ */ jsxRuntime.jsx("p", { className: "resira-service-card-desc", children: product.description })
1738
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "resira-service-overlay-card-gradient" }),
1739
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "resira-service-overlay-card-content", children: [
1740
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "resira-service-overlay-card-top", children: [
1741
+ /* @__PURE__ */ jsxRuntime.jsx("h3", { className: "resira-service-overlay-card-name", children: product.name }),
1742
+ product.description && /* @__PURE__ */ jsxRuntime.jsx("p", { className: "resira-service-overlay-card-desc", children: product.description })
1679
1743
  ] }),
1680
- /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "resira-service-card-bottom", children: [
1681
- /* @__PURE__ */ jsxRuntime.jsxs("span", { className: "resira-service-card-price", children: [
1744
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "resira-service-overlay-card-bottom", children: [
1745
+ /* @__PURE__ */ jsxRuntime.jsxs("span", { className: "resira-service-overlay-card-price", children: [
1682
1746
  formatPrice2(product.priceCents, currency),
1683
- /* @__PURE__ */ jsxRuntime.jsxs("span", { className: "resira-service-card-price-unit", children: [
1747
+ /* @__PURE__ */ jsxRuntime.jsxs("span", { className: "resira-service-overlay-card-price-unit", children: [
1684
1748
  "/",
1685
1749
  priceLabel
1686
1750
  ] })
1687
1751
  ] }),
1688
- /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "resira-service-card-pills", children: [
1689
- product.durationMinutes > 0 && /* @__PURE__ */ jsxRuntime.jsxs("span", { className: "resira-service-card-pill", children: [
1690
- /* @__PURE__ */ jsxRuntime.jsx(ClockIcon, { size: 12 }),
1752
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "resira-service-overlay-card-pills", children: [
1753
+ product.durationMinutes > 0 && /* @__PURE__ */ jsxRuntime.jsxs("span", { className: "resira-service-overlay-card-pill", children: [
1754
+ /* @__PURE__ */ jsxRuntime.jsx(ClockIcon, { size: 11 }),
1691
1755
  formatDuration2(product.durationMinutes)
1692
1756
  ] }),
1693
- product.pricingModel === "per_person" && /* @__PURE__ */ jsxRuntime.jsxs("span", { className: "resira-service-card-pill", children: [
1694
- /* @__PURE__ */ jsxRuntime.jsx(UsersIcon, { size: 12 }),
1695
- product.maxPartySize ? `1\u2013${product.maxPartySize}` : locale.perPerson
1696
- ] }),
1697
- product.maxPartySize && product.pricingModel !== "per_person" && /* @__PURE__ */ jsxRuntime.jsxs("span", { className: "resira-service-card-pill", children: [
1698
- /* @__PURE__ */ jsxRuntime.jsx(UsersIcon, { size: 12 }),
1757
+ product.maxPartySize && /* @__PURE__ */ jsxRuntime.jsxs("span", { className: "resira-service-overlay-card-pill", children: [
1758
+ /* @__PURE__ */ jsxRuntime.jsx(UsersIcon, { size: 11 }),
1699
1759
  "max ",
1700
1760
  product.maxPartySize
1701
1761
  ] })
@@ -1723,7 +1783,8 @@ function ProductSelector({
1723
1783
  error = null,
1724
1784
  layout: layoutProp,
1725
1785
  visibleCount: visibleCountProp,
1726
- groupByCategory: groupByCategoryProp
1786
+ groupByCategory: groupByCategoryProp,
1787
+ initialCategory
1727
1788
  }) {
1728
1789
  const {
1729
1790
  locale,
@@ -1737,21 +1798,37 @@ function ProductSelector({
1737
1798
  const visibleCount = visibleCountProp ?? providerCount;
1738
1799
  const groupByCategory = groupByCategoryProp ?? providerGroupByCategory;
1739
1800
  const containerRef = react.useRef(null);
1801
+ const [activeCategory, setActiveCategory] = react.useState(initialCategory ?? null);
1740
1802
  const productGroups = react.useMemo(
1741
- () => groupByCategory ? groupProductsByCategory(products, resources) : [{ id: "all", label: "", products }],
1803
+ () => groupByCategory ? groupProductsByCategory(products, resources) : [{ id: "all", label: "", imageUrl: void 0, products }],
1742
1804
  [groupByCategory, products, resources]
1743
1805
  );
1744
- const containerStyle = react.useMemo(() => {
1745
- if (layout === "horizontal") {
1746
- return {};
1806
+ const skipCategoryView = productGroups.length <= 1;
1807
+ react.useEffect(() => {
1808
+ if (initialCategory && productGroups.some((g) => g.id === initialCategory)) {
1809
+ setActiveCategory(initialCategory);
1810
+ }
1811
+ }, [initialCategory, productGroups]);
1812
+ react.useEffect(() => {
1813
+ if (activeCategory && !productGroups.some((g) => g.id === activeCategory)) {
1814
+ setActiveCategory(null);
1747
1815
  }
1748
- const cardHeight = 88;
1749
- const gap = 10;
1750
- const sectionHeaderHeight = groupByCategory ? productGroups.length * 30 : 0;
1751
- const sectionGap = groupByCategory ? Math.max(productGroups.length - 1, 0) * 12 : 0;
1752
- const maxHeight = visibleCount * cardHeight + (visibleCount - 1) * gap + sectionHeaderHeight + sectionGap;
1816
+ }, [activeCategory, productGroups]);
1817
+ const activeCategoryGroup = react.useMemo(
1818
+ () => productGroups.find((g) => g.id === activeCategory) ?? null,
1819
+ [productGroups, activeCategory]
1820
+ );
1821
+ const displayProducts = react.useMemo(() => {
1822
+ if (skipCategoryView) return products;
1823
+ return activeCategoryGroup?.products ?? [];
1824
+ }, [skipCategoryView, products, activeCategoryGroup]);
1825
+ const containerStyle = react.useMemo(() => {
1826
+ if (layout === "horizontal") return {};
1827
+ const cardHeight = 180;
1828
+ const gap = 12;
1829
+ const maxHeight = visibleCount * cardHeight + (visibleCount - 1) * gap;
1753
1830
  return { maxHeight, overflowY: "auto" };
1754
- }, [groupByCategory, layout, productGroups.length, visibleCount]);
1831
+ }, [layout, visibleCount]);
1755
1832
  if (loading) {
1756
1833
  return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "resira-loading", role: "status", "aria-live": "polite", "aria-busy": "true", children: [
1757
1834
  /* @__PURE__ */ jsxRuntime.jsx("div", { className: "resira-spinner", "aria-hidden": "true" }),
@@ -1764,54 +1841,64 @@ function ProductSelector({
1764
1841
  if (products.length === 0) {
1765
1842
  return /* @__PURE__ */ jsxRuntime.jsx("div", { className: "resira-empty", children: /* @__PURE__ */ jsxRuntime.jsx("p", { children: "No services available at the moment." }) });
1766
1843
  }
1844
+ if (groupByCategory && !skipCategoryView && !activeCategory) {
1845
+ return /* @__PURE__ */ jsxRuntime.jsx("div", { className: "resira-service-picker", children: /* @__PURE__ */ jsxRuntime.jsx("div", { className: "resira-category-grid", children: productGroups.map((group) => /* @__PURE__ */ jsxRuntime.jsx(
1846
+ CategoryTile,
1847
+ {
1848
+ group,
1849
+ onClick: () => setActiveCategory(group.id)
1850
+ },
1851
+ group.id
1852
+ )) }) });
1853
+ }
1767
1854
  const listClassName = [
1768
- "resira-service-list",
1769
- `resira-service-list--${layout}`,
1855
+ "resira-service-overlay-list",
1770
1856
  classNames.serviceList
1771
1857
  ].filter(Boolean).join(" ");
1772
- const groupedListClassName = [
1773
- listClassName,
1774
- groupByCategory ? "resira-service-list--grouped" : ""
1775
- ].filter(Boolean).join(" ");
1776
1858
  return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "resira-service-picker", children: [
1859
+ groupByCategory && !skipCategoryView && activeCategory && /* @__PURE__ */ jsxRuntime.jsxs(
1860
+ "button",
1861
+ {
1862
+ type: "button",
1863
+ className: "resira-category-back",
1864
+ onClick: () => setActiveCategory(null),
1865
+ children: [
1866
+ /* @__PURE__ */ jsxRuntime.jsx(BackArrow, {}),
1867
+ /* @__PURE__ */ jsxRuntime.jsx("span", { children: activeCategoryGroup?.label ?? "All categories" })
1868
+ ]
1869
+ }
1870
+ ),
1777
1871
  /* @__PURE__ */ jsxRuntime.jsx(
1778
1872
  "div",
1779
1873
  {
1780
1874
  ref: containerRef,
1781
- className: groupByCategory ? "resira-service-group-list" : listClassName,
1875
+ className: listClassName,
1782
1876
  style: containerStyle,
1783
- children: productGroups.map((group) => /* @__PURE__ */ jsxRuntime.jsxs("section", { className: "resira-service-group", "aria-label": group.label || locale.chooseService, children: [
1784
- groupByCategory && group.label && /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "resira-service-group-header", children: [
1785
- /* @__PURE__ */ jsxRuntime.jsx("h3", { className: "resira-service-group-title", children: group.label }),
1786
- /* @__PURE__ */ jsxRuntime.jsx("span", { className: "resira-service-group-count", children: group.products.length })
1787
- ] }),
1788
- /* @__PURE__ */ jsxRuntime.jsx("div", { className: groupByCategory ? groupedListClassName : listClassName, children: group.products.map((product) => {
1789
- const isSelected = selectedId === product.id;
1790
- return /* @__PURE__ */ jsxRuntime.jsx(
1791
- "button",
1792
- {
1793
- type: "button",
1794
- className: "resira-service-card-btn",
1795
- onClick: () => onSelect(product),
1796
- "aria-pressed": isSelected,
1797
- children: renderServiceCard ? renderServiceCard(product, isSelected) : /* @__PURE__ */ jsxRuntime.jsx(
1798
- DefaultServiceCard,
1799
- {
1800
- product,
1801
- isSelected,
1802
- layout,
1803
- locale,
1804
- cardClassName: isSelected ? classNames.serviceCardSelected : classNames.serviceCard
1805
- }
1806
- )
1807
- },
1808
- product.id
1809
- );
1810
- }) })
1811
- ] }, group.id))
1877
+ children: displayProducts.map((product) => {
1878
+ const isSelected = selectedId === product.id;
1879
+ return /* @__PURE__ */ jsxRuntime.jsx(
1880
+ "button",
1881
+ {
1882
+ type: "button",
1883
+ className: "resira-service-card-btn",
1884
+ onClick: () => onSelect(product),
1885
+ "aria-pressed": isSelected,
1886
+ children: renderServiceCard ? renderServiceCard(product, isSelected) : /* @__PURE__ */ jsxRuntime.jsx(
1887
+ ServiceOverlayCard,
1888
+ {
1889
+ product,
1890
+ isSelected,
1891
+ locale,
1892
+ cardClassName: isSelected ? classNames.serviceCardSelected : classNames.serviceCard
1893
+ }
1894
+ )
1895
+ },
1896
+ product.id
1897
+ );
1898
+ })
1812
1899
  }
1813
1900
  ),
1814
- layout === "vertical" && products.length > visibleCount && /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "resira-service-scroll-hint", children: [
1901
+ displayProducts.length > visibleCount && /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "resira-service-scroll-hint", children: [
1815
1902
  /* @__PURE__ */ jsxRuntime.jsx("span", { children: "Scroll for more" }),
1816
1903
  /* @__PURE__ */ jsxRuntime.jsx("svg", { width: "12", height: "12", viewBox: "0 0 16 16", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", children: /* @__PURE__ */ jsxRuntime.jsx("path", { d: "M4 6l4 4 4-4" }) })
1817
1904
  ] })
@@ -2606,7 +2693,13 @@ function ResiraBookingWidget() {
2606
2693
  showWaiver,
2607
2694
  showRemainingSpots,
2608
2695
  depositPercent,
2609
- onClose
2696
+ onClose,
2697
+ showStepIndicator,
2698
+ deeplink,
2699
+ deeplinkGuest,
2700
+ onStepChange,
2701
+ onBookingComplete,
2702
+ onError
2610
2703
  } = useResira();
2611
2704
  const isDateBased = domain === "rental";
2612
2705
  const isTimeBased = domain === "restaurant" || domain === "watersport" || domain === "service";
@@ -2964,8 +3057,9 @@ function ResiraBookingWidget() {
2964
3057
  setStep("confirmation");
2965
3058
  }
2966
3059
  }, [createPayment, paymentPayload, confirmPayment]);
2967
- const handlePaymentError = react.useCallback((_msg) => {
2968
- }, []);
3060
+ const handlePaymentError = react.useCallback((msg) => {
3061
+ onError?.("payment_error", msg);
3062
+ }, [onError]);
2969
3063
  const handleSubmitNoPayment = react.useCallback(async () => {
2970
3064
  if (showTerms && !termsAccepted) {
2971
3065
  setTermsError(locale.termsRequired);
@@ -2995,9 +3089,63 @@ function ResiraBookingWidget() {
2995
3089
  }, [guest, selection, locale, submit, termsAccepted, waiverAccepted, showTerms, showWaiver, activeResourceId, selectedProduct]);
2996
3090
  const footerBusy = submitting || creatingPayment || confirmingPayment || paymentFormSubmitting;
2997
3091
  const paymentActionDisabled = !paymentIntent || !paymentFormReady || paymentFormSubmitting || confirmingPayment;
3092
+ const [deeplinkApplied, setDeeplinkApplied] = react.useState(false);
3093
+ react.useEffect(() => {
3094
+ if (deeplinkApplied || !deeplink) return;
3095
+ if (deeplink.productId && products.length > 0 && !selectedProduct) {
3096
+ const target = products.find((p) => p.id === deeplink.productId);
3097
+ if (target) {
3098
+ handleProductSelect(target);
3099
+ }
3100
+ }
3101
+ if (deeplink.partySize || deeplink.duration || deeplink.date) {
3102
+ setSelection((prev) => ({
3103
+ ...prev,
3104
+ ...deeplink.partySize ? { partySize: deeplink.partySize } : {},
3105
+ ...deeplink.duration ? { duration: deeplink.duration } : {},
3106
+ ...deeplink.date ? { startDate: deeplink.date } : {}
3107
+ }));
3108
+ if (deeplink.date) setSlotDate(deeplink.date);
3109
+ }
3110
+ if (deeplinkGuest) {
3111
+ setGuest((prev) => ({
3112
+ guestName: deeplinkGuest.name ?? prev.guestName,
3113
+ guestEmail: deeplinkGuest.email ?? prev.guestEmail,
3114
+ guestPhone: deeplinkGuest.phone ?? prev.guestPhone,
3115
+ notes: deeplinkGuest.notes ?? prev.notes
3116
+ }));
3117
+ }
3118
+ if (deeplink.productId && step === "resource" && isServiceBased) {
3119
+ const target = products.find((p) => p.id === deeplink.productId);
3120
+ if (target) {
3121
+ setStep(STEPS[stepIndex("resource", STEPS) + 1]);
3122
+ }
3123
+ }
3124
+ setDeeplinkApplied(true);
3125
+ }, [deeplink, deeplinkGuest, products, selectedProduct, step, isServiceBased, STEPS, handleProductSelect]);
3126
+ react.useEffect(() => {
3127
+ onStepChange?.(step);
3128
+ }, [step, onStepChange]);
3129
+ react.useEffect(() => {
3130
+ if (step === "confirmation") {
3131
+ const bookingId = reservation?.id ?? paymentIntent?.reservationId;
3132
+ onBookingComplete?.({
3133
+ reservationId: bookingId,
3134
+ product: selectedProduct ?? void 0,
3135
+ selection,
3136
+ guest
3137
+ });
3138
+ }
3139
+ }, [step]);
3140
+ react.useEffect(() => {
3141
+ if (submitError) onError?.("submit_error", submitError);
3142
+ }, [submitError, onError]);
3143
+ react.useEffect(() => {
3144
+ if (paymentError) onError?.("payment_error", paymentError);
3145
+ }, [paymentError, onError]);
2998
3146
  return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "resira-widget", children: [
2999
3147
  step !== "confirmation" && /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "resira-widget-topbar", children: [
3000
- /* @__PURE__ */ jsxRuntime.jsx("nav", { className: "resira-steps", "aria-label": "Booking steps", children: STEPS.filter((s) => s !== "confirmation").map((s, i) => {
3148
+ showStepIndicator && /* @__PURE__ */ jsxRuntime.jsx("nav", { className: "resira-steps", "aria-label": "Booking steps", children: STEPS.filter((s) => s !== "confirmation").map((s, i) => {
3001
3149
  const isCompleted = stepIndex(step, STEPS) > i;
3002
3150
  const isActive = s === step;
3003
3151
  return /* @__PURE__ */ jsxRuntime.jsxs(