storefront-layouts 1.0.13 → 1.0.15

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.
Files changed (88) hide show
  1. package/dist/editor/blocks/defaults/clothing-minimal-home.d.ts.map +1 -1
  2. package/dist/editor/blocks/defaults/clothing-minimal-home.js +0 -17
  3. package/dist/editor/payaza-form/PayazaFormEditor.d.ts.map +1 -1
  4. package/dist/editor/payaza-form/PayazaFormEditor.js +0 -2
  5. package/dist/editor/payaza-form/data-transformer.d.ts.map +1 -1
  6. package/dist/editor/payaza-form/data-transformer.js +1 -8
  7. package/dist/editor/shared/useEditorChromeMetrics.js +1 -2
  8. package/dist/hooks/use-pdp-variation.d.ts +1 -13
  9. package/dist/hooks/use-pdp-variation.d.ts.map +1 -1
  10. package/dist/hooks/use-product-variation-selection.d.ts +1 -13
  11. package/dist/hooks/use-product-variation-selection.d.ts.map +1 -1
  12. package/dist/index.d.ts +8 -4
  13. package/dist/index.d.ts.map +1 -1
  14. package/dist/index.js +7 -4
  15. package/dist/layouts/booking/pages/ProductDetailPage.d.ts.map +1 -1
  16. package/dist/layouts/booking/pages/ProductDetailPage.js +2 -1
  17. package/dist/layouts/booking-agenda/pages/ProductDetailPage.d.ts.map +1 -1
  18. package/dist/layouts/booking-agenda/pages/ProductDetailPage.js +2 -1
  19. package/dist/layouts/clothing/components/ClothingHomePage.d.ts.map +1 -1
  20. package/dist/layouts/clothing/components/ClothingHomePage.js +7 -11
  21. package/dist/layouts/clothing/pages/ProductDetailPage.d.ts.map +1 -1
  22. package/dist/layouts/clothing/pages/ProductDetailPage.js +1 -1
  23. package/dist/layouts/clothing-minimal/components/ClothingHomePageMinimal.d.ts.map +1 -1
  24. package/dist/layouts/clothing-minimal/components/ClothingHomePageMinimal.js +1 -7
  25. package/dist/layouts/clothing-minimal/components/clothing-minimal-block-render.d.ts +1 -1
  26. package/dist/layouts/clothing-minimal/components/clothing-minimal-block-render.d.ts.map +1 -1
  27. package/dist/layouts/clothing-minimal/components/clothing-minimal-block-render.js +5 -21
  28. package/dist/layouts/clothing-minimal/pages/ProductDetailPage.d.ts.map +1 -1
  29. package/dist/layouts/clothing-minimal/pages/ProductDetailPage.js +1 -1
  30. package/dist/layouts/electronics/components/ElectronicsGridProductCard.d.ts.map +1 -1
  31. package/dist/layouts/electronics/components/ElectronicsGridProductCard.js +2 -2
  32. package/dist/layouts/electronics/pages/ProductDetailPage.d.ts.map +1 -1
  33. package/dist/layouts/electronics/pages/ProductDetailPage.js +2 -1
  34. package/dist/layouts/electronics-grid/components/ElectronicsGridProductCard.d.ts.map +1 -1
  35. package/dist/layouts/electronics-grid/components/ElectronicsGridProductCard.js +2 -2
  36. package/dist/layouts/electronics-grid/pages/ProductDetailPage.d.ts.map +1 -1
  37. package/dist/layouts/electronics-grid/pages/ProductDetailPage.js +2 -1
  38. package/dist/layouts/food/pages/MenuPage.d.ts.map +1 -1
  39. package/dist/layouts/food/pages/MenuPage.js +1 -0
  40. package/dist/layouts/food/pages/ProductDetailPage.d.ts.map +1 -1
  41. package/dist/layouts/food/pages/ProductDetailPage.js +2 -1
  42. package/dist/layouts/food-modern/components/FoodHomePageModern.js +1 -1
  43. package/dist/layouts/food-modern/pages/MenuPage.d.ts.map +1 -1
  44. package/dist/layouts/food-modern/pages/MenuPage.js +1 -0
  45. package/dist/layouts/food-modern/pages/ProductDetailPage.d.ts.map +1 -1
  46. package/dist/layouts/food-modern/pages/ProductDetailPage.js +2 -1
  47. package/dist/layouts/shared/components/PromoBanner.d.ts +4 -1
  48. package/dist/layouts/shared/components/PromoBanner.d.ts.map +1 -1
  49. package/dist/layouts/shared/components/PromoBanner.js +2 -2
  50. package/dist/layouts/shared/components/StoreCartSheet.d.ts.map +1 -1
  51. package/dist/layouts/shared/components/StoreCartSheet.js +4 -2
  52. package/dist/layouts/shared/components/StoreFooter.js +2 -2
  53. package/dist/layouts/shared/components/StoreHeader.js +4 -4
  54. package/dist/layouts/shared/pages/CartPage.d.ts.map +1 -1
  55. package/dist/layouts/shared/pages/CartPage.js +4 -28
  56. package/dist/layouts/shared/pages/CheckoutPage.d.ts.map +1 -1
  57. package/dist/layouts/shared/pages/CheckoutPage.js +203 -81
  58. package/dist/layouts/shared/pages/MaintenancePage.js +1 -1
  59. package/dist/layouts/shared/wrappers/ClothingLayout.js +5 -5
  60. package/dist/layouts/shared/wrappers/ClothingMinimalLayout.js +4 -4
  61. package/dist/layouts/shared/wrappers/ElectronicsLayout.js +4 -4
  62. package/dist/layouts/shared/wrappers/FoodModernLayout.js +4 -4
  63. package/dist/layouts/shared/wrappers/MotivationalLayout.js +4 -4
  64. package/dist/layouts/shared/wrappers/StandardLayout.js +4 -4
  65. package/dist/lib/cart-charge-currency.d.ts +16 -0
  66. package/dist/lib/cart-charge-currency.d.ts.map +1 -0
  67. package/dist/lib/cart-charge-currency.js +38 -0
  68. package/dist/lib/checkout-phone-validation.d.ts +3 -0
  69. package/dist/lib/checkout-phone-validation.d.ts.map +1 -0
  70. package/dist/lib/checkout-phone-validation.js +41 -0
  71. package/dist/lib/preview-data.d.ts.map +1 -1
  72. package/dist/lib/preview-data.js +2 -0
  73. package/dist/lib/rich-html.d.ts +1 -2
  74. package/dist/lib/rich-html.d.ts.map +1 -1
  75. package/dist/lib/services/fees.service.d.ts +40 -57
  76. package/dist/lib/services/fees.service.d.ts.map +1 -1
  77. package/dist/lib/services/fees.service.js +24 -120
  78. package/dist/lib/store-types.d.ts +27 -34
  79. package/dist/lib/store-types.d.ts.map +1 -1
  80. package/dist/lib/utils/fee-calculations.d.ts.map +1 -1
  81. package/dist/lib/utils/fee-calculations.js +23 -11
  82. package/dist/preview/LayoutPreview.d.ts.map +1 -1
  83. package/dist/preview/LayoutPreview.js +5 -22
  84. package/dist/styles/index.css +36 -0
  85. package/dist/utils/price-display.d.ts +3 -0
  86. package/dist/utils/price-display.d.ts.map +1 -0
  87. package/dist/utils/price-display.js +8 -0
  88. package/package.json +2 -1
@@ -5,14 +5,14 @@ import { Button } from '../../../components/ui/button';
5
5
  import { cn } from '../../../lib/utils';
6
6
  import { useStore } from '../../../lib/store-context';
7
7
  import { toStoreHref } from '../../../lib/utils/url-helpers';
8
- export function PromoBanner({ config, layoutStyle = 'default', className }) {
8
+ export function PromoBanner({ config, layoutStyle = 'default', className, storeSlug: storeSlugProp }) {
9
9
  const { store } = useStore();
10
10
  if (!config?.show)
11
11
  return null;
12
12
  const hasContent = config.image || config.title || config.subtitle || config.buttonText;
13
13
  if (!hasContent)
14
14
  return null;
15
- const storeSlug = store?.slug || '';
15
+ const storeSlug = storeSlugProp || store?.slug || '';
16
16
  const buttonHref = storeSlug
17
17
  ? toStoreHref(storeSlug, config.buttonLink, '/products')
18
18
  : (config.buttonLink || '/products');
@@ -1 +1 @@
1
- {"version":3,"file":"StoreCartSheet.d.ts","sourceRoot":"","sources":["../../../../src/layouts/shared/components/StoreCartSheet.tsx"],"names":[],"mappings":"AAQA,OAAO,EAAE,WAAW,EAAE,MAAM,0BAA0B,CAAC;AAKvD,UAAU,mBAAmB;IAC3B,WAAW,EAAE,WAAW,CAAC;IACzB,KAAK,EAAE,MAAM,CAAC;CACf;AAED,wBAAgB,cAAc,CAAC,EAAE,WAAW,EAAE,KAAK,EAAE,EAAE,mBAAmB,+BA+GzE"}
1
+ {"version":3,"file":"StoreCartSheet.d.ts","sourceRoot":"","sources":["../../../../src/layouts/shared/components/StoreCartSheet.tsx"],"names":[],"mappings":"AASA,OAAO,EAAE,WAAW,EAAE,MAAM,0BAA0B,CAAC;AAMvD,UAAU,mBAAmB;IAC3B,WAAW,EAAE,WAAW,CAAC;IACzB,KAAK,EAAE,MAAM,CAAC;CACf;AAED,wBAAgB,cAAc,CAAC,EAAE,WAAW,EAAE,KAAK,EAAE,EAAE,mBAAmB,+BA+GzE"}
@@ -5,12 +5,14 @@ import { Sheet } from '../../../components/ui/sheet';
5
5
  import { Button } from '../../../components/ui/button';
6
6
  import { CheckoutButton } from '../../../components/ui/checkout-button';
7
7
  import { ImageWithFallback } from '../../../components/ui/image-with-fallback';
8
+ import { useMemo } from 'react';
8
9
  import { useStore } from '../../../lib/store-context';
9
10
  import { formatCurrency } from '../../../lib/utils';
10
11
  import { getCartLineImage } from '../../../lib/product-variation-utils';
11
12
  import { getLayoutText } from '../../../lib/utils/asset-helpers';
13
+ import { resolveCartChargeCurrency } from '../../../lib/cart-charge-currency';
12
14
  export function StoreCartSheet({ storeConfig, title }) {
13
15
  const { cart, removeFromCart, updateCartQuantity, isCartOpen, setIsCartOpen, cartTotal, } = useStore();
14
- const currency = storeConfig.settings?.currency || 'USD';
15
- return (_jsx(Sheet, { isOpen: isCartOpen, onClose: () => setIsCartOpen(false), title: title, side: "right", portal: true, className: "w-full max-w-[min(28rem,100dvw)] box-border border-l border-gray-100", contentClassName: "flex flex-col min-h-0 h-full !px-0 !pb-0", children: cart.length === 0 ? (_jsxs("div", { className: "flex flex-col items-center justify-center flex-1 px-6 pb-8 text-center space-y-4", children: [_jsx(ShoppingBag, { className: "h-12 w-12 text-gray-300" }), _jsx("p", { className: "text-gray-500", children: getLayoutText(storeConfig, 'header.cartEmpty', 'Your bag is empty.') }), _jsx(Button, { onClick: () => setIsCartOpen(false), variant: "outline", className: "mt-2", children: getLayoutText(storeConfig, 'common.continueShopping', 'Continue Shopping') })] })) : (_jsxs("div", { className: "flex flex-col flex-1 min-h-0", children: [_jsx("div", { className: "flex-1 min-h-0 overflow-y-auto overflow-x-hidden px-5 sm:px-6 py-4 space-y-6", children: cart.map((item) => (_jsxs("div", { className: "flex gap-3 sm:gap-4 min-w-0", children: [_jsx("div", { className: "w-[4.5rem] sm:w-20 h-24 bg-gray-100 shrink-0 overflow-hidden relative", children: _jsx(ImageWithFallback, { src: getCartLineImage(item) || item.product.images[0], alt: item.product.name, className: "w-full h-full object-cover" }) }), _jsxs("div", { className: "flex-1 min-w-0 flex flex-col justify-between gap-2", children: [_jsxs("div", { className: "min-w-0", children: [_jsx("h4", { className: "font-medium text-sm line-clamp-2 text-gray-900", children: item.product.name }), item.variantName ? (_jsx("p", { className: "text-gray-500 text-xs mt-1 truncate", children: item.variantName })) : null] }), _jsxs("div", { className: "flex flex-wrap items-center justify-between gap-x-2 gap-y-2", children: [_jsxs("div", { className: "flex items-center border border-gray-200 rounded-full shrink-0", children: [_jsx("button", { type: "button", onClick: () => updateCartQuantity(item.id, item.quantity - 1), className: "p-1 hover:bg-gray-100 rounded-full", "aria-label": "Decrease quantity", children: _jsx(Minus, { className: "h-3 w-3" }) }), _jsx("span", { className: "text-xs font-medium px-2 tabular-nums", children: item.quantity }), _jsx("button", { type: "button", onClick: () => updateCartQuantity(item.id, item.quantity + 1), className: "p-1 hover:bg-gray-100 rounded-full", "aria-label": "Increase quantity", children: _jsx(Plus, { className: "h-3 w-3" }) })] }), _jsxs("div", { className: "flex items-center gap-2 shrink-0 ml-auto", children: [_jsx("span", { className: "font-medium text-sm tabular-nums whitespace-nowrap text-gray-900", children: formatCurrency(item.price * item.quantity, item.product?.currency || currency) }), _jsx("button", { type: "button", onClick: () => removeFromCart(item.id), className: "text-gray-400 hover:text-red-500 p-1", "aria-label": "Remove item", children: _jsx(Trash2, { className: "h-4 w-4" }) })] })] })] })] }, item.id))) }), _jsxs("div", { className: "shrink-0 border-t border-gray-100 px-5 sm:px-6 pt-4 pb-6 pb-[max(1.5rem,env(safe-area-inset-bottom))] space-y-4 bg-white", children: [_jsxs("div", { className: "flex justify-between items-center gap-3 font-medium text-lg min-w-0", children: [_jsx("span", { className: "text-gray-900", children: "Subtotal" }), _jsx("span", { className: "tabular-nums whitespace-nowrap text-gray-900", children: formatCurrency(cartTotal, currency) })] }), _jsx("p", { className: "text-xs text-gray-500 text-center", children: "Shipping and taxes calculated at checkout." }), _jsx(CheckoutButton, { storeConfig: storeConfig, className: "w-full h-12 bg-black text-white hover:bg-gray-800 rounded-full font-bold" })] })] })) }));
16
+ const { currency: cartCurrency } = useMemo(() => resolveCartChargeCurrency(cart, storeConfig.settings?.currency), [cart, storeConfig.settings?.currency]);
17
+ return (_jsx(Sheet, { isOpen: isCartOpen, onClose: () => setIsCartOpen(false), title: title, side: "right", portal: true, className: "w-full max-w-[min(28rem,100dvw)] box-border border-l border-gray-100", contentClassName: "flex flex-col min-h-0 h-full !px-0 !pb-0", children: cart.length === 0 ? (_jsxs("div", { className: "flex flex-col items-center justify-center flex-1 px-6 pb-8 text-center space-y-4", children: [_jsx(ShoppingBag, { className: "h-12 w-12 text-gray-300" }), _jsx("p", { className: "text-gray-500", children: getLayoutText(storeConfig, 'header.cartEmpty', 'Your bag is empty.') }), _jsx(Button, { onClick: () => setIsCartOpen(false), variant: "outline", className: "mt-2", children: getLayoutText(storeConfig, 'common.continueShopping', 'Continue Shopping') })] })) : (_jsxs("div", { className: "flex flex-col flex-1 min-h-0", children: [_jsx("div", { className: "flex-1 min-h-0 overflow-y-auto overflow-x-hidden px-5 sm:px-6 py-4 space-y-6", children: cart.map((item) => (_jsxs("div", { className: "flex gap-3 sm:gap-4 min-w-0", children: [_jsx("div", { className: "w-[4.5rem] sm:w-20 h-24 bg-gray-100 shrink-0 overflow-hidden relative", children: _jsx(ImageWithFallback, { src: getCartLineImage(item) || item.product.images[0], alt: item.product.name, className: "w-full h-full object-cover" }) }), _jsxs("div", { className: "flex-1 min-w-0 flex flex-col justify-between gap-2", children: [_jsxs("div", { className: "min-w-0", children: [_jsx("h4", { className: "font-medium text-sm line-clamp-2 text-gray-900", children: item.product.name }), item.variantName ? (_jsx("p", { className: "text-gray-500 text-xs mt-1 truncate", children: item.variantName })) : null] }), _jsxs("div", { className: "flex flex-wrap items-center justify-between gap-x-2 gap-y-2", children: [_jsxs("div", { className: "flex items-center border border-gray-200 rounded-full shrink-0", children: [_jsx("button", { type: "button", onClick: () => updateCartQuantity(item.id, item.quantity - 1), className: "p-1 hover:bg-gray-100 rounded-full", "aria-label": "Decrease quantity", children: _jsx(Minus, { className: "h-3 w-3" }) }), _jsx("span", { className: "text-xs font-medium px-2 tabular-nums", children: item.quantity }), _jsx("button", { type: "button", onClick: () => updateCartQuantity(item.id, item.quantity + 1), className: "p-1 hover:bg-gray-100 rounded-full", "aria-label": "Increase quantity", children: _jsx(Plus, { className: "h-3 w-3" }) })] }), _jsxs("div", { className: "flex items-center gap-2 shrink-0 ml-auto", children: [_jsx("span", { className: "font-medium text-sm tabular-nums whitespace-nowrap text-gray-900", children: formatCurrency(item.price * item.quantity, cartCurrency) }), _jsx("button", { type: "button", onClick: () => removeFromCart(item.id), className: "text-gray-400 hover:text-red-500 p-1", "aria-label": "Remove item", children: _jsx(Trash2, { className: "h-4 w-4" }) })] })] })] })] }, item.id))) }), _jsxs("div", { className: "shrink-0 border-t border-gray-100 px-5 sm:px-6 pt-4 pb-6 pb-[max(1.5rem,env(safe-area-inset-bottom))] space-y-4 bg-white", children: [_jsxs("div", { className: "flex justify-between items-center gap-3 font-medium text-lg min-w-0", children: [_jsx("span", { className: "text-gray-900", children: "Subtotal" }), _jsx("span", { className: "tabular-nums whitespace-nowrap text-gray-900", children: formatCurrency(cartTotal, cartCurrency) })] }), _jsx("p", { className: "text-xs text-gray-500 text-center", children: "Shipping and taxes calculated at checkout." }), _jsx(CheckoutButton, { storeConfig: storeConfig, className: "w-full h-12 bg-black text-white hover:bg-gray-800 rounded-full font-bold" })] })] })) }));
16
18
  }
@@ -17,8 +17,8 @@ export function StoreFooter({ storeConfig: initialConfig }) {
17
17
  const { store } = useStore();
18
18
  const storeConfig = store || initialConfig;
19
19
  const currentYear = getCurrentYear();
20
- const primaryColor = storeConfig.branding?.primaryColor || '#000000';
21
- const isDarkTheme = storeConfig.branding?.theme === 'dark';
20
+ const primaryColor = storeConfig.branding.primaryColor || '#000000';
21
+ const isDarkTheme = storeConfig.branding.theme === 'dark';
22
22
  const isPreview = isEditorPreviewMode(storeConfig);
23
23
  const footerConfig = storeConfig.layoutConfig?.sections?.footer;
24
24
  const inline = (path) => payazaInlineBindingProps(isPreview, 'footer', path);
@@ -73,9 +73,9 @@ export function StoreHeader({ storeConfig: initialConfig, cartItemCount = 0 }) {
73
73
  searchInputRef.current.focus();
74
74
  }
75
75
  }, [searchOpen]);
76
- const primaryColor = storeConfig.branding?.primaryColor || '#000000';
76
+ const primaryColor = storeConfig.branding.primaryColor;
77
77
  const storeDisplayName = formatStoreDisplayName(storeConfig.name);
78
- const isDarkTheme = storeConfig.branding?.theme === 'dark' || storeConfig.type === 'electronics';
78
+ const isDarkTheme = storeConfig.branding.theme === 'dark' || storeConfig.type === 'electronics';
79
79
  if (!mounted) {
80
80
  return null;
81
81
  }
@@ -85,7 +85,7 @@ export function StoreHeader({ storeConfig: initialConfig, cartItemCount = 0 }) {
85
85
  return (_jsxs(_Fragment, { children: [_jsx("header", { "data-section": "header", className: cn("sticky top-0 z-[60] w-full transition-all duration-500 backdrop-blur-xl border-b", scrolled
86
86
  ? "shadow-lg border-gray-100 dark:border-white/5 py-0 sm:py-1"
87
87
  : "border-transparent py-0 sm:py-2.5"), style: { backgroundColor: headerBg }, children: _jsx("div", { className: "container mx-auto px-3 sm:px-6 lg:px-8", children: _jsxs("div", { className: "flex h-12 items-center gap-2 sm:gap-3 sm:h-14 lg:h-16", children: [_jsx("div", { className: "flex items-center flex-shrink-0 z-20", children: _jsxs(Link, { href: `/${storeConfig.slug}`, className: "flex items-center gap-3 group", children: [_jsx(StoreLogo, { storeConfig: storeConfig, className: "h-8 w-8 transition-transform duration-500 group-hover:scale-105 sm:h-9 sm:w-9 lg:h-10 lg:w-10", alt: storeDisplayName }), !getLogoUrl(storeConfig) && (_jsx("span", { className: cn("text-xl font-bold tracking-tight transition-colors hidden xl:block", isDarkTheme ? "text-white" : "text-gray-900"), children: storeDisplayName }))] }) }), _jsx("nav", { className: "hidden lg:flex flex-1 min-w-0 items-center justify-center gap-1 overflow-hidden px-2 pr-4 mr-1", children: (() => {
88
- const menuItems = storeConfig.navigation?.main ?? [];
88
+ const menuItems = storeConfig.navigation.main;
89
89
  const categories = parentCategories;
90
90
  // Logic to determine how many categories to show directly
91
91
  // Max total top-level items (menu + categories)
@@ -101,7 +101,7 @@ export function StoreHeader({ storeConfig: initialConfig, cartItemCount = 0 }) {
101
101
  callbackUrl: pathname || `/${storeConfig.slug}`,
102
102
  }), children: storeConfig.headerConfig?.loginButtonText || 'Log In' })), _jsx(Button, { variant: "ghost", size: "icon", className: "lg:hidden h-9 w-9 sm:h-10 sm:w-10 rounded-full", onClick: () => setMobileMenuOpen(true), children: _jsx(Menu, { className: "h-6 w-6" }) })] })] }) }) }), _jsx(AnimatePresence, { children: searchOpen && (_jsx(motion.div, { initial: { opacity: 0 }, animate: { opacity: 1 }, exit: { opacity: 0 }, className: "fixed inset-0 z-[100] flex flex-col items-center justify-start pt-24 px-4 bg-white/80 dark:bg-black/80 backdrop-blur-2xl", children: _jsxs(motion.div, { initial: { y: -20, opacity: 0 }, animate: { y: 0, opacity: 1 }, exit: { y: -20, opacity: 0 }, className: "w-full max-w-3xl relative", children: [_jsxs("div", { className: "relative flex items-center group", children: [_jsx(Search, { className: "absolute left-6 h-6 w-6 text-gray-400 group-focus-within:text-black dark:group-focus-within:text-white transition-colors" }), _jsx("input", { ref: searchInputRef, type: "text", placeholder: storeConfig.headerConfig?.searchPlaceholder || "Search products, categories, guides...", value: searchQuery, onChange: (e) => setSearchQuery(e.target.value), className: cn("w-full pl-16 pr-24 py-6 text-2xl md:text-3xl font-light bg-transparent border-b-2 border-gray-200 dark:border-white/10 focus:outline-none focus:border-black dark:focus:border-white transition-all", isDarkTheme ? "text-white placeholder-gray-600" : "text-black placeholder-gray-300"), onKeyDown: (e) => e.key === 'Escape' && setSearchOpen(false) }), _jsxs("div", { className: "absolute right-0 flex items-center gap-2", children: [searchQuery && (_jsx(Button, { variant: "ghost", size: "icon", onClick: () => setSearchQuery(''), className: "h-10 w-10 rounded-full", children: _jsx(X, { className: "h-5 w-5 text-gray-400" }) })), _jsx(Button, { variant: "ghost", size: "sm", onClick: () => setSearchOpen(false), className: "text-xs font-bold uppercase tracking-widest text-gray-400 hover:text-black dark:hover:text-white", children: "Esc" })] })] }), _jsxs("div", { className: "mt-12 grid grid-cols-1 md:grid-cols-2 gap-12", children: [_jsxs("div", { className: "space-y-6", children: [_jsx("h4", { className: "text-[10px] font-black uppercase tracking-[0.3em] text-gray-400", children: "Quick Links" }), _jsx("div", { className: "flex flex-wrap gap-2", children: ['Courses', 'Mentorship', 'Free Guides', 'Memberships', 'Contact'].map((tag) => (_jsx("button", { className: cn("px-4 py-2 rounded-full text-sm border transition-all", isDarkTheme
103
103
  ? "border-white/10 hover:border-white text-gray-400 hover:text-white"
104
- : "border-gray-200 hover:border-black text-gray-600 hover:text-black"), children: tag }, tag))) })] }), _jsxs("div", { className: "space-y-6", children: [_jsx("h4", { className: "text-[10px] font-black uppercase tracking-[0.3em] text-gray-400", children: "Popular Categories" }), _jsx("div", { className: "grid gap-3", children: parentCategories.map((cat) => (_jsxs(Link, { href: `/${storeConfig.slug}/categories/${cat.slug}`, className: "flex items-center gap-3 group", onClick: () => setSearchOpen(false), children: [_jsx("div", { className: "w-10 h-10 rounded-lg bg-gray-100 dark:bg-white/5 flex items-center justify-center group-hover:scale-110 transition-transform", children: _jsx(ChevronRight, { className: "w-4 h-4 text-gray-400" }) }), _jsx("span", { className: "text-lg font-medium text-gray-500 group-hover:text-black dark:group-hover:text-white transition-colors", children: cat.name })] }, cat.id))) })] })] })] }) })) }), _jsx(AnimatePresence, { children: mobileMenuOpen && (_jsxs("div", { className: "fixed inset-0 z-[100] lg:hidden", children: [_jsx(motion.div, { initial: { opacity: 0 }, animate: { opacity: 1 }, exit: { opacity: 0 }, className: "absolute inset-0 bg-black/60 backdrop-blur-md", onClick: () => setMobileMenuOpen(false) }), _jsxs(motion.nav, { initial: { x: '100%' }, animate: { x: 0 }, exit: { x: '100%' }, transition: { type: "spring", damping: 25, stiffness: 200 }, className: cn("absolute right-0 top-0 bottom-0 w-[300px] shadow-2xl p-8 flex flex-col", isDarkTheme ? "bg-black text-white" : "bg-white text-black"), children: [_jsxs("div", { className: "flex items-center justify-between mb-12", children: [_jsx(StoreLogo, { storeConfig: storeConfig, className: "h-8 w-8" }), _jsx(Button, { variant: "ghost", size: "icon", onClick: () => setMobileMenuOpen(false), children: _jsx(X, { className: "h-6 w-6" }) })] }), _jsxs("div", { className: "flex flex-col gap-8", children: [(storeConfig.navigation?.main ?? []).map((item, i) => {
104
+ : "border-gray-200 hover:border-black text-gray-600 hover:text-black"), children: tag }, tag))) })] }), _jsxs("div", { className: "space-y-6", children: [_jsx("h4", { className: "text-[10px] font-black uppercase tracking-[0.3em] text-gray-400", children: "Popular Categories" }), _jsx("div", { className: "grid gap-3", children: parentCategories.map((cat) => (_jsxs(Link, { href: `/${storeConfig.slug}/categories/${cat.slug}`, className: "flex items-center gap-3 group", onClick: () => setSearchOpen(false), children: [_jsx("div", { className: "w-10 h-10 rounded-lg bg-gray-100 dark:bg-white/5 flex items-center justify-center group-hover:scale-110 transition-transform", children: _jsx(ChevronRight, { className: "w-4 h-4 text-gray-400" }) }), _jsx("span", { className: "text-lg font-medium text-gray-500 group-hover:text-black dark:group-hover:text-white transition-colors", children: cat.name })] }, cat.id))) })] })] })] }) })) }), _jsx(AnimatePresence, { children: mobileMenuOpen && (_jsxs("div", { className: "fixed inset-0 z-[100] lg:hidden", children: [_jsx(motion.div, { initial: { opacity: 0 }, animate: { opacity: 1 }, exit: { opacity: 0 }, className: "absolute inset-0 bg-black/60 backdrop-blur-md", onClick: () => setMobileMenuOpen(false) }), _jsxs(motion.nav, { initial: { x: '100%' }, animate: { x: 0 }, exit: { x: '100%' }, transition: { type: "spring", damping: 25, stiffness: 200 }, className: cn("absolute right-0 top-0 bottom-0 w-[300px] shadow-2xl p-8 flex flex-col", isDarkTheme ? "bg-black text-white" : "bg-white text-black"), children: [_jsxs("div", { className: "flex items-center justify-between mb-12", children: [_jsx(StoreLogo, { storeConfig: storeConfig, className: "h-8 w-8" }), _jsx(Button, { variant: "ghost", size: "icon", onClick: () => setMobileMenuOpen(false), children: _jsx(X, { className: "h-6 w-6" }) })] }), _jsxs("div", { className: "flex flex-col gap-8", children: [storeConfig.navigation.main.map((item, i) => {
105
105
  return (_jsx(Link, { href: `/${storeConfig.slug}${item.href}`, className: "text-3xl font-bold tracking-tight hover:opacity-50 transition-opacity flex items-center gap-4", onClick: () => setMobileMenuOpen(false), children: item.label }, i));
106
106
  }), _jsxs("div", { className: "pt-8 space-y-4 border-t border-gray-100 dark:border-white/10", children: [_jsx("h4", { className: "text-[10px] font-black uppercase tracking-[0.3em] text-gray-400", children: "Catalogue" }), _jsx("div", { className: "grid gap-4", children: parentCategories.map((cat) => (_jsx(Link, { href: `/${storeConfig.slug}/categories/${cat.slug}`, className: "text-lg font-medium text-gray-500 hover:text-black dark:hover:text-white hover:underline underline-offset-4 transition-colors", onClick: () => setMobileMenuOpen(false), children: cat.name }, cat.id))) })] })] }), _jsxs("div", { className: "mt-auto pt-8 border-t border-gray-100 dark:border-white/10 flex flex-col gap-6", children: [!isAuthenticated ? (_jsx(Button, { className: "w-full h-14 rounded-full font-bold text-sm uppercase tracking-widest shadow-xl", style: { backgroundColor: primaryColor, color: 'white' }, onClick: () => {
107
107
  setMobileMenuOpen(false);
@@ -1 +1 @@
1
- {"version":3,"file":"CartPage.d.ts","sourceRoot":"","sources":["../../../../src/layouts/shared/pages/CartPage.tsx"],"names":[],"mappings":"AAEA,OAAO,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAC;AAWhD,UAAU,aAAa;IACrB,WAAW,EAAE,WAAW,CAAC;CAC1B;AAED,wBAAgB,QAAQ,CAAC,EAAE,WAAW,EAAE,EAAE,aAAa,+BAiRtD"}
1
+ {"version":3,"file":"CartPage.d.ts","sourceRoot":"","sources":["../../../../src/layouts/shared/pages/CartPage.tsx"],"names":[],"mappings":"AAEA,OAAO,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAC;AAYhD,UAAU,aAAa;IACrB,WAAW,EAAE,WAAW,CAAC;CAC1B;AAED,wBAAgB,QAAQ,CAAC,EAAE,WAAW,EAAE,EAAE,aAAa,+BAmQtD"}
@@ -4,11 +4,12 @@ import { Button } from '../../../components/ui/button.js';
4
4
  import { Minus, Plus, Trash2, ArrowRight, ShoppingBag, ArrowLeft, Lock } from 'lucide-react';
5
5
  import Link from 'next/link';
6
6
  import Image from 'next/image';
7
- import { useEffect, useState } from 'react';
7
+ import { useEffect, useMemo, useState } from 'react';
8
8
  import { useStore } from '../../../lib/store-context.js';
9
9
  import { formatCurrency } from '../../../lib/utils/index.js';
10
10
  import { getCartLineImage } from '../../../lib/product-variation-utils.js';
11
11
  import { calculateVAT, calculateServiceCharge, getVATLabel, getServiceChargeLabel } from '../../../lib/utils/fee-calculations.js';
12
+ import { resolveCartChargeCurrency } from '../../../lib/cart-charge-currency.js';
12
13
  export function CartPage({ storeConfig }) {
13
14
  const primaryColor = storeConfig.branding.primaryColor;
14
15
  const { cart, updateCartQuantity, removeFromCart, cartTotal, isCartLoading } = useStore();
@@ -18,32 +19,7 @@ export function CartPage({ storeConfig }) {
18
19
  useEffect(() => {
19
20
  setIsClient(true);
20
21
  }, []);
21
- // Helper function to determine cart currency consistently
22
- const getCartCurrency = () => {
23
- if (cart.length === 0) {
24
- return storeConfig.settings?.currency || 'USD';
25
- }
26
- // Get currencies from cart items
27
- const currencies = cart
28
- .map(item => item.product?.currency)
29
- .filter(Boolean);
30
- if (currencies.length === 0) {
31
- return storeConfig.settings?.currency || 'USD';
32
- }
33
- // If all items have the same currency, use it
34
- const uniqueCurrencies = [...new Set(currencies)];
35
- if (uniqueCurrencies.length === 1) {
36
- return uniqueCurrencies[0];
37
- }
38
- // If mixed currencies, use the most common one
39
- const currencyCounts = {};
40
- currencies.forEach(currency => {
41
- currencyCounts[currency] = (currencyCounts[currency] || 0) + 1;
42
- });
43
- const mostCommonCurrency = Object.entries(currencyCounts).reduce((a, b) => currencyCounts[a[0]] > currencyCounts[b[0]] ? a : b)[0];
44
- return mostCommonCurrency;
45
- };
46
- const cartCurrency = getCartCurrency();
22
+ const { currency: cartCurrency, mixed: hasMixedCartCurrencies } = useMemo(() => resolveCartChargeCurrency(cart, storeConfig.settings?.currency), [cart, storeConfig.settings?.currency]);
47
23
  const subtotal = cartTotal;
48
24
  // Calculate VAT and service charge using store configuration
49
25
  const vatAmount = calculateVAT(subtotal, storeConfig.settings?.vat, storeConfig.settings?.taxRate);
@@ -81,5 +57,5 @@ export function CartPage({ storeConfig }) {
81
57
  finally {
82
58
  setRemovingItemId(null);
83
59
  }
84
- }, className: "text-gray-400 hover:text-red-500 transition-colors p-2 hover:bg-red-50 rounded-sm disabled:opacity-50", "aria-label": "Remove item", disabled: removingItemId === item.id || isCartLoading, children: removingItemId === item.id ? (_jsx("div", { className: "w-5 h-5 border-2 border-red-400 border-t-transparent rounded-full animate-spin" })) : (_jsx(Trash2, { className: "w-5 h-5" })) })] })] })] }, item.id))) }), _jsx("div", { className: "w-full lg:w-96 flex-shrink-0", children: _jsxs("div", { className: "bg-white rounded-sm border border-gray-200/90 shadow-sm px-8 pt-8 pb-10 sticky top-24", children: [_jsx("h2", { className: "text-2xl font-black text-gray-900 mb-8", children: "Summary" }), _jsxs("div", { className: "space-y-4 mb-8", children: [_jsxs("div", { className: "flex justify-between text-gray-600 font-medium", children: [_jsx("span", { children: "Subtotal" }), _jsx("span", { className: "text-gray-900", children: formatCurrency(subtotal, cartCurrency) })] }), vatAmount > 0 && (_jsxs("div", { className: "flex justify-between text-gray-600 font-medium", children: [_jsx("span", { children: vatLabel }), _jsx("span", { className: "text-gray-900", children: formatCurrency(vatAmount, cartCurrency) })] })), serviceChargeAmount > 0 && (_jsxs("div", { className: "flex justify-between text-gray-600 font-medium", children: [_jsx("span", { children: serviceChargeLabel }), _jsx("span", { className: "text-gray-900", children: formatCurrency(serviceChargeAmount, cartCurrency) })] })), _jsxs("div", { className: "pt-6 border-t-2 border-gray-100 flex justify-between items-end mt-4", children: [_jsx("span", { className: "text-lg font-bold text-gray-900", children: "Total" }), _jsx("span", { className: "text-4xl font-black text-gray-900 tracking-tight", children: formatCurrency(total, cartCurrency) })] })] }), _jsxs("div", { className: "space-y-5", children: [_jsx(Link, { href: `/${storeConfig.slug}/checkout`, className: "block", children: _jsxs(Button, { className: "w-full h-14 rounded-sm text-lg font-bold shadow-sm transition-all hover:opacity-95 active:scale-[0.99]", style: { backgroundColor: primaryColor, color: 'white' }, children: ["Checkout ", _jsx(ArrowRight, { className: "ml-2 w-5 h-5" })] }) }), _jsxs("div", { className: "flex items-center justify-center gap-2 pt-1 text-xs font-medium text-gray-400", children: [_jsx(Lock, { className: "w-3 h-3" }), "Secure Encrypted Checkout"] })] })] }) })] })] }) }));
60
+ }, className: "text-gray-400 hover:text-red-500 transition-colors p-2 hover:bg-red-50 rounded-sm disabled:opacity-50", "aria-label": "Remove item", disabled: removingItemId === item.id || isCartLoading, children: removingItemId === item.id ? (_jsx("div", { className: "w-5 h-5 border-2 border-red-400 border-t-transparent rounded-full animate-spin" })) : (_jsx(Trash2, { className: "w-5 h-5" })) })] })] })] }, item.id))) }), _jsx("div", { className: "w-full lg:w-96 flex-shrink-0", children: _jsxs("div", { className: "bg-white rounded-sm border border-gray-200/90 shadow-sm px-8 pt-8 pb-10 sticky top-24", children: [_jsx("h2", { className: "text-2xl font-black text-gray-900 mb-8", children: "Summary" }), _jsxs("div", { className: "space-y-4 mb-8", children: [_jsxs("div", { className: "flex justify-between text-gray-600 font-medium", children: [_jsx("span", { children: "Subtotal" }), _jsx("span", { className: "text-gray-900", children: formatCurrency(subtotal, cartCurrency) })] }), vatAmount > 0 && (_jsxs("div", { className: "flex justify-between text-gray-600 font-medium", children: [_jsx("span", { children: vatLabel }), _jsx("span", { className: "text-gray-900", children: formatCurrency(vatAmount, cartCurrency) })] })), serviceChargeAmount > 0 && (_jsxs("div", { className: "flex justify-between text-gray-600 font-medium", children: [_jsx("span", { children: serviceChargeLabel }), _jsx("span", { className: "text-gray-900", children: formatCurrency(serviceChargeAmount, cartCurrency) })] })), _jsxs("div", { className: "pt-6 border-t-2 border-gray-100 flex justify-between items-end mt-4", children: [_jsx("span", { className: "text-lg font-bold text-gray-900", children: "Total" }), _jsx("span", { className: "text-4xl font-black text-gray-900 tracking-tight", children: formatCurrency(total, cartCurrency) })] })] }), hasMixedCartCurrencies ? (_jsx("p", { className: "mb-4 text-sm text-red-600 font-medium", role: "alert", children: "Your cart mixes currencies. Remove items until everything uses the same currency to checkout." })) : null, _jsxs("div", { className: "space-y-5", children: [hasMixedCartCurrencies ? (_jsxs(Button, { className: "w-full h-14 rounded-sm text-lg font-bold shadow-sm", style: { backgroundColor: primaryColor, color: 'white' }, disabled: true, children: ["Checkout ", _jsx(ArrowRight, { className: "ml-2 w-5 h-5" })] })) : (_jsx(Link, { href: `/${storeConfig.slug}/checkout`, className: "block", children: _jsxs(Button, { className: "w-full h-14 rounded-sm text-lg font-bold shadow-sm transition-all hover:opacity-95 active:scale-[0.99]", style: { backgroundColor: primaryColor, color: 'white' }, children: ["Checkout ", _jsx(ArrowRight, { className: "ml-2 w-5 h-5" })] }) })), _jsxs("div", { className: "flex items-center justify-center gap-2 pt-1 text-xs font-medium text-gray-400", children: [_jsx(Lock, { className: "w-3 h-3" }), "Secure Encrypted Checkout"] })] })] }) })] })] }) }));
85
61
  }
@@ -1 +1 @@
1
- {"version":3,"file":"CheckoutPage.d.ts","sourceRoot":"","sources":["../../../../src/layouts/shared/pages/CheckoutPage.tsx"],"names":[],"mappings":"AAEA,OAAO,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAC;AAkChD,UAAU,iBAAiB;IACzB,WAAW,EAAE,WAAW,CAAC;CAC1B;AAkID,wBAAgB,YAAY,CAAC,EAAE,WAAW,EAAE,EAAE,iBAAiB,+BA69C9D"}
1
+ {"version":3,"file":"CheckoutPage.d.ts","sourceRoot":"","sources":["../../../../src/layouts/shared/pages/CheckoutPage.tsx"],"names":[],"mappings":"AAEA,OAAO,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAC;AAiChD,UAAU,iBAAiB;IACzB,WAAW,EAAE,WAAW,CAAC;CAC1B;AA8LD,wBAAgB,YAAY,CAAC,EAAE,WAAW,EAAE,EAAE,iBAAiB,+BAynD9D"}