storefront-layouts 1.0.9 → 1.0.13

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 (108) hide show
  1. package/dist/components/shipping/ProductDeliveryInfoSection.d.ts.map +1 -1
  2. package/dist/components/shipping/ProductDeliveryInfoSection.js +10 -3
  3. package/dist/components/ui/address-selector.d.ts.map +1 -1
  4. package/dist/components/ui/address-selector.js +4 -3
  5. package/dist/components/ui/button.js +1 -1
  6. package/dist/components/ui/checkout-button.d.ts.map +1 -1
  7. package/dist/components/ui/checkout-button.js +2 -1
  8. package/dist/components/ui/product-card.d.ts.map +1 -1
  9. package/dist/components/ui/product-card.js +15 -4
  10. package/dist/components/ui/route-skeleton-loader.d.ts.map +1 -1
  11. package/dist/components/ui/route-skeleton-loader.js +6 -2
  12. package/dist/components/ui/sheet.d.ts.map +1 -1
  13. package/dist/components/ui/sheet.js +2 -2
  14. package/dist/components/ui/skeletons/checkout-skeleton.js +1 -1
  15. package/dist/editor/blocks/defaults/clothing-minimal-home.js +1 -1
  16. package/dist/editor/blocks/migrate-clothing-minimal.js +1 -1
  17. package/dist/hooks/use-pdp-variation.d.ts +57 -0
  18. package/dist/hooks/use-pdp-variation.d.ts.map +1 -0
  19. package/dist/hooks/use-pdp-variation.js +69 -0
  20. package/dist/hooks/use-product-by-slug.d.ts.map +1 -1
  21. package/dist/hooks/use-product-by-slug.js +0 -5
  22. package/dist/hooks/use-product-variation-selection.js +1 -1
  23. package/dist/hooks/use-shipping-quote.d.ts +1 -0
  24. package/dist/hooks/use-shipping-quote.d.ts.map +1 -1
  25. package/dist/hooks/use-shipping-quote.js +3 -0
  26. package/dist/layouts/booking/pages/ProductDetailPage.d.ts.map +1 -1
  27. package/dist/layouts/booking/pages/ProductDetailPage.js +130 -223
  28. package/dist/layouts/booking-agenda/pages/ProductDetailPage.d.ts.map +1 -1
  29. package/dist/layouts/booking-agenda/pages/ProductDetailPage.js +130 -223
  30. package/dist/layouts/clothing/components/ClothingHomePage.d.ts.map +1 -1
  31. package/dist/layouts/clothing/components/ClothingHomePage.js +40 -2
  32. package/dist/layouts/clothing/pages/ProductDetailPage.d.ts.map +1 -1
  33. package/dist/layouts/clothing/pages/ProductDetailPage.js +127 -209
  34. package/dist/layouts/clothing/pages/ProductsPage.d.ts.map +1 -1
  35. package/dist/layouts/clothing/pages/ProductsPage.js +2 -5
  36. package/dist/layouts/clothing-minimal/components/ClothingHomePageMinimal.d.ts.map +1 -1
  37. package/dist/layouts/clothing-minimal/components/ClothingHomePageMinimal.js +5 -3
  38. package/dist/layouts/clothing-minimal/components/clothing-minimal-block-render.d.ts.map +1 -1
  39. package/dist/layouts/clothing-minimal/components/clothing-minimal-block-render.js +13 -4
  40. package/dist/layouts/clothing-minimal/pages/ProductDetailPage.d.ts.map +1 -1
  41. package/dist/layouts/clothing-minimal/pages/ProductDetailPage.js +130 -223
  42. package/dist/layouts/clothing-minimal/pages/ProductsPage.d.ts.map +1 -1
  43. package/dist/layouts/clothing-minimal/pages/ProductsPage.js +2 -5
  44. package/dist/layouts/electronics/components/ElectronicsGridProductCard.js +1 -1
  45. package/dist/layouts/electronics/components/ElectronicsStoreHeader.d.ts.map +1 -1
  46. package/dist/layouts/electronics/components/ElectronicsStoreHeader.js +10 -9
  47. package/dist/layouts/electronics/pages/ProductDetailPage.d.ts.map +1 -1
  48. package/dist/layouts/electronics/pages/ProductDetailPage.js +130 -223
  49. package/dist/layouts/electronics/pages/ProductsPage.d.ts.map +1 -1
  50. package/dist/layouts/electronics/pages/ProductsPage.js +2 -5
  51. package/dist/layouts/electronics-grid/components/ElectronicsGridProductCard.js +1 -1
  52. package/dist/layouts/electronics-grid/components/ElectronicsHomePageGrid.d.ts.map +1 -1
  53. package/dist/layouts/electronics-grid/components/ElectronicsHomePageGrid.js +4 -5
  54. package/dist/layouts/electronics-grid/pages/ProductDetailPage.d.ts.map +1 -1
  55. package/dist/layouts/electronics-grid/pages/ProductDetailPage.js +130 -223
  56. package/dist/layouts/electronics-grid/pages/ProductsPage.d.ts.map +1 -1
  57. package/dist/layouts/electronics-grid/pages/ProductsPage.js +2 -5
  58. package/dist/layouts/food/pages/MenuPage.js +1 -1
  59. package/dist/layouts/food/pages/ProductDetailPage.d.ts.map +1 -1
  60. package/dist/layouts/food/pages/ProductDetailPage.js +37 -140
  61. package/dist/layouts/food/pages/ProductsPage.d.ts.map +1 -1
  62. package/dist/layouts/food/pages/ProductsPage.js +2 -5
  63. package/dist/layouts/food-modern/pages/MenuPage.js +1 -1
  64. package/dist/layouts/food-modern/pages/ProductDetailPage.d.ts.map +1 -1
  65. package/dist/layouts/food-modern/pages/ProductDetailPage.js +130 -223
  66. package/dist/layouts/food-modern/pages/ProductsPage.d.ts.map +1 -1
  67. package/dist/layouts/food-modern/pages/ProductsPage.js +2 -5
  68. package/dist/layouts/motivational-speaker/pages/ProductsPage.d.ts.map +1 -1
  69. package/dist/layouts/motivational-speaker/pages/ProductsPage.js +2 -5
  70. package/dist/layouts/shared/components/PdpColorSwatch.d.ts +11 -0
  71. package/dist/layouts/shared/components/PdpColorSwatch.d.ts.map +1 -0
  72. package/dist/layouts/shared/components/PdpColorSwatch.js +29 -0
  73. package/dist/layouts/shared/components/PdpVariationSelectors.d.ts +13 -0
  74. package/dist/layouts/shared/components/PdpVariationSelectors.d.ts.map +1 -0
  75. package/dist/layouts/shared/components/PdpVariationSelectors.js +65 -0
  76. package/dist/layouts/shared/components/StoreCartSheet.d.ts +8 -0
  77. package/dist/layouts/shared/components/StoreCartSheet.d.ts.map +1 -0
  78. package/dist/layouts/shared/components/StoreCartSheet.js +16 -0
  79. package/dist/layouts/shared/components/StoreHeader.d.ts.map +1 -1
  80. package/dist/layouts/shared/components/StoreHeader.js +13 -12
  81. package/dist/layouts/shared/components/headers/ClothingMinimalHeader.d.ts.map +1 -1
  82. package/dist/layouts/shared/components/headers/ClothingMinimalHeader.js +5 -7
  83. package/dist/layouts/shared/pages/CartPage.d.ts.map +1 -1
  84. package/dist/layouts/shared/pages/CartPage.js +6 -5
  85. package/dist/layouts/shared/pages/CheckoutPage.d.ts.map +1 -1
  86. package/dist/layouts/shared/pages/CheckoutPage.js +209 -107
  87. package/dist/layouts/shared/product-category-filter-chip.d.ts +2 -0
  88. package/dist/layouts/shared/product-category-filter-chip.d.ts.map +1 -0
  89. package/dist/layouts/shared/product-category-filter-chip.js +6 -0
  90. package/dist/lib/pdp-cart-actions.d.ts +5 -0
  91. package/dist/lib/pdp-cart-actions.d.ts.map +1 -0
  92. package/dist/lib/pdp-cart-actions.js +19 -0
  93. package/dist/lib/product-variation-utils.d.ts +17 -0
  94. package/dist/lib/product-variation-utils.d.ts.map +1 -1
  95. package/dist/lib/product-variation-utils.js +193 -14
  96. package/dist/lib/services/checkout.service.d.ts +11 -0
  97. package/dist/lib/services/checkout.service.d.ts.map +1 -1
  98. package/dist/lib/services/shipping.service.d.ts +3 -0
  99. package/dist/lib/services/shipping.service.d.ts.map +1 -1
  100. package/dist/lib/store-config-utils.d.ts.map +1 -1
  101. package/dist/lib/store-config-utils.js +104 -11
  102. package/dist/lib/store-context.d.ts.map +1 -1
  103. package/dist/lib/store-context.js +23 -8
  104. package/dist/lib/utils/asset-helpers.d.ts +2 -0
  105. package/dist/lib/utils/asset-helpers.d.ts.map +1 -1
  106. package/dist/lib/utils/asset-helpers.js +24 -0
  107. package/dist/styles/index.css +143 -45
  108. package/package.json +8 -2
@@ -5,8 +5,8 @@ import { useProductReviews } from '../../../hooks/use-product-reviews.js';
5
5
  import { Button } from '../../../components/ui/button';
6
6
  import { Breadcrumbs } from '../../../components/ui/breadcrumbs';
7
7
  import { Modal } from '../../../components/ui/modal';
8
- import { ShoppingCart, Heart, Star, CheckCircle, ThumbsUp, User, Minus, Plus, Ruler, Share2, Info } from 'lucide-react';
9
- import { useState, useMemo, useEffect } from 'react';
8
+ import { ShoppingCart, Heart, Star, CheckCircle, ThumbsUp, User, Minus, Plus, Share2, Info } from 'lucide-react';
9
+ import { useState, useMemo, useEffect, useCallback } from 'react';
10
10
  import { useStore } from '../../../lib/store-context';
11
11
  import { useToast } from '../../../components/ui/toast';
12
12
  import { useRouter } from 'next/navigation';
@@ -23,8 +23,12 @@ import { ButtonLoader } from '../../../components/ui/page-loader';
23
23
  import { extractImageUrls } from '../../../lib/store-config-utils';
24
24
  import { OutOfStockOverlay } from '../../../components/ui/out-of-stock-overlay';
25
25
  import { cn, formatCurrency, filterActiveProducts, isEditorPreviewMode, resolvePdpProduct } from '../../../lib/utils';
26
+ import { usePdpVariation } from '../../../hooks/use-pdp-variation';
27
+ import { buildPdpBuyNowItem } from '../../../lib/pdp-cart-actions';
28
+ import { assertVariationSelected } from '../../../lib/product-variation-utils';
26
29
  import { shippingService } from '../../../lib/services/shipping.service';
27
30
  import { ProductDeliveryInfoSection } from '../../../components/shipping/ProductDeliveryInfoSection';
31
+ import { PdpVariationSelectors } from '../../shared/components/PdpVariationSelectors';
28
32
  import { formatDateSafe } from '../../../utils/date';
29
33
  // Helper function to format date
30
34
  const formatDate = (dateString) => {
@@ -117,21 +121,11 @@ export function ProductDetailPage({ storeConfig: initialConfig, productSlug }) {
117
121
  addToast('Failed to update wishlist', 'error');
118
122
  }
119
123
  };
120
- // Extract color and size variants
121
- const colorVariants = product?.variants?.filter(v => v.name === 'Color') || [];
122
- const sizeVariants = product?.variants?.filter(v => v.name === 'Size') || [];
123
- // Initialize state with defaults
124
+ const { optionGroups, selectedAttributes, setAttribute, isOptionAvailable, selectedVariation, galleryImages, displayPrice, displayCompareAt, displaySKU, isOutOfStock, getOptionImage, } = usePdpVariation(product);
124
125
  const [selectedImage, setSelectedImage] = useState(0);
125
- // Ensure selectedImage is within bounds when images change
126
126
  useEffect(() => {
127
- if (product && product.images && product.images.length > 0 && selectedImage >= product.images.length) {
128
- // Use setTimeout to avoid synchronous setState in effect
129
- const timer = setTimeout(() => setSelectedImage(0), 0);
130
- return () => clearTimeout(timer);
131
- }
132
- }, [product, selectedImage]);
133
- const [selectedColor, setSelectedColor] = useState(colorVariants.length > 0 ? colorVariants[0].value : null);
134
- const [selectedSize, setSelectedSize] = useState(sizeVariants.length > 0 ? sizeVariants[0].value : null);
127
+ setSelectedImage(0);
128
+ }, [selectedVariation?.id, galleryImages.join('|')]);
135
129
  const [quantity, setQuantity] = useState(1);
136
130
  const [showAllReviews, setShowAllReviews] = useState(false);
137
131
  const [isReviewModalOpen, setIsReviewModalOpen] = useState(false);
@@ -152,6 +146,102 @@ export function ProductDetailPage({ storeConfig: initialConfig, productSlug }) {
152
146
  const [shippingError, setShippingError] = useState(null);
153
147
  const [freeShippingEligible, setFreeShippingEligible] = useState(false);
154
148
  const [freeShippingThreshold, setFreeShippingThreshold] = useState(undefined);
149
+ const calculateShipping = useCallback(async () => {
150
+ if (!product || !shippingAddress.country || !shippingAddress.city) {
151
+ setShippingError('Please provide country and city to calculate shipping');
152
+ return;
153
+ }
154
+ setIsCalculatingShipping(true);
155
+ setShippingError(null);
156
+ try {
157
+ const currency = product.currency || storeConfig.settings?.currency || 'USD';
158
+ const subtotal = displayPrice * quantity;
159
+ const result = await shippingService.calculateShipping({
160
+ storeId: storeConfig.id,
161
+ address: {
162
+ country: shippingAddress.country,
163
+ city: shippingAddress.city,
164
+ zipCode: shippingAddress.zipCode,
165
+ },
166
+ items: [{ productId: product.id, quantity }],
167
+ subtotal,
168
+ currency,
169
+ });
170
+ setShippingMethods(result.methods || []);
171
+ setFreeShippingEligible(result.freeShippingEligible || false);
172
+ setFreeShippingThreshold(result.freeShippingThreshold);
173
+ }
174
+ catch (error) {
175
+ console.error('Shipping calculation error:', error);
176
+ setShippingError(error instanceof Error ? error.message : 'Failed to calculate shipping. Please try again.');
177
+ setShippingMethods([]);
178
+ }
179
+ finally {
180
+ setIsCalculatingShipping(false);
181
+ }
182
+ }, [
183
+ product,
184
+ shippingAddress.country,
185
+ shippingAddress.city,
186
+ shippingAddress.zipCode,
187
+ quantity,
188
+ displayPrice,
189
+ storeConfig.id,
190
+ storeConfig.settings?.currency,
191
+ ]);
192
+ useEffect(() => {
193
+ const loadAddresses = async () => {
194
+ if (isAuthenticated) {
195
+ try {
196
+ const addresses = await customerService.getAddresses();
197
+ setSavedAddresses(addresses);
198
+ if (addresses.length > 0) {
199
+ const defaultAddress = addresses.find((a) => a.isDefault) || addresses[0];
200
+ if (defaultAddress) {
201
+ setShippingAddress({
202
+ country: defaultAddress.country || '',
203
+ city: defaultAddress.city || '',
204
+ zipCode: defaultAddress.zipCode || '',
205
+ });
206
+ }
207
+ }
208
+ }
209
+ catch (error) {
210
+ console.error('Failed to load addresses:', error);
211
+ }
212
+ }
213
+ };
214
+ loadAddresses();
215
+ }, [isAuthenticated]);
216
+ useEffect(() => {
217
+ if (!shippingAddress.country || !shippingAddress.city || !product)
218
+ return;
219
+ const timer = setTimeout(() => {
220
+ calculateShipping();
221
+ }, 500);
222
+ return () => clearTimeout(timer);
223
+ }, [
224
+ shippingAddress.country,
225
+ shippingAddress.city,
226
+ shippingAddress.zipCode,
227
+ quantity,
228
+ product?.id,
229
+ displayPrice,
230
+ calculateShipping,
231
+ ]);
232
+ useEffect(() => {
233
+ if (product && storeConfig?.id) {
234
+ trackEvent({
235
+ eventType: 'product_view',
236
+ metadata: {
237
+ productId: product.id,
238
+ productName: product.name,
239
+ categoryId: product.categoryId,
240
+ price: displayPrice,
241
+ },
242
+ });
243
+ }
244
+ }, [product?.id, storeConfig?.id, displayPrice, trackEvent]);
155
245
  const handleSubmitReview = async (e) => {
156
246
  e.preventDefault();
157
247
  if (!product?.id)
@@ -190,8 +280,6 @@ export function ProductDetailPage({ storeConfig: initialConfig, productSlug }) {
190
280
  if (!product) {
191
281
  return (_jsx("div", { className: "min-h-screen bg-white flex items-center justify-center p-8", children: _jsxs("div", { className: "text-center", children: [_jsx("h1", { className: "text-2xl font-bold mb-2", children: "Product Not Found" }), _jsx("p", { className: "text-gray-600 mb-4", children: "The product you're looking for doesn't exist." })] }) }));
192
282
  }
193
- // Use SKU for display, fallback to generated SKU from slug if not available
194
- const displaySKU = product.sku || product.slug?.toUpperCase().replace(/-/g, '').substring(0, 12).padEnd(8, 'X') || 'N/A';
195
283
  // Build category breadcrumb path with all parent categories
196
284
  const buildCategoryPath = (categoryId) => {
197
285
  if (!categoryId)
@@ -225,42 +313,7 @@ export function ProductDetailPage({ storeConfig: initialConfig, productSlug }) {
225
313
  // Extract product images - product.images should already be string[] from transformation,
226
314
  // but handle edge cases where it might still be in object format
227
315
  // Use the comprehensive extraction utility from store-config-utils for consistency
228
- const productImages = (() => {
229
- // Debug: Log the raw product.images to understand its format
230
- if (process.env.NODE_ENV === 'development') {
231
- console.log('[ProductDetailPage] Raw product.images:', {
232
- productId: product.id,
233
- productName: product.name,
234
- images: product.images,
235
- imagesType: typeof product.images,
236
- imagesIsArray: Array.isArray(product.images),
237
- imagesLength: Array.isArray(product.images) ? product.images.length : 'N/A',
238
- firstItem: Array.isArray(product.images) && product.images.length > 0 ? product.images[0] : 'N/A',
239
- firstItemType: Array.isArray(product.images) && product.images.length > 0 ? typeof product.images[0] : 'N/A'
240
- });
241
- }
242
- // If already an array of strings (normal case after transformation)
243
- if (Array.isArray(product.images) && product.images.length > 0) {
244
- const firstItem = product.images[0];
245
- if (typeof firstItem === 'string') {
246
- const filtered = product.images.filter((img) => typeof img === 'string' && img.trim().length > 0);
247
- if (process.env.NODE_ENV === 'development') {
248
- console.log('[ProductDetailPage] Using string array directly:', { count: filtered.length, firstImage: filtered[0]?.substring(0, 100) });
249
- }
250
- return filtered;
251
- }
252
- // If array but not strings, extract using utility
253
- if (process.env.NODE_ENV === 'development') {
254
- console.log('[ProductDetailPage] Array but not strings, using extractImageUrls');
255
- }
256
- }
257
- // Use the comprehensive extraction utility for any other format (objects, nested structures, etc.)
258
- const extracted = extractImageUrls(product.images);
259
- if (process.env.NODE_ENV === 'development') {
260
- console.log('[ProductDetailPage] Extracted using utility:', { count: extracted.length, firstImage: extracted[0]?.substring(0, 100) });
261
- }
262
- return extracted;
263
- })();
316
+ const productImages = galleryImages.length > 0 ? galleryImages : extractImageUrls(product.images);
264
317
  // For gallery, use actual images only (don't duplicate to fill 5 slots)
265
318
  const displayImages = productImages.slice(0, 5);
266
319
  const hasMoreImages = productImages.length > 5;
@@ -285,20 +338,6 @@ export function ProductDetailPage({ storeConfig: initialConfig, productSlug }) {
285
338
  });
286
339
  }
287
340
  }
288
- // Color swatch mapping
289
- const colorSwatches = {
290
- 'White': '#FFFFFF',
291
- 'Black': '#000000',
292
- 'Gray': '#808080',
293
- 'Grey': '#808080',
294
- 'Blue': '#3B82F6',
295
- 'Red': '#EF4444',
296
- 'Green': '#10B981',
297
- 'Yellow': '#FBBF24',
298
- 'Brown': '#92400E',
299
- 'Navy': '#1E3A8A',
300
- 'Beige': '#F5F5DC',
301
- };
302
341
  // Reviews loaded from API (product payload does not include review rows)
303
342
  const displayedReviews = showAllReviews ? reviews : reviews.slice(0, 3);
304
343
  const hasMoreReviews = reviews.length > 3;
@@ -307,7 +346,6 @@ export function ProductDetailPage({ storeConfig: initialConfig, productSlug }) {
307
346
  ? reviews.reduce((sum, review) => sum + review.rating, 0) / reviews.length
308
347
  : Number(product.rating) || 0;
309
348
  // Out of stock check
310
- const isOutOfStock = !product.inStock;
311
349
  // Calculate rating distribution
312
350
  const ratingDistribution = [5, 4, 3, 2, 1].map(rating => {
313
351
  const count = reviews.filter(r => Math.round(r.rating) === rating).length;
@@ -317,29 +355,19 @@ export function ProductDetailPage({ storeConfig: initialConfig, productSlug }) {
317
355
  percentage: reviews.length > 0 ? (count / reviews.length) * 100 : 0,
318
356
  };
319
357
  });
320
- const handleAddToCart = () => {
358
+ const handleAddToCart = async () => {
321
359
  if (isOutOfStock) {
322
360
  addToast('This product is currently out of stock', 'error');
323
361
  return;
324
362
  }
325
- // Find the matching variation UUID based on selected attributes
326
- // Note: StoreProduct uses 'variants' not 'variations'
327
- let variationId;
328
- if (product.variants && Array.isArray(product.variants) && product.variants.length > 0) {
329
- const matchingVariation = product.variants.find((variation) => {
330
- const attrs = variation.attributes || {};
331
- const colorMatch = !selectedColor || attrs.color === selectedColor;
332
- const sizeMatch = !selectedSize || attrs.size === selectedSize;
333
- return colorMatch && sizeMatch;
334
- });
335
- variationId = matchingVariation?.id;
363
+ try {
364
+ assertVariationSelected(product, selectedVariation, selectedAttributes);
365
+ await addToCart(product, quantity, selectedVariation?.id);
366
+ addToast(`${product.name} added to cart`, 'success');
367
+ }
368
+ catch (error) {
369
+ addToast(error instanceof Error ? error.message : 'Could not add to cart', 'error');
336
370
  }
337
- // Fallback to composite string for backward compatibility if no variation found
338
- const variantId = variationId || (selectedColor || selectedSize
339
- ? [selectedColor, selectedSize].filter(Boolean).join(' / ')
340
- : undefined);
341
- addToCart(product, quantity, variantId);
342
- addToast(`${product.name} added to cart`, 'success');
343
371
  };
344
372
  const handleBuyNow = async () => {
345
373
  if (isOutOfStock) {
@@ -351,43 +379,14 @@ export function ProductDetailPage({ storeConfig: initialConfig, productSlug }) {
351
379
  }
352
380
  setIsBuyNowLoading(true);
353
381
  try {
354
- // Find the matching variation UUID based on selected attributes
355
- // Note: StoreProduct uses 'variants' not 'variations'
356
- let variationId;
357
- let variantName;
358
- if (product.variants && Array.isArray(product.variants) && product.variants.length > 0) {
359
- const matchingVariation = product.variants.find((variation) => {
360
- const attrs = variation.attributes || {};
361
- const colorMatch = !selectedColor || attrs.color === selectedColor;
362
- const sizeMatch = !selectedSize || attrs.size === selectedSize;
363
- return colorMatch && sizeMatch;
364
- });
365
- variationId = matchingVariation?.id;
366
- variantName = matchingVariation?.name || matchingVariation?.value;
367
- }
368
- // Fallback to composite string for backward compatibility if no variation found
369
- const variantId = variationId || (selectedColor || selectedSize
370
- ? [selectedColor, selectedSize].filter(Boolean).join(' / ')
371
- : undefined);
372
- // Create buy now item (don't add to cart)
373
- const buyNowItemId = variantId ? `${product.id}-${variantId}` : product.id;
374
- const buyNowItem = {
375
- id: buyNowItemId,
376
- productId: product.id,
377
- quantity,
378
- variantId,
379
- variantName: variantName || variantId,
380
- price: product.price,
381
- product
382
- };
383
- // Set buy now item and navigate directly to checkout
382
+ const buyNowItem = buildPdpBuyNowItem(product, quantity, selectedVariation, displayPrice, selectedAttributes);
384
383
  setBuyNowItem(buyNowItem);
385
384
  // Navigate to checkout page for direct checkout
386
385
  router.push(`/${storeConfig.slug}/checkout`);
387
386
  }
388
387
  catch (error) {
389
388
  console.error('Buy Now error:', error);
390
- addToast('Failed to proceed to checkout. Please try again.', 'error');
389
+ addToast(error instanceof Error ? error.message : 'Failed to proceed to checkout. Please try again.', 'error');
391
390
  }
392
391
  finally {
393
392
  setIsBuyNowLoading(false);
@@ -400,116 +399,24 @@ export function ProductDetailPage({ storeConfig: initialConfig, productSlug }) {
400
399
  // Note: This is now only used for other flows, not buy now
401
400
  // Buy now now redirects to checkout page
402
401
  };
403
- // Calculate shipping when address is provided
404
- const calculateShipping = async () => {
405
- if (!product || !shippingAddress.country || !shippingAddress.city) {
406
- setShippingError('Please provide country and city to calculate shipping');
407
- return;
408
- }
409
- setIsCalculatingShipping(true);
410
- setShippingError(null);
411
- try {
412
- const currency = product.currency || storeConfig.settings?.currency || 'USD';
413
- const subtotal = product.price * quantity;
414
- const result = await shippingService.calculateShipping({
415
- storeId: storeConfig.id,
416
- address: {
417
- country: shippingAddress.country,
418
- city: shippingAddress.city,
419
- zipCode: shippingAddress.zipCode,
420
- },
421
- items: [{
422
- productId: product.id,
423
- quantity: quantity,
424
- }],
425
- subtotal: subtotal,
426
- currency: currency,
427
- });
428
- setShippingMethods(result.methods || []);
429
- setFreeShippingEligible(result.freeShippingEligible || false);
430
- setFreeShippingThreshold(result.freeShippingThreshold);
431
- }
432
- catch (error) {
433
- console.error('Shipping calculation error:', error);
434
- setShippingError(error.message || 'Failed to calculate shipping. Please try again.');
435
- setShippingMethods([]);
436
- }
437
- finally {
438
- setIsCalculatingShipping(false);
439
- }
440
- };
441
- // Load saved addresses for authenticated users and auto-populate shipping address
442
- useEffect(() => {
443
- const loadAddresses = async () => {
444
- if (isAuthenticated) {
445
- try {
446
- const addresses = await customerService.getAddresses();
447
- setSavedAddresses(addresses);
448
- // Auto-populate with default address or first address if available
449
- if (addresses.length > 0) {
450
- const defaultAddress = addresses.find(a => a.isDefault) || addresses[0];
451
- if (defaultAddress) {
452
- setShippingAddress({
453
- country: defaultAddress.country || '',
454
- city: defaultAddress.city || '',
455
- zipCode: defaultAddress.zipCode || '',
456
- });
457
- }
458
- }
459
- }
460
- catch (error) {
461
- console.error('Failed to load addresses:', error);
462
- // Don't show error toast - addresses are optional
463
- // Users can still manually enter shipping address
464
- }
465
- }
466
- };
467
- loadAddresses();
468
- // eslint-disable-next-line react-hooks/exhaustive-deps
469
- }, [isAuthenticated]);
470
- // Auto-calculate shipping when address fields are filled (debounced)
471
- useEffect(() => {
472
- if (!shippingAddress.country || !shippingAddress.city || !product) {
473
- return;
474
- }
475
- const timer = setTimeout(() => {
476
- calculateShipping();
477
- }, 500); // Debounce 500ms
478
- return () => clearTimeout(timer);
479
- // eslint-disable-next-line react-hooks/exhaustive-deps
480
- }, [shippingAddress.country, shippingAddress.city, shippingAddress.zipCode, quantity, product?.id, product?.price]);
481
- // Track product view
482
- useEffect(() => {
483
- if (product && storeConfig?.id) {
484
- trackEvent({
485
- eventType: 'product_view',
486
- metadata: {
487
- productId: product.id,
488
- productName: product.name,
489
- categoryId: product.categoryId,
490
- price: product.price,
491
- },
492
- });
493
- }
494
- }, [product?.id, storeConfig?.id, trackEvent]);
495
402
  return (_jsxs("div", { className: "min-h-screen bg-white", children: [_jsxs("div", { "data-content-ready": true, className: "container mx-auto px-4 sm:px-6 lg:px-8 py-8 lg:py-12 max-w-7xl", children: [_jsx("div", { className: "mb-6", children: _jsx(Breadcrumbs, { items: breadcrumbItems }) }), _jsxs("div", { className: "grid grid-cols-1 lg:grid-cols-2 gap-8 lg:gap-16 mb-20", children: [_jsxs("div", { className: "relative lg:sticky lg:top-24 self-start z-0", children: [_jsxs("div", { className: "relative aspect-square bg-gray-50 rounded-xl overflow-hidden mb-4 border border-gray-100 shadow-sm group", children: [_jsx(ImageWithFallback, { "data-product-image": true, src: displayImages[selectedImage] || displayImages[0] || productImages[0], alt: product.name, className: cn("w-full h-full object-cover transition-transform duration-300 group-hover:scale-105", isOutOfStock && "grayscale"), skeletonAspectRatio: "square" }), isOutOfStock && (_jsx(OutOfStockOverlay, { badgePosition: "center" }))] }), displayImages.length > 0 && (_jsxs("div", { className: "grid grid-cols-5 gap-2", children: [displayImages.map((image, index) => (_jsx("button", { onClick: () => setSelectedImage(Math.min(index, displayImages.length - 1)), className: `aspect-square rounded-lg overflow-hidden border-2 transition-all ${selectedImage === index
496
403
  ? 'border-gray-900 ring-2 ring-gray-900 ring-offset-2'
497
404
  : 'border-gray-200 hover:border-gray-300'}`, children: _jsx(ImageWithFallback, { src: image, alt: `${product.name} view ${index + 1}`, className: cn("w-full h-full object-cover", isOutOfStock && "grayscale"), skeletonAspectRatio: "square" }) }, index))), hasMoreImages && (_jsxs("div", { className: "aspect-square rounded-lg border-2 border-gray-200 bg-gray-50 flex items-center justify-center text-xs text-gray-500 font-medium", children: ["+", productImages.length - 5, " more"] }))] }))] }), _jsxs("div", { className: "flex flex-col", children: [_jsxs("div", { className: "flex items-center justify-between mb-2", children: [_jsx("span", { className: "text-sm font-semibold text-gray-700 uppercase tracking-wide", children: (() => {
498
405
  const brandMatch = product.name.match(/\b(Reebok|Nike|Adidas|Puma|New Balance|Vans|Converse)\b/i);
499
406
  return brandMatch ? brandMatch[1] : storeConfig.name;
500
- })() }), _jsx("button", { onClick: handleShare, className: "text-gray-400 hover:text-gray-900 transition-colors p-2 rounded-full hover:bg-gray-100", title: "Share this product", children: _jsx(Share2, { className: "h-5 w-5" }) })] }), _jsx("h1", { "data-product-name": true, className: "text-3xl sm:text-4xl lg:text-5xl font-bold text-gray-900 mb-3 leading-tight tracking-tight", children: product.name }), _jsxs("p", { className: "text-sm text-gray-500 mb-6 font-mono", children: ["SKU: ", displaySKU] }), _jsx("div", { className: "mb-4 flex items-center gap-2", children: product.inStock ? (_jsxs("span", { className: "inline-flex items-center px-2.5 py-0.5 rounded-full text-xs font-medium bg-green-100 text-green-800", children: [_jsx(CheckCircle, { className: "w-3 h-3 mr-1" }), "In Stock"] })) : (_jsxs("span", { className: "inline-flex items-center px-2.5 py-0.5 rounded-full text-xs font-medium bg-red-100 text-red-800", children: [_jsx(Info, { className: "w-3 h-3 mr-1" }), "Out of Stock"] })) }), _jsxs("div", { className: "mb-8", children: [_jsxs("div", { className: "flex items-baseline gap-4", children: [_jsx("span", { "data-product-price": true, className: "text-4xl lg:text-5xl font-bold text-gray-900", children: formatCurrency(product.price, product.currency || 'USD') }), product.compareAtPrice != null && product.compareAtPrice > product.price && (_jsx("span", { className: "text-2xl text-gray-500 line-through", children: formatCurrency(product.compareAtPrice, product.currency || 'USD') }))] }), product.compareAtPrice != null && product.compareAtPrice > product.price && (_jsxs("span", { className: "inline-block mt-2 bg-red-100 text-red-700 text-xs font-bold px-2 py-1 rounded", children: ["SAVE ", Math.round(((product.compareAtPrice - product.price) / product.compareAtPrice) * 100), "%"] }))] }), colorVariants.length > 0 && (_jsxs("div", { className: "mb-8", children: [_jsxs("div", { className: "flex items-center gap-2 mb-4", children: [_jsx("span", { className: "text-sm font-semibold text-gray-900", children: "Color:" }), _jsx("span", { className: "text-sm text-gray-600 capitalize", children: selectedColor || colorVariants[0]?.value })] }), _jsx("div", { className: "flex gap-3", children: colorVariants.map((variant) => {
501
- const colorValue = variant.value;
502
- const swatchColor = colorSwatches[colorValue] || '#CCCCCC';
503
- const isSelected = selectedColor === colorValue;
504
- return (_jsx("button", { onClick: () => setSelectedColor(colorValue), className: `relative w-12 h-12 rounded-full border-2 transition-all ${isSelected
505
- ? 'border-gray-900 ring-2 ring-gray-900 ring-offset-2 scale-105'
506
- : 'border-gray-200 hover:border-gray-300'}`, style: { backgroundColor: swatchColor }, title: colorValue, children: isSelected && (_jsx("div", { className: "absolute inset-0 flex items-center justify-center", children: _jsx(CheckCircle, { className: `h-5 w-5 ${['White', 'Beige', 'Yellow'].includes(colorValue) ? 'text-gray-900' : 'text-white'} drop-shadow-sm` }) })) }, variant.id));
507
- }) })] })), sizeVariants.length > 0 && (_jsxs("div", { className: "mb-8", children: [_jsxs("div", { className: "flex items-center justify-between mb-4", children: [_jsxs("div", { className: "flex items-center gap-2", children: [_jsx("span", { className: "text-sm font-semibold text-gray-900", children: "Size:" }), _jsx("span", { className: "text-sm text-gray-600", children: selectedSize || 'Select Size' })] }), productDetailConfig?.showSizeGuide !== false && (_jsxs("a", { href: `/${storeConfig.slug}/size-guide`, target: "_blank", rel: "noopener noreferrer", className: "flex items-center gap-1.5 text-xs font-medium text-gray-600 hover:text-gray-900 underline decoration-gray-400 underline-offset-4", children: [_jsx(Ruler, { className: "h-3.5 w-3.5" }), " Size guide"] }))] }), _jsx("div", { className: "grid grid-cols-4 sm:grid-cols-5 gap-2.5", children: sizeVariants.map((variant) => {
508
- const isSelected = selectedSize === variant.value;
509
- return (_jsx("button", { onClick: () => setSelectedSize(variant.value), className: `px-4 py-3 rounded-lg border-2 text-sm font-semibold transition-all ${isSelected
510
- ? 'bg-gray-900 text-white border-gray-900 shadow-md transform -translate-y-0.5'
511
- : 'bg-white text-gray-700 border-gray-200 hover:border-gray-300 hover:bg-gray-50'}`, children: variant.value }, variant.id));
512
- }) })] })), _jsxs("div", { className: "flex flex-col gap-4 mb-8", children: [_jsxs("div", { className: "flex items-center gap-3", children: [_jsx("span", { className: "text-sm font-semibold text-gray-900", children: "Quantity" }), _jsxs("div", { className: "flex items-center border border-gray-300 rounded-lg", children: [_jsx("button", { className: "p-3 text-gray-600 hover:bg-gray-100 rounded-l-lg disabled:opacity-50", onClick: () => setQuantity(Math.max(1, quantity - 1)), disabled: quantity <= 1, children: _jsx(Minus, { className: "h-4 w-4" }) }), _jsx("span", { className: "w-12 text-center font-semibold text-gray-900", children: quantity }), _jsx("button", { className: "p-3 text-gray-600 hover:bg-gray-100 rounded-r-lg", onClick: () => setQuantity(quantity + 1), children: _jsx(Plus, { className: "h-4 w-4" }) })] })] }), _jsxs("div", { className: "flex flex-col sm:flex-row gap-3 mt-2", children: [productDetailConfig?.showAddToCart !== false && (_jsx(Button, { "data-add-to-cart": true, size: "lg", onClick: handleAddToCart, disabled: isCartLoading || isOutOfStock, className: "flex-1 bg-white border-2 border-gray-900 text-gray-900 hover:bg-gray-50 rounded-full h-14 text-base font-bold shadow-sm transition-all disabled:opacity-50 disabled:cursor-not-allowed", children: isCartLoading ? (_jsxs(_Fragment, { children: [_jsx(ButtonLoader, { className: "mr-2" }), "Adding..."] })) : (_jsxs(_Fragment, { children: [_jsx(ShoppingCart, { className: "h-5 w-5 mr-2" }), "Add to Cart"] })) })), productDetailConfig?.showAddToCart !== false && (_jsx(Button, { size: "lg", onClick: handleBuyNow, disabled: isOutOfStock || isBuyNowLoading || isCartLoading, className: "flex-1 bg-gray-900 hover:bg-gray-800 text-white rounded-full h-14 text-base font-bold shadow-lg hover:shadow-xl transition-all disabled:opacity-50 disabled:cursor-not-allowed", children: isBuyNowLoading || isCartLoading ? (_jsxs(_Fragment, { children: [_jsx(ButtonLoader, { className: "mr-2" }), "Adding..."] })) : ('Buy Now') })), storeConfig.features.wishlist && productDetailConfig?.showWishlist !== false && (_jsx(Button, { variant: "outline", size: "lg", onClick: handleWishlistClick, disabled: isWishlistLoading || !product, className: cn("h-14 w-14 rounded-full border-2 transition-all disabled:opacity-50 disabled:cursor-not-allowed", product && isInWishlist(product.id)
407
+ })() }), _jsx("button", { onClick: handleShare, className: "text-gray-400 hover:text-gray-900 transition-colors p-2 rounded-full hover:bg-gray-100", title: "Share this product", children: _jsx(Share2, { className: "h-5 w-5" }) })] }), _jsx("h1", { "data-product-name": true, className: "text-3xl sm:text-4xl lg:text-5xl font-bold text-gray-900 mb-3 leading-tight tracking-tight", children: product.name }), _jsxs("p", { className: "text-sm text-gray-500 mb-6 font-mono", children: ["SKU: ", displaySKU] }), _jsx("div", { className: "mb-4 flex items-center gap-2", children: !isOutOfStock ? (_jsxs("span", { className: "inline-flex items-center px-2.5 py-0.5 rounded-full text-xs font-medium bg-green-100 text-green-800", children: [_jsx(CheckCircle, { className: "w-3 h-3 mr-1" }), "In Stock"] })) : (_jsxs("span", { className: "inline-flex items-center px-2.5 py-0.5 rounded-full text-xs font-medium bg-red-100 text-red-800", children: [_jsx(Info, { className: "w-3 h-3 mr-1" }), "Out of Stock"] })) }), _jsxs("div", { className: "mb-8", children: [_jsxs("div", { className: "flex items-baseline gap-4", children: [_jsx("span", { "data-product-price": true, className: "text-4xl lg:text-5xl font-bold text-gray-900", children: formatCurrency(displayPrice, product.currency || 'USD') }), displayCompareAt != null && displayCompareAt > displayPrice && (_jsx("span", { className: "text-2xl text-gray-500 line-through", children: formatCurrency(displayCompareAt, product.currency || 'USD') }))] }), displayCompareAt != null && displayCompareAt > displayPrice && (_jsxs("span", { className: "inline-block mt-2 bg-red-100 text-red-700 text-xs font-bold px-2 py-1 rounded", children: ["SAVE ", Math.round(((displayCompareAt - displayPrice) / displayCompareAt) * 100), "%"] }))] }), _jsx(PdpVariationSelectors, { optionGroups: optionGroups, selectedAttributes: selectedAttributes, setAttribute: setAttribute, isOptionAvailable: isOptionAvailable, getOptionImage: getOptionImage, storeSlug: storeConfig.slug, showSizeGuide: productDetailConfig?.showSizeGuide !== false, onAfterClearAttribute: () => setQuantity(1) }), _jsxs("div", { className: "flex flex-col gap-4 mb-8", children: [_jsxs("div", { className: "flex items-center gap-2.5", children: [_jsx("span", { className: "text-sm font-normal leading-5 text-[#191C1F]", children: "Quantity" }), _jsxs("div", { className: "inline-flex h-8 w-fit items-center rounded-[2px] border border-[#E4E7E9] bg-white", children: [_jsx("button", { type: "button", className: "flex h-8 w-7 items-center justify-center text-[#475156] hover:bg-gray-50 disabled:opacity-40", onClick: () => setQuantity(Math.max(1, quantity - 1)), disabled: quantity <= 1, "aria-label": "Decrease quantity", children: _jsx(Minus, { className: "h-3.5 w-3.5" }) }), _jsx("span", { className: "w-6 text-center text-sm font-medium tabular-nums text-[#191C1F]", children: quantity }), _jsx("button", { type: "button", className: "flex h-8 w-7 items-center justify-center text-[#475156] hover:bg-gray-50", onClick: () => setQuantity(quantity + 1), "aria-label": "Increase quantity", children: _jsx(Plus, { className: "h-3.5 w-3.5" }) })] })] }), _jsxs("div", { className: "flex flex-row items-stretch gap-2 sm:gap-3 mt-2 w-full", children: [productDetailConfig?.showAddToCart !== false && (_jsx(Button, { "data-add-to-cart": true, variant: "outline", size: "lg", onClick: handleAddToCart, disabled: isCartLoading || isOutOfStock, className: "flex-1 min-w-0 bg-white border-2 border-gray-900 text-gray-900 hover:bg-gray-50 rounded-full h-12 sm:h-14 px-3 sm:px-6 text-sm sm:text-base font-bold [&_svg]:text-gray-900 shadow-sm transition-all disabled:opacity-50 disabled:cursor-not-allowed", children: isCartLoading ? (_jsxs(_Fragment, { children: [_jsx(ButtonLoader, { className: "mr-1.5 sm:mr-2" }), "Adding..."] })) : (_jsxs(_Fragment, { children: [_jsx(ShoppingCart, { className: "h-4 w-4 sm:h-5 sm:w-5 mr-1.5 sm:mr-2 shrink-0 text-gray-900" }), "Add to Cart"] })) })), productDetailConfig?.showAddToCart !== false && (_jsx(Button, { size: "lg", onClick: handleBuyNow, disabled: isOutOfStock || isBuyNowLoading || isCartLoading, className: "flex-1 min-w-0 bg-gray-900 hover:bg-gray-800 text-white rounded-full h-12 sm:h-14 px-3 sm:px-6 text-sm sm:text-base font-bold shadow-lg hover:shadow-xl transition-all disabled:opacity-50 disabled:cursor-not-allowed", children: isBuyNowLoading || isCartLoading ? (_jsxs(_Fragment, { children: [_jsx(ButtonLoader, { className: "mr-1.5 sm:mr-2" }), "Adding..."] })) : ('Buy Now') })), storeConfig.features.wishlist && productDetailConfig?.showWishlist !== false && (_jsx(Button, { variant: "outline", size: "lg", onClick: handleWishlistClick, disabled: isWishlistLoading || !product, className: cn("h-12 w-12 sm:h-14 sm:w-14 shrink-0 rounded-full border-2 transition-all disabled:opacity-50 disabled:cursor-not-allowed", product && isInWishlist(product.id)
513
408
  ? "border-red-500 bg-red-50 text-red-500 hover:bg-red-100"
514
- : "border-gray-200 hover:border-gray-300 hover:bg-gray-50"), children: isWishlistLoading ? (_jsx(ButtonLoader, { className: "h-5 w-5" })) : (_jsx(Heart, { className: cn("h-5 w-5", product && isInWishlist(product.id) && "fill-current") })) }))] })] }), _jsx(ProductDeliveryInfoSection, { storeConfig: storeConfig, currency: storeConfig.settings?.currency || 'USD', productCurrency: product.currency, productPrice: product.price, quantity: quantity, shippingAddress: shippingAddress, updateShippingAddress: (patch) => setShippingAddress((prev) => ({ ...prev, ...patch })), shippingMethods: shippingMethods, isCalculatingShipping: isCalculatingShipping, shippingError: shippingError, freeShippingEligible: freeShippingEligible, freeShippingThreshold: freeShippingThreshold, onToast: addToast }), product.description && (_jsxs("div", { className: "mb-8", children: [_jsx("h2", { className: "text-lg font-semibold text-gray-900 mb-3", children: "Description" }), _jsxs("div", { className: "prose prose-sm max-w-none text-gray-600 leading-relaxed", children: [_jsx(SafeRichHtml, { value: product.description, fallbackPlain: "", as: "div" }), _jsxs("ul", { className: "list-disc pl-5 space-y-1 mt-2", children: [_jsx("li", { children: "Premium quality material" }), _jsx("li", { children: "Comfortable fit for all-day wear" }), _jsx("li", { children: "Durable stitching and finish" })] })] })] })), product.specifications && Object.keys(product.specifications).length > 0 && (_jsxs("div", { className: "mb-8", children: [_jsx("h2", { className: "text-lg font-semibold text-gray-900 mb-4", children: "Specifications" }), _jsx("dl", { className: "grid grid-cols-1 sm:grid-cols-2 gap-x-4 gap-y-4", children: Object.entries(product.specifications).map(([key, value]) => (_jsxs("div", { className: "flex flex-col p-3 bg-gray-50 rounded-lg", children: [_jsx("dt", { className: "text-xs text-gray-500 font-medium uppercase tracking-wider mb-1", children: key }), _jsx("dd", { className: "text-sm font-bold text-gray-900", children: value })] }, key))) })] }))] })] }), productDetailConfig?.showReviews !== false && (_jsx("div", { className: "mt-20 pt-16 border-t border-gray-200", children: _jsxs("div", { className: "max-w-4xl", children: [_jsxs("div", { className: "flex flex-col sm:flex-row items-start sm:items-center justify-between gap-6 mb-10", children: [_jsxs("div", { className: "flex-1", children: [_jsx("h2", { className: "text-2xl lg:text-3xl font-bold text-gray-900 mb-4", children: "Customer Reviews" }), product.reviewCount && product.reviewCount > 0 && product.rating && product.rating > 0 ? (_jsxs("div", { className: "flex items-center gap-4", children: [_jsxs("div", { className: "flex items-center gap-2", children: [_jsx(StarRating, { rating: typeof product.rating === 'number' ? product.rating : Number(product.rating) || 0, size: "lg" }), _jsx("span", { className: "text-2xl font-bold text-gray-900", children: Number(product.rating).toFixed(1) }), _jsx("span", { className: "text-lg text-gray-500", children: "out of 5" })] }), _jsx("span", { className: "text-gray-300", children: "|" }), _jsxs("span", { className: "text-lg text-gray-600", children: [product.reviewCount, " ", product.reviewCount === 1 ? 'review' : 'reviews'] })] })) : (_jsx("div", { className: "text-gray-500", children: _jsx("p", { className: "text-base", children: "Be the first to review this product" }) }))] }), _jsx(Button, { onClick: () => setIsReviewModalOpen(true), className: "rounded-full shrink-0 bg-gray-900 hover:bg-gray-800 text-white", children: product.reviewCount && product.reviewCount > 0 ? 'Write a Review' : 'Be the First to Review' })] }), ratingDistribution.some(r => r.count > 0) && (_jsxs("div", { className: "mb-10 p-6 bg-gray-50 rounded-xl border border-gray-100", children: [_jsx("h3", { className: "text-sm font-semibold text-gray-900 mb-5", children: "Rating Breakdown" }), _jsx("div", { className: "space-y-3", children: ratingDistribution.map(({ rating, count, percentage }) => (_jsxs("div", { className: "flex items-center gap-4", children: [_jsxs("div", { className: "flex items-center gap-1.5 w-20", children: [_jsx("span", { className: "text-sm font-semibold text-gray-900", children: rating }), _jsx(Star, { className: "h-4 w-4 fill-gray-900 text-gray-900" })] }), _jsx("div", { className: "flex-1 h-2.5 bg-gray-200 rounded-full overflow-hidden", children: _jsx("div", { className: "h-full bg-gradient-to-r from-gray-700 to-gray-900 rounded-full transition-all duration-500", style: { width: `${percentage}%` } }) }), _jsx("span", { className: "text-sm font-medium text-gray-700 w-12 text-right", children: count })] }, rating))) })] })), reviews.length > 0 ? (_jsxs(_Fragment, { children: [_jsx("div", { className: "space-y-6", children: displayedReviews.map((review) => (_jsx(ReviewCard, { review: review }, review.id))) }), hasMoreReviews && !showAllReviews && (_jsx("div", { className: "mt-8 text-center", children: _jsxs(Button, { variant: "outline", onClick: () => setShowAllReviews(true), className: "px-8 rounded-full h-12 border-gray-300", children: ["Show All ", reviews.length, " Reviews"] }) }))] })) : (_jsxs("div", { className: "text-center py-12 bg-gray-50 rounded-xl border border-dashed border-gray-300", children: [_jsx("div", { className: "inline-flex items-center justify-center w-12 h-12 rounded-full bg-white shadow-sm mb-4", children: _jsx(Star, { className: "h-6 w-6 text-gray-400" }) }), _jsx("h3", { className: "text-lg font-semibold text-gray-900 mb-2", children: "No Reviews Yet" }), _jsx("p", { className: "text-gray-500 max-w-sm mx-auto mb-6", children: "Be the first to review this product and help other customers make informed decisions." }), _jsx(Button, { onClick: () => setIsReviewModalOpen(true), className: "bg-gray-900 hover:bg-gray-800 text-white", children: "Write the First Review" })] }))] }) })), productDetailConfig?.showRelatedProducts !== false && relatedProducts.length > 0 && (_jsxs("div", { className: "mt-20 pt-16 border-t border-gray-200", children: [_jsx("h2", { className: "text-2xl lg:text-3xl font-bold text-gray-900 mb-8", children: "You Might Also Like" }), _jsx("div", { className: "grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-4 gap-6", children: relatedProducts.map((relatedProduct) => (_jsx("div", { className: "h-[400px]", children: _jsx(ProductCard, { product: relatedProduct, storeSlug: storeConfig.slug, onAddToCart: (p) => addToCart(p, 1), onQuickView: handleQuickView, className: "h-full" }) }, relatedProduct.id))) })] }))] }), _jsx(Modal, { isOpen: isReviewModalOpen, onClose: () => setIsReviewModalOpen(false), title: "Write a Review", children: _jsxs("form", { onSubmit: handleSubmitReview, className: "space-y-4", children: [_jsxs("div", { children: [_jsx("label", { className: "block text-sm font-medium text-gray-700 mb-2", children: "Rating" }), _jsxs("div", { className: "flex items-center gap-1", children: [[1, 2, 3, 4, 5].map((star) => (_jsxs("div", { className: "relative group", children: [_jsx("button", { type: "button", onClick: () => setNewReview({ ...newReview, rating: star - 0.5 }), className: "absolute top-0 left-0 w-1/2 h-full z-20 focus:outline-none", title: `${star - 0.5} stars` }), _jsx("button", { type: "button", onClick: () => setNewReview({ ...newReview, rating: star }), className: "absolute top-0 right-0 w-1/2 h-full z-20 focus:outline-none", title: `${star} stars` }), _jsxs("div", { className: "relative pointer-events-none transition-transform group-hover:scale-110", children: [_jsx(Star, { className: "h-8 w-8 text-gray-200 fill-gray-200" }), newReview.rating >= star - 0.5 && (_jsx("div", { className: "absolute inset-0 overflow-hidden", style: { width: newReview.rating === star - 0.5 ? '50%' : '100%' }, children: _jsx(Star, { className: "h-8 w-8 text-gray-900 fill-gray-900" }) }))] })] }, star))), _jsx("span", { className: "ml-3 text-lg font-medium text-gray-900 w-12", children: newReview.rating })] })] }), _jsxs("div", { children: [_jsx("label", { htmlFor: "title", className: "block text-sm font-medium text-gray-700 mb-1", children: "Review Title" }), _jsx("input", { type: "text", id: "title", required: true, className: "w-full rounded-md border border-gray-300 px-3 py-2 text-sm focus:outline-none focus:ring-2 focus:ring-black", placeholder: "Summarize your thoughts", value: newReview.title, onChange: (e) => setNewReview({ ...newReview, title: e.target.value }) })] }), _jsxs("div", { children: [_jsx("label", { htmlFor: "comment", className: "block text-sm font-medium text-gray-700 mb-1", children: "Review" }), _jsx("textarea", { id: "comment", required: true, rows: 4, className: "w-full rounded-md border border-gray-300 px-3 py-2 text-sm focus:outline-none focus:ring-2 focus:ring-black", placeholder: "What did you like or dislike?", value: newReview.comment, onChange: (e) => setNewReview({ ...newReview, comment: e.target.value }) })] }), _jsxs("div", { className: "pt-4 flex justify-end gap-3", children: [_jsx(Button, { type: "button", variant: "outline", onClick: () => setIsReviewModalOpen(false), children: "Cancel" }), _jsx(Button, { type: "submit", className: "bg-black text-white hover:bg-gray-800", children: "Submit Review" })] })] }) }), _jsx(GuestCheckoutModal, { isOpen: showGuestModal, onClose: () => setShowGuestModal(false), onSubmit: handleGuestInfoSubmit, initialData: getGuestUserInfo() || undefined })] }));
409
+ : "border-gray-200 hover:border-gray-300 hover:bg-gray-50"), children: isWishlistLoading ? (_jsx(ButtonLoader, { className: "h-5 w-5" })) : (_jsx(Heart, { className: cn("h-5 w-5", product && isInWishlist(product.id) && "fill-current") })) }))] })] }), _jsx(ProductDeliveryInfoSection, { storeConfig: storeConfig, currency: storeConfig.settings?.currency || 'USD', productCurrency: product.currency, productPrice: displayPrice, quantity: quantity, shippingAddress: shippingAddress, updateShippingAddress: (patch) => setShippingAddress((prev) => ({ ...prev, ...patch })), shippingMethods: shippingMethods, isCalculatingShipping: isCalculatingShipping, shippingError: shippingError, freeShippingEligible: freeShippingEligible, freeShippingThreshold: freeShippingThreshold, onToast: addToast }), product.description && (_jsxs("div", { className: "mb-8", children: [_jsx("h2", { className: "text-lg font-semibold text-gray-900 mb-3", children: "Description" }), _jsxs("div", { className: "prose prose-sm max-w-none text-gray-600 leading-relaxed", children: [_jsx(SafeRichHtml, { value: product.description, fallbackPlain: "", as: "div" }), _jsxs("ul", { className: "list-disc pl-5 space-y-1 mt-2", children: [_jsx("li", { children: "Premium quality material" }), _jsx("li", { children: "Comfortable fit for all-day wear" }), _jsx("li", { children: "Durable stitching and finish" })] })] })] })), product.specifications && Object.keys(product.specifications).length > 0 && (_jsxs("div", { className: "mb-8", children: [_jsx("h2", { className: "text-lg font-semibold text-gray-900 mb-4", children: "Specifications" }), _jsx("dl", { className: "grid grid-cols-1 sm:grid-cols-2 gap-x-4 gap-y-4", children: Object.entries(product.specifications).map(([key, value]) => (_jsxs("div", { className: "flex flex-col p-3 bg-gray-50 rounded-lg", children: [_jsx("dt", { className: "text-xs text-gray-500 font-medium uppercase tracking-wider mb-1", children: key }), _jsx("dd", { className: "text-sm font-bold text-gray-900", children: value })] }, key))) })] }))] })] }), productDetailConfig?.showReviews !== false && (_jsx("div", { className: "mt-20 pt-16 border-t border-gray-200", children: _jsxs("div", { className: "max-w-4xl", children: [_jsxs("div", { className: "flex flex-col sm:flex-row items-start sm:items-center justify-between gap-6 mb-10", children: [_jsxs("div", { className: "flex-1", children: [_jsx("h2", { className: "text-2xl lg:text-3xl font-bold text-gray-900 mb-4", children: "Customer Reviews" }), reviewCount > 0 && averageRating > 0 && (_jsxs("div", { className: "flex items-center gap-4", children: [_jsxs("div", { className: "flex items-center gap-2", children: [_jsx(StarRating, { rating: averageRating, size: "lg" }), _jsx("span", { className: "text-2xl font-bold text-gray-900", children: averageRating.toFixed(1) }), _jsx("span", { className: "text-lg text-gray-500", children: "out of 5" })] }), _jsx("span", { className: "text-gray-300", children: "|" }), _jsxs("span", { className: "text-lg text-gray-600", children: [reviewCount, " ", reviewCount === 1 ? 'review' : 'reviews'] })] }))] }), reviewCount > 0 && (_jsx(Button, { onClick: () => {
410
+ if (!isAuthenticated) {
411
+ openLogin({ onSuccess: () => setIsReviewModalOpen(true) });
412
+ return;
413
+ }
414
+ setIsReviewModalOpen(true);
415
+ }, className: "rounded-full shrink-0 bg-gray-900 hover:bg-gray-800 text-white", children: "Write a Review" }))] }), ratingDistribution.some(r => r.count > 0) && (_jsxs("div", { className: "mb-10 p-6 bg-gray-50 rounded-xl border border-gray-100", children: [_jsx("h3", { className: "text-sm font-semibold text-gray-900 mb-5", children: "Rating Breakdown" }), _jsx("div", { className: "space-y-3", children: ratingDistribution.map(({ rating, count, percentage }) => (_jsxs("div", { className: "flex items-center gap-4", children: [_jsxs("div", { className: "flex items-center gap-1.5 w-20", children: [_jsx("span", { className: "text-sm font-semibold text-gray-900", children: rating }), _jsx(Star, { className: "h-4 w-4 fill-gray-900 text-gray-900" })] }), _jsx("div", { className: "flex-1 h-2.5 bg-gray-200 rounded-full overflow-hidden", children: _jsx("div", { className: "h-full bg-gradient-to-r from-gray-700 to-gray-900 rounded-full transition-all duration-500", style: { width: `${percentage}%` } }) }), _jsx("span", { className: "text-sm font-medium text-gray-700 w-12 text-right", children: count })] }, rating))) })] })), reviews.length > 0 ? (_jsxs(_Fragment, { children: [_jsx("div", { className: "space-y-6", children: displayedReviews.map((review) => (_jsx(ReviewCard, { review: review }, review.id))) }), hasMoreReviews && !showAllReviews && (_jsx("div", { className: "mt-8 text-center", children: _jsxs(Button, { variant: "outline", onClick: () => setShowAllReviews(true), className: "px-8 rounded-full h-12 border-gray-300", children: ["Show All ", reviews.length, " Reviews"] }) }))] })) : (_jsxs("div", { className: "text-center py-12 bg-gray-50 rounded-xl border border-dashed border-gray-300", children: [_jsx("div", { className: "inline-flex items-center justify-center w-12 h-12 rounded-full bg-white shadow-sm mb-4", children: _jsx(Star, { className: "h-6 w-6 text-gray-400" }) }), _jsx("h3", { className: "text-lg font-semibold text-gray-900 mb-2", children: "No Reviews Yet" }), _jsx("p", { className: "text-gray-500 max-w-sm mx-auto mb-6", children: "Be the first to review this product and help other customers make informed decisions." }), _jsx(Button, { onClick: () => {
416
+ if (!isAuthenticated) {
417
+ openLogin({ onSuccess: () => setIsReviewModalOpen(true) });
418
+ return;
419
+ }
420
+ setIsReviewModalOpen(true);
421
+ }, className: "bg-gray-900 hover:bg-gray-800 text-white", children: "Write a Review" })] }))] }) })), productDetailConfig?.showRelatedProducts !== false && relatedProducts.length > 0 && (_jsxs("div", { className: "mt-20 pt-16 border-t border-gray-200", children: [_jsx("h2", { className: "text-2xl lg:text-3xl font-bold text-gray-900 mb-8", children: "You Might Also Like" }), _jsx("div", { className: "grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-4 gap-6", children: relatedProducts.map((relatedProduct) => (_jsx("div", { className: "h-[400px]", children: _jsx(ProductCard, { product: relatedProduct, storeSlug: storeConfig.slug, onAddToCart: (p) => addToCart(p, 1), onQuickView: handleQuickView, className: "h-full" }) }, relatedProduct.id))) })] }))] }), _jsx(Modal, { isOpen: isReviewModalOpen, onClose: () => setIsReviewModalOpen(false), title: "Write a Review", children: _jsxs("form", { onSubmit: handleSubmitReview, className: "space-y-4", children: [_jsxs("div", { children: [_jsx("label", { className: "block text-sm font-medium text-gray-700 mb-2", children: "Rating" }), _jsxs("div", { className: "flex items-center gap-1", children: [[1, 2, 3, 4, 5].map((star) => (_jsxs("div", { className: "relative group", children: [_jsx("button", { type: "button", onClick: () => setNewReview({ ...newReview, rating: star - 0.5 }), className: "absolute top-0 left-0 w-1/2 h-full z-20 focus:outline-none", title: `${star - 0.5} stars` }), _jsx("button", { type: "button", onClick: () => setNewReview({ ...newReview, rating: star }), className: "absolute top-0 right-0 w-1/2 h-full z-20 focus:outline-none", title: `${star} stars` }), _jsxs("div", { className: "relative pointer-events-none transition-transform group-hover:scale-110", children: [_jsx(Star, { className: "h-8 w-8 text-gray-200 fill-gray-200" }), newReview.rating >= star - 0.5 && (_jsx("div", { className: "absolute inset-0 overflow-hidden", style: { width: newReview.rating === star - 0.5 ? '50%' : '100%' }, children: _jsx(Star, { className: "h-8 w-8 text-gray-900 fill-gray-900" }) }))] })] }, star))), _jsx("span", { className: "ml-3 text-lg font-medium text-gray-900 w-12", children: newReview.rating })] })] }), _jsxs("div", { children: [_jsx("label", { htmlFor: "title", className: "block text-sm font-medium text-gray-700 mb-1", children: "Review Title" }), _jsx("input", { type: "text", id: "title", required: true, className: "w-full rounded-md border border-gray-300 px-3 py-2 text-sm focus:outline-none focus:ring-2 focus:ring-black", placeholder: "Summarize your thoughts", value: newReview.title, onChange: (e) => setNewReview({ ...newReview, title: e.target.value }) })] }), _jsxs("div", { children: [_jsx("label", { htmlFor: "comment", className: "block text-sm font-medium text-gray-700 mb-1", children: "Review" }), _jsx("textarea", { id: "comment", required: true, rows: 4, className: "w-full rounded-md border border-gray-300 px-3 py-2 text-sm focus:outline-none focus:ring-2 focus:ring-black", placeholder: "What did you like or dislike?", value: newReview.comment, onChange: (e) => setNewReview({ ...newReview, comment: e.target.value }) })] }), _jsxs("div", { className: "pt-4 flex justify-end gap-3", children: [_jsx(Button, { type: "button", variant: "outline", onClick: () => setIsReviewModalOpen(false), children: "Cancel" }), _jsx(Button, { type: "submit", className: "bg-black text-white hover:bg-gray-800", children: "Submit Review" })] })] }) }), _jsx(GuestCheckoutModal, { isOpen: showGuestModal, onClose: () => setShowGuestModal(false), onSubmit: handleGuestInfoSubmit, initialData: getGuestUserInfo() || undefined })] }));
515
422
  }
@@ -1 +1 @@
1
- {"version":3,"file":"ProductsPage.d.ts","sourceRoot":"","sources":["../../../../src/layouts/clothing-minimal/pages/ProductsPage.tsx"],"names":[],"mappings":"AAEA,OAAO,EAAE,WAAW,EAAgB,MAAM,0BAA0B,CAAC;AAkCrE,UAAU,iBAAiB;IACzB,WAAW,EAAE,WAAW,CAAC;CAC1B;AAuwBD,wBAAgB,YAAY,CAAC,KAAK,EAAE,iBAAiB,+BAMpD"}
1
+ {"version":3,"file":"ProductsPage.d.ts","sourceRoot":"","sources":["../../../../src/layouts/clothing-minimal/pages/ProductsPage.tsx"],"names":[],"mappings":"AAEA,OAAO,EAAE,WAAW,EAAgB,MAAM,0BAA0B,CAAC;AAmCrE,UAAU,iBAAiB;IACzB,WAAW,EAAE,WAAW,CAAC;CAC1B;AAiwBD,wBAAgB,YAAY,CAAC,KAAK,EAAE,iBAAiB,+BAMpD"}
@@ -21,6 +21,7 @@ import { getAllCategoryIds, flattenCategoryTree, buildCategoryTree } from '../..
21
21
  import { getBannerImage, getLayoutText } from '../../../lib/utils/asset-helpers';
22
22
  import { SafeRichHtml } from '../../../components/rich-text/SafeRichHtml';
23
23
  import { shouldUseAPI } from '../../../lib/utils/demo-detection';
24
+ import { productCategoryFilterChipClass } from '../../shared/product-category-filter-chip';
24
25
  import { useCatalogPriceRange } from '../../../hooks/use-catalog-price-range.js';
25
26
  import { getApiPriceFilterActive, isWithinActivePriceRange } from '../../../lib/utils/catalog-price-bounds.js';
26
27
  import { buildFilteredProductQuery, getFilteredProductQueryKey, productMatchesCategoryIds, productMatchesCategorySlug, } from '../../../lib/utils/filtered-product-query.js';
@@ -312,11 +313,7 @@ function ProductsPageContent({ storeConfig: initialConfig }) {
312
313
  element?.scrollIntoView({ behavior: 'smooth' });
313
314
  }, children: layoutConfig?.sections?.productsHeader?.primaryButtonText || primaryButtonText })), (layoutConfig?.sections?.productsHeader?.secondaryButtonText || secondaryButtonText) && (layoutConfig?.sections?.productsHeader?.secondaryButtonText || secondaryButtonText).trim() !== '' && (_jsx(Button, { className: `h-12 px-8 rounded-full text-base font-medium shadow-lg hover:shadow-xl transition-all duration-300 transform hover:-translate-y-1 ${(layoutConfig?.sections?.productsHeader?.image || categoryHeaderImage) ? 'bg-white/10 backdrop-blur-md text-white border border-white/30 hover:bg-white/20' : 'bg-gray-100 text-gray-900 hover:bg-gray-200 border border-gray-200'}`, onClick: () => {
314
315
  window.location.href = `/${storeConfig.slug}/categories`;
315
- }, children: layoutConfig?.sections?.productsHeader?.secondaryButtonText || secondaryButtonText }))] })] }) }) })] })), _jsxs("div", { "data-content-ready": true, className: "container mx-auto px-4 sm:px-6 lg:px-8 py-8", children: [_jsxs("div", { className: "flex gap-3 mb-8 overflow-x-auto pb-2 scrollbar-hide", children: [_jsx("button", { onClick: () => setSelectedCategoryFilter(null), className: `px-6 py-2.5 rounded-lg text-sm font-semibold whitespace-nowrap transition-all ${!selectedCategoryFilter
316
- ? 'bg-gray-900 text-white hover:bg-blue-600'
317
- : 'bg-gray-100 text-gray-700 hover:bg-blue-50 hover:text-blue-600'}`, children: "All" }), categories.map((category) => (_jsx("button", { onClick: () => setSelectedCategoryFilter(selectedCategoryFilter === category.slug ? null : category.slug), className: `px-6 py-2.5 rounded-lg text-sm font-semibold whitespace-nowrap transition-all ${selectedCategoryFilter === category.slug
318
- ? 'bg-gray-900 text-white hover:bg-blue-600'
319
- : 'bg-gray-100 text-gray-700 hover:bg-blue-50 hover:text-blue-600'}`, children: category.name }, category.id)))] }), _jsxs("div", { id: "product-grid", className: "grid grid-cols-1 lg:grid-cols-4 gap-8", children: [_jsxs("aside", { className: "hidden lg:block col-span-1 space-y-8 sticky top-24 h-fit max-h-[calc(100vh-8rem)] overflow-y-auto pr-2 scrollbar-thin scrollbar-thumb-gray-200 scrollbar-track-transparent", children: [_jsxs("div", { className: "flex items-center justify-between pb-4 border-b border-gray-100", children: [_jsx("h2", { className: "text-xl font-bold text-gray-900", children: "Filters" }), hasActiveFilters && (_jsxs("button", { onClick: clearAllFilters, className: "text-xs font-medium text-red-500 hover:text-red-700 flex items-center gap-1", children: [_jsx(RotateCcw, { className: "h-3 w-3" }), " Reset"] }))] }), _jsxs("div", { children: [_jsx("h3", { className: "text-sm font-semibold text-gray-900 uppercase tracking-wide mb-4", children: "Availability" }), _jsxs("label", { className: "flex items-center gap-3 cursor-pointer group p-2 -ml-2 rounded-lg hover:bg-gray-50 transition-colors", children: [_jsx("div", { className: `w-5 h-5 rounded border flex items-center justify-center transition-colors ${showInStockOnly ? 'bg-black border-black' : 'border-gray-300 group-hover:border-black'}`, children: showInStockOnly && _jsx(Check, { className: "h-3 w-3 text-white" }) }), _jsx("input", { type: "checkbox", className: "hidden", checked: showInStockOnly, onChange: () => setShowInStockOnly(!showInStockOnly) }), _jsx("span", { className: "text-sm text-gray-700 font-medium", children: "In Stock Only" })] })] }), _jsxs("div", { children: [_jsxs("div", { className: "flex items-center justify-between mb-4", children: [_jsx("h3", { className: "text-sm font-semibold text-gray-900 uppercase tracking-wide", children: "Brand" }), selectedBrands.length > 0 && (_jsx("button", { onClick: () => setSelectedBrands([]), className: "text-xs text-gray-600 hover:text-blue-600 font-medium transition-colors", children: "Clear" }))] }), brands.length > 0 ? (_jsxs("div", { className: "space-y-2", children: [(showMoreBrands ? brands : brands.slice(0, 6)).map((brand) => (_jsx("label", { className: "flex items-center justify-between cursor-pointer group hover:bg-blue-50 hover:text-blue-600 p-2 rounded-lg -ml-2 -mr-2 transition-colors", children: _jsxs("div", { className: "flex items-center gap-3", children: [_jsx("input", { type: "checkbox", checked: selectedBrands.includes(brand), onChange: () => setSelectedBrands(prev => prev.includes(brand) ? prev.filter(b => b !== brand) : [...prev, brand]), className: "w-4 h-4 text-blue-600 border-gray-300 rounded focus:ring-blue-500" }), _jsx("span", { className: "text-sm text-gray-700 font-medium group-hover:text-blue-600", children: brand })] }) }, brand))), brands.length > 6 && (_jsx("button", { onClick: () => setShowMoreBrands(!showMoreBrands), className: "text-sm text-gray-600 hover:text-blue-600 font-medium mt-2 flex items-center gap-1 transition-colors", children: showMoreBrands ? _jsxs(_Fragment, { children: ["Show less ", _jsx(ChevronUp, { className: "h-4 w-4" })] }) : _jsxs(_Fragment, { children: ["Show more ", _jsx(ChevronDown, { className: "h-4 w-4" })] }) }))] })) : (_jsx("p", { className: "text-xs text-gray-500 italic", children: "No brands available" }))] }), _jsxs("div", { children: [_jsx("h3", { className: "text-sm font-semibold text-gray-900 uppercase tracking-wide mb-4", children: "Category" }), _jsx(CategoryTree, { categories: categories, selectedCategoryIds: selectedCategories, onCategoryToggle: (categoryId, isSelected) => {
316
+ }, children: layoutConfig?.sections?.productsHeader?.secondaryButtonText || secondaryButtonText }))] })] }) }) })] })), _jsxs("div", { "data-content-ready": true, className: "container mx-auto px-4 sm:px-6 lg:px-8 py-8", children: [_jsxs("div", { className: "flex gap-3 mb-8 overflow-x-auto pb-2 scrollbar-hide", children: [_jsx("button", { onClick: () => setSelectedCategoryFilter(null), className: productCategoryFilterChipClass(!selectedCategoryFilter), children: "All" }), categories.map((category) => (_jsx("button", { onClick: () => setSelectedCategoryFilter(selectedCategoryFilter === category.slug ? null : category.slug), className: productCategoryFilterChipClass(selectedCategoryFilter === category.slug), children: category.name }, category.id)))] }), _jsxs("div", { id: "product-grid", className: "grid grid-cols-1 lg:grid-cols-4 gap-8", children: [_jsxs("aside", { className: "hidden lg:block col-span-1 space-y-8 sticky top-24 h-fit max-h-[calc(100vh-8rem)] overflow-y-auto pr-2 scrollbar-thin scrollbar-thumb-gray-200 scrollbar-track-transparent", children: [_jsxs("div", { className: "flex items-center justify-between pb-4 border-b border-gray-100", children: [_jsx("h2", { className: "text-xl font-bold text-gray-900", children: "Filters" }), hasActiveFilters && (_jsxs("button", { onClick: clearAllFilters, className: "text-xs font-medium text-red-500 hover:text-red-700 flex items-center gap-1", children: [_jsx(RotateCcw, { className: "h-3 w-3" }), " Reset"] }))] }), _jsxs("div", { children: [_jsx("h3", { className: "text-sm font-semibold text-gray-900 uppercase tracking-wide mb-4", children: "Availability" }), _jsxs("label", { className: "flex items-center gap-3 cursor-pointer group p-2 -ml-2 rounded-lg hover:bg-gray-50 transition-colors", children: [_jsx("div", { className: `w-5 h-5 rounded border flex items-center justify-center transition-colors ${showInStockOnly ? 'bg-black border-black' : 'border-gray-300 group-hover:border-black'}`, children: showInStockOnly && _jsx(Check, { className: "h-3 w-3 text-white" }) }), _jsx("input", { type: "checkbox", className: "hidden", checked: showInStockOnly, onChange: () => setShowInStockOnly(!showInStockOnly) }), _jsx("span", { className: "text-sm text-gray-700 font-medium", children: "In Stock Only" })] })] }), _jsxs("div", { children: [_jsxs("div", { className: "flex items-center justify-between mb-4", children: [_jsx("h3", { className: "text-sm font-semibold text-gray-900 uppercase tracking-wide", children: "Brand" }), selectedBrands.length > 0 && (_jsx("button", { onClick: () => setSelectedBrands([]), className: "text-xs text-gray-600 hover:text-blue-600 font-medium transition-colors", children: "Clear" }))] }), brands.length > 0 ? (_jsxs("div", { className: "space-y-2", children: [(showMoreBrands ? brands : brands.slice(0, 6)).map((brand) => (_jsx("label", { className: "flex items-center justify-between cursor-pointer group hover:bg-blue-50 hover:text-blue-600 p-2 rounded-lg -ml-2 -mr-2 transition-colors", children: _jsxs("div", { className: "flex items-center gap-3", children: [_jsx("input", { type: "checkbox", checked: selectedBrands.includes(brand), onChange: () => setSelectedBrands(prev => prev.includes(brand) ? prev.filter(b => b !== brand) : [...prev, brand]), className: "w-4 h-4 text-blue-600 border-gray-300 rounded focus:ring-blue-500" }), _jsx("span", { className: "text-sm text-gray-700 font-medium group-hover:text-blue-600", children: brand })] }) }, brand))), brands.length > 6 && (_jsx("button", { onClick: () => setShowMoreBrands(!showMoreBrands), className: "text-sm text-gray-600 hover:text-blue-600 font-medium mt-2 flex items-center gap-1 transition-colors", children: showMoreBrands ? _jsxs(_Fragment, { children: ["Show less ", _jsx(ChevronUp, { className: "h-4 w-4" })] }) : _jsxs(_Fragment, { children: ["Show more ", _jsx(ChevronDown, { className: "h-4 w-4" })] }) }))] })) : (_jsx("p", { className: "text-xs text-gray-500 italic", children: "No brands available" }))] }), _jsxs("div", { children: [_jsx("h3", { className: "text-sm font-semibold text-gray-900 uppercase tracking-wide mb-4", children: "Category" }), _jsx(CategoryTree, { categories: categories, selectedCategoryIds: selectedCategories, onCategoryToggle: (categoryId, isSelected) => {
320
317
  setSelectedCategories(prev => isSelected
321
318
  ? [...prev, categoryId]
322
319
  : prev.filter(id => id !== categoryId));
@@ -110,7 +110,7 @@ export function ElectronicsGridProductCard({ product, storeSlug, onAddToCart, in
110
110
  if (!isOutOfStock) {
111
111
  onAddToCart(product);
112
112
  }
113
- }, disabled: isOutOfStock, className: cn("w-full bg-gray-900 hover:bg-gray-800 text-white border-none rounded-none h-11 font-normal text-sm transition-colors", isOutOfStock && "opacity-50 cursor-not-allowed"), children: [_jsx(Plus, { className: "w-4 h-4 mr-2" }), " Quick Add"] }) }), _jsx("button", { disabled: isOutOfStock, className: cn("lg:hidden absolute bottom-3 right-3 z-30 w-10 h-10 rounded-full bg-gray-900 text-white flex items-center justify-center hover:bg-gray-800 active:scale-95 transition-colors", isOutOfStock && "opacity-50 cursor-not-allowed"), onClick: (e) => {
113
+ }, disabled: isOutOfStock, className: cn("w-full bg-gray-900 hover:bg-gray-800 text-white border-none rounded-none h-11 font-normal text-sm transition-colors", isOutOfStock && "opacity-50 cursor-not-allowed"), children: [_jsx(Plus, { className: "w-4 h-4 mr-2" }), " Add to cart"] }) }), _jsx("button", { disabled: isOutOfStock, className: cn("lg:hidden absolute bottom-3 right-3 z-30 w-10 h-10 rounded-full bg-gray-900 text-white flex items-center justify-center hover:bg-gray-800 active:scale-95 transition-colors", isOutOfStock && "opacity-50 cursor-not-allowed"), onClick: (e) => {
114
114
  e.preventDefault();
115
115
  if (!isOutOfStock) {
116
116
  onAddToCart(product);
@@ -1 +1 @@
1
- {"version":3,"file":"ElectronicsStoreHeader.d.ts","sourceRoot":"","sources":["../../../../src/layouts/electronics/components/ElectronicsStoreHeader.tsx"],"names":[],"mappings":"AAEA,OAAO,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAC;AAYhD,UAAU,2BAA2B;IACnC,WAAW,EAAE,WAAW,CAAC;CAC1B;AAED,wBAAgB,sBAAsB,CAAC,EAAE,WAAW,EAAE,EAAE,2BAA2B,+BAoJlF"}
1
+ {"version":3,"file":"ElectronicsStoreHeader.d.ts","sourceRoot":"","sources":["../../../../src/layouts/electronics/components/ElectronicsStoreHeader.tsx"],"names":[],"mappings":"AAEA,OAAO,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAC;AAahD,UAAU,2BAA2B;IACnC,WAAW,EAAE,WAAW,CAAC;CAC1B;AAED,wBAAgB,sBAAsB,CAAC,EAAE,WAAW,EAAE,EAAE,2BAA2B,+BA4JlF"}