@resira/ui 0.4.5 → 0.4.6
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/README.md +238 -238
- package/dist/index.cjs +112 -127
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +1 -114
- package/dist/index.d.ts +1 -114
- package/dist/index.js +113 -126
- package/dist/index.js.map +1 -1
- package/dist/styles.css +2972 -2879
- package/package.json +59 -59
package/dist/index.d.cts
CHANGED
|
@@ -790,119 +790,6 @@ 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[]>;
|
|
906
793
|
|
|
907
794
|
/** Default theme values. */
|
|
908
795
|
declare const DEFAULT_THEME: Required<ResiraTheme>;
|
|
@@ -942,4 +829,4 @@ declare function TagIcon({ size, className }: IconProps): react_jsx_runtime.JSX.
|
|
|
942
829
|
declare function CubeIcon({ size, className }: IconProps): react_jsx_runtime.JSX.Element;
|
|
943
830
|
declare function ViewfinderIcon({ size, className }: IconProps): react_jsx_runtime.JSX.Element;
|
|
944
831
|
|
|
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
|
|
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 };
|
package/dist/index.d.ts
CHANGED
|
@@ -790,119 +790,6 @@ 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[]>;
|
|
906
793
|
|
|
907
794
|
/** Default theme values. */
|
|
908
795
|
declare const DEFAULT_THEME: Required<ResiraTheme>;
|
|
@@ -942,4 +829,4 @@ declare function TagIcon({ size, className }: IconProps): react_jsx_runtime.JSX.
|
|
|
942
829
|
declare function CubeIcon({ size, className }: IconProps): react_jsx_runtime.JSX.Element;
|
|
943
830
|
declare function ViewfinderIcon({ size, className }: IconProps): react_jsx_runtime.JSX.Element;
|
|
944
831
|
|
|
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
|
|
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 };
|
package/dist/index.js
CHANGED
|
@@ -650,101 +650,6 @@ 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
|
-
}
|
|
748
653
|
var defaultSize = 20;
|
|
749
654
|
function CalendarIcon({ size = defaultSize, className }) {
|
|
750
655
|
return /* @__PURE__ */ jsxs(
|
|
@@ -1699,6 +1604,99 @@ function formatDuration(minutes) {
|
|
|
1699
1604
|
return `${h}h ${m}m`;
|
|
1700
1605
|
}
|
|
1701
1606
|
var PLACEHOLDER_IMG = "data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='400' height='240' fill='%23e2e8f0'%3E%3Crect width='400' height='240' rx='8'/%3E%3Ctext x='50%25' y='50%25' dominant-baseline='middle' text-anchor='middle' font-family='system-ui' font-size='16' fill='%2394a3b8'%3ENo image%3C/text%3E%3C/svg%3E";
|
|
1607
|
+
function resolveImages(resource) {
|
|
1608
|
+
if (resource.images && resource.images.length > 0) return resource.images;
|
|
1609
|
+
if (resource.imageUrl) return [{ url: resource.imageUrl }];
|
|
1610
|
+
return [];
|
|
1611
|
+
}
|
|
1612
|
+
function ChevronLeftIcon2() {
|
|
1613
|
+
return /* @__PURE__ */ jsx("svg", { width: "14", height: "14", viewBox: "0 0 16 16", fill: "none", children: /* @__PURE__ */ jsx("path", { d: "M10 12L6 8L10 4", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round" }) });
|
|
1614
|
+
}
|
|
1615
|
+
function ChevronRightIcon2() {
|
|
1616
|
+
return /* @__PURE__ */ jsx("svg", { width: "14", height: "14", viewBox: "0 0 16 16", fill: "none", children: /* @__PURE__ */ jsx("path", { d: "M6 4L10 8L6 12", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round" }) });
|
|
1617
|
+
}
|
|
1618
|
+
function ResourceImageCarousel({
|
|
1619
|
+
images,
|
|
1620
|
+
alt
|
|
1621
|
+
}) {
|
|
1622
|
+
const [idx, setIdx] = useState(0);
|
|
1623
|
+
const hasMultiple = images.length > 1;
|
|
1624
|
+
const current = images[idx] ?? images[0];
|
|
1625
|
+
const goPrev = useCallback(
|
|
1626
|
+
(e) => {
|
|
1627
|
+
e.stopPropagation();
|
|
1628
|
+
setIdx((i) => (i - 1 + images.length) % images.length);
|
|
1629
|
+
},
|
|
1630
|
+
[images.length]
|
|
1631
|
+
);
|
|
1632
|
+
const goNext = useCallback(
|
|
1633
|
+
(e) => {
|
|
1634
|
+
e.stopPropagation();
|
|
1635
|
+
setIdx((i) => (i + 1) % images.length);
|
|
1636
|
+
},
|
|
1637
|
+
[images.length]
|
|
1638
|
+
);
|
|
1639
|
+
if (!current) {
|
|
1640
|
+
return /* @__PURE__ */ jsx(
|
|
1641
|
+
"img",
|
|
1642
|
+
{
|
|
1643
|
+
src: PLACEHOLDER_IMG,
|
|
1644
|
+
alt,
|
|
1645
|
+
loading: "lazy"
|
|
1646
|
+
}
|
|
1647
|
+
);
|
|
1648
|
+
}
|
|
1649
|
+
const ds = current.displaySettings;
|
|
1650
|
+
return /* @__PURE__ */ jsxs("div", { className: "resira-resource-carousel", children: [
|
|
1651
|
+
/* @__PURE__ */ jsx(
|
|
1652
|
+
"img",
|
|
1653
|
+
{
|
|
1654
|
+
src: current.url,
|
|
1655
|
+
alt,
|
|
1656
|
+
loading: "lazy",
|
|
1657
|
+
style: {
|
|
1658
|
+
objectFit: ds?.objectFit ?? "cover",
|
|
1659
|
+
objectPosition: ds?.objectPosition ?? "center center",
|
|
1660
|
+
transform: ds?.zoom && ds.zoom !== 1 ? `scale(${ds.zoom})` : void 0
|
|
1661
|
+
}
|
|
1662
|
+
}
|
|
1663
|
+
),
|
|
1664
|
+
hasMultiple && /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
1665
|
+
/* @__PURE__ */ jsx(
|
|
1666
|
+
"button",
|
|
1667
|
+
{
|
|
1668
|
+
type: "button",
|
|
1669
|
+
className: "resira-resource-carousel-btn resira-resource-carousel-prev",
|
|
1670
|
+
onClick: goPrev,
|
|
1671
|
+
"aria-label": "Previous image",
|
|
1672
|
+
children: /* @__PURE__ */ jsx(ChevronLeftIcon2, {})
|
|
1673
|
+
}
|
|
1674
|
+
),
|
|
1675
|
+
/* @__PURE__ */ jsx(
|
|
1676
|
+
"button",
|
|
1677
|
+
{
|
|
1678
|
+
type: "button",
|
|
1679
|
+
className: "resira-resource-carousel-btn resira-resource-carousel-next",
|
|
1680
|
+
onClick: goNext,
|
|
1681
|
+
"aria-label": "Next image",
|
|
1682
|
+
children: /* @__PURE__ */ jsx(ChevronRightIcon2, {})
|
|
1683
|
+
}
|
|
1684
|
+
),
|
|
1685
|
+
/* @__PURE__ */ jsx("div", { className: "resira-resource-carousel-dots", children: images.map((_, i) => /* @__PURE__ */ jsx(
|
|
1686
|
+
"span",
|
|
1687
|
+
{
|
|
1688
|
+
className: `resira-resource-carousel-dot${i === idx ? " resira-resource-carousel-dot--active" : ""}`
|
|
1689
|
+
},
|
|
1690
|
+
i
|
|
1691
|
+
)) }),
|
|
1692
|
+
/* @__PURE__ */ jsxs("span", { className: "resira-resource-carousel-count", children: [
|
|
1693
|
+
idx + 1,
|
|
1694
|
+
"/",
|
|
1695
|
+
images.length
|
|
1696
|
+
] })
|
|
1697
|
+
] })
|
|
1698
|
+
] });
|
|
1699
|
+
}
|
|
1702
1700
|
function ResourcePicker({
|
|
1703
1701
|
resources,
|
|
1704
1702
|
selectedIds,
|
|
@@ -1734,14 +1732,17 @@ function ResourcePicker({
|
|
|
1734
1732
|
onClick: () => onSelect(resource.id),
|
|
1735
1733
|
"aria-pressed": isSelected,
|
|
1736
1734
|
children: [
|
|
1737
|
-
/* @__PURE__ */ jsx("div", { className: "resira-resource-card-image", children:
|
|
1738
|
-
|
|
1739
|
-
{
|
|
1740
|
-
|
|
1741
|
-
|
|
1742
|
-
|
|
1743
|
-
|
|
1744
|
-
|
|
1735
|
+
/* @__PURE__ */ jsx("div", { className: "resira-resource-card-image", children: (() => {
|
|
1736
|
+
const images = resolveImages(resource);
|
|
1737
|
+
return images.length > 0 ? /* @__PURE__ */ jsx(ResourceImageCarousel, { images, alt: resource.name }) : /* @__PURE__ */ jsx(
|
|
1738
|
+
"img",
|
|
1739
|
+
{
|
|
1740
|
+
src: PLACEHOLDER_IMG,
|
|
1741
|
+
alt: resource.name,
|
|
1742
|
+
loading: "lazy"
|
|
1743
|
+
}
|
|
1744
|
+
);
|
|
1745
|
+
})() }),
|
|
1745
1746
|
/* @__PURE__ */ jsxs("div", { className: "resira-resource-card-content", children: [
|
|
1746
1747
|
/* @__PURE__ */ jsx("h3", { className: "resira-resource-card-name", children: resource.name }),
|
|
1747
1748
|
resource.description && /* @__PURE__ */ jsx("p", { className: "resira-resource-card-description", children: resource.description }),
|
|
@@ -1809,17 +1810,18 @@ function groupProductsByCategory(products, resources) {
|
|
|
1809
1810
|
const groupId = categoryType?.toLowerCase() ?? UNCATEGORIZED_CATEGORY_KEY;
|
|
1810
1811
|
const label = categoryType ? formatCategoryLabel(categoryType) : UNCATEGORIZED_CATEGORY_LABEL;
|
|
1811
1812
|
if (!groups.has(groupId)) {
|
|
1813
|
+
const catImage = categoryResource?.images?.[0]?.url ?? categoryResource?.imageUrl ?? void 0;
|
|
1812
1814
|
groups.set(groupId, {
|
|
1813
1815
|
id: groupId,
|
|
1814
1816
|
label,
|
|
1815
|
-
imageUrl:
|
|
1817
|
+
imageUrl: catImage,
|
|
1816
1818
|
products: []
|
|
1817
1819
|
});
|
|
1818
1820
|
}
|
|
1819
1821
|
const group = groups.get(groupId);
|
|
1820
1822
|
group.products.push(product);
|
|
1821
|
-
if (!group.imageUrl && categoryResource
|
|
1822
|
-
group.imageUrl = categoryResource.imageUrl;
|
|
1823
|
+
if (!group.imageUrl && categoryResource) {
|
|
1824
|
+
group.imageUrl = categoryResource.images?.[0]?.url ?? categoryResource.imageUrl;
|
|
1823
1825
|
}
|
|
1824
1826
|
});
|
|
1825
1827
|
return Array.from(groups.values());
|
|
@@ -1872,10 +1874,6 @@ function ServiceOverlayCard({
|
|
|
1872
1874
|
}) {
|
|
1873
1875
|
const currency = product.currency ?? "EUR";
|
|
1874
1876
|
const priceLabel = product.pricingModel === "per_rider" ? "per rider" : product.pricingModel === "per_person" ? locale.perPerson : locale.perSession;
|
|
1875
|
-
const hasMultipleOptions = (product.durationPricing?.length ?? 0) > 1 || (product.riderTierPricing?.length ?? 0) > 1;
|
|
1876
|
-
const lowestPrice = hasMultipleOptions && product.durationPricing?.length ? Math.min(...product.durationPricing.map((dp) => dp.priceCents)) : product.priceCents;
|
|
1877
|
-
const pricePrefix = hasMultipleOptions ? "from " : "";
|
|
1878
|
-
const optionCount = product.durationPricing?.length ?? (product.riderTierPricing?.length ?? 0);
|
|
1879
1877
|
let className = "resira-service-overlay-card";
|
|
1880
1878
|
if (isSelected) className += " resira-service-overlay-card--selected";
|
|
1881
1879
|
if (cardClassName) className += ` ${cardClassName}`;
|
|
@@ -1896,22 +1894,17 @@ function ServiceOverlayCard({
|
|
|
1896
1894
|
] }),
|
|
1897
1895
|
/* @__PURE__ */ jsxs("div", { className: "resira-service-overlay-card-bottom", children: [
|
|
1898
1896
|
/* @__PURE__ */ jsxs("span", { className: "resira-service-overlay-card-price", children: [
|
|
1899
|
-
|
|
1900
|
-
formatPrice2(lowestPrice, currency),
|
|
1897
|
+
formatPrice2(product.priceCents, currency),
|
|
1901
1898
|
/* @__PURE__ */ jsxs("span", { className: "resira-service-overlay-card-price-unit", children: [
|
|
1902
1899
|
"/",
|
|
1903
1900
|
priceLabel
|
|
1904
1901
|
] })
|
|
1905
1902
|
] }),
|
|
1906
1903
|
/* @__PURE__ */ jsxs("div", { className: "resira-service-overlay-card-pills", children: [
|
|
1907
|
-
|
|
1908
|
-
/* @__PURE__ */ jsx(ClockIcon, { size: 11 }),
|
|
1909
|
-
optionCount,
|
|
1910
|
-
" options"
|
|
1911
|
-
] }) : product.durationMinutes > 0 ? /* @__PURE__ */ jsxs("span", { className: "resira-service-overlay-card-pill", children: [
|
|
1904
|
+
product.durationMinutes > 0 && /* @__PURE__ */ jsxs("span", { className: "resira-service-overlay-card-pill", children: [
|
|
1912
1905
|
/* @__PURE__ */ jsx(ClockIcon, { size: 11 }),
|
|
1913
1906
|
formatDuration2(product.durationMinutes)
|
|
1914
|
-
] })
|
|
1907
|
+
] }),
|
|
1915
1908
|
product.maxPartySize && /* @__PURE__ */ jsxs("span", { className: "resira-service-overlay-card-pill", children: [
|
|
1916
1909
|
/* @__PURE__ */ jsx(UsersIcon, { size: 11 }),
|
|
1917
1910
|
"max ",
|
|
@@ -3128,14 +3121,8 @@ function ResiraBookingWidget() {
|
|
|
3128
3121
|
partySize: clampedPartySize
|
|
3129
3122
|
};
|
|
3130
3123
|
});
|
|
3131
|
-
if (step === "resource" && isServiceBased) {
|
|
3132
|
-
const nextIdx = stepIndex("resource", STEPS) + 1;
|
|
3133
|
-
if (nextIdx < STEPS.length) {
|
|
3134
|
-
setStep(STEPS[nextIdx]);
|
|
3135
|
-
}
|
|
3136
|
-
}
|
|
3137
3124
|
},
|
|
3138
|
-
[setActiveResourceId, domainConfig.maxPartySize
|
|
3125
|
+
[setActiveResourceId, domainConfig.maxPartySize]
|
|
3139
3126
|
);
|
|
3140
3127
|
const handleResourceSelect = useCallback(
|
|
3141
3128
|
(resourceId) => {
|
|
@@ -4317,6 +4304,6 @@ function DishShowcase({
|
|
|
4317
4304
|
] });
|
|
4318
4305
|
}
|
|
4319
4306
|
|
|
4320
|
-
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,
|
|
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 };
|
|
4321
4308
|
//# sourceMappingURL=index.js.map
|
|
4322
4309
|
//# sourceMappingURL=index.js.map
|