feat: add Priority 4 and 5 demos to components showcase page
Add interactive demos for all new e-commerce macros: Priority 4 - Navigation & Discovery: - Category navigation with mega menu - Breadcrumb variants - Search bar with autocomplete - Filter sidebar with all filter types - Sort dropdown Priority 5 - Social Proof & Trust: - Star ratings (static, dynamic, sizes) - Compact ratings for lists - Interactive rating input - Rating summary with distribution - Review cards with helpful voting - Review form - Trust badges grid - Trust banner variants - Payment method icons - Guarantee badges - Security seals - Checkout trust section Demo state includes sample data for categories, filters, reviews, and ratings to showcase all component features. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
@@ -619,6 +619,756 @@ html {
|
||||
Copy Code
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<!-- Category Navigation Demo (Priority 4) -->
|
||||
<div class="mb-8 pt-8 border-t border-gray-200 dark:border-gray-700">
|
||||
<h3 class="text-lg font-semibold text-gray-700 dark:text-gray-300 mb-3">Category Navigation</h3>
|
||||
<p class="text-sm text-gray-600 dark:text-gray-400 mb-4">
|
||||
Sidebar navigation with nested categories, collapsible sections, and active state tracking.
|
||||
</p>
|
||||
<div class="grid lg:grid-cols-2 gap-6">
|
||||
{# Sidebar Navigation #}
|
||||
<div class="bg-gray-50 dark:bg-gray-900 rounded-lg p-4">
|
||||
<h4 class="text-sm font-medium text-gray-600 dark:text-gray-400 mb-3">Sidebar Navigation</h4>
|
||||
<nav
|
||||
x-data="{
|
||||
expandedCategories: new Set([1, 11]),
|
||||
toggleCategory(id) {
|
||||
if (this.expandedCategories.has(id)) {
|
||||
this.expandedCategories.delete(id);
|
||||
} else {
|
||||
this.expandedCategories.add(id);
|
||||
}
|
||||
},
|
||||
isExpanded(id) {
|
||||
return this.expandedCategories.has(id);
|
||||
},
|
||||
isActive(category) {
|
||||
return currentCategory?.id === category.id;
|
||||
}
|
||||
}"
|
||||
class="space-y-1"
|
||||
>
|
||||
<template x-for="category in demoCategories" :key="category.id">
|
||||
<div>
|
||||
<div class="flex items-center">
|
||||
<a
|
||||
:href="category.url"
|
||||
class="flex-1 flex items-center justify-between px-3 py-2 text-sm rounded-lg transition-colors"
|
||||
:class="{
|
||||
'bg-purple-100 dark:bg-purple-900/30 text-purple-700 dark:text-purple-300 font-medium': isActive(category),
|
||||
'text-gray-700 dark:text-gray-300 hover:bg-gray-100 dark:hover:bg-gray-700': !isActive(category)
|
||||
}"
|
||||
@click.prevent
|
||||
>
|
||||
<span x-text="category.name"></span>
|
||||
<span class="text-xs text-gray-500 dark:text-gray-400" x-text="'(' + category.product_count + ')'"></span>
|
||||
</a>
|
||||
<button
|
||||
x-show="category.children?.length"
|
||||
type="button"
|
||||
@click="toggleCategory(category.id)"
|
||||
class="p-2 text-gray-400 hover:text-gray-600 dark:hover:text-gray-300"
|
||||
>
|
||||
<span x-html="$icon('chevron-right', 'w-4 h-4 transition-transform')" :class="isExpanded(category.id) && 'rotate-90'"></span>
|
||||
</button>
|
||||
</div>
|
||||
<div x-show="isExpanded(category.id) && category.children?.length" x-collapse class="ml-4 mt-1 space-y-1">
|
||||
<template x-for="child in category.children" :key="child.id">
|
||||
<a
|
||||
:href="child.url"
|
||||
class="flex items-center justify-between px-3 py-2 text-sm rounded-lg transition-colors"
|
||||
:class="{
|
||||
'bg-purple-100 dark:bg-purple-900/30 text-purple-700 dark:text-purple-300 font-medium': isActive(child),
|
||||
'text-gray-700 dark:text-gray-300 hover:bg-gray-100 dark:hover:bg-gray-700': !isActive(child)
|
||||
}"
|
||||
@click.prevent
|
||||
>
|
||||
<span x-text="child.name"></span>
|
||||
<span class="text-xs text-gray-500 dark:text-gray-400" x-text="'(' + child.product_count + ')'"></span>
|
||||
</a>
|
||||
</template>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
</nav>
|
||||
</div>
|
||||
{# Horizontal Category Menu #}
|
||||
<div class="bg-gray-50 dark:bg-gray-900 rounded-lg p-4">
|
||||
<h4 class="text-sm font-medium text-gray-600 dark:text-gray-400 mb-3">Horizontal Pills</h4>
|
||||
<nav class="flex flex-wrap gap-2">
|
||||
<template x-for="category in demoCategories" :key="category.id">
|
||||
<a
|
||||
:href="category.url"
|
||||
class="px-4 py-2 rounded-full text-sm font-medium transition-colors"
|
||||
:class="currentCategory?.id === category.id ? 'bg-purple-600 text-white' : 'bg-gray-100 dark:bg-gray-700 text-gray-700 dark:text-gray-300 hover:bg-gray-200 dark:hover:bg-gray-600'"
|
||||
@click.prevent
|
||||
>
|
||||
<span x-text="category.name"></span>
|
||||
<span class="ml-1 text-xs opacity-75" x-text="'(' + category.product_count + ')'"></span>
|
||||
</a>
|
||||
</template>
|
||||
</nav>
|
||||
</div>
|
||||
</div>
|
||||
<button @click="copyCode(`{% raw %}{% from 'shared/macros/shop/category-nav.html' import category_nav, category_tree %}
|
||||
|
||||
{# Sidebar with nested categories #}
|
||||
{{ category_nav(categories_var='categories', current_var='currentCategory', show_count=true) }}
|
||||
|
||||
{# Horizontal pills #}
|
||||
{{ category_tree(categories_var='categories', layout='horizontal') }}{% endraw %}`)" class="mt-4 text-sm text-purple-600 hover:text-purple-700 dark:text-purple-400 flex items-center">
|
||||
<span x-html="$icon('duplicate', 'w-4 h-4 mr-1')"></span>
|
||||
Copy Code
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<!-- Breadcrumbs Demo (Priority 4) -->
|
||||
<div class="mb-8 pt-8 border-t border-gray-200 dark:border-gray-700">
|
||||
<h3 class="text-lg font-semibold text-gray-700 dark:text-gray-300 mb-3">Breadcrumbs</h3>
|
||||
<p class="text-sm text-gray-600 dark:text-gray-400 mb-4">
|
||||
Navigation trail showing category hierarchy with home link, separator icons, and current page indicator.
|
||||
</p>
|
||||
<div class="space-y-6">
|
||||
{# Standard Breadcrumbs #}
|
||||
<div class="bg-gray-50 dark:bg-gray-900 rounded-lg p-4">
|
||||
<h4 class="text-sm font-medium text-gray-600 dark:text-gray-400 mb-3">Standard Breadcrumbs</h4>
|
||||
<nav aria-label="Breadcrumb" class="flex items-center text-sm">
|
||||
<ol class="flex items-center flex-wrap gap-1">
|
||||
<li class="flex items-center">
|
||||
<a href="#" class="flex items-center gap-1 text-gray-500 dark:text-gray-400 hover:text-purple-600 dark:hover:text-purple-400 transition-colors" @click.prevent>
|
||||
<span x-html="$icon('home', 'w-4 h-4')"></span>
|
||||
<span class="sr-only sm:not-sr-only">Home</span>
|
||||
</a>
|
||||
</li>
|
||||
<li class="flex items-center text-gray-400 dark:text-gray-500">
|
||||
<span x-html="$icon('chevron-right', 'w-4 h-4')"></span>
|
||||
</li>
|
||||
<template x-for="(item, index) in demoBreadcrumbs" :key="index">
|
||||
<li class="flex items-center">
|
||||
<template x-if="item.url && index < demoBreadcrumbs.length - 1">
|
||||
<a :href="item.url" class="text-gray-500 dark:text-gray-400 hover:text-purple-600 dark:hover:text-purple-400 transition-colors" x-text="item.label" @click.prevent></a>
|
||||
</template>
|
||||
<template x-if="!item.url || index === demoBreadcrumbs.length - 1">
|
||||
<span class="text-gray-900 dark:text-white font-medium" x-text="item.label"></span>
|
||||
</template>
|
||||
<span x-show="index < demoBreadcrumbs.length - 1" class="mx-2 text-gray-400 dark:text-gray-500" x-html="$icon('chevron-right', 'w-4 h-4')"></span>
|
||||
</li>
|
||||
</template>
|
||||
</ol>
|
||||
</nav>
|
||||
</div>
|
||||
{# Compact Breadcrumbs #}
|
||||
<div class="bg-gray-50 dark:bg-gray-900 rounded-lg p-4">
|
||||
<h4 class="text-sm font-medium text-gray-600 dark:text-gray-400 mb-3">Compact (Mobile-friendly)</h4>
|
||||
<nav aria-label="Breadcrumb" class="flex items-center text-sm">
|
||||
<a href="#" class="flex items-center gap-1 text-gray-500 dark:text-gray-400 hover:text-purple-600 dark:hover:text-purple-400 transition-colors" @click.prevent>
|
||||
<span x-html="$icon('arrow-left', 'w-4 h-4')"></span>
|
||||
<span>Audio</span>
|
||||
</a>
|
||||
<span class="mx-2 text-gray-400 dark:text-gray-500">/</span>
|
||||
<span class="text-gray-900 dark:text-white font-medium">Headphones</span>
|
||||
</nav>
|
||||
</div>
|
||||
</div>
|
||||
<button @click="copyCode(`{% raw %}{% from 'shared/macros/shop/breadcrumbs.html' import shop_breadcrumbs, compact_breadcrumbs %}
|
||||
|
||||
{# Static breadcrumbs #}
|
||||
{{ shop_breadcrumbs(items=[
|
||||
{'label': 'Electronics', 'url': '/category/electronics'},
|
||||
{'label': 'Audio', 'url': '/category/audio'},
|
||||
{'label': 'Headphones'}
|
||||
]) }}
|
||||
|
||||
{# Dynamic from Alpine.js #}
|
||||
{{ shop_breadcrumbs(items_var='breadcrumbs') }}
|
||||
|
||||
{# Compact for mobile #}
|
||||
{{ compact_breadcrumbs(parent={'label': 'Audio', 'url': '/audio'}, current='Headphones') }}{% endraw %}`)" class="mt-4 text-sm text-purple-600 hover:text-purple-700 dark:text-purple-400 flex items-center">
|
||||
<span x-html="$icon('duplicate', 'w-4 h-4 mr-1')"></span>
|
||||
Copy Code
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<!-- Search Bar Demo (Priority 4) -->
|
||||
<div class="mb-8 pt-8 border-t border-gray-200 dark:border-gray-700">
|
||||
<h3 class="text-lg font-semibold text-gray-700 dark:text-gray-300 mb-3">Search Bar</h3>
|
||||
<p class="text-sm text-gray-600 dark:text-gray-400 mb-4">
|
||||
Product search with autocomplete, recent searches, popular suggestions, and mobile-optimized full-screen search.
|
||||
</p>
|
||||
<div class="grid lg:grid-cols-2 gap-6">
|
||||
{# Basic Search #}
|
||||
<div class="bg-gray-50 dark:bg-gray-900 rounded-lg p-4">
|
||||
<h4 class="text-sm font-medium text-gray-600 dark:text-gray-400 mb-3">Basic Search</h4>
|
||||
<form class="relative flex items-center gap-2" @submit.prevent>
|
||||
<div class="relative flex-1">
|
||||
<span class="absolute left-3 top-1/2 -translate-y-1/2 w-5 h-5 text-gray-400 pointer-events-none">
|
||||
<span x-html="$icon('search', 'w-full h-full')"></span>
|
||||
</span>
|
||||
<input
|
||||
type="search"
|
||||
placeholder="Search products..."
|
||||
class="w-full py-2.5 pl-10 pr-4 text-sm bg-white dark:bg-gray-800 border border-gray-300 dark:border-gray-600 rounded-lg text-gray-900 dark:text-white placeholder-gray-400 dark:placeholder-gray-500 focus:border-purple-500 dark:focus:border-purple-400 focus:ring-2 focus:ring-purple-500/20 outline-none transition-colors"
|
||||
>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
{# Search with Button #}
|
||||
<div class="bg-gray-50 dark:bg-gray-900 rounded-lg p-4">
|
||||
<h4 class="text-sm font-medium text-gray-600 dark:text-gray-400 mb-3">With Search Button</h4>
|
||||
<form class="relative flex items-center gap-2" @submit.prevent>
|
||||
<div class="relative flex-1">
|
||||
<span class="absolute left-3 top-1/2 -translate-y-1/2 w-5 h-5 text-gray-400 pointer-events-none">
|
||||
<span x-html="$icon('search', 'w-full h-full')"></span>
|
||||
</span>
|
||||
<input
|
||||
type="search"
|
||||
placeholder="What are you looking for?"
|
||||
class="w-full py-2.5 pl-10 pr-4 text-sm bg-white dark:bg-gray-800 border border-gray-300 dark:border-gray-600 rounded-lg text-gray-900 dark:text-white placeholder-gray-400 dark:placeholder-gray-500 focus:border-purple-500 dark:focus:border-purple-400 focus:ring-2 focus:ring-purple-500/20 outline-none transition-colors"
|
||||
>
|
||||
</div>
|
||||
<button type="submit" class="px-4 py-2.5 text-sm bg-purple-600 hover:bg-purple-700 text-white font-medium rounded-lg transition-colors">
|
||||
Search
|
||||
</button>
|
||||
</form>
|
||||
</div>
|
||||
{# Autocomplete Search #}
|
||||
<div class="bg-gray-50 dark:bg-gray-900 rounded-lg p-4 lg:col-span-2">
|
||||
<h4 class="text-sm font-medium text-gray-600 dark:text-gray-400 mb-3">With Autocomplete Dropdown</h4>
|
||||
<div
|
||||
x-data="{
|
||||
query: '',
|
||||
isOpen: false,
|
||||
recentSearches: ['Wireless headphones', 'Smart watch', 'Bluetooth speaker'],
|
||||
popularSearches: ['Electronics', 'Clothing', 'Home & Garden', 'Sports']
|
||||
}"
|
||||
class="relative max-w-xl"
|
||||
@click.away="isOpen = false"
|
||||
>
|
||||
<form @submit.prevent>
|
||||
<div class="relative">
|
||||
<span class="absolute left-3 top-1/2 -translate-y-1/2 w-5 h-5 text-gray-400 pointer-events-none">
|
||||
<span x-html="$icon('search', 'w-full h-full')"></span>
|
||||
</span>
|
||||
<input
|
||||
type="search"
|
||||
x-model="query"
|
||||
@focus="isOpen = true"
|
||||
placeholder="Search products..."
|
||||
autocomplete="off"
|
||||
class="w-full py-2.5 pl-10 pr-10 text-sm bg-white dark:bg-gray-800 border border-gray-300 dark:border-gray-600 rounded-lg text-gray-900 dark:text-white placeholder-gray-400 dark:placeholder-gray-500 focus:border-purple-500 dark:focus:border-purple-400 focus:ring-2 focus:ring-purple-500/20 outline-none transition-colors"
|
||||
>
|
||||
<button
|
||||
type="button"
|
||||
x-show="query.length > 0"
|
||||
@click="query = ''"
|
||||
class="absolute right-3 top-1/2 -translate-y-1/2 w-5 h-5 text-gray-400 hover:text-gray-600 dark:hover:text-gray-300"
|
||||
>
|
||||
<span x-html="$icon('x', 'w-full h-full')"></span>
|
||||
</button>
|
||||
</div>
|
||||
</form>
|
||||
<div
|
||||
x-show="isOpen && query.length < 2"
|
||||
x-transition
|
||||
class="absolute left-0 right-0 mt-2 bg-white dark:bg-gray-800 rounded-lg shadow-lg border border-gray-200 dark:border-gray-700 overflow-hidden z-50"
|
||||
>
|
||||
<div class="py-2">
|
||||
<div class="flex items-center justify-between px-4 py-2">
|
||||
<span class="text-xs font-medium text-gray-500 dark:text-gray-400 uppercase tracking-wider">Recent Searches</span>
|
||||
<button type="button" class="text-xs text-purple-600 dark:text-purple-400 hover:underline">Clear</button>
|
||||
</div>
|
||||
<template x-for="term in recentSearches" :key="term">
|
||||
<button type="button" @click="query = term" class="w-full flex items-center gap-3 px-4 py-2 text-left text-sm hover:bg-gray-50 dark:hover:bg-gray-700/50 text-gray-700 dark:text-gray-300">
|
||||
<span x-html="$icon('clock', 'w-4 h-4 text-gray-400 flex-shrink-0')"></span>
|
||||
<span x-text="term"></span>
|
||||
</button>
|
||||
</template>
|
||||
<div class="border-t border-gray-200 dark:border-gray-700 mt-2 pt-2">
|
||||
<div class="px-4 py-2">
|
||||
<span class="text-xs font-medium text-gray-500 dark:text-gray-400 uppercase tracking-wider">Popular Searches</span>
|
||||
</div>
|
||||
<template x-for="term in popularSearches" :key="term">
|
||||
<button type="button" @click="query = term" class="w-full flex items-center gap-3 px-4 py-2 text-left text-sm hover:bg-gray-50 dark:hover:bg-gray-700/50 text-gray-700 dark:text-gray-300">
|
||||
<span x-html="$icon('trending-up', 'w-4 h-4 text-gray-400 flex-shrink-0')"></span>
|
||||
<span x-text="term"></span>
|
||||
</button>
|
||||
</template>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<button @click="copyCode(`{% raw %}{% from 'shared/macros/shop/search-bar.html' import search_bar, search_autocomplete %}
|
||||
|
||||
{# Basic search #}
|
||||
{{ search_bar(placeholder='Search products...') }}
|
||||
|
||||
{# With search button #}
|
||||
{{ search_bar(placeholder='What are you looking for?', show_button=true) }}
|
||||
|
||||
{# With autocomplete dropdown #}
|
||||
{{ search_autocomplete(search_endpoint='/api/products/search', show_recent=true, show_popular=true) }}{% endraw %}`)" class="mt-4 text-sm text-purple-600 hover:text-purple-700 dark:text-purple-400 flex items-center">
|
||||
<span x-html="$icon('duplicate', 'w-4 h-4 mr-1')"></span>
|
||||
Copy Code
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<!-- Filter Sidebar Demo (Priority 4) -->
|
||||
<div class="mb-8 pt-8 border-t border-gray-200 dark:border-gray-700">
|
||||
<h3 class="text-lg font-semibold text-gray-700 dark:text-gray-300 mb-3">Filter Sidebar</h3>
|
||||
<p class="text-sm text-gray-600 dark:text-gray-400 mb-4">
|
||||
Product filtering with price range, categories, brands, colors, sizes, ratings, and availability.
|
||||
</p>
|
||||
<div class="grid lg:grid-cols-3 gap-6">
|
||||
{# Filter Sidebar #}
|
||||
<div class="bg-gray-50 dark:bg-gray-900 rounded-lg p-4">
|
||||
<div class="flex items-center justify-between mb-4">
|
||||
<h4 class="text-sm font-semibold text-gray-900 dark:text-white">Filters</h4>
|
||||
<button type="button" x-show="demoActiveFilters.categories?.length > 0 || demoActiveFilters.brands?.length > 0" @click="demoActiveFilters = {categories: [], brands: [], priceMin: undefined, priceMax: undefined, rating: undefined, attributes: {}, inStock: false}" class="text-xs text-purple-600 dark:text-purple-400 hover:underline">Clear all</button>
|
||||
</div>
|
||||
{# Categories #}
|
||||
<div class="border-t border-gray-200 dark:border-gray-700 pt-4 mb-4">
|
||||
<h5 class="text-sm font-medium text-gray-900 dark:text-white mb-3">Categories</h5>
|
||||
<div class="space-y-2">
|
||||
<template x-for="cat in demoFilters.categories" :key="cat.id">
|
||||
<label class="flex items-center gap-3 cursor-pointer group">
|
||||
<input type="checkbox" :value="cat.id" :checked="demoActiveFilters.categories?.includes(cat.id)" @change="demoActiveFilters.categories?.includes(cat.id) ? demoActiveFilters.categories = demoActiveFilters.categories.filter(c => c !== cat.id) : demoActiveFilters.categories = [...(demoActiveFilters.categories || []), cat.id]" class="w-4 h-4 rounded border-gray-300 dark:border-gray-600 text-purple-600 focus:ring-purple-500">
|
||||
<span class="flex-1 text-sm text-gray-700 dark:text-gray-300" x-text="cat.name"></span>
|
||||
<span class="text-xs text-gray-400" x-text="'(' + cat.count + ')'"></span>
|
||||
</label>
|
||||
</template>
|
||||
</div>
|
||||
</div>
|
||||
{# Price Range #}
|
||||
<div class="border-t border-gray-200 dark:border-gray-700 pt-4 mb-4">
|
||||
<h5 class="text-sm font-medium text-gray-900 dark:text-white mb-3">Price Range</h5>
|
||||
<div class="flex items-center gap-3">
|
||||
<div class="flex-1">
|
||||
<div class="relative">
|
||||
<span class="absolute left-3 top-1/2 -translate-y-1/2 text-gray-400 text-sm">$</span>
|
||||
<input type="number" x-model.number="demoActiveFilters.priceMin" placeholder="Min" class="w-full pl-7 pr-3 py-2 text-sm border border-gray-300 dark:border-gray-600 rounded-lg bg-white dark:bg-gray-800 text-gray-900 dark:text-white">
|
||||
</div>
|
||||
</div>
|
||||
<span class="text-gray-400">-</span>
|
||||
<div class="flex-1">
|
||||
<div class="relative">
|
||||
<span class="absolute left-3 top-1/2 -translate-y-1/2 text-gray-400 text-sm">$</span>
|
||||
<input type="number" x-model.number="demoActiveFilters.priceMax" placeholder="Max" class="w-full pl-7 pr-3 py-2 text-sm border border-gray-300 dark:border-gray-600 rounded-lg bg-white dark:bg-gray-800 text-gray-900 dark:text-white">
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{# Availability #}
|
||||
<div class="border-t border-gray-200 dark:border-gray-700 pt-4">
|
||||
<label class="flex items-center gap-3 cursor-pointer">
|
||||
<input type="checkbox" x-model="demoActiveFilters.inStock" class="w-4 h-4 rounded border-gray-300 dark:border-gray-600 text-purple-600 focus:ring-purple-500">
|
||||
<span class="text-sm text-gray-700 dark:text-gray-300">In Stock Only</span>
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
{# Color & Size Filters #}
|
||||
<div class="bg-gray-50 dark:bg-gray-900 rounded-lg p-4">
|
||||
<h4 class="text-sm font-semibold text-gray-900 dark:text-white mb-4">Attribute Filters</h4>
|
||||
{# Colors #}
|
||||
<div class="mb-4">
|
||||
<h5 class="text-sm font-medium text-gray-900 dark:text-white mb-3">Color</h5>
|
||||
<div class="flex flex-wrap gap-2">
|
||||
<template x-for="color in demoFilters.attributes.color" :key="color.value">
|
||||
<button type="button" @click="!demoActiveFilters.attributes.color ? demoActiveFilters.attributes.color = [color.value] : (demoActiveFilters.attributes.color.includes(color.value) ? demoActiveFilters.attributes.color = demoActiveFilters.attributes.color.filter(c => c !== color.value) : demoActiveFilters.attributes.color.push(color.value))" class="w-8 h-8 rounded-full border-2 transition-all" :class="demoActiveFilters.attributes.color?.includes(color.value) ? 'border-purple-500 ring-2 ring-purple-500/30' : 'border-gray-300 dark:border-gray-600 hover:border-gray-400'" :style="'background-color: ' + color.hex" :title="color.label">
|
||||
<span class="sr-only" x-text="color.label"></span>
|
||||
<span x-show="demoActiveFilters.attributes.color?.includes(color.value)" x-html="$icon('check', 'w-4 h-4 mx-auto')" :class="color.value === 'white' ? 'text-gray-800' : 'text-white'"></span>
|
||||
</button>
|
||||
</template>
|
||||
</div>
|
||||
</div>
|
||||
{# Sizes #}
|
||||
<div>
|
||||
<h5 class="text-sm font-medium text-gray-900 dark:text-white mb-3">Size</h5>
|
||||
<div class="flex flex-wrap gap-2">
|
||||
<template x-for="size in demoFilters.attributes.size" :key="size.value">
|
||||
<button type="button" @click="!demoActiveFilters.attributes.size ? demoActiveFilters.attributes.size = [size.value] : (demoActiveFilters.attributes.size.includes(size.value) ? demoActiveFilters.attributes.size = demoActiveFilters.attributes.size.filter(s => s !== size.value) : demoActiveFilters.attributes.size.push(size.value))" class="px-3 py-1.5 text-sm border rounded-lg transition-colors" :class="demoActiveFilters.attributes.size?.includes(size.value) ? 'bg-purple-600 text-white border-purple-600' : 'bg-white dark:bg-gray-800 text-gray-700 dark:text-gray-300 border-gray-300 dark:border-gray-600 hover:border-purple-500'">
|
||||
<span x-text="size.label"></span>
|
||||
</button>
|
||||
</template>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{# Rating & Sort #}
|
||||
<div class="bg-gray-50 dark:bg-gray-900 rounded-lg p-4">
|
||||
<h4 class="text-sm font-semibold text-gray-900 dark:text-white mb-4">Rating & Sort</h4>
|
||||
{# Rating Filter #}
|
||||
<div class="mb-4">
|
||||
<h5 class="text-sm font-medium text-gray-900 dark:text-white mb-3">Rating</h5>
|
||||
<div class="space-y-2">
|
||||
<template x-for="rating in demoFilters.ratings.slice(0, 4)" :key="rating.value">
|
||||
<label class="flex items-center gap-3 cursor-pointer">
|
||||
<input type="radio" name="demo-rating" :value="rating.value" :checked="demoActiveFilters.rating === rating.value" @change="demoActiveFilters.rating = rating.value" class="w-4 h-4 border-gray-300 dark:border-gray-600 text-purple-600 focus:ring-purple-500">
|
||||
<span class="flex items-center gap-0.5">
|
||||
<template x-for="i in 5" :key="i">
|
||||
<span x-html="$icon('star', 'w-4 h-4')" :class="i <= rating.value ? 'text-yellow-400' : 'text-gray-300 dark:text-gray-600'"></span>
|
||||
</template>
|
||||
<span class="text-sm text-gray-500 dark:text-gray-400 ml-1">& up</span>
|
||||
</span>
|
||||
</label>
|
||||
</template>
|
||||
</div>
|
||||
</div>
|
||||
{# Sort Dropdown #}
|
||||
<div>
|
||||
<h5 class="text-sm font-medium text-gray-900 dark:text-white mb-3">Sort By</h5>
|
||||
<div x-data="{ open: false }" class="relative" @click.away="open = false">
|
||||
<button type="button" @click="open = !open" class="w-full flex items-center justify-between px-4 py-2 bg-white dark:bg-gray-800 border border-gray-300 dark:border-gray-600 rounded-lg text-sm text-gray-700 dark:text-gray-300">
|
||||
<span x-text="[{value: 'relevance', label: 'Relevance'}, {value: 'price_asc', label: 'Price: Low to High'}, {value: 'price_desc', label: 'Price: High to Low'}, {value: 'newest', label: 'Newest First'}].find(o => o.value === demoSortBy)?.label || 'Relevance'"></span>
|
||||
<span x-html="$icon('chevron-down', 'w-4 h-4 text-gray-400')"></span>
|
||||
</button>
|
||||
<div x-show="open" x-transition class="absolute left-0 right-0 mt-2 bg-white dark:bg-gray-800 rounded-lg shadow-lg border border-gray-200 dark:border-gray-700 py-1 z-50">
|
||||
<button type="button" @click="demoSortBy = 'relevance'; open = false" class="w-full px-4 py-2 text-left text-sm hover:bg-gray-100 dark:hover:bg-gray-700" :class="demoSortBy === 'relevance' ? 'text-purple-600 dark:text-purple-400 font-medium' : 'text-gray-700 dark:text-gray-300'">Relevance</button>
|
||||
<button type="button" @click="demoSortBy = 'price_asc'; open = false" class="w-full px-4 py-2 text-left text-sm hover:bg-gray-100 dark:hover:bg-gray-700" :class="demoSortBy === 'price_asc' ? 'text-purple-600 dark:text-purple-400 font-medium' : 'text-gray-700 dark:text-gray-300'">Price: Low to High</button>
|
||||
<button type="button" @click="demoSortBy = 'price_desc'; open = false" class="w-full px-4 py-2 text-left text-sm hover:bg-gray-100 dark:hover:bg-gray-700" :class="demoSortBy === 'price_desc' ? 'text-purple-600 dark:text-purple-400 font-medium' : 'text-gray-700 dark:text-gray-300'">Price: High to Low</button>
|
||||
<button type="button" @click="demoSortBy = 'newest'; open = false" class="w-full px-4 py-2 text-left text-sm hover:bg-gray-100 dark:hover:bg-gray-700" :class="demoSortBy === 'newest' ? 'text-purple-600 dark:text-purple-400 font-medium' : 'text-gray-700 dark:text-gray-300'">Newest First</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<button @click="copyCode(`{% raw %}{% from 'shared/macros/shop/filter-sidebar.html' import filter_sidebar, price_filter, rating_filter, sort_dropdown %}
|
||||
|
||||
{# Complete filter sidebar #}
|
||||
{{ filter_sidebar(filters_var='filters', active_filters_var='activeFilters', on_change='filterProducts()') }}
|
||||
|
||||
{# Standalone price filter #}
|
||||
{{ price_filter(min=0, max=500, on_change='updateFilters()') }}
|
||||
|
||||
{# Rating filter #}
|
||||
{{ rating_filter(value_var='minRating', on_change='filterByRating()') }}
|
||||
|
||||
{# Sort dropdown #}
|
||||
{{ sort_dropdown(value_var='sortBy', on_change='sortProducts()') }}{% endraw %}`)" class="mt-4 text-sm text-purple-600 hover:text-purple-700 dark:text-purple-400 flex items-center">
|
||||
<span x-html="$icon('duplicate', 'w-4 h-4 mr-1')"></span>
|
||||
Copy Code
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<!-- PRIORITY 5: SOCIAL PROOF & TRUST -->
|
||||
|
||||
<!-- Star Rating -->
|
||||
<section id="star-rating" class="scroll-mt-24">
|
||||
{% from 'shared/macros/shop/star-rating.html' import star_rating, rating_input, rating_summary, compact_rating %}
|
||||
<div class="bg-white dark:bg-gray-800 rounded-lg shadow-md p-6">
|
||||
<h2 class="text-2xl font-bold text-gray-800 dark:text-gray-200 mb-6 flex items-center">
|
||||
<span x-html="$icon('star', 'w-6 h-6 mr-2 text-yellow-400')"></span>
|
||||
Star Rating
|
||||
</h2>
|
||||
<p class="text-gray-600 dark:text-gray-400 mb-6">
|
||||
Flexible star rating components for displaying and collecting ratings.
|
||||
</p>
|
||||
|
||||
<div class="space-y-8">
|
||||
<!-- Static Star Ratings -->
|
||||
<div>
|
||||
<h3 class="text-lg font-semibold text-gray-700 dark:text-gray-300 mb-4">Static Ratings</h3>
|
||||
<div class="grid gap-6 md:grid-cols-2 lg:grid-cols-3">
|
||||
<div class="p-4 bg-gray-50 dark:bg-gray-900 rounded-lg">
|
||||
<p class="text-sm text-gray-500 dark:text-gray-400 mb-2">Full Stars</p>
|
||||
{{ star_rating(rating=4) }}
|
||||
</div>
|
||||
<div class="p-4 bg-gray-50 dark:bg-gray-900 rounded-lg">
|
||||
<p class="text-sm text-gray-500 dark:text-gray-400 mb-2">Half Stars</p>
|
||||
{{ star_rating(rating=4.5) }}
|
||||
</div>
|
||||
<div class="p-4 bg-gray-50 dark:bg-gray-900 rounded-lg">
|
||||
<p class="text-sm text-gray-500 dark:text-gray-400 mb-2">Exact Precision</p>
|
||||
{{ star_rating(rating=3.7, precision='exact') }}
|
||||
</div>
|
||||
<div class="p-4 bg-gray-50 dark:bg-gray-900 rounded-lg">
|
||||
<p class="text-sm text-gray-500 dark:text-gray-400 mb-2">With Value</p>
|
||||
{{ star_rating(rating=4.5, show_value=true) }}
|
||||
</div>
|
||||
<div class="p-4 bg-gray-50 dark:bg-gray-900 rounded-lg">
|
||||
<p class="text-sm text-gray-500 dark:text-gray-400 mb-2">With Count</p>
|
||||
{{ star_rating(rating=4.5, show_count=true, count=127) }}
|
||||
</div>
|
||||
<div class="p-4 bg-gray-50 dark:bg-gray-900 rounded-lg">
|
||||
<p class="text-sm text-gray-500 dark:text-gray-400 mb-2">Sizes</p>
|
||||
<div class="space-y-2">
|
||||
{{ star_rating(rating=4, size='xs') }}
|
||||
{{ star_rating(rating=4, size='sm') }}
|
||||
{{ star_rating(rating=4, size='md') }}
|
||||
{{ star_rating(rating=4, size='lg') }}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Dynamic Rating -->
|
||||
<div>
|
||||
<h3 class="text-lg font-semibold text-gray-700 dark:text-gray-300 mb-4">Dynamic Rating (Alpine.js)</h3>
|
||||
<div class="p-4 bg-gray-50 dark:bg-gray-900 rounded-lg">
|
||||
<div class="flex items-center gap-4 mb-4">
|
||||
<span class="text-sm text-gray-500 dark:text-gray-400">Adjust rating:</span>
|
||||
<input type="range" min="0" max="5" step="0.5" x-model.number="demoRating" class="w-48">
|
||||
<span class="text-sm font-medium text-gray-900 dark:text-white" x-text="demoRating"></span>
|
||||
</div>
|
||||
{{ star_rating(rating_var='demoRating', show_value=true, show_count=true, count_var='demoReviewCount') }}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Compact Rating -->
|
||||
<div>
|
||||
<h3 class="text-lg font-semibold text-gray-700 dark:text-gray-300 mb-4">Compact Rating</h3>
|
||||
<div class="grid gap-4 md:grid-cols-3">
|
||||
<div class="p-4 bg-gray-50 dark:bg-gray-900 rounded-lg">
|
||||
<p class="text-sm text-gray-500 dark:text-gray-400 mb-2">Extra Small</p>
|
||||
{{ compact_rating(rating=4.5, count=127, size='xs') }}
|
||||
</div>
|
||||
<div class="p-4 bg-gray-50 dark:bg-gray-900 rounded-lg">
|
||||
<p class="text-sm text-gray-500 dark:text-gray-400 mb-2">Small</p>
|
||||
{{ compact_rating(rating=4.5, count=127, size='sm') }}
|
||||
</div>
|
||||
<div class="p-4 bg-gray-50 dark:bg-gray-900 rounded-lg">
|
||||
<p class="text-sm text-gray-500 dark:text-gray-400 mb-2">Medium</p>
|
||||
{{ compact_rating(rating=4.5, count=127, size='md') }}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Rating Input -->
|
||||
<div>
|
||||
<h3 class="text-lg font-semibold text-gray-700 dark:text-gray-300 mb-4">Interactive Rating Input</h3>
|
||||
<div class="grid gap-6 md:grid-cols-2">
|
||||
<div class="p-4 bg-gray-50 dark:bg-gray-900 rounded-lg">
|
||||
<p class="text-sm text-gray-500 dark:text-gray-400 mb-2">Standard Input</p>
|
||||
{{ rating_input(model='demoUserRating', size='md') }}
|
||||
</div>
|
||||
<div class="p-4 bg-gray-50 dark:bg-gray-900 rounded-lg">
|
||||
<p class="text-sm text-gray-500 dark:text-gray-400 mb-2">Large with Half Stars</p>
|
||||
{{ rating_input(model='demoUserRating', size='lg', allow_half=true) }}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Rating Summary -->
|
||||
<div>
|
||||
<h3 class="text-lg font-semibold text-gray-700 dark:text-gray-300 mb-4">Rating Summary</h3>
|
||||
<div class="p-4 bg-gray-50 dark:bg-gray-900 rounded-lg">
|
||||
{{ rating_summary(rating_var='demoRating', count_var='demoReviewCount', distribution_var='demoRatingDistribution') }}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<button @click="copyCode(`{% raw %}{% from 'shared/macros/shop/star-rating.html' import star_rating, rating_input, rating_summary, compact_rating %}
|
||||
|
||||
{# Static star rating #}
|
||||
{{ star_rating(rating=4.5) }}
|
||||
{{ star_rating(rating=4.5, show_value=true, show_count=true, count=127) }}
|
||||
|
||||
{# Dynamic rating from Alpine.js #}
|
||||
{{ star_rating(rating_var='product.rating', show_count=true, count_var='product.review_count') }}
|
||||
|
||||
{# Compact rating for lists #}
|
||||
{{ compact_rating(rating=4.5, count=127, size='sm') }}
|
||||
|
||||
{# Interactive rating input #}
|
||||
{{ rating_input(model='userRating', size='lg', allow_half=true) }}
|
||||
|
||||
{# Rating summary with distribution #}
|
||||
{{ rating_summary(rating_var='rating', count_var='reviewCount', distribution_var='ratingDistribution') }}{% endraw %}`)" class="mt-4 text-sm text-purple-600 hover:text-purple-700 dark:text-purple-400 flex items-center">
|
||||
<span x-html="$icon('duplicate', 'w-4 h-4 mr-1')"></span>
|
||||
Copy Code
|
||||
</button>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<!-- Reviews -->
|
||||
<section id="reviews" class="scroll-mt-24">
|
||||
{% from 'shared/macros/shop/reviews.html' import review_card, review_list, review_form, review_summary_section %}
|
||||
<div class="bg-white dark:bg-gray-800 rounded-lg shadow-md p-6">
|
||||
<h2 class="text-2xl font-bold text-gray-800 dark:text-gray-200 mb-6 flex items-center">
|
||||
<span x-html="$icon('chat', 'w-6 h-6 mr-2 text-purple-600 dark:text-purple-400')"></span>
|
||||
Reviews
|
||||
</h2>
|
||||
<p class="text-gray-600 dark:text-gray-400 mb-6">
|
||||
Complete review system with cards, lists, forms, and summary sections.
|
||||
</p>
|
||||
|
||||
<div class="space-y-8">
|
||||
<!-- Review Card -->
|
||||
<div>
|
||||
<h3 class="text-lg font-semibold text-gray-700 dark:text-gray-300 mb-4">Review Card</h3>
|
||||
<div class="p-4 bg-gray-50 dark:bg-gray-900 rounded-lg">
|
||||
<template x-for="review in demoReviews.slice(0, 2)" :key="review.id">
|
||||
<div class="mb-4 last:mb-0">
|
||||
{{ review_card(review_var='review', on_helpful='demoMarkHelpful(review.id)') }}
|
||||
</div>
|
||||
</template>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Review Summary Section -->
|
||||
<div>
|
||||
<h3 class="text-lg font-semibold text-gray-700 dark:text-gray-300 mb-4">Review Summary Section</h3>
|
||||
<div class="p-4 bg-gray-50 dark:bg-gray-900 rounded-lg">
|
||||
{{ review_summary_section(
|
||||
rating_var='demoRating',
|
||||
count_var='demoReviewCount',
|
||||
distribution_var='demoRatingDistribution',
|
||||
on_write='showReviewForm = true'
|
||||
) }}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Review Form -->
|
||||
<div>
|
||||
<h3 class="text-lg font-semibold text-gray-700 dark:text-gray-300 mb-4">Review Form</h3>
|
||||
<div class="p-4 bg-gray-50 dark:bg-gray-900 rounded-lg">
|
||||
{{ review_form(
|
||||
rating_model='demoNewReview.rating',
|
||||
title_model='demoNewReview.title',
|
||||
content_model='demoNewReview.content',
|
||||
on_submit='demoSubmitReview()',
|
||||
submitting_var='submittingReview'
|
||||
) }}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<button @click="copyCode(`{% raw %}{% from 'shared/macros/shop/reviews.html' import review_card, review_list, review_form, review_summary_section %}
|
||||
|
||||
{# Single review card #}
|
||||
{{ review_card(review_var='review', on_helpful='markHelpful(review.id)') }}
|
||||
|
||||
{# Review list with sorting #}
|
||||
{{ review_list(reviews_var='reviews', sort_var='reviewSort', on_helpful='markHelpful') }}
|
||||
|
||||
{# Review submission form #}
|
||||
{{ review_form(
|
||||
rating_model='newReview.rating',
|
||||
title_model='newReview.title',
|
||||
content_model='newReview.content',
|
||||
on_submit='submitReview()',
|
||||
submitting_var='submitting'
|
||||
) }}
|
||||
|
||||
{# Complete review summary section #}
|
||||
{{ review_summary_section(
|
||||
rating_var='product.rating',
|
||||
count_var='product.review_count',
|
||||
distribution_var='product.rating_distribution',
|
||||
on_write='openReviewForm()'
|
||||
) }}{% endraw %}`)" class="mt-4 text-sm text-purple-600 hover:text-purple-700 dark:text-purple-400 flex items-center">
|
||||
<span x-html="$icon('duplicate', 'w-4 h-4 mr-1')"></span>
|
||||
Copy Code
|
||||
</button>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<!-- Trust Badges -->
|
||||
<section id="trust-badges" class="scroll-mt-24">
|
||||
{% from 'shared/macros/shop/trust-badges.html' import trust_badges, trust_banner, payment_icons, guarantee_badge, security_seals, checkout_trust_section %}
|
||||
<div class="bg-white dark:bg-gray-800 rounded-lg shadow-md p-6">
|
||||
<h2 class="text-2xl font-bold text-gray-800 dark:text-gray-200 mb-6 flex items-center">
|
||||
<span x-html="$icon('shield-check', 'w-6 h-6 mr-2 text-green-600 dark:text-green-400')"></span>
|
||||
Trust Badges
|
||||
</h2>
|
||||
<p class="text-gray-600 dark:text-gray-400 mb-6">
|
||||
Trust signals and security indicators to build customer confidence.
|
||||
</p>
|
||||
|
||||
<div class="space-y-8">
|
||||
<!-- Trust Badges Grid -->
|
||||
<div>
|
||||
<h3 class="text-lg font-semibold text-gray-700 dark:text-gray-300 mb-4">Trust Badges</h3>
|
||||
<div class="p-4 bg-gray-50 dark:bg-gray-900 rounded-lg">
|
||||
{{ trust_badges(badges=['secure_payment', 'free_shipping', 'easy_returns', 'support_24_7'], layout='grid', size='md') }}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Trust Banner -->
|
||||
<div>
|
||||
<h3 class="text-lg font-semibold text-gray-700 dark:text-gray-300 mb-4">Trust Banner</h3>
|
||||
<div class="p-4 bg-gray-50 dark:bg-gray-900 rounded-lg">
|
||||
{{ trust_banner(variant='detailed') }}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Payment Icons -->
|
||||
<div>
|
||||
<h3 class="text-lg font-semibold text-gray-700 dark:text-gray-300 mb-4">Payment Icons</h3>
|
||||
<div class="grid gap-4 md:grid-cols-2">
|
||||
<div class="p-4 bg-gray-50 dark:bg-gray-900 rounded-lg">
|
||||
<p class="text-sm text-gray-500 dark:text-gray-400 mb-2">All Methods</p>
|
||||
{{ payment_icons(methods=['visa', 'mastercard', 'paypal', 'apple_pay', 'google_pay', 'amex']) }}
|
||||
</div>
|
||||
<div class="p-4 bg-gray-50 dark:bg-gray-900 rounded-lg">
|
||||
<p class="text-sm text-gray-500 dark:text-gray-400 mb-2">With Label</p>
|
||||
{{ payment_icons(methods=['visa', 'mastercard', 'paypal'], size='lg') }}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Guarantee Badge -->
|
||||
<div>
|
||||
<h3 class="text-lg font-semibold text-gray-700 dark:text-gray-300 mb-4">Guarantee Badges</h3>
|
||||
<div class="grid gap-4 md:grid-cols-2">
|
||||
<div class="p-4 bg-gray-50 dark:bg-gray-900 rounded-lg">
|
||||
<p class="text-sm text-gray-500 dark:text-gray-400 mb-2">Money Back</p>
|
||||
{{ guarantee_badge(type='money_back', days=30) }}
|
||||
</div>
|
||||
<div class="p-4 bg-gray-50 dark:bg-gray-900 rounded-lg">
|
||||
<p class="text-sm text-gray-500 dark:text-gray-400 mb-2">Satisfaction</p>
|
||||
{{ guarantee_badge(type='satisfaction', variant='filled') }}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Security Seals -->
|
||||
<div>
|
||||
<h3 class="text-lg font-semibold text-gray-700 dark:text-gray-300 mb-4">Security Seals</h3>
|
||||
<div class="p-4 bg-gray-50 dark:bg-gray-900 rounded-lg">
|
||||
{{ security_seals(seals=['ssl', 'secure_checkout', 'verified']) }}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Checkout Trust Section -->
|
||||
<div>
|
||||
<h3 class="text-lg font-semibold text-gray-700 dark:text-gray-300 mb-4">Checkout Trust Section</h3>
|
||||
<div class="p-4 bg-gray-50 dark:bg-gray-900 rounded-lg">
|
||||
{{ checkout_trust_section() }}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<button @click="copyCode(`{% raw %}{% from 'shared/macros/shop/trust-badges.html' import trust_badges, trust_banner, payment_icons, guarantee_badge, security_seals, checkout_trust_section %}
|
||||
|
||||
{# Trust badges grid #}
|
||||
{{ trust_badges(badges=['secure_payment', 'free_shipping', 'easy_returns', 'support_24_7'], layout='grid') }}
|
||||
|
||||
{# Trust banner variants #}
|
||||
{{ trust_banner() }}
|
||||
{{ trust_banner(variant='compact') }}
|
||||
{{ trust_banner(variant='detailed') }}
|
||||
|
||||
{# Payment method icons #}
|
||||
{{ payment_icons(methods=['visa', 'mastercard', 'paypal', 'apple_pay']) }}
|
||||
{{ payment_icons(methods=['visa', 'mastercard'], size='lg') }}
|
||||
|
||||
{# Guarantee badges #}
|
||||
{{ guarantee_badge(type='money_back', days=30) }}
|
||||
{{ guarantee_badge(type='satisfaction', variant='filled') }}
|
||||
|
||||
{# Security seals #}
|
||||
{{ security_seals(seals=['ssl', 'verified']) }}
|
||||
|
||||
{# Complete checkout trust section #}
|
||||
{{ checkout_trust_section() }}
|
||||
{{ checkout_trust_section(show_guarantee=true, show_payment=true, show_security=false) }}{% endraw %}`)" class="mt-4 text-sm text-purple-600 hover:text-purple-700 dark:text-purple-400 flex items-center">
|
||||
<span x-html="$icon('duplicate', 'w-4 h-4 mr-1')"></span>
|
||||
Copy Code
|
||||
</button>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
|
||||
@@ -223,6 +223,187 @@ function adminComponents() {
|
||||
selectedColor: null,
|
||||
activeProductTab: 'description',
|
||||
|
||||
// Navigation & Discovery Demo (Priority 4)
|
||||
demoCategories: [
|
||||
{
|
||||
id: 1,
|
||||
name: 'Electronics',
|
||||
slug: 'electronics',
|
||||
url: '/category/electronics',
|
||||
product_count: 245,
|
||||
children: [
|
||||
{ id: 11, name: 'Audio', slug: 'audio', url: '/category/audio', product_count: 89, children: [
|
||||
{ id: 111, name: 'Headphones', slug: 'headphones', url: '/category/headphones', product_count: 45 },
|
||||
{ id: 112, name: 'Speakers', slug: 'speakers', url: '/category/speakers', product_count: 32 }
|
||||
]},
|
||||
{ id: 12, name: 'Wearables', slug: 'wearables', url: '/category/wearables', product_count: 67 },
|
||||
{ id: 13, name: 'Accessories', slug: 'accessories', url: '/category/accessories', product_count: 89 }
|
||||
]
|
||||
},
|
||||
{
|
||||
id: 2,
|
||||
name: 'Clothing',
|
||||
slug: 'clothing',
|
||||
url: '/category/clothing',
|
||||
product_count: 512,
|
||||
children: [
|
||||
{ id: 21, name: 'Men', slug: 'men', url: '/category/men', product_count: 234 },
|
||||
{ id: 22, name: 'Women', slug: 'women', url: '/category/women', product_count: 278 }
|
||||
]
|
||||
},
|
||||
{
|
||||
id: 3,
|
||||
name: 'Home & Garden',
|
||||
slug: 'home-garden',
|
||||
url: '/category/home-garden',
|
||||
product_count: 189
|
||||
}
|
||||
],
|
||||
currentCategory: { id: 111, name: 'Headphones', slug: 'headphones' },
|
||||
demoBreadcrumbs: [
|
||||
{ label: 'Electronics', url: '/category/electronics' },
|
||||
{ label: 'Audio', url: '/category/audio' },
|
||||
{ label: 'Headphones' }
|
||||
],
|
||||
|
||||
// Filter Demo State
|
||||
demoFilters: {
|
||||
categories: [
|
||||
{ id: 'headphones', name: 'Headphones', count: 45 },
|
||||
{ id: 'earbuds', name: 'Earbuds', count: 32 },
|
||||
{ id: 'speakers', name: 'Speakers', count: 28 }
|
||||
],
|
||||
brands: [
|
||||
{ id: 'audiotech', name: 'AudioTech', count: 24 },
|
||||
{ id: 'soundmax', name: 'SoundMax', count: 18 },
|
||||
{ id: 'beatspro', name: 'BeatsPro', count: 15 },
|
||||
{ id: 'sonicwave', name: 'SonicWave', count: 12 }
|
||||
],
|
||||
priceRange: { min: 0, max: 500 },
|
||||
attributes: {
|
||||
color: [
|
||||
{ value: 'black', label: 'Black', hex: '#1a1a1a', count: 35 },
|
||||
{ value: 'white', label: 'White', hex: '#ffffff', count: 28 },
|
||||
{ value: 'silver', label: 'Silver', hex: '#C0C0C0', count: 22 },
|
||||
{ value: 'blue', label: 'Blue', hex: '#3B82F6', count: 15 }
|
||||
],
|
||||
size: [
|
||||
{ value: 'compact', label: 'Compact', count: 18 },
|
||||
{ value: 'standard', label: 'Standard', count: 42 },
|
||||
{ value: 'over-ear', label: 'Over-ear', count: 25 }
|
||||
]
|
||||
},
|
||||
ratings: [
|
||||
{ value: 5, count: 12 },
|
||||
{ value: 4, count: 28 },
|
||||
{ value: 3, count: 15 },
|
||||
{ value: 2, count: 5 },
|
||||
{ value: 1, count: 2 }
|
||||
]
|
||||
},
|
||||
demoActiveFilters: {
|
||||
categories: [],
|
||||
brands: [],
|
||||
priceMin: undefined,
|
||||
priceMax: undefined,
|
||||
rating: undefined,
|
||||
attributes: {},
|
||||
inStock: false
|
||||
},
|
||||
demoSortBy: 'relevance',
|
||||
showMobileSearch: false,
|
||||
showMobileFilters: false,
|
||||
showCategoryDrawer: false,
|
||||
|
||||
// Search Demo Methods
|
||||
demoFilterProducts() {
|
||||
componentsLog.debug('Filtering products with:', this.demoActiveFilters);
|
||||
if (typeof Utils !== 'undefined' && Utils.showToast) {
|
||||
Utils.showToast('Filters applied!', 'info');
|
||||
}
|
||||
},
|
||||
demoSortProducts() {
|
||||
componentsLog.debug('Sorting by:', this.demoSortBy);
|
||||
if (typeof Utils !== 'undefined' && Utils.showToast) {
|
||||
Utils.showToast('Sorted by: ' + this.demoSortBy, 'info');
|
||||
}
|
||||
},
|
||||
|
||||
// Social Proof Demo (Priority 5)
|
||||
demoRating: 4.5,
|
||||
demoReviewCount: 127,
|
||||
demoRatingDistribution: { 5: 68, 4: 35, 3: 15, 2: 6, 3: 3 },
|
||||
demoUserRating: 0,
|
||||
demoReviews: [
|
||||
{
|
||||
id: 1,
|
||||
author_name: 'John D.',
|
||||
author_avatar: null,
|
||||
rating: 5,
|
||||
title: 'Best headphones I\'ve ever owned',
|
||||
content: 'The noise cancellation is incredible. I use these for work calls and they block out everything. Battery life is amazing too. Would highly recommend to anyone looking for premium audio quality.',
|
||||
verified: true,
|
||||
created_at: '2025-01-15',
|
||||
helpful_count: 42,
|
||||
images: []
|
||||
},
|
||||
{
|
||||
id: 2,
|
||||
author_name: 'Sarah M.',
|
||||
author_avatar: null,
|
||||
rating: 4,
|
||||
title: 'Great sound, slightly tight fit',
|
||||
content: 'Sound quality is phenomenal and the build quality feels premium. My only complaint is they feel a bit tight after a few hours of continuous use. Otherwise perfect for music lovers.',
|
||||
verified: true,
|
||||
created_at: '2025-01-10',
|
||||
helpful_count: 18,
|
||||
images: []
|
||||
},
|
||||
{
|
||||
id: 3,
|
||||
author_name: 'Mike R.',
|
||||
author_avatar: null,
|
||||
rating: 5,
|
||||
title: 'Worth every penny',
|
||||
content: 'These headphones are absolutely worth the investment. The sound stage is wide, bass is punchy but not overwhelming, and the noise cancellation is top-notch.',
|
||||
verified: false,
|
||||
created_at: '2025-01-05',
|
||||
helpful_count: 7,
|
||||
images: []
|
||||
}
|
||||
],
|
||||
demoReviewSort: 'newest',
|
||||
demoNewReview: {
|
||||
rating: 0,
|
||||
title: '',
|
||||
content: '',
|
||||
images: []
|
||||
},
|
||||
submittingReview: false,
|
||||
showReviewForm: false,
|
||||
|
||||
// Review Demo Methods
|
||||
demoSubmitReview() {
|
||||
this.submittingReview = true;
|
||||
setTimeout(() => {
|
||||
this.submittingReview = false;
|
||||
this.showReviewForm = false;
|
||||
this.demoNewReview = { rating: 0, title: '', content: '', images: [] };
|
||||
if (typeof Utils !== 'undefined' && Utils.showToast) {
|
||||
Utils.showToast('Review submitted successfully!', 'success');
|
||||
}
|
||||
}, 1500);
|
||||
},
|
||||
demoMarkHelpful(reviewId) {
|
||||
const review = this.demoReviews.find(r => r.id === reviewId);
|
||||
if (review) {
|
||||
review.helpful_count++;
|
||||
}
|
||||
if (typeof Utils !== 'undefined' && Utils.showToast) {
|
||||
Utils.showToast('Thanks for your feedback!', 'info');
|
||||
}
|
||||
},
|
||||
|
||||
// Sample form data for examples
|
||||
exampleForm: {
|
||||
textInput: 'Sample text',
|
||||
|
||||
Reference in New Issue
Block a user