Rename all "shop" directories and references to "storefront" to match the API and route naming convention already in use. Renamed directories: - app/templates/shop/ → app/templates/storefront/ - static/shop/ → static/storefront/ - app/templates/shared/macros/shop/ → .../macros/storefront/ - docs/frontend/shop/ → docs/frontend/storefront/ Renamed files: - shop.css → storefront.css - shop-layout.js → storefront-layout.js Updated references in: - app/routes/storefront_pages.py (21 template references) - app/modules/cms/routes/pages/vendor.py - app/templates/storefront/base.html (static paths) - All storefront templates (extends/includes) - docs/architecture/frontend-structure.md This aligns the template/static naming with: - Route file: storefront_pages.py - API directory: app/api/v1/storefront/ - Module routes: */routes/api/storefront.py - URL paths: /storefront/* Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
263 lines
9.3 KiB
HTML
263 lines
9.3 KiB
HTML
{#
|
|
Breadcrumbs Components
|
|
======================
|
|
Navigation breadcrumb trail for shop pages.
|
|
|
|
Usage:
|
|
{% from 'shared/macros/shop/breadcrumbs.html' import shop_breadcrumbs, auto_breadcrumbs %}
|
|
#}
|
|
|
|
|
|
{#
|
|
Shop Breadcrumbs
|
|
================
|
|
Breadcrumb navigation trail.
|
|
|
|
Parameters:
|
|
- items: List of breadcrumb items (static)
|
|
- items_var: Alpine.js expression for items (dynamic)
|
|
- separator: Separator icon (default: 'chevron-right')
|
|
- show_home: Show home link (default: true)
|
|
- home_url: URL for home (default: '/')
|
|
- home_label: Label for home (default: 'Home')
|
|
|
|
Item structure:
|
|
{
|
|
label: 'Category Name',
|
|
url: '/category/...', // Optional, last item typically has no URL
|
|
icon: 'folder' // Optional icon
|
|
}
|
|
|
|
Usage (static):
|
|
{{ shop_breadcrumbs(items=[
|
|
{'label': 'Electronics', 'url': '/category/electronics'},
|
|
{'label': 'Headphones', 'url': '/category/headphones'},
|
|
{'label': 'Wireless'}
|
|
]) }}
|
|
|
|
Usage (dynamic):
|
|
{{ shop_breadcrumbs(items_var='breadcrumbs') }}
|
|
#}
|
|
{% macro shop_breadcrumbs(
|
|
items=none,
|
|
items_var=none,
|
|
separator='chevron-right',
|
|
show_home=true,
|
|
home_url='/',
|
|
home_label='Home'
|
|
) %}
|
|
<nav aria-label="Breadcrumb" class="flex items-center text-sm">
|
|
<ol class="flex items-center flex-wrap gap-1">
|
|
{% if show_home %}
|
|
<li class="flex items-center">
|
|
<a
|
|
href="{{ home_url }}"
|
|
class="flex items-center gap-1 text-gray-500 dark:text-gray-400 hover:text-purple-600 dark:hover:text-purple-400 transition-colors"
|
|
>
|
|
<span x-html="$icon('home', 'w-4 h-4')"></span>
|
|
<span class="sr-only sm:not-sr-only">{{ home_label }}</span>
|
|
</a>
|
|
</li>
|
|
<li class="flex items-center text-gray-400 dark:text-gray-500" aria-hidden="true">
|
|
<span x-html="$icon('{{ separator }}', 'w-4 h-4')"></span>
|
|
</li>
|
|
{% endif %}
|
|
|
|
{% if items %}
|
|
{# Static items #}
|
|
{% for item in items %}
|
|
<li class="flex items-center">
|
|
{% if item.url and not loop.last %}
|
|
<a
|
|
href="{{ item.url }}"
|
|
class="flex items-center gap-1 text-gray-500 dark:text-gray-400 hover:text-purple-600 dark:hover:text-purple-400 transition-colors"
|
|
>
|
|
{% if item.icon %}
|
|
<span x-html="$icon('{{ item.icon }}', 'w-4 h-4')"></span>
|
|
{% endif %}
|
|
<span>{{ item.label }}</span>
|
|
</a>
|
|
{% else %}
|
|
<span class="flex items-center gap-1 text-gray-900 dark:text-white font-medium" aria-current="page">
|
|
{% if item.icon %}
|
|
<span x-html="$icon('{{ item.icon }}', 'w-4 h-4')"></span>
|
|
{% endif %}
|
|
<span>{{ item.label }}</span>
|
|
</span>
|
|
{% endif %}
|
|
</li>
|
|
{% if not loop.last %}
|
|
<li class="flex items-center text-gray-400 dark:text-gray-500" aria-hidden="true">
|
|
<span x-html="$icon('{{ separator }}', 'w-4 h-4')"></span>
|
|
</li>
|
|
{% endif %}
|
|
{% endfor %}
|
|
|
|
{% elif items_var %}
|
|
{# Dynamic items from Alpine.js #}
|
|
<template x-for="(item, index) in {{ items_var }}" :key="index">
|
|
<li class="flex items-center">
|
|
<template x-if="item.url && index < {{ items_var }}.length - 1">
|
|
<a
|
|
:href="item.url"
|
|
class="flex items-center gap-1 text-gray-500 dark:text-gray-400 hover:text-purple-600 dark:hover:text-purple-400 transition-colors"
|
|
>
|
|
<span x-show="item.icon" x-html="$icon(item.icon, 'w-4 h-4')"></span>
|
|
<span x-text="item.label"></span>
|
|
</a>
|
|
</template>
|
|
<template x-if="!item.url || index === {{ items_var }}.length - 1">
|
|
<span class="flex items-center gap-1 text-gray-900 dark:text-white font-medium" aria-current="page">
|
|
<span x-show="item.icon" x-html="$icon(item.icon, 'w-4 h-4')"></span>
|
|
<span x-text="item.label"></span>
|
|
</span>
|
|
</template>
|
|
</li>
|
|
</template>
|
|
<template x-for="(item, index) in {{ items_var }}.slice(0, -1)" :key="'sep-' + index">
|
|
<li class="flex items-center text-gray-400 dark:text-gray-500 order-last" aria-hidden="true" :style="'order: ' + (index * 2 + 1)">
|
|
<span x-html="$icon('{{ separator }}', 'w-4 h-4')"></span>
|
|
</li>
|
|
</template>
|
|
{% endif %}
|
|
</ol>
|
|
</nav>
|
|
{% endmacro %}
|
|
|
|
|
|
{#
|
|
Auto Breadcrumbs
|
|
================
|
|
Automatically generates breadcrumbs from category hierarchy.
|
|
|
|
Parameters:
|
|
- product_var: Alpine.js expression for product (optional)
|
|
- category_var: Alpine.js expression for current category
|
|
- show_home: Show home link (default: true)
|
|
|
|
Usage:
|
|
{{ auto_breadcrumbs(category_var='currentCategory') }}
|
|
{{ auto_breadcrumbs(product_var='product') }}
|
|
#}
|
|
{% macro auto_breadcrumbs(
|
|
product_var=none,
|
|
category_var='currentCategory',
|
|
show_home=true
|
|
) %}
|
|
<nav
|
|
aria-label="Breadcrumb"
|
|
class="flex items-center text-sm"
|
|
x-data="{
|
|
get breadcrumbs() {
|
|
const items = [];
|
|
{% if product_var %}
|
|
// Build from product's category
|
|
let cat = {{ product_var }}?.category;
|
|
{% else %}
|
|
let cat = {{ category_var }};
|
|
{% endif %}
|
|
|
|
// Build category path (from parent to child)
|
|
const categoryPath = [];
|
|
while (cat) {
|
|
categoryPath.unshift(cat);
|
|
cat = cat.parent;
|
|
}
|
|
|
|
categoryPath.forEach(c => {
|
|
items.push({
|
|
label: c.name,
|
|
url: c.url || '/category/' + c.slug
|
|
});
|
|
});
|
|
|
|
{% if product_var %}
|
|
// Add product as last item
|
|
items.push({
|
|
label: {{ product_var }}?.name,
|
|
url: null
|
|
});
|
|
{% endif %}
|
|
|
|
return items;
|
|
}
|
|
}"
|
|
>
|
|
<ol class="flex items-center flex-wrap gap-1">
|
|
{% if show_home %}
|
|
<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">
|
|
<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" aria-hidden="true">
|
|
<span x-html="$icon('chevron-right', 'w-4 h-4')"></span>
|
|
</li>
|
|
{% endif %}
|
|
|
|
<template x-for="(item, index) in breadcrumbs" :key="index">
|
|
<li class="flex items-center">
|
|
<template x-if="item.url && index < breadcrumbs.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"></a>
|
|
</template>
|
|
<template x-if="!item.url || index === breadcrumbs.length - 1">
|
|
<span class="text-gray-900 dark:text-white font-medium" aria-current="page" x-text="item.label"></span>
|
|
</template>
|
|
<span x-show="index < breadcrumbs.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>
|
|
{% endmacro %}
|
|
|
|
|
|
{#
|
|
Compact Breadcrumbs
|
|
===================
|
|
Mobile-friendly breadcrumbs showing only parent and current.
|
|
|
|
Parameters:
|
|
- parent: Parent item (static)
|
|
- parent_var: Alpine.js expression for parent
|
|
- current: Current page label
|
|
- current_var: Alpine.js expression for current
|
|
|
|
Usage:
|
|
{{ compact_breadcrumbs(parent={'label': 'Electronics', 'url': '/electronics'}, current='Headphones') }}
|
|
#}
|
|
{% macro compact_breadcrumbs(
|
|
parent=none,
|
|
parent_var=none,
|
|
current=none,
|
|
current_var=none
|
|
) %}
|
|
<nav aria-label="Breadcrumb" class="flex items-center text-sm">
|
|
{% if parent %}
|
|
<a
|
|
href="{{ parent.url }}"
|
|
class="flex items-center gap-1 text-gray-500 dark:text-gray-400 hover:text-purple-600 dark:hover:text-purple-400 transition-colors"
|
|
>
|
|
<span x-html="$icon('arrow-left', 'w-4 h-4')"></span>
|
|
<span>{{ parent.label }}</span>
|
|
</a>
|
|
{% elif parent_var %}
|
|
<a
|
|
:href="{{ parent_var }}?.url || '/'"
|
|
class="flex items-center gap-1 text-gray-500 dark:text-gray-400 hover:text-purple-600 dark:hover:text-purple-400 transition-colors"
|
|
>
|
|
<span x-html="$icon('arrow-left', 'w-4 h-4')"></span>
|
|
<span x-text="{{ parent_var }}?.label || 'Back'"></span>
|
|
</a>
|
|
{% endif %}
|
|
|
|
<span class="mx-2 text-gray-400 dark:text-gray-500">/</span>
|
|
|
|
{% if current %}
|
|
<span class="text-gray-900 dark:text-white font-medium" aria-current="page">{{ current }}</span>
|
|
{% elif current_var %}
|
|
<span class="text-gray-900 dark:text-white font-medium" aria-current="page" x-text="{{ current_var }}"></span>
|
|
{% endif %}
|
|
</nav>
|
|
{% endmacro %}
|