From 487bbc562a881d0f86ac219f74b8660bf0a5f84a Mon Sep 17 00:00:00 2001 From: Samir Boulahtit Date: Sun, 7 Dec 2025 17:14:27 +0100 Subject: [PATCH] feat: add Priority 3 product detail e-commerce macros MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit New macros in shared/macros/shop/: - product-gallery.html: Image gallery with thumbnails, zoom, lightbox - variant-selector.html: Size buttons, color swatches, multi-variant - product-info.html: Product details with price, rating, stock, badges - product-tabs.html: Tabbed content (description, specs, reviews) All components support: - Dark mode via Tailwind dark: classes - Alpine.js integration for reactivity - Responsive design - Accessible markup 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 --- .../shared/macros/shop/product-gallery.html | 398 +++++++++++++++++ .../shared/macros/shop/product-info.html | 359 +++++++++++++++ .../shared/macros/shop/product-tabs.html | 400 +++++++++++++++++ .../shared/macros/shop/variant-selector.html | 418 ++++++++++++++++++ 4 files changed, 1575 insertions(+) create mode 100644 app/templates/shared/macros/shop/product-gallery.html create mode 100644 app/templates/shared/macros/shop/product-info.html create mode 100644 app/templates/shared/macros/shop/product-tabs.html create mode 100644 app/templates/shared/macros/shop/variant-selector.html diff --git a/app/templates/shared/macros/shop/product-gallery.html b/app/templates/shared/macros/shop/product-gallery.html new file mode 100644 index 00000000..799720a0 --- /dev/null +++ b/app/templates/shared/macros/shop/product-gallery.html @@ -0,0 +1,398 @@ +{# + Product Gallery Components + ========================== + Image gallery with thumbnails, zoom, and lightbox for product detail pages. + + Usage: + {% from 'shared/macros/shop/product-gallery.html' import product_gallery, gallery_thumbnails %} +#} + + +{# + Product Gallery + =============== + Full image gallery with main image and thumbnails. + + Parameters: + - images_var: Alpine.js expression for images array (default: 'product.images') + - selected_var: Alpine.js variable for selected image index (default: 'selectedImage') + - show_thumbnails: Show thumbnail navigation (default: true) + - enable_zoom: Enable hover zoom on main image (default: true) + - enable_lightbox: Enable fullscreen lightbox (default: true) + - max_thumbnails: Max thumbnails to show (default: 5) + - aspect_ratio: Main image aspect ratio (default: 'square') + + Expected image object: + { + id: 1, + url: 'https://...', + thumbnail_url: 'https://...', + alt: 'Product image' + } + + Usage: + {{ product_gallery(images_var='product.images') }} +#} +{% macro product_gallery( + images_var='product.images', + selected_var='selectedImage', + show_thumbnails=true, + enable_zoom=true, + enable_lightbox=true, + max_thumbnails=5, + aspect_ratio='square' +) %} +{% set aspects = { + 'square': 'aspect-square', + '4:3': 'aspect-[4/3]', + '3:4': 'aspect-[3/4]', + '16:9': 'aspect-video', + '3:2': 'aspect-[3/2]' +} %} +{% set aspect_class = aspects.get(aspect_ratio, 'aspect-square') %} + +
+ {# Main Image Container #} +
+ {# Main Image #} +
+ {# Product Image #} + + + {# Zoom Indicator #} + {% if enable_zoom %} +
+ + Hover to zoom +
+ {% endif %} + + {# Lightbox Button #} + {% if enable_lightbox %} + + {% endif %} +
+ + {# Navigation Arrows #} + + + {# Image Counter #} + +
+ + {# Thumbnails #} + {% if show_thumbnails %} +
+
+ + + {# More Images Indicator #} + +
+
+ {% endif %} + + {# Lightbox Modal #} + {% if enable_lightbox %} + + {% endif %} +
+{% endmacro %} + + +{# + Gallery Thumbnails Only + ======================= + Standalone thumbnail strip for custom layouts. + + Parameters: + - images_var: Alpine.js expression for images array + - selected_var: Alpine.js variable for selected index + - orientation: 'horizontal' | 'vertical' (default: 'horizontal') + - size: 'sm' | 'md' | 'lg' (default: 'md') + + Usage: + {{ gallery_thumbnails(images_var='product.images', orientation='vertical') }} +#} +{% macro gallery_thumbnails( + images_var='product.images', + selected_var='selectedImage', + orientation='horizontal', + size='md' +) %} +{% set sizes = { + 'sm': {'container': 'w-12 h-12', 'gap': 'gap-1'}, + 'md': {'container': 'w-16 h-16', 'gap': 'gap-2'}, + 'lg': {'container': 'w-20 h-20', 'gap': 'gap-3'} +} %} +{% set s = sizes[size] %} +{% set is_vertical = orientation == 'vertical' %} + +
+ +
+{% endmacro %} + + +{# + Simple Image Viewer + =================== + Single image with optional lightbox (for simple product pages). + + Parameters: + - image_var: Alpine.js expression for image object or URL string + - enable_lightbox: Enable click to enlarge (default: true) + - aspect_ratio: Image aspect ratio (default: 'square') + + Usage: + {{ simple_image_viewer(image_var='product.image_url') }} +#} +{% macro simple_image_viewer( + image_var='product.image_url', + enable_lightbox=true, + aspect_ratio='square' +) %} +{% set aspects = { + 'square': 'aspect-square', + '4:3': 'aspect-[4/3]', + '3:4': 'aspect-[3/4]', + '16:9': 'aspect-video' +} %} +{% set aspect_class = aspects.get(aspect_ratio, 'aspect-square') %} + +
+
+ + + {% if enable_lightbox %} +
+ +
+ {% endif %} +
+ + {% if enable_lightbox %} + {# Lightbox #} + + {% endif %} +
+{% endmacro %} diff --git a/app/templates/shared/macros/shop/product-info.html b/app/templates/shared/macros/shop/product-info.html new file mode 100644 index 00000000..a7fa18a4 --- /dev/null +++ b/app/templates/shared/macros/shop/product-info.html @@ -0,0 +1,359 @@ +{# + Product Info Components + ======================= + Product details section for product detail pages. + + Usage: + {% from 'shared/macros/shop/product-info.html' import product_info, product_price, product_rating, stock_status %} +#} + + +{# + Product Info Block + ================== + Complete product information section. + + Parameters: + - product_var: Alpine.js expression for product (default: 'product') + - show_sku: Show SKU (default: false) + - show_stock: Show stock status (default: true) + - show_rating: Show star rating (default: true) + - show_vendor: Show vendor name - for marketplace (default: false) + - show_category: Show category breadcrumb (default: false) + - title_tag: HTML tag for title (default: 'h1') + + Expected product object: + { + name: 'Product Name', + sku: 'SKU-123', + price: 99.99, + sale_price: 79.99, + rating: 4.5, + review_count: 127, + stock: 15, + short_description: '...', + vendor: { name: 'Vendor Name', url: '/vendor/...' }, + category: { name: 'Category', url: '/category/...' } + } + + Usage: + {{ product_info(product_var='product', show_vendor=true) }} +#} +{% macro product_info( + product_var='product', + show_sku=false, + show_stock=true, + show_rating=true, + show_vendor=false, + show_category=false, + title_tag='h1' +) %} +
+ {# Category / Vendor (if marketplace) #} + {% if show_category or show_vendor %} +
+ {% if show_category %} + + {% endif %} + {% if show_category and show_vendor %} + + {% endif %} + {% if show_vendor %} + + {% endif %} +
+ {% endif %} + + {# Product Title #} + <{{ title_tag }} + class="text-2xl md:text-3xl font-bold text-gray-900 dark:text-white" + x-text="{{ product_var }}.name" + > + + {# Rating and Review Count #} + {% if show_rating %} +
+ {{ product_rating(product_var=product_var, size='md', clickable=true) }} +
+ {% endif %} + + {# Price #} + {{ product_price(product_var=product_var, size='lg') }} + + {# Short Description #} +
+ + {# Stock Status #} + {% if show_stock %} + {{ stock_status(product_var=product_var) }} + {% endif %} + + {# SKU #} + {% if show_sku %} +
+ SKU: +
+ {% endif %} +
+{% endmacro %} + + +{# + Product Price + ============= + Price display with sale price support. + + Parameters: + - product_var: Alpine.js expression for product + - size: 'sm' | 'md' | 'lg' (default: 'md') + - show_discount: Show discount percentage (default: true) + + Usage: + {{ product_price(product_var='product', size='lg') }} +#} +{% macro product_price( + product_var='product', + size='md', + show_discount=true +) %} +{% set sizes = { + 'sm': {'price': 'text-lg', 'original': 'text-sm', 'badge': 'text-xs px-1.5 py-0.5'}, + 'md': {'price': 'text-xl', 'original': 'text-base', 'badge': 'text-xs px-2 py-0.5'}, + 'lg': {'price': 'text-2xl md:text-3xl', 'original': 'text-lg', 'badge': 'text-sm px-2 py-1'} +} %} +{% set s = sizes[size] %} + +
+ {# Current Price (sale or regular) #} + + + {# Original Price (if on sale) #} + + + {# Discount Badge #} + {% if show_discount %} + + {% endif %} +
+{% endmacro %} + + +{# + Product Rating + ============== + Star rating display with review count. + + Parameters: + - product_var: Alpine.js expression for product + - size: 'sm' | 'md' | 'lg' (default: 'md') + - clickable: Make clickable to scroll to reviews (default: false) + - show_count: Show review count (default: true) + + Usage: + {{ product_rating(product_var='product', clickable=true) }} +#} +{% macro product_rating( + product_var='product', + size='md', + clickable=false, + show_count=true +) %} +{% set sizes = { + 'sm': {'star': 'w-4 h-4', 'text': 'text-xs'}, + 'md': {'star': 'w-5 h-5', 'text': 'text-sm'}, + 'lg': {'star': 'w-6 h-6', 'text': 'text-base'} +} %} +{% set s = sizes[size] %} + +
+ {# Stars #} +
+ +
+ + {# Rating Number #} + + + {# Review Count #} + {% if show_count %} + + ( reviews) + + {% endif %} +
+{% endmacro %} + + +{# + Stock Status + ============ + Stock availability indicator. + + Parameters: + - product_var: Alpine.js expression for product (or stock number) + - low_stock_threshold: Show warning below this number (default: 10) + + Usage: + {{ stock_status(product_var='product') }} + {{ stock_status(product_var='selectedVariant.stock') }} +#} +{% macro stock_status( + product_var='product', + low_stock_threshold=10 +) %} +
+ {# In Stock #} + + + {# Low Stock #} + + + {# Out of Stock #} + +
+{% endmacro %} + + +{# + Product Badges + ============== + Display multiple product badges (new, sale, bestseller, etc.). + + Parameters: + - product_var: Alpine.js expression for product + + Usage: + {{ product_badges(product_var='product') }} +#} +{% macro product_badges(product_var='product') %} +
+ {# Sale Badge #} + + + {# New Badge #} + + + {# Bestseller Badge #} + + + {# Limited Edition Badge #} + +
+{% endmacro %} + + +{# + Trust Indicators + ================ + Display trust/shipping information below add to cart. + + Parameters: + - show_shipping: Show free shipping info (default: true) + - show_returns: Show returns policy (default: true) + - show_secure: Show secure checkout badge (default: true) + - free_shipping_threshold: Amount for free shipping (default: 50) + + Usage: + {{ trust_indicators(free_shipping_threshold=75) }} +#} +{% macro trust_indicators( + show_shipping=true, + show_returns=true, + show_secure=true, + free_shipping_threshold=50 +) %} +
+ {% if show_shipping %} +
+ + Free shipping on orders over ${{ free_shipping_threshold }} +
+ {% endif %} + + {% if show_returns %} +
+ + 30-day easy returns +
+ {% endif %} + + {% if show_secure %} +
+ + Secure checkout +
+ {% endif %} +
+{% endmacro %} diff --git a/app/templates/shared/macros/shop/product-tabs.html b/app/templates/shared/macros/shop/product-tabs.html new file mode 100644 index 00000000..28094940 --- /dev/null +++ b/app/templates/shared/macros/shop/product-tabs.html @@ -0,0 +1,400 @@ +{# + Product Tabs Components + ======================= + Tabbed content sections for product detail pages. + + Usage: + {% from 'shared/macros/shop/product-tabs.html' import product_tabs, tab_description, tab_specifications, tab_reviews %} +#} + + +{# + Product Tabs + ============ + Tabbed container for product information sections. + + Parameters: + - product_var: Alpine.js expression for product (default: 'product') + - tabs: List of tab IDs to show (default: ['description', 'specifications', 'reviews']) + - default_tab: Initially active tab (default: 'description') + - tab_var: Alpine.js variable for active tab (default: 'activeProductTab') + + Usage: + {{ product_tabs(tabs=['description', 'specifications', 'reviews', 'shipping']) }} +#} +{% macro product_tabs( + product_var='product', + tabs=['description', 'specifications', 'reviews'], + default_tab='description', + tab_var='activeProductTab' +) %} +{% set tab_config = { + 'description': {'label': 'Description', 'icon': 'document-text'}, + 'specifications': {'label': 'Specifications', 'icon': 'clipboard-list'}, + 'reviews': {'label': 'Reviews', 'icon': 'star'}, + 'shipping': {'label': 'Shipping & Returns', 'icon': 'truck'}, + 'warranty': {'label': 'Warranty', 'icon': 'shield-check'} +} %} + +
+ {# Tab Navigation #} +
+ +
+ + {# Tab Panels #} +
+ {% if 'description' in tabs %} + {{ tab_description(product_var, tab_var) }} + {% endif %} + + {% if 'specifications' in tabs %} + {{ tab_specifications(product_var, tab_var) }} + {% endif %} + + {% if 'reviews' in tabs %} + {{ tab_reviews(product_var, tab_var) }} + {% endif %} + + {% if 'shipping' in tabs %} + {{ tab_shipping(tab_var) }} + {% endif %} + + {% if 'warranty' in tabs %} + {{ tab_warranty(product_var, tab_var) }} + {% endif %} +
+
+{% endmacro %} + + +{# + Tab: Description + ================ + Product description content panel. +#} +{% macro tab_description(product_var='product', tab_var='activeProductTab') %} +
+
+ + {# Features List (if available) #} + +
+{% endmacro %} + + +{# + Tab: Specifications + =================== + Product specifications table panel. +#} +{% macro tab_specifications(product_var='product', tab_var='activeProductTab') %} +
+ + + +
+{% endmacro %} + + +{# + Tab: Reviews + ============ + Product reviews list and summary panel. +#} +{% macro tab_reviews(product_var='product', tab_var='activeProductTab') %} +
+ {# Reviews Summary #} +
+ {# Overall Rating #} +
+
+
+ +
+
+ reviews +
+
+ + {# Rating Distribution #} + +
+ + {# Reviews List #} + + + {# No Reviews #} + +
+{% endmacro %} + + +{# + Review Card + =========== + Individual review display. +#} +{% macro review_card() %} +
+ {# Header #} +
+
+ {# Avatar #} +
+ +
+
+
+ + +
+
+
+
+ + {# Rating #} +
+ +
+
+ + {# Title #} + + + {# Content #} +

+ + {# Review Images #} + + + {# Helpful Actions #} +
+ + +
+
+{% endmacro %} + + +{# + Tab: Shipping & Returns + ======================= + Shipping and returns policy panel. +#} +{% macro tab_shipping(tab_var='activeProductTab') %} +
+
+ {# Shipping Info #} +
+

+ + Shipping Information +

+
+
    +
  • Free standard shipping on orders over $50
  • +
  • Standard shipping (3-5 business days): $4.99
  • +
  • Express shipping (1-2 business days): $9.99
  • +
  • Same-day delivery available in select areas
  • +
+
+
+ + {# Returns Info #} +
+

+ + Returns & Exchanges +

+
+
    +
  • 30-day return policy for unused items
  • +
  • Free returns on all orders
  • +
  • Items must be in original packaging
  • +
  • Refunds processed within 5-7 business days
  • +
+
+
+
+
+{% endmacro %} + + +{# + Tab: Warranty + ============= + Product warranty information panel. +#} +{% macro tab_warranty(product_var='product', tab_var='activeProductTab') %} +
+
+

+ + Warranty Information +

+ + + + +
+
+{% endmacro %} diff --git a/app/templates/shared/macros/shop/variant-selector.html b/app/templates/shared/macros/shop/variant-selector.html new file mode 100644 index 00000000..a4e04007 --- /dev/null +++ b/app/templates/shared/macros/shop/variant-selector.html @@ -0,0 +1,418 @@ +{# + Variant Selector Components + =========================== + Product variant selection (size, color, etc.) for product detail pages. + + Usage: + {% from 'shared/macros/shop/variant-selector.html' import variant_selector, size_selector, color_swatches %} +#} + + +{# + Variant Selector + ================ + Generic variant selector that adapts to variant type. + + Parameters: + - variants_var: Alpine.js expression for variants array (default: 'product.variants') + - selected_var: Alpine.js variable for selected variant (default: 'selectedVariant') + - type: 'buttons' | 'dropdown' | 'swatches' (default: 'buttons') + - label: Label text (default: 'Select Option') + - show_stock: Show stock status per variant (default: true) + - on_change: Custom change handler (default: none) + + Expected variant object: + { + id: 1, + name: 'Large', + value: 'L', + stock: 10, + price_modifier: 0, + color_hex: '#FF0000', // For swatches + image_url: '...' // For swatches with preview + } + + Usage: + {{ variant_selector(variants_var='product.sizes', label='Size') }} +#} +{% macro variant_selector( + variants_var='product.variants', + selected_var='selectedVariant', + type='buttons', + label='Select Option', + show_stock=true, + on_change=none +) %} +
+ {# Label #} +
+ + +
+ + {% if type == 'buttons' %} + {{ _variant_buttons(variants_var, selected_var, show_stock, on_change) }} + {% elif type == 'dropdown' %} + {{ _variant_dropdown(variants_var, selected_var, show_stock, on_change) }} + {% elif type == 'swatches' %} + {{ _variant_swatches(variants_var, selected_var, show_stock, on_change) }} + {% endif %} +
+{% endmacro %} + + +{# + Internal: Variant Buttons +#} +{% macro _variant_buttons(variants_var, selected_var, show_stock, on_change) %} +
+ +
+{% endmacro %} + + +{# + Internal: Variant Dropdown +#} +{% macro _variant_dropdown(variants_var, selected_var, show_stock, on_change) %} + +{% endmacro %} + + +{# + Internal: Variant Swatches (for colors) +#} +{% macro _variant_swatches(variants_var, selected_var, show_stock, on_change) %} +
+ +
+{% endmacro %} + + +{# + Size Selector + ============= + Specialized selector for clothing/shoe sizes. + + Parameters: + - sizes_var: Alpine.js expression for sizes array + - selected_var: Alpine.js variable for selected size + - show_guide: Show size guide link (default: true) + - guide_action: Action for size guide button (default: none) + + Usage: + {{ size_selector(sizes_var='product.sizes', guide_action='showSizeGuide = true') }} +#} +{% macro size_selector( + sizes_var='product.sizes', + selected_var='selectedSize', + show_guide=true, + guide_action=none +) %} +
+ {# Label with Size Guide #} +
+ +
+ + {% if show_guide %} + + {% endif %} +
+
+ + {# Size Buttons #} +
+ +
+
+{% endmacro %} + + +{# + Color Swatches + ============== + Specialized selector for color options with preview. + + Parameters: + - colors_var: Alpine.js expression for colors array + - selected_var: Alpine.js variable for selected color + - size: 'sm' | 'md' | 'lg' (default: 'md') + - on_change: Custom change handler (triggers image change, etc.) + + Expected color object: + { + id: 1, + name: 'Red', + value: 'red', + color_hex: '#FF0000', + stock: 10, + image_url: '...' // Optional: product image for this color + } + + Usage: + {{ color_swatches(colors_var='product.colors', on_change='updateProductImage(selectedColor)') }} +#} +{% macro color_swatches( + colors_var='product.colors', + selected_var='selectedColor', + size='md', + on_change=none +) %} +{% set sizes = { + 'sm': {'swatch': 'w-8 h-8', 'icon': 'w-4 h-4'}, + 'md': {'swatch': 'w-10 h-10', 'icon': 'w-5 h-5'}, + 'lg': {'swatch': 'w-12 h-12', 'icon': 'w-6 h-6'} +} %} +{% set s = sizes[size] %} + +
+ {# Label #} +
+ + +
+ + {# Color Swatches #} +
+ +
+
+{% endmacro %} + + +{# + Multi-Option Variant Selector + ============================= + Combined selector for products with multiple option types (size + color). + + Parameters: + - product_var: Alpine.js expression for product + - on_change: Callback when any variant changes + + Expected product structure: + { + options: [ + { name: 'Size', values: [...] }, + { name: 'Color', values: [...] } + ], + variants: [ + { id: 1, options: { size: 'M', color: 'Red' }, stock: 10, price: 99.99 } + ] + } + + Usage: + {{ multi_variant_selector(product_var='product') }} +#} +{% macro multi_variant_selector( + product_var='product', + on_change=none +) %} +
+ + + {# Selected Variant Info #} +
+
+ + In Stock + + Out of Stock + + +
+
+
+{% endmacro %}