diff --git a/app/templates/shared/macros/shop/reviews.html b/app/templates/shared/macros/shop/reviews.html new file mode 100644 index 00000000..b240899f --- /dev/null +++ b/app/templates/shared/macros/shop/reviews.html @@ -0,0 +1,459 @@ +{# + Review Components + ================= + Product review display and submission components. + + Usage: + {% from 'shared/macros/shop/reviews.html' import review_list, review_card, review_form, review_summary %} +#} + +{% from 'shared/macros/shop/star-rating.html' import star_rating, rating_input, rating_summary %} + + +{# + Review Card + =========== + Individual product review display. + + Parameters: + - review: Static review object + - review_var: Alpine.js expression for review (dynamic) + - show_avatar: Show reviewer avatar (default: true) + - show_verified: Show verified purchase badge (default: true) + - show_helpful: Show helpful buttons (default: true) + - show_images: Show review images (default: true) + - on_helpful: Callback for helpful button click + + Review structure: + { + id: 1, + author_name: 'John D.', + author_avatar: null, + rating: 5, + title: 'Great product!', + content: 'Really happy with this purchase...', + verified: true, + created_at: '2025-01-15', + helpful_count: 42, + images: [] + } + + Usage: + {{ review_card(review_var='review') }} +#} +{% macro review_card( + review=none, + review_var=none, + show_avatar=true, + show_verified=true, + show_helpful=true, + show_images=true, + on_helpful=none +) %} +{% if review_var %} +
+ {# Header #} +
+
+ {% if show_avatar %} +
+ + +
+ {% endif %} +
+
+ + {% if show_verified %} + + + Verified Purchase + + {% endif %} +
+
+
+
+
+ +
+
+ + {# Title #} +

+ + {# Content #} +

+ + {% if show_images %} + {# Review Images #} +
+ +
+ {% endif %} + + {% if show_helpful %} + {# Helpful Actions #} +
+ Was this review helpful? +
+ + +
+
+ {% endif %} +
+{% endif %} +{% endmacro %} + + +{# + Review List + =========== + List of reviews with optional sorting and pagination. + + Parameters: + - reviews_var: Alpine.js expression for reviews array + - loading_var: Alpine.js expression for loading state + - empty_message: Message when no reviews (default: 'No reviews yet') + - show_sort: Show sort dropdown (default: true) + - sort_var: Alpine.js var for sort value (default: 'reviewSort') + - on_sort: Callback when sort changes + + Usage: + {{ review_list(reviews_var='reviews', loading_var='loadingReviews') }} +#} +{% macro review_list( + reviews_var='reviews', + loading_var='loading', + empty_message='No reviews yet. Be the first to review this product!', + show_sort=true, + sort_var='reviewSort', + on_sort=none +) %} +
+ {% if show_sort %} + {# Sort Controls #} +
+ + reviews + +
+ +
+ {% for opt in [ + {'value': 'newest', 'label': 'Newest'}, + {'value': 'oldest', 'label': 'Oldest'}, + {'value': 'highest', 'label': 'Highest Rated'}, + {'value': 'lowest', 'label': 'Lowest Rated'}, + {'value': 'helpful', 'label': 'Most Helpful'} + ] %} + + {% endfor %} +
+
+
+ {% endif %} + + {# Loading State #} + + + {# Empty State #} + + + {# Reviews #} + +
+{% endmacro %} + + +{# + Review Form + =========== + Form for submitting a product review. + + Parameters: + - rating_model: Alpine.js model for rating (default: 'newReview.rating') + - title_model: Alpine.js model for title (default: 'newReview.title') + - content_model: Alpine.js model for content (default: 'newReview.content') + - images_model: Alpine.js model for images (default: 'newReview.images') + - submitting_var: Alpine.js var for submitting state (default: 'submittingReview') + - on_submit: Submit handler + - show_images: Allow image upload (default: true) + - require_purchase: Show purchase requirement message (default: false) + + Usage: + {{ review_form(on_submit='submitReview()') }} +#} +{% macro review_form( + rating_model='newReview.rating', + title_model='newReview.title', + content_model='newReview.content', + images_model='newReview.images', + submitting_var='submittingReview', + on_submit='submitReview()', + show_images=true, + require_purchase=false +) %} +
+

Write a Review

+ + {% if require_purchase %} +
+ +

You must have purchased this product to leave a review.

+
+ {% endif %} + + {# Rating #} +
+ + {{ rating_input(model=rating_model, size='lg') }} +
+ + {# Title #} +
+ + +
+ + {# Content #} +
+ + +

Minimum 50 characters

+
+ + {% if show_images %} + {# Image Upload #} +
+ +
+ + +
+

Up to 5 photos

+
+ {% endif %} + + {# Submit Button #} +
+ +

+ By submitting, you agree to our review guidelines. +

+
+
+{% endmacro %} + + +{# + Review Summary Section + ====================== + Complete review summary with rating distribution and write review button. + + Parameters: + - rating_var: Alpine.js expression for average rating + - count_var: Alpine.js expression for total reviews + - distribution_var: Alpine.js expression for distribution + - show_write_button: Show write review button (default: true) + - on_write: Callback for write review button + + Usage: + {{ review_summary_section(rating_var='product.rating', count_var='product.review_count', distribution_var='product.rating_distribution') }} +#} +{% macro review_summary_section( + rating_var='rating', + count_var='reviewCount', + distribution_var='ratingDistribution', + show_write_button=true, + on_write='showReviewForm = true' +) %} +
+
+ {# Rating Summary #} +
+ {{ rating_summary(rating_var=rating_var, count_var=count_var, distribution_var=distribution_var) }} +
+ + {% if show_write_button %} + {# Write Review CTA #} +
+
+

Share your thoughts

+

+ Help others by sharing your experience with this product. +

+ +
+
+ {% endif %} +
+
+{% endmacro %} diff --git a/app/templates/shared/macros/shop/star-rating.html b/app/templates/shared/macros/shop/star-rating.html new file mode 100644 index 00000000..536fe06b --- /dev/null +++ b/app/templates/shared/macros/shop/star-rating.html @@ -0,0 +1,389 @@ +{# + Star Rating Components + ====================== + Reusable star rating display and input for product reviews. + + Usage: + {% from 'shared/macros/shop/star-rating.html' import star_rating, rating_input, rating_summary %} +#} + + +{# + Star Rating Display + =================== + Static star rating display. + + Parameters: + - rating: Numeric rating value (0-5) + - rating_var: Alpine.js expression for rating (dynamic) + - max: Maximum stars (default: 5) + - size: 'xs' | 'sm' | 'md' | 'lg' | 'xl' (default: 'md') + - show_value: Show numeric value (default: false) + - show_count: Show review count (default: false) + - count: Number of reviews + - count_var: Alpine.js expression for count + - precision: 'full' | 'half' | 'exact' (default: 'half') + - color: Star color class (default: 'text-yellow-400') + - empty_color: Empty star color (default: 'text-gray-300 dark:text-gray-600') + + Usage: + {{ star_rating(rating=4.5) }} + {{ star_rating(rating_var='product.rating', show_count=true, count_var='product.review_count') }} +#} +{% macro star_rating( + rating=none, + rating_var=none, + max=5, + size='md', + show_value=false, + show_count=false, + count=none, + count_var=none, + precision='half', + color='text-yellow-400', + empty_color='text-gray-300 dark:text-gray-600' +) %} +{% set sizes = { + 'xs': 'w-3 h-3', + 'sm': 'w-4 h-4', + 'md': 'w-5 h-5', + 'lg': 'w-6 h-6', + 'xl': 'w-8 h-8' +} %} +{% set text_sizes = { + 'xs': 'text-xs', + 'sm': 'text-sm', + 'md': 'text-sm', + 'lg': 'text-base', + 'xl': 'text-lg' +} %} +
+ {% if rating is not none %} + {# Static rating #} +
+ {% for i in range(1, max + 1) %} + {% if precision == 'half' %} + {% set fill = 'full' if rating >= i else ('half' if rating >= i - 0.5 else 'empty') %} + {% elif precision == 'exact' %} + {% set fill_percent = ((rating - (i - 1)) * 100) | int %} + {% set fill_percent = [0, [fill_percent, 100] | min] | max %} + {% else %} + {% set fill = 'full' if rating >= i - 0.5 else 'empty' %} + {% endif %} + + {% if precision == 'exact' %} + + + + + + + + + {% elif fill == 'half' %} + + + + + + + + + {% else %} + + + + {% endif %} + {% endfor %} +
+ {% if show_value %} + {{ rating }} + {% endif %} + {% if show_count and count is not none %} + ({{ count }}) + {% endif %} + + {% elif rating_var %} + {# Dynamic rating from Alpine.js #} +
+ +
+ {% if show_value %} + + {% endif %} + {% if show_count and count_var %} + + {% elif show_count and count is not none %} + ({{ count }}) + {% endif %} + {% endif %} +
+{% endmacro %} + + +{# + Rating Input + ============ + Interactive star rating input for submitting reviews. + + Parameters: + - model: Alpine.js model for rating value (default: 'rating') + - max: Maximum stars (default: 5) + - size: 'sm' | 'md' | 'lg' (default: 'md') + - allow_half: Allow half-star ratings (default: false) + - allow_clear: Allow clearing rating (default: true) + - disabled_var: Alpine.js expression for disabled state + - on_change: Callback when rating changes + + Usage: + {{ rating_input(model='reviewRating') }} + {{ rating_input(model='rating', size='lg', allow_half=true) }} +#} +{% macro rating_input( + model='rating', + max=5, + size='md', + allow_half=false, + allow_clear=true, + disabled_var=none, + on_change=none +) %} +{% set sizes = { + 'sm': 'w-6 h-6', + 'md': 'w-8 h-8', + 'lg': 'w-10 h-10' +} %} +
+ {% for i in range(1, max + 1) %} + + {% endfor %} + + {# Rating label #} + +
+{% endmacro %} + + +{# + Rating Summary + ============== + Rating distribution summary (typically shown on product pages). + + Parameters: + - rating_var: Alpine.js expression for average rating + - count_var: Alpine.js expression for total reviews + - distribution_var: Alpine.js expression for distribution object {5: count, 4: count, ...} + - show_bars: Show distribution bars (default: true) + - size: 'sm' | 'md' | 'lg' (default: 'md') + + Usage: + {{ rating_summary(rating_var='product.rating', count_var='product.review_count', distribution_var='product.rating_distribution') }} +#} +{% macro rating_summary( + rating_var='rating', + count_var='reviewCount', + distribution_var='ratingDistribution', + show_bars=true, + size='md' +) %} +{% set sizes = { + 'sm': {'star': 'w-4 h-4', 'text': 'text-3xl', 'bar_h': 'h-1.5'}, + 'md': {'star': 'w-5 h-5', 'text': 'text-4xl', 'bar_h': 'h-2'}, + 'lg': {'star': 'w-6 h-6', 'text': 'text-5xl', 'bar_h': 'h-2.5'} +} %} +
+ {# Average Rating #} +
+
+
+ +
+
+ Based on reviews +
+
+ + {% if show_bars %} + {# Rating Distribution #} +
+ {% for i in range(5, 0, -1) %} +
+ {{ i }} +
+
+
+ +
+ {% endfor %} +
+ {% endif %} +
+{% endmacro %} + + +{# + Compact Rating + ============== + Inline compact rating for lists and cards. + + Parameters: + - rating: Static rating value + - rating_var: Alpine.js expression for rating + - count: Review count + - count_var: Alpine.js expression for count + - size: 'xs' | 'sm' | 'md' (default: 'sm') + + Usage: + {{ compact_rating(rating=4.5, count=127) }} + {{ compact_rating(rating_var='item.rating', count_var='item.reviews') }} +#} +{% macro compact_rating( + rating=none, + rating_var=none, + count=none, + count_var=none, + size='sm' +) %} +{% set sizes = { + 'xs': {'star': 'w-3 h-3', 'text': 'text-xs'}, + 'sm': {'star': 'w-4 h-4', 'text': 'text-sm'}, + 'md': {'star': 'w-5 h-5', 'text': 'text-base'} +} %} +
+ + + + {% if rating is not none %} + {{ rating }} + {% if count is not none %} + ({{ count }}) + {% endif %} + {% elif rating_var %} + + {% if count_var %} + + {% elif count is not none %} + ({{ count }}) + {% endif %} + {% endif %} +
+{% endmacro %} diff --git a/app/templates/shared/macros/shop/trust-badges.html b/app/templates/shared/macros/shop/trust-badges.html new file mode 100644 index 00000000..b7cb423c --- /dev/null +++ b/app/templates/shared/macros/shop/trust-badges.html @@ -0,0 +1,436 @@ +{# + Trust Badge Components + ====================== + Trust signals and security indicators for e-commerce. + + Usage: + {% from 'shared/macros/shop/trust-badges.html' import trust_badges, trust_banner, payment_icons, guarantee_badge %} +#} + + +{# + Trust Badges + ============ + Grid of trust indicators. + + Parameters: + - badges: List of badge types to show (default: all) + - layout: 'grid' | 'inline' | 'vertical' (default: 'grid') + - size: 'sm' | 'md' | 'lg' (default: 'md') + - show_text: Show badge text (default: true) + - free_shipping_threshold: Threshold for free shipping (default: none) + + Badge Types: + - secure_payment + - free_shipping + - easy_returns + - support_24_7 + - money_back + - ssl_secured + - fast_delivery + - quality_guarantee + + Usage: + {{ trust_badges(badges=['secure_payment', 'free_shipping', 'easy_returns']) }} + {{ trust_badges(layout='inline', size='sm') }} +#} +{% macro trust_badges( + badges=none, + layout='grid', + size='md', + show_text=true, + free_shipping_threshold=none +) %} +{% set all_badges = [ + { + 'id': 'secure_payment', + 'icon': 'lock-closed', + 'title': 'Secure Payment', + 'description': '256-bit SSL encryption' + }, + { + 'id': 'free_shipping', + 'icon': 'truck', + 'title': 'Free Shipping', + 'description': 'On orders over $' ~ (free_shipping_threshold or 50) + }, + { + 'id': 'easy_returns', + 'icon': 'refresh', + 'title': 'Easy Returns', + 'description': '30-day return policy' + }, + { + 'id': 'support_24_7', + 'icon': 'support', + 'title': '24/7 Support', + 'description': 'Always here to help' + }, + { + 'id': 'money_back', + 'icon': 'cash', + 'title': 'Money Back', + 'description': '100% guarantee' + }, + { + 'id': 'ssl_secured', + 'icon': 'shield-check', + 'title': 'SSL Secured', + 'description': 'Protected checkout' + }, + { + 'id': 'fast_delivery', + 'icon': 'lightning-bolt', + 'title': 'Fast Delivery', + 'description': '2-5 business days' + }, + { + 'id': 'quality_guarantee', + 'icon': 'badge-check', + 'title': 'Quality Guarantee', + 'description': 'Premium products' + } +] %} +{% set selected_badges = badges if badges else ['secure_payment', 'free_shipping', 'easy_returns', 'support_24_7'] %} +{% set sizes = { + 'sm': {'icon': 'w-5 h-5', 'title': 'text-xs', 'desc': 'text-xs', 'padding': 'p-2', 'gap': 'gap-1'}, + 'md': {'icon': 'w-6 h-6', 'title': 'text-sm', 'desc': 'text-xs', 'padding': 'p-3', 'gap': 'gap-2'}, + 'lg': {'icon': 'w-8 h-8', 'title': 'text-base', 'desc': 'text-sm', 'padding': 'p-4', 'gap': 'gap-3'} +} %} +{% set layouts = { + 'grid': 'grid grid-cols-2 md:grid-cols-4 gap-4', + 'inline': 'flex flex-wrap items-center justify-center gap-6', + 'vertical': 'flex flex-col gap-3' +} %} + +
+ {% for badge_id in selected_badges %} + {% for badge in all_badges %} + {% if badge.id == badge_id %} +
+
+ +
+ {% if show_text %} +
+

{{ badge.title }}

+ {% if layout != 'inline' %} +

{{ badge.description }}

+ {% endif %} +
+ {% endif %} +
+ {% endif %} + {% endfor %} + {% endfor %} +
+{% endmacro %} + + +{# + Trust Banner + ============ + Full-width trust banner for product pages or checkout. + + Parameters: + - variant: 'default' | 'compact' | 'detailed' (default: 'default') + - show_icons: Show payment/security icons (default: true) + + Usage: + {{ trust_banner() }} + {{ trust_banner(variant='compact') }} +#} +{% macro trust_banner( + variant='default', + show_icons=true +) %} +{% if variant == 'compact' %} +
+ + + Free shipping over $50 + + + + 30-day returns + + + + Secure checkout + +
+ +{% elif variant == 'detailed' %} +
+
+
+
+ +
+

Free Shipping

+

On all orders over $50. International shipping available.

+
+
+
+ +
+

Easy Returns

+

30-day hassle-free return policy. No questions asked.

+
+
+
+ +
+

Secure Payment

+

Your payment info is protected with 256-bit encryption.

+
+
+ {% if show_icons %} +
+ {{ payment_icons(size='sm') }} +
+ {% endif %} +
+ +{% else %} +{# Default variant #} +
+
+
+ +
+

Secure Shopping Guarantee

+

Shop with confidence - your data is protected

+
+
+ {% if show_icons %} + {{ payment_icons(size='sm') }} + {% endif %} +
+
+{% endif %} +{% endmacro %} + + +{# + Payment Icons + ============= + Display accepted payment method icons. + + Parameters: + - methods: List of payment methods (default: common cards) + - size: 'xs' | 'sm' | 'md' | 'lg' (default: 'md') + - grayscale: Show in grayscale (default: false) + + Methods: visa, mastercard, amex, paypal, apple_pay, google_pay, discover, klarna + + Usage: + {{ payment_icons() }} + {{ payment_icons(methods=['visa', 'mastercard', 'paypal'], size='lg') }} +#} +{% macro payment_icons( + methods=none, + size='md', + grayscale=false +) %} +{% set all_methods = { + 'visa': {'name': 'Visa', 'color': 'text-blue-600'}, + 'mastercard': {'name': 'Mastercard', 'color': 'text-orange-500'}, + 'amex': {'name': 'American Express', 'color': 'text-blue-500'}, + 'paypal': {'name': 'PayPal', 'color': 'text-blue-700'}, + 'apple_pay': {'name': 'Apple Pay', 'color': 'text-gray-900 dark:text-white'}, + 'google_pay': {'name': 'Google Pay', 'color': 'text-gray-700'}, + 'discover': {'name': 'Discover', 'color': 'text-orange-600'}, + 'klarna': {'name': 'Klarna', 'color': 'text-pink-500'} +} %} +{% set selected = methods if methods else ['visa', 'mastercard', 'amex', 'paypal'] %} +{% set sizes = { + 'xs': 'h-4', + 'sm': 'h-6', + 'md': 'h-8', + 'lg': 'h-10' +} %} + +
+ {% for method in selected %} + {% if method in all_methods %} +
+ {{ all_methods[method].name[:4] }} +
+ {% endif %} + {% endfor %} +
+{% endmacro %} + + +{# + Guarantee Badge + =============== + Individual guarantee/warranty badge. + + Parameters: + - type: 'money_back' | 'warranty' | 'authentic' | 'satisfaction' (default: 'money_back') + - days: Number of days (for money_back/warranty) + - size: 'sm' | 'md' | 'lg' (default: 'md') + - variant: 'default' | 'outlined' | 'filled' (default: 'default') + + Usage: + {{ guarantee_badge(type='money_back', days=30) }} + {{ guarantee_badge(type='warranty', days=365, variant='filled') }} +#} +{% macro guarantee_badge( + type='money_back', + days=30, + size='md', + variant='default' +) %} +{% set badges = { + 'money_back': { + 'icon': 'cash', + 'title': days ~ '-Day Money Back', + 'subtitle': 'Guarantee' + }, + 'warranty': { + 'icon': 'shield-check', + 'title': (days // 365) ~ '-Year Warranty' if days >= 365 else days ~ '-Day Warranty', + 'subtitle': 'Included' + }, + 'authentic': { + 'icon': 'badge-check', + 'title': '100% Authentic', + 'subtitle': 'Guaranteed' + }, + 'satisfaction': { + 'icon': 'emoji-happy', + 'title': 'Satisfaction', + 'subtitle': 'Guaranteed' + } +} %} +{% set badge = badges[type] %} +{% set sizes = { + 'sm': {'icon': 'w-8 h-8', 'title': 'text-xs', 'subtitle': 'text-xs', 'padding': 'p-3'}, + 'md': {'icon': 'w-10 h-10', 'title': 'text-sm', 'subtitle': 'text-xs', 'padding': 'p-4'}, + 'lg': {'icon': 'w-12 h-12', 'title': 'text-base', 'subtitle': 'text-sm', 'padding': 'p-5'} +} %} +{% set variants = { + 'default': 'bg-white dark:bg-gray-800 border border-gray-200 dark:border-gray-700', + 'outlined': 'border-2 border-purple-600 dark:border-purple-400', + 'filled': 'bg-purple-600 text-white' +} %} + +
+
+ +
+

{{ badge.title }}

+

{{ badge.subtitle }}

+
+{% endmacro %} + + +{# + Security Seals + ============== + Security certification seals. + + Parameters: + - seals: List of seal types to show + - layout: 'horizontal' | 'vertical' (default: 'horizontal') + + Seal Types: ssl, pci, mcafee, norton, trustpilot + + Usage: + {{ security_seals(seals=['ssl', 'pci']) }} +#} +{% macro security_seals( + seals=none, + layout='horizontal' +) %} +{% set all_seals = [ + {'id': 'ssl', 'name': 'SSL Secure', 'icon': 'lock-closed'}, + {'id': 'pci', 'name': 'PCI Compliant', 'icon': 'shield-check'}, + {'id': 'verified', 'name': 'Verified Business', 'icon': 'badge-check'}, + {'id': 'secure_checkout', 'name': 'Secure Checkout', 'icon': 'shield-check'} +] %} +{% set selected = seals if seals else ['ssl', 'verified'] %} + +
+ {% for seal_id in selected %} + {% for seal in all_seals %} + {% if seal.id == seal_id %} +
+ + {{ seal.name }} +
+ {% endif %} + {% endfor %} + {% endfor %} +
+{% endmacro %} + + +{# + Checkout Trust Section + ====================== + Combined trust elements for checkout pages. + + Parameters: + - show_guarantee: Show money-back guarantee (default: true) + - show_payment: Show payment icons (default: true) + - show_security: Show security seals (default: true) + + Usage: + {{ checkout_trust_section() }} +#} +{% macro checkout_trust_section( + show_guarantee=true, + show_payment=true, + show_security=true +) %} +
+ {% if show_guarantee %} +
+
+ +
+
+

100% Secure Checkout

+

Your payment information is encrypted and secure. We never store your card details.

+
+
+ {% endif %} + + {% if show_payment %} +
+

We accept:

+ {{ payment_icons(size='md') }} +
+ {% endif %} + + {% if show_security %} +
+ {{ security_seals() }} +
+ {% endif %} + +
+
+ + + 256-bit SSL + + + + 30-day returns + + + + 24/7 support + +
+
+
+{% endmacro %}