@resira/ui 0.4.6 → 0.4.8

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.d.cts CHANGED
@@ -1,7 +1,7 @@
1
1
  import * as react_jsx_runtime from 'react/jsx-runtime';
2
2
  import React from 'react';
3
3
  import { Product, WeightedBaseUrl, Resira, RefundRule, TimeSlotAvailability, DurationPrice, PublicResource, ValidatePromoCodeResponse, Reservation, AvailabilityParams, Availability, CheckoutSession, Dish, PaymentIntentResponse, CreatePaymentIntentRequest, CreateReservationRequest } from '@resira/sdk';
4
- export { CheckoutConfig, CheckoutSession, CheckoutSessionResponse, ConfirmPaymentRequest, ConfirmPaymentResponse, CreateCheckoutSessionRequest, CreateCheckoutSessionResponse, CreatePaymentIntentRequest, Dish, DishListResponse, DishModel, DishResponse, PaymentIntentResponse, PricingModel, Product, ProductListResponse, ProductVariant, PublicResource, ResourceListResponse } from '@resira/sdk';
4
+ export { CheckoutConfig, CheckoutSession, CheckoutSessionResponse, ConfirmPaymentRequest, ConfirmPaymentResponse, CreateCheckoutSessionRequest, CreateCheckoutSessionResponse, CreatePaymentIntentRequest, Dish, DishListResponse, DishModel, DishResponse, DurationPrice, PaymentIntentResponse, PricingModel, Product, ProductCategory, ProductImage, ProductListResponse, ProductVariant, PublicResource, ResourceListResponse, RiderTierPrice } from '@resira/sdk';
5
5
 
6
6
  type ResiraDomain = "rental" | "restaurant" | "watersport" | "service";
7
7
  /** Layout direction for the product/service selector. */
@@ -790,6 +790,119 @@ interface UseCheckoutSessionReturn {
790
790
  * ```
791
791
  */
792
792
  declare function useCheckoutSession(token: string | undefined): UseCheckoutSessionReturn;
793
+ /** A single duration/price option for a service. */
794
+ interface ServiceOption {
795
+ /** Duration in minutes. */
796
+ durationMinutes: number;
797
+ /** Human-readable duration label (e.g. "1h 30m"). */
798
+ durationLabel: string;
799
+ /** Price in minor units (cents). */
800
+ priceCents: number;
801
+ /** Formatted price string (e.g. "€50.00"). */
802
+ priceFormatted: string;
803
+ }
804
+ /** Enriched service data ready for display. */
805
+ interface Service {
806
+ /** Unique service ID. */
807
+ id: string;
808
+ /** Service name. */
809
+ name: string;
810
+ /** Optional description. */
811
+ description?: string;
812
+ /** Image URL. */
813
+ imageUrl?: string;
814
+ /** ISO 4217 currency code. */
815
+ currency: string;
816
+ /** Whether the service is active. */
817
+ active: boolean;
818
+ /** Pricing model: "per_session" | "per_person" | "per_rider". */
819
+ pricingModel: string;
820
+ /** Default/base price in minor units. */
821
+ priceCents: number;
822
+ /** Formatted base price (e.g. "€50.00"). */
823
+ priceFormatted: string;
824
+ /** Lowest price across all options (for "from €X" display). */
825
+ lowestPriceCents: number;
826
+ /** Formatted lowest price. */
827
+ lowestPriceFormatted: string;
828
+ /** Whether multiple duration/price options exist. */
829
+ hasMultipleOptions: boolean;
830
+ /** Default duration in minutes. */
831
+ durationMinutes: number;
832
+ /** Human-readable default duration (e.g. "2h"). */
833
+ durationLabel: string;
834
+ /** All available duration/price options. */
835
+ options: ServiceOption[];
836
+ /** Maximum party/group size. */
837
+ maxPartySize?: number;
838
+ /** IDs of linked equipment/resources. */
839
+ equipmentIds: string[];
840
+ /** Names of linked equipment/resources. */
841
+ equipmentNames?: string[];
842
+ /** Display color. */
843
+ serviceColor?: string;
844
+ /** Sort order. */
845
+ sortOrder?: number;
846
+ /** Raw product object from the API. */
847
+ raw: Product;
848
+ }
849
+ interface UseServicesReturn {
850
+ /** Enriched service list. */
851
+ services: Service[];
852
+ /** Whether loading. */
853
+ loading: boolean;
854
+ /** Error message. */
855
+ error: string | null;
856
+ /** Re-fetch services. */
857
+ refetch: () => void;
858
+ }
859
+ /**
860
+ * Fetch all services for the organisation with enriched display data.
861
+ *
862
+ * Returns formatted prices, duration labels, and "from €X" logic
863
+ * so you can render service cards with your own styling.
864
+ *
865
+ * Requires `<ResiraProvider>` ancestor.
866
+ *
867
+ * ```tsx
868
+ * const { services, loading, error } = useServices();
869
+ *
870
+ * return services.map(s => (
871
+ * <div key={s.id}>
872
+ * <h3>{s.name}</h3>
873
+ * <img src={s.imageUrl} alt={s.name} />
874
+ * <p>{s.hasMultipleOptions ? `from ${s.lowestPriceFormatted}` : s.priceFormatted}</p>
875
+ * <p>{s.durationLabel}</p>
876
+ * {s.options.map(opt => (
877
+ * <span key={opt.durationMinutes}>{opt.durationLabel} — {opt.priceFormatted}</span>
878
+ * ))}
879
+ * </div>
880
+ * ));
881
+ * ```
882
+ */
883
+ declare function useServices(): UseServicesReturn;
884
+ /**
885
+ * Fetch all services for an organisation — standalone, no React needed.
886
+ *
887
+ * Use this in non-React contexts, server components, scripts, or
888
+ * when you want full control over rendering.
889
+ *
890
+ * ```ts
891
+ * import { fetchServices } from "@resira/ui";
892
+ *
893
+ * const services = await fetchServices("your-api-key");
894
+ * services.forEach(s => {
895
+ * console.log(s.name, s.lowestPriceFormatted, s.options);
896
+ * });
897
+ * ```
898
+ *
899
+ * @param apiKey Your Resira public API key
900
+ * @param opts Optional: baseUrl override
901
+ * @returns Array of enriched Service objects
902
+ */
903
+ declare function fetchServices(apiKey: string, opts?: {
904
+ baseUrl?: string;
905
+ }): Promise<Service[]>;
793
906
 
794
907
  /** Default theme values. */
795
908
  declare const DEFAULT_THEME: Required<ResiraTheme>;
@@ -829,4 +942,4 @@ declare function TagIcon({ size, className }: IconProps): react_jsx_runtime.JSX.
829
942
  declare function CubeIcon({ size, className }: IconProps): react_jsx_runtime.JSX.Element;
830
943
  declare function ViewfinderIcon({ size, className }: IconProps): react_jsx_runtime.JSX.Element;
831
944
 
832
- export { AlertCircleIcon, BookingCalendar, BookingModal, type BookingSelection, type BookingStep, CalendarIcon, CheckCircleIcon, CheckIcon, type CheckoutSessionErrorCode, ChevronLeftIcon, ChevronRightIcon, ClockIcon, ConfirmationView, CreditCardIcon, CubeIcon, DEFAULT_LOCALE, DEFAULT_THEME, type DeeplinkGuest, type DeeplinkSelection, DishShowcase, type DishShowcaseProps, type DomainConfig, GuestForm, type GuestFormErrors, type GuestFormValues, LockIcon, MailIcon, MinusIcon, NoteIcon, PaymentForm, PhoneIcon, PlusIcon, ProductSelector, ResiraBookingWidget, type ResiraClassNames, type ResiraContextValue, type ResiraDomain, type ResiraLocale, ResiraProvider, type ResiraProviderConfig, type ResiraProviderProps, type ResiraTheme, ResourcePicker, type ServiceLayout, ShieldIcon, SummaryPreview, TagIcon, TimeSlotPicker, UserIcon, UsersIcon, ViewfinderIcon, WaiverConsent, XIcon, resolveTheme, themeToCSS, useAvailability, useCheckoutSession, useDish, useDishes, usePaymentIntent, useProducts, useReservation, useResira, useResources, validateGuestForm };
945
+ export { AlertCircleIcon, BookingCalendar, BookingModal, type BookingSelection, type BookingStep, CalendarIcon, CheckCircleIcon, CheckIcon, type CheckoutSessionErrorCode, ChevronLeftIcon, ChevronRightIcon, ClockIcon, ConfirmationView, CreditCardIcon, CubeIcon, DEFAULT_LOCALE, DEFAULT_THEME, type DeeplinkGuest, type DeeplinkSelection, DishShowcase, type DishShowcaseProps, type DomainConfig, GuestForm, type GuestFormErrors, type GuestFormValues, LockIcon, MailIcon, MinusIcon, NoteIcon, PaymentForm, PhoneIcon, PlusIcon, ProductSelector, ResiraBookingWidget, type ResiraClassNames, type ResiraContextValue, type ResiraDomain, type ResiraLocale, ResiraProvider, type ResiraProviderConfig, type ResiraProviderProps, type ResiraTheme, ResourcePicker, type Service, type ServiceLayout, type ServiceOption, ShieldIcon, SummaryPreview, TagIcon, TimeSlotPicker, type UseServicesReturn, UserIcon, UsersIcon, ViewfinderIcon, WaiverConsent, XIcon, fetchServices, resolveTheme, themeToCSS, useAvailability, useCheckoutSession, useDish, useDishes, usePaymentIntent, useProducts, useReservation, useResira, useResources, useServices, validateGuestForm };
package/dist/index.d.ts CHANGED
@@ -1,7 +1,7 @@
1
1
  import * as react_jsx_runtime from 'react/jsx-runtime';
2
2
  import React from 'react';
3
3
  import { Product, WeightedBaseUrl, Resira, RefundRule, TimeSlotAvailability, DurationPrice, PublicResource, ValidatePromoCodeResponse, Reservation, AvailabilityParams, Availability, CheckoutSession, Dish, PaymentIntentResponse, CreatePaymentIntentRequest, CreateReservationRequest } from '@resira/sdk';
4
- export { CheckoutConfig, CheckoutSession, CheckoutSessionResponse, ConfirmPaymentRequest, ConfirmPaymentResponse, CreateCheckoutSessionRequest, CreateCheckoutSessionResponse, CreatePaymentIntentRequest, Dish, DishListResponse, DishModel, DishResponse, PaymentIntentResponse, PricingModel, Product, ProductListResponse, ProductVariant, PublicResource, ResourceListResponse } from '@resira/sdk';
4
+ export { CheckoutConfig, CheckoutSession, CheckoutSessionResponse, ConfirmPaymentRequest, ConfirmPaymentResponse, CreateCheckoutSessionRequest, CreateCheckoutSessionResponse, CreatePaymentIntentRequest, Dish, DishListResponse, DishModel, DishResponse, DurationPrice, PaymentIntentResponse, PricingModel, Product, ProductCategory, ProductImage, ProductListResponse, ProductVariant, PublicResource, ResourceListResponse, RiderTierPrice } from '@resira/sdk';
5
5
 
6
6
  type ResiraDomain = "rental" | "restaurant" | "watersport" | "service";
7
7
  /** Layout direction for the product/service selector. */
@@ -790,6 +790,119 @@ interface UseCheckoutSessionReturn {
790
790
  * ```
791
791
  */
792
792
  declare function useCheckoutSession(token: string | undefined): UseCheckoutSessionReturn;
793
+ /** A single duration/price option for a service. */
794
+ interface ServiceOption {
795
+ /** Duration in minutes. */
796
+ durationMinutes: number;
797
+ /** Human-readable duration label (e.g. "1h 30m"). */
798
+ durationLabel: string;
799
+ /** Price in minor units (cents). */
800
+ priceCents: number;
801
+ /** Formatted price string (e.g. "€50.00"). */
802
+ priceFormatted: string;
803
+ }
804
+ /** Enriched service data ready for display. */
805
+ interface Service {
806
+ /** Unique service ID. */
807
+ id: string;
808
+ /** Service name. */
809
+ name: string;
810
+ /** Optional description. */
811
+ description?: string;
812
+ /** Image URL. */
813
+ imageUrl?: string;
814
+ /** ISO 4217 currency code. */
815
+ currency: string;
816
+ /** Whether the service is active. */
817
+ active: boolean;
818
+ /** Pricing model: "per_session" | "per_person" | "per_rider". */
819
+ pricingModel: string;
820
+ /** Default/base price in minor units. */
821
+ priceCents: number;
822
+ /** Formatted base price (e.g. "€50.00"). */
823
+ priceFormatted: string;
824
+ /** Lowest price across all options (for "from €X" display). */
825
+ lowestPriceCents: number;
826
+ /** Formatted lowest price. */
827
+ lowestPriceFormatted: string;
828
+ /** Whether multiple duration/price options exist. */
829
+ hasMultipleOptions: boolean;
830
+ /** Default duration in minutes. */
831
+ durationMinutes: number;
832
+ /** Human-readable default duration (e.g. "2h"). */
833
+ durationLabel: string;
834
+ /** All available duration/price options. */
835
+ options: ServiceOption[];
836
+ /** Maximum party/group size. */
837
+ maxPartySize?: number;
838
+ /** IDs of linked equipment/resources. */
839
+ equipmentIds: string[];
840
+ /** Names of linked equipment/resources. */
841
+ equipmentNames?: string[];
842
+ /** Display color. */
843
+ serviceColor?: string;
844
+ /** Sort order. */
845
+ sortOrder?: number;
846
+ /** Raw product object from the API. */
847
+ raw: Product;
848
+ }
849
+ interface UseServicesReturn {
850
+ /** Enriched service list. */
851
+ services: Service[];
852
+ /** Whether loading. */
853
+ loading: boolean;
854
+ /** Error message. */
855
+ error: string | null;
856
+ /** Re-fetch services. */
857
+ refetch: () => void;
858
+ }
859
+ /**
860
+ * Fetch all services for the organisation with enriched display data.
861
+ *
862
+ * Returns formatted prices, duration labels, and "from €X" logic
863
+ * so you can render service cards with your own styling.
864
+ *
865
+ * Requires `<ResiraProvider>` ancestor.
866
+ *
867
+ * ```tsx
868
+ * const { services, loading, error } = useServices();
869
+ *
870
+ * return services.map(s => (
871
+ * <div key={s.id}>
872
+ * <h3>{s.name}</h3>
873
+ * <img src={s.imageUrl} alt={s.name} />
874
+ * <p>{s.hasMultipleOptions ? `from ${s.lowestPriceFormatted}` : s.priceFormatted}</p>
875
+ * <p>{s.durationLabel}</p>
876
+ * {s.options.map(opt => (
877
+ * <span key={opt.durationMinutes}>{opt.durationLabel} — {opt.priceFormatted}</span>
878
+ * ))}
879
+ * </div>
880
+ * ));
881
+ * ```
882
+ */
883
+ declare function useServices(): UseServicesReturn;
884
+ /**
885
+ * Fetch all services for an organisation — standalone, no React needed.
886
+ *
887
+ * Use this in non-React contexts, server components, scripts, or
888
+ * when you want full control over rendering.
889
+ *
890
+ * ```ts
891
+ * import { fetchServices } from "@resira/ui";
892
+ *
893
+ * const services = await fetchServices("your-api-key");
894
+ * services.forEach(s => {
895
+ * console.log(s.name, s.lowestPriceFormatted, s.options);
896
+ * });
897
+ * ```
898
+ *
899
+ * @param apiKey Your Resira public API key
900
+ * @param opts Optional: baseUrl override
901
+ * @returns Array of enriched Service objects
902
+ */
903
+ declare function fetchServices(apiKey: string, opts?: {
904
+ baseUrl?: string;
905
+ }): Promise<Service[]>;
793
906
 
794
907
  /** Default theme values. */
795
908
  declare const DEFAULT_THEME: Required<ResiraTheme>;
@@ -829,4 +942,4 @@ declare function TagIcon({ size, className }: IconProps): react_jsx_runtime.JSX.
829
942
  declare function CubeIcon({ size, className }: IconProps): react_jsx_runtime.JSX.Element;
830
943
  declare function ViewfinderIcon({ size, className }: IconProps): react_jsx_runtime.JSX.Element;
831
944
 
832
- export { AlertCircleIcon, BookingCalendar, BookingModal, type BookingSelection, type BookingStep, CalendarIcon, CheckCircleIcon, CheckIcon, type CheckoutSessionErrorCode, ChevronLeftIcon, ChevronRightIcon, ClockIcon, ConfirmationView, CreditCardIcon, CubeIcon, DEFAULT_LOCALE, DEFAULT_THEME, type DeeplinkGuest, type DeeplinkSelection, DishShowcase, type DishShowcaseProps, type DomainConfig, GuestForm, type GuestFormErrors, type GuestFormValues, LockIcon, MailIcon, MinusIcon, NoteIcon, PaymentForm, PhoneIcon, PlusIcon, ProductSelector, ResiraBookingWidget, type ResiraClassNames, type ResiraContextValue, type ResiraDomain, type ResiraLocale, ResiraProvider, type ResiraProviderConfig, type ResiraProviderProps, type ResiraTheme, ResourcePicker, type ServiceLayout, ShieldIcon, SummaryPreview, TagIcon, TimeSlotPicker, UserIcon, UsersIcon, ViewfinderIcon, WaiverConsent, XIcon, resolveTheme, themeToCSS, useAvailability, useCheckoutSession, useDish, useDishes, usePaymentIntent, useProducts, useReservation, useResira, useResources, validateGuestForm };
945
+ export { AlertCircleIcon, BookingCalendar, BookingModal, type BookingSelection, type BookingStep, CalendarIcon, CheckCircleIcon, CheckIcon, type CheckoutSessionErrorCode, ChevronLeftIcon, ChevronRightIcon, ClockIcon, ConfirmationView, CreditCardIcon, CubeIcon, DEFAULT_LOCALE, DEFAULT_THEME, type DeeplinkGuest, type DeeplinkSelection, DishShowcase, type DishShowcaseProps, type DomainConfig, GuestForm, type GuestFormErrors, type GuestFormValues, LockIcon, MailIcon, MinusIcon, NoteIcon, PaymentForm, PhoneIcon, PlusIcon, ProductSelector, ResiraBookingWidget, type ResiraClassNames, type ResiraContextValue, type ResiraDomain, type ResiraLocale, ResiraProvider, type ResiraProviderConfig, type ResiraProviderProps, type ResiraTheme, ResourcePicker, type Service, type ServiceLayout, type ServiceOption, ShieldIcon, SummaryPreview, TagIcon, TimeSlotPicker, type UseServicesReturn, UserIcon, UsersIcon, ViewfinderIcon, WaiverConsent, XIcon, fetchServices, resolveTheme, themeToCSS, useAvailability, useCheckoutSession, useDish, useDishes, usePaymentIntent, useProducts, useReservation, useResira, useResources, useServices, validateGuestForm };
package/dist/index.js CHANGED
@@ -650,6 +650,101 @@ function useCheckoutSession(token) {
650
650
  }, [client, token]);
651
651
  return { session, loading, error, errorCode };
652
652
  }
653
+ function formatPriceCentsUtil(cents, currency) {
654
+ return new Intl.NumberFormat("default", { style: "currency", currency }).format(cents / 100);
655
+ }
656
+ function formatDurationUtil(minutes) {
657
+ if (minutes < 60) return `${minutes} min`;
658
+ const h = Math.floor(minutes / 60);
659
+ const m = minutes % 60;
660
+ return m > 0 ? `${h}h ${m}m` : `${h}h`;
661
+ }
662
+ function enrichProduct(product) {
663
+ const currency = product.currency ?? "EUR";
664
+ const options = [];
665
+ if (product.durationPricing?.length) {
666
+ for (const dp of product.durationPricing) {
667
+ options.push({
668
+ durationMinutes: dp.durationMinutes,
669
+ durationLabel: formatDurationUtil(dp.durationMinutes),
670
+ priceCents: dp.priceCents,
671
+ priceFormatted: formatPriceCentsUtil(dp.priceCents, currency)
672
+ });
673
+ }
674
+ } else {
675
+ options.push({
676
+ durationMinutes: product.durationMinutes,
677
+ durationLabel: formatDurationUtil(product.durationMinutes),
678
+ priceCents: product.priceCents,
679
+ priceFormatted: formatPriceCentsUtil(product.priceCents, currency)
680
+ });
681
+ }
682
+ const lowestPriceCents = Math.min(...options.map((o) => o.priceCents));
683
+ return {
684
+ id: product.id,
685
+ name: product.name,
686
+ description: product.description,
687
+ imageUrl: product.imageUrl,
688
+ currency,
689
+ active: product.active,
690
+ pricingModel: product.pricingModel,
691
+ priceCents: product.priceCents,
692
+ priceFormatted: formatPriceCentsUtil(product.priceCents, currency),
693
+ lowestPriceCents,
694
+ lowestPriceFormatted: formatPriceCentsUtil(lowestPriceCents, currency),
695
+ hasMultipleOptions: options.length > 1,
696
+ durationMinutes: product.durationMinutes,
697
+ durationLabel: formatDurationUtil(product.durationMinutes),
698
+ options,
699
+ maxPartySize: product.maxPartySize,
700
+ equipmentIds: product.equipmentIds,
701
+ equipmentNames: product.equipmentNames,
702
+ serviceColor: product.serviceColor,
703
+ sortOrder: product.sortOrder,
704
+ raw: product
705
+ };
706
+ }
707
+ function useServices() {
708
+ const { client } = useResira();
709
+ const [services, setServices] = useState([]);
710
+ const [loading, setLoading] = useState(true);
711
+ const [error, setError] = useState(null);
712
+ const [fetchCount, setFetchCount] = useState(0);
713
+ useEffect(() => {
714
+ let cancelled = false;
715
+ setLoading(true);
716
+ setError(null);
717
+ async function load() {
718
+ try {
719
+ const data = await client.listProducts();
720
+ if (!cancelled) {
721
+ setServices((data.products ?? []).map(enrichProduct));
722
+ }
723
+ } catch (err) {
724
+ if (!cancelled) {
725
+ setError(err instanceof Error ? err.message : "Failed to load services");
726
+ }
727
+ } finally {
728
+ if (!cancelled) setLoading(false);
729
+ }
730
+ }
731
+ load();
732
+ return () => {
733
+ cancelled = true;
734
+ };
735
+ }, [client, fetchCount]);
736
+ const refetch = useCallback(() => setFetchCount((c) => c + 1), []);
737
+ return { services, loading, error, refetch };
738
+ }
739
+ async function fetchServices(apiKey, opts) {
740
+ const { Resira: Resira2 } = await import('@resira/sdk');
741
+ const client = new Resira2({
742
+ apiKey,
743
+ ...opts?.baseUrl ? { baseUrl: opts.baseUrl } : {}
744
+ });
745
+ const data = await client.listProducts();
746
+ return (data.products ?? []).map(enrichProduct);
747
+ }
653
748
  var defaultSize = 20;
654
749
  function CalendarIcon({ size = defaultSize, className }) {
655
750
  return /* @__PURE__ */ jsxs(
@@ -1874,6 +1969,10 @@ function ServiceOverlayCard({
1874
1969
  }) {
1875
1970
  const currency = product.currency ?? "EUR";
1876
1971
  const priceLabel = product.pricingModel === "per_rider" ? "per rider" : product.pricingModel === "per_person" ? locale.perPerson : locale.perSession;
1972
+ const hasMultipleOptions = (product.durationPricing?.length ?? 0) > 1 || (product.riderTierPricing?.length ?? 0) > 1;
1973
+ const lowestPrice = hasMultipleOptions && product.durationPricing?.length ? Math.min(...product.durationPricing.map((dp) => dp.priceCents)) : product.priceCents;
1974
+ const pricePrefix = hasMultipleOptions ? "from " : "";
1975
+ const optionCount = product.durationPricing?.length ?? (product.riderTierPricing?.length ?? 0);
1877
1976
  let className = "resira-service-overlay-card";
1878
1977
  if (isSelected) className += " resira-service-overlay-card--selected";
1879
1978
  if (cardClassName) className += ` ${cardClassName}`;
@@ -1894,17 +1993,22 @@ function ServiceOverlayCard({
1894
1993
  ] }),
1895
1994
  /* @__PURE__ */ jsxs("div", { className: "resira-service-overlay-card-bottom", children: [
1896
1995
  /* @__PURE__ */ jsxs("span", { className: "resira-service-overlay-card-price", children: [
1897
- formatPrice2(product.priceCents, currency),
1996
+ pricePrefix,
1997
+ formatPrice2(lowestPrice, currency),
1898
1998
  /* @__PURE__ */ jsxs("span", { className: "resira-service-overlay-card-price-unit", children: [
1899
1999
  "/",
1900
2000
  priceLabel
1901
2001
  ] })
1902
2002
  ] }),
1903
2003
  /* @__PURE__ */ jsxs("div", { className: "resira-service-overlay-card-pills", children: [
1904
- product.durationMinutes > 0 && /* @__PURE__ */ jsxs("span", { className: "resira-service-overlay-card-pill", children: [
2004
+ hasMultipleOptions && optionCount > 1 ? /* @__PURE__ */ jsxs("span", { className: "resira-service-overlay-card-pill", children: [
2005
+ /* @__PURE__ */ jsx(ClockIcon, { size: 11 }),
2006
+ optionCount,
2007
+ " options"
2008
+ ] }) : product.durationMinutes > 0 ? /* @__PURE__ */ jsxs("span", { className: "resira-service-overlay-card-pill", children: [
1905
2009
  /* @__PURE__ */ jsx(ClockIcon, { size: 11 }),
1906
2010
  formatDuration2(product.durationMinutes)
1907
- ] }),
2011
+ ] }) : null,
1908
2012
  product.maxPartySize && /* @__PURE__ */ jsxs("span", { className: "resira-service-overlay-card-pill", children: [
1909
2013
  /* @__PURE__ */ jsx(UsersIcon, { size: 11 }),
1910
2014
  "max ",
@@ -2985,10 +3089,10 @@ function ResiraBookingWidget() {
2985
3089
  const currency = selectedProduct?.currency ?? availability?.currency ?? "EUR";
2986
3090
  const activeRiderDurationPricing = useMemo(() => {
2987
3091
  if (selectedProduct?.pricingModel === "per_rider" && selectedProduct.riderTierPricing?.length) {
2988
- const sorted = [...selectedProduct.riderTierPricing].sort((a, b) => a.riders - b.riders);
2989
- const exact = sorted.find((t) => t.riders === selection.partySize);
3092
+ const sorted = [...selectedProduct.riderTierPricing].sort((a, b) => (a.riders ?? a.minRiders ?? 0) - (b.riders ?? b.minRiders ?? 0));
3093
+ const exact = sorted.find((t) => t.riders === selection.partySize || t.minRiders != null && t.maxRiders != null && selection.partySize >= t.minRiders && selection.partySize <= t.maxRiders);
2990
3094
  if (exact) return exact.durationPricing;
2991
- const closest = sorted.find((t) => t.riders >= selection.partySize);
3095
+ const closest = sorted.find((t) => (t.riders ?? t.minRiders ?? 0) >= selection.partySize);
2992
3096
  return closest?.durationPricing ?? sorted[sorted.length - 1]?.durationPricing ?? [];
2993
3097
  }
2994
3098
  return null;
@@ -3121,8 +3225,14 @@ function ResiraBookingWidget() {
3121
3225
  partySize: clampedPartySize
3122
3226
  };
3123
3227
  });
3228
+ if (step === "resource" && isServiceBased) {
3229
+ const nextIdx = stepIndex("resource", STEPS) + 1;
3230
+ if (nextIdx < STEPS.length) {
3231
+ setStep(STEPS[nextIdx]);
3232
+ }
3233
+ }
3124
3234
  },
3125
- [setActiveResourceId, domainConfig.maxPartySize]
3235
+ [setActiveResourceId, domainConfig.maxPartySize, step, isServiceBased, STEPS]
3126
3236
  );
3127
3237
  const handleResourceSelect = useCallback(
3128
3238
  (resourceId) => {
@@ -4304,6 +4414,6 @@ function DishShowcase({
4304
4414
  ] });
4305
4415
  }
4306
4416
 
4307
- export { AlertCircleIcon, BookingCalendar, BookingModal, CalendarIcon, CheckCircleIcon, CheckIcon, ChevronLeftIcon, ChevronRightIcon, ClockIcon, ConfirmationView, CreditCardIcon, CubeIcon, DEFAULT_LOCALE, DEFAULT_THEME, DishShowcase, GuestForm, LockIcon, MailIcon, MinusIcon, NoteIcon, PaymentForm, PhoneIcon, PlusIcon, ProductSelector, ResiraBookingWidget, ResiraProvider, ResourcePicker, ShieldIcon, SummaryPreview, TagIcon, TimeSlotPicker, UserIcon, UsersIcon, ViewfinderIcon, WaiverConsent, XIcon, resolveTheme, themeToCSS, useAvailability, useCheckoutSession, useDish, useDishes, usePaymentIntent, useProducts, useReservation, useResira, useResources, validateGuestForm };
4417
+ export { AlertCircleIcon, BookingCalendar, BookingModal, CalendarIcon, CheckCircleIcon, CheckIcon, ChevronLeftIcon, ChevronRightIcon, ClockIcon, ConfirmationView, CreditCardIcon, CubeIcon, DEFAULT_LOCALE, DEFAULT_THEME, DishShowcase, GuestForm, LockIcon, MailIcon, MinusIcon, NoteIcon, PaymentForm, PhoneIcon, PlusIcon, ProductSelector, ResiraBookingWidget, ResiraProvider, ResourcePicker, ShieldIcon, SummaryPreview, TagIcon, TimeSlotPicker, UserIcon, UsersIcon, ViewfinderIcon, WaiverConsent, XIcon, fetchServices, resolveTheme, themeToCSS, useAvailability, useCheckoutSession, useDish, useDishes, usePaymentIntent, useProducts, useReservation, useResira, useResources, useServices, validateGuestForm };
4308
4418
  //# sourceMappingURL=index.js.map
4309
4419
  //# sourceMappingURL=index.js.map