From 95a8ffc64588fb2417e871bbd6360cd8269f5827 Mon Sep 17 00:00:00 2001 From: Samir Boulahtit Date: Sun, 7 Dec 2025 17:04:28 +0100 Subject: [PATCH] docs: add architecture rules and docs for e-commerce components MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Architecture rules added: - FE-008: Use number_stepper macro for quantity inputs - FE-009: Use product_card macro for product displays - FE-010: Use product_grid macro for product listings - FE-011: Use add_to_cart macros for cart interactions - FE-012: Use mini_cart macro for cart dropdown Documentation: - Update ui-components-quick-reference.md with e-commerce section - Add component-standards.md for standardization guidelines - Add ecommerce-components-proposal.md with full 20-component roadmap - Update validate_architecture.py with FE-008 detection 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 --- .architecture-rules.yaml | 116 ++++ docs/development/architecture-rules.md | 126 +++- docs/frontend/shared/component-standards.md | 461 +++++++++++++ .../shared/ui-components-quick-reference.md | 234 +++++++ .../shop/ecommerce-components-proposal.md | 622 ++++++++++++++++++ mkdocs.yml | 2 + scripts/validate_architecture.py | 77 ++- 7 files changed, 1629 insertions(+), 9 deletions(-) create mode 100644 docs/frontend/shared/component-standards.md create mode 100644 docs/frontend/shop/ecommerce-components-proposal.md diff --git a/.architecture-rules.yaml b/.architecture-rules.yaml index 6c858f24..32d425f2 100644 --- a/.architecture-rules.yaml +++ b/.architecture-rules.yaml @@ -686,6 +686,122 @@ frontend_component_rules: encouraged_patterns: - "{% from 'shared/macros/forms.html' import" + - id: "FE-008" + name: "Use number_stepper macro for quantity inputs" + severity: "warning" + description: | + Use the shared number_stepper macro instead of raw . + This ensures consistent styling, proper dark mode support, and hides + native browser spinners that render inconsistently. + + WRONG (raw number input): + + + RIGHT (use macro): + {% from 'shared/macros/inputs.html' import number_stepper %} + {{ number_stepper(model='quantity', min=1, max=99) }} + + Exceptions (use noqa): + - ID fields where users type specific values (add {# noqa: FE-008 #}) + - Fields that are better as regular text inputs + + Suppress with: + - {# noqa: FE-008 #} on the line or at file level + pattern: + file_pattern: "app/templates/**/*.html" + anti_patterns: + - 'type="number"' + - "type='number'" + exceptions: + - "shared/macros/inputs.html" # The macro file itself + - "components.html" # Showcase page + + - id: "FE-009" + name: "Use product_card macro for product displays" + severity: "info" + description: | + Use the shared product_card macro for consistent product presentation. + Import from shared/macros/shop/product-card.html. + + Features: + - Size variants (sm, md, lg) + - Sale badge, new badge, out-of-stock badge + - Star ratings with review count + - Wishlist toggle button + - Quick add to cart button + + Usage: + {% from 'shared/macros/shop/product-card.html' import product_card %} + {{ product_card(product_var='item', size='md') }} + pattern: + file_pattern: "app/templates/shop/**/*.html" + encouraged_patterns: + - "{% from 'shared/macros/shop/product-card.html' import" + + - id: "FE-010" + name: "Use product_grid macro for product listings" + severity: "info" + description: | + Use the shared product_grid macro for responsive product grids. + Import from shared/macros/shop/product-grid.html. + + Features: + - Responsive column count (auto or fixed) + - Loading state with skeleton placeholders + - Empty state with customizable icon/message + - Integrated product_card component + + Usage: + {% from 'shared/macros/shop/product-grid.html' import product_grid %} + {{ product_grid(products_var='products', loading_var='loading') }} + pattern: + file_pattern: "app/templates/shop/**/*.html" + encouraged_patterns: + - "{% from 'shared/macros/shop/product-grid.html' import" + + - id: "FE-011" + name: "Use add_to_cart macros for cart interactions" + severity: "info" + description: | + Use the shared add-to-cart macros for consistent cart functionality. + Import from shared/macros/shop/add-to-cart.html. + + Available macros: + - add_to_cart_button() - Simple add to cart button + - add_to_cart_form() - Complete form with quantity selector + - buy_now_button() - Direct checkout button + - shop_quantity_selector() - Stock-aware quantity selector + + Usage: + {% from 'shared/macros/shop/add-to-cart.html' import add_to_cart_form %} + {{ add_to_cart_form(product_var='product') }} + pattern: + file_pattern: "app/templates/shop/**/*.html" + encouraged_patterns: + - "{% from 'shared/macros/shop/add-to-cart.html' import" + + - id: "FE-012" + name: "Use mini_cart macro for cart dropdown" + severity: "info" + description: | + Use the shared mini_cart macros for header cart functionality. + Import from shared/macros/shop/mini-cart.html. + + Available macros: + - mini_cart_icon() - Cart icon with item count badge + - mini_cart_dropdown() - Expandable cart preview + - mini_cart() - Combined icon + dropdown + - cart_item() - Individual cart item display + - cart_summary() - Subtotal, shipping, total + + Usage: + {% from 'shared/macros/shop/mini-cart.html' import mini_cart %} + {{ mini_cart(cart_var='cart', show_var='showCart') }} + pattern: + file_pattern: "app/templates/shop/**/*.html" + encouraged_patterns: + - "{% from 'shared/macros/shop/mini-cart.html' import" + # ============================================================================ # FRONTEND STYLING RULES # ============================================================================ diff --git a/docs/development/architecture-rules.md b/docs/development/architecture-rules.md index edfc6dbe..7f73d8d1 100644 --- a/docs/development/architecture-rules.md +++ b/docs/development/architecture-rules.md @@ -663,6 +663,124 @@ Show empty state when lists have no items. --- +### Jinja Macro Rules (app/templates/**/*.html) + +#### MAC-001: Use Shared Macros for Repeated Patterns +**Severity:** Error + +Never copy-paste HTML patterns. Use or create shared macros in `app/templates/shared/macros/`. + +```jinja +{# ✅ Good - Using shared macro #} +{% from 'shared/macros/tables.html' import table_wrapper, table_header %} +{% call table_wrapper() %} + {{ table_header(['Name', 'Email', 'Status']) }} + ... +{% endcall %} + +{# ❌ Bad - Copy-pasting table HTML #} +
+
+ + ... + ... +
+
+
+``` + +#### MAC-002: Import Macros at Template Top +**Severity:** Warning + +All macro imports should be at the top of the template, after `{% extends %}`. + +```jinja +{# ✅ Good #} +{% extends "admin/base.html" %} +{% from 'shared/macros/pagination.html' import pagination %} +{% from 'shared/macros/tables.html' import table_wrapper %} +{% from 'shared/macros/alerts.html' import loading_state %} + +{% block content %} +... +{% endblock %} +``` + +#### MAC-003: Use {% call %} for Wrapper Macros +**Severity:** Error + +Wrapper macros that use `{{ caller() }}` must be invoked with `{% call %}`. + +```jinja +{# ✅ Good #} +{% call table_wrapper() %} + {{ table_header(['Col1', 'Col2']) }} +{% endcall %} + +{% call tabs_nav() %} + {{ tab_button('tab1', 'First') }} +{% endcall %} + +{# ❌ Bad - Missing call block #} +{{ table_wrapper() }} {# This won't render inner content! #} +``` + +#### MAC-004: Document New Macros +**Severity:** Warning + +New macros must have JSDoc-style documentation comments. + +```jinja +{# ✅ Good #} +{# + Number Stepper + ============== + A number input with +/- buttons. + + Parameters: + - model: Alpine.js x-model variable (required) + - min: Minimum value (default: 1) + - max: Maximum value (optional) + + Usage: + {{ number_stepper(model='quantity', min=1, max=99) }} +#} +{% macro number_stepper(model, min=1, max=none) %} +... +{% endmacro %} +``` + +#### MAC-005: Add Components to Reference Page +**Severity:** Warning + +New shared components should be added to `/admin/components` page with live demos. + +--- + +### Frontend Component Rules (app/templates/**/*.html) + +#### FE-008: Use number_stepper Macro for Quantity Inputs +**Severity:** Warning + +Use the shared `number_stepper` macro instead of raw `` for consistent styling and dark mode support. + +```jinja +{# ✅ Good - Using number_stepper macro #} +{% from 'shared/macros/inputs.html' import number_stepper %} +{{ number_stepper(model='quantity', min=1, max=99) }} + +{# ❌ Bad - Raw number input #} + +``` + +**Suppress with `noqa` for ID fields:** +```jinja +{# noqa: FE-008 - User ID is typed directly, not incremented #} + +``` + +--- + ### Styling Rules (app/templates/**/*.html) #### CSS-001: Use Tailwind Utility Classes @@ -885,13 +1003,15 @@ All rules are defined in `.architecture-rules.yaml`. To modify rules: | Backend | 20 | 15 | 5 | | Frontend JS | 7 | 6 | 1 | | Frontend Templates | 7 | 3 | 4 | +| Frontend Macros | 5 | 2 | 3 | +| Frontend Components | 1 | 0 | 1 | | Frontend Styling | 4 | 1 | 3 | | Naming | 5 | 3 | 2 | | Security | 5 | 5 | 0 | | Quality | 3 | 2 | 1 | -| **Total** | **51** | **35** | **16** | +| **Total** | **57** | **37** | **20** | --- -**Last Updated:** 2025-12-04 -**Version:** 2.2 +**Last Updated:** 2025-12-07 +**Version:** 2.3 diff --git a/docs/frontend/shared/component-standards.md b/docs/frontend/shared/component-standards.md new file mode 100644 index 00000000..8ee239ef --- /dev/null +++ b/docs/frontend/shared/component-standards.md @@ -0,0 +1,461 @@ +# Frontend Component Standards + +**Version:** 1.0 +**Last Updated:** December 2025 +**Audience:** Frontend Developers + +--- + +## Overview + +This document defines mandatory standards for all frontend components across Admin, Vendor, and Shop frontends. Following these standards ensures consistency, maintainability, and a unified user experience. + +--- + +## Golden Rules + +1. **Use Jinja Macros** - Never copy-paste HTML patterns; use shared macros +2. **Alpine.js for Interactivity** - All client-side logic uses Alpine.js +3. **Dark Mode Required** - Every component must support dark mode +4. **Accessibility First** - ARIA labels, keyboard navigation, focus states +5. **Mobile Responsive** - Mobile-first design with Tailwind breakpoints + +--- + +## Jinja Macro System + +### Available Macro Files + +All macros are located in `app/templates/shared/macros/`: + +| File | Purpose | Key Macros | +|------|---------|------------| +| `pagination.html` | Table pagination | `pagination()`, `pagination_simple()` | +| `alerts.html` | Alerts and toasts | `loading_state()`, `error_state()`, `alert_dynamic()` | +| `badges.html` | Status indicators | `badge()`, `status_badge()`, `role_badge()` | +| `buttons.html` | Button variants | `btn_primary()`, `btn_secondary()`, `action_button()` | +| `forms.html` | Form inputs | `form_input()`, `form_select()`, `form_textarea()` | +| `tables.html` | Table components | `table_wrapper()`, `table_header()`, `table_empty_state()` | +| `cards.html` | Card layouts | `stat_card()`, `card()`, `info_card()` | +| `headers.html` | Page headers | `page_header()`, `page_header_flex()`, `refresh_button()` | +| `modals.html` | Modal dialogs | `modal()`, `confirm_modal()`, `job_details_modal()` | +| `tabs.html` | Tab navigation | `tabs_nav()`, `tabs_inline()`, `tab_button()` | +| `inputs.html` | Specialized inputs | `search_autocomplete()`, `number_stepper()` | + +### Macro Usage Rules + +#### RULE 1: Always Import Before Use + +```jinja +{# ✅ CORRECT - Import at top of template #} +{% from 'shared/macros/tables.html' import table_wrapper, table_header %} +{% from 'shared/macros/pagination.html' import pagination %} + +{% block content %} + {% call table_wrapper() %} + {{ table_header(['Name', 'Email', 'Status']) }} + ... + {% endcall %} + {{ pagination() }} +{% endblock %} + +{# ❌ WRONG - Inline HTML instead of macro #} +{% block content %} +
+
+ + ... +
+
+
+{% endblock %} +``` + +#### RULE 2: Use Macros for Repeated Patterns + +If you find yourself copying HTML more than once, create or use a macro. + +```jinja +{# ✅ CORRECT - Using badge macro #} +{{ status_badge(status='active') }} +{{ status_badge(status='pending') }} +{{ status_badge(status='inactive') }} + +{# ❌ WRONG - Copy-pasting badge HTML #} +Active +Pending +``` + +#### RULE 3: Use `{% call %}` for Wrapper Macros + +Macros that wrap content use the `caller()` pattern: + +```jinja +{# ✅ CORRECT - Using call block #} +{% call table_wrapper() %} + {{ table_header(['Column 1', 'Column 2']) }} + ... +{% endcall %} + +{% call tabs_nav() %} + {{ tab_button('tab1', 'First Tab', icon='home') }} + {{ tab_button('tab2', 'Second Tab', icon='cog') }} +{% endcall %} +``` + +#### RULE 4: Prefer Macro Parameters Over Custom CSS + +```jinja +{# ✅ CORRECT - Using size parameter #} +{{ number_stepper(model='qty', size='sm') }} +{{ number_stepper(model='qty', size='lg') }} + +{# ❌ WRONG - Adding custom classes #} +{{ number_stepper(model='qty') }} + +``` + +--- + +## Component Categories + +### 1. Layout Components + +#### Page Headers +```jinja +{% from 'shared/macros/headers.html' import page_header_flex, refresh_button %} + +{% call page_header_flex(title='Page Title', subtitle='Optional description') %} + {{ refresh_button(onclick='refreshData()') }} + +{% endcall %} +``` + +#### Cards +```jinja +{% from 'shared/macros/cards.html' import stat_card %} + +{{ stat_card( + label='Total Users', + value='1,234', + icon='user-group', + color='purple' +) }} +``` + +### 2. Data Display Components + +#### Tables with Pagination +```jinja +{% from 'shared/macros/tables.html' import table_wrapper, table_header, table_empty_state %} +{% from 'shared/macros/pagination.html' import pagination %} + +{% call table_wrapper() %} + {{ table_header(['Name', 'Email', 'Status', 'Actions']) }} + + + +{% endcall %} + +{{ table_empty_state( + message='No items found', + icon='inbox', + show_condition='!loading && items.length === 0' +) }} + +{{ pagination() }} +``` + +#### Badges +```jinja +{% from 'shared/macros/badges.html' import status_badge %} + +{{ status_badge(status='active') }} {# Green #} +{{ status_badge(status='pending') }} {# Yellow #} +{{ status_badge(status='inactive') }} {# Red #} +{{ status_badge(status='processing') }} {# Blue #} +``` + +### 3. Form Components + +#### Standard Inputs +```jinja +{% from 'shared/macros/forms.html' import form_input, form_select %} + +{{ form_input( + name='email', + label='Email Address', + type='email', + model='formData.email', + required=true, + error_var='errors.email' +) }} + +{{ form_select( + name='status', + label='Status', + model='formData.status', + options=[ + {'value': 'active', 'label': 'Active'}, + {'value': 'inactive', 'label': 'Inactive'} + ] +) }} +``` + +#### Number Stepper +```jinja +{% from 'shared/macros/inputs.html' import number_stepper %} + +{# Cart quantity #} +{{ number_stepper(model='quantity', min=1, max=99, size='md') }} + +{# Batch size with step #} +{{ number_stepper(model='batchSize', min=100, max=5000, step=100, size='lg') }} +``` + +#### Search Autocomplete +```jinja +{% from 'shared/macros/inputs.html' import search_autocomplete, selected_item_display %} + +{{ search_autocomplete( + search_var='searchQuery', + results_var='searchResults', + show_dropdown_var='showDropdown', + loading_var='searching', + select_action='selectItem(item)', + display_field='name', + secondary_field='email', + placeholder='Search users...' +) }} + +{{ selected_item_display( + selected_var='selectedUser', + display_field='name', + secondary_field='email', + clear_action='clearSelection()' +) }} +``` + +### 4. Navigation Components + +#### Tabs +```jinja +{% from 'shared/macros/tabs.html' import tabs_nav, tabs_inline, tab_button %} + +{# Full-width navigation tabs #} +{% call tabs_nav() %} + {{ tab_button('overview', 'Overview', icon='home') }} + {{ tab_button('details', 'Details', icon='document') }} + {{ tab_button('settings', 'Settings', icon='cog') }} +{% endcall %} + +{# Inline tabs with counts #} +{% call tabs_inline() %} + {{ tab_button('all', 'All', count_var='items.length') }} + {{ tab_button('active', 'Active', count_var='activeCount') }} + {{ tab_button('archived', 'Archived', count_var='archivedCount') }} +{% endcall %} +``` + +### 5. Feedback Components + +#### Alerts +```jinja +{% from 'shared/macros/alerts.html' import alert_dynamic, loading_state, error_state %} + +{{ alert_dynamic(type='success', message_var='successMessage', show_condition='successMessage') }} +{{ alert_dynamic(type='error', message_var='errorMessage', show_condition='errorMessage') }} + +{{ loading_state(message='Loading data...', show_condition='loading') }} +{{ error_state(title='Error', show_condition='error') }} +``` + +#### Modals +```jinja +{% from 'shared/macros/modals.html' import confirm_modal %} + +{{ confirm_modal( + show_var='showDeleteModal', + title='Confirm Delete', + message='Are you sure you want to delete this item?', + confirm_action='deleteItem()', + confirm_text='Delete', + confirm_class='bg-red-600 hover:bg-red-700' +) }} +``` + +--- + +## Dark Mode Requirements + +Every component MUST support dark mode using Tailwind's `dark:` prefix. + +### Color Pairing Reference + +| Element | Light Mode | Dark Mode | +|---------|------------|-----------| +| Background | `bg-white` | `dark:bg-gray-800` | +| Card Background | `bg-gray-50` | `dark:bg-gray-900` | +| Text Primary | `text-gray-700` | `dark:text-gray-200` | +| Text Secondary | `text-gray-500` | `dark:text-gray-400` | +| Border | `border-gray-300` | `dark:border-gray-600` | +| Hover Background | `hover:bg-gray-100` | `dark:hover:bg-gray-700` | +| Input Background | `bg-white` | `dark:bg-gray-700` | +| Focus Ring | `focus:ring-purple-500` | `dark:focus:ring-purple-400` | + +### Example +```html +
+

Title

+

Description

+
+``` + +--- + +## Accessibility Requirements + +### Required ARIA Attributes + +```html +{# Button with loading state #} + + +{# Icon-only button #} + + +{# Number stepper group #} +
+ + + +
+``` + +### Focus States + +All interactive elements must have visible focus states: +```html + +``` + +--- + +## Icon System + +### Usage +```html +{# Standard icon #} + + +{# Icon with color #} + + +{# Inline icon in button #} + +``` + +### Common Icons Reference + +| Icon | Usage | +|------|-------| +| `home` | Dashboard/Home | +| `user` | Users/Profile | +| `cog` | Settings | +| `plus` | Add/Create | +| `edit` | Edit/Modify | +| `trash` | Delete | +| `check-circle` | Success/Verified | +| `x-circle` | Error/Failed | +| `clock` | Pending/Time | +| `refresh` | Reload/Sync | +| `eye` | View/Details | +| `download` | Download/Export | +| `upload` | Upload/Import | +| `search` | Search | +| `filter` | Filter | +| `minus` | Decrease | + +--- + +## Creating New Components + +### When to Create a Macro + +Create a new macro when: +- Pattern is used 3+ times across templates +- Component has configurable options +- Component requires consistent styling + +### Macro Template +```jinja +{# + Component Name + ============== + Brief description of what the component does. + + Parameters: + - param1: Description (required/optional, default: value) + - param2: Description + + Usage: + {{ component_name(param1='value', param2='value') }} +#} +{% macro component_name( + param1, + param2='default' +) %} +
+ {# Component HTML #} +
+{% endmacro %} +``` + +### Checklist for New Components + +- [ ] Supports dark mode (`dark:` variants) +- [ ] Has ARIA labels for accessibility +- [ ] Documented with JSDoc-style comment +- [ ] Added to `/admin/components` page +- [ ] Added to this documentation +- [ ] Uses existing design tokens (colors, spacing) +- [ ] Tested in both light and dark mode +- [ ] Works on mobile viewport + +--- + +## Component Reference Page + +For live examples and copy-paste code, visit: + +**Admin Panel:** `/admin/components` + +This page includes: +- Interactive demos +- Code snippets with copy button +- All available macros +- Usage examples + +--- + +## Related Documentation + +- [UI Components Quick Reference](ui-components-quick-reference.md) +- [Icons Guide](../../development/icons-guide.md) +- [Tailwind CSS Guide](../tailwind-css.md) +- [Frontend Overview](../overview.md) +- [Architecture Rules](../../development/architecture-rules.md) diff --git a/docs/frontend/shared/ui-components-quick-reference.md b/docs/frontend/shared/ui-components-quick-reference.md index d9979497..9c13491f 100644 --- a/docs/frontend/shared/ui-components-quick-reference.md +++ b/docs/frontend/shared/ui-components-quick-reference.md @@ -116,6 +116,108 @@ ``` +## Number Stepper + +A number input with +/- buttons for quantity selection. Ideal for cart quantities, batch sizes, and product pages. + +### Basic Number Stepper +```html +{% from 'shared/macros/inputs.html' import number_stepper %} + +{# Basic usage - cart quantity #} +{{ number_stepper(model='quantity', min=1, max=99) }} +``` + +### Size Variants +```html +{# Small - compact for tables/lists #} +{{ number_stepper(model='item.qty', min=1, max='item.stock', size='sm') }} + +{# Medium (default) #} +{{ number_stepper(model='quantity', min=1, max=99) }} + +{# Large - prominent placement #} +{{ number_stepper(model='batchSize', min=100, max=5000, step=100, size='lg') }} +``` + +### With Disabled State +```html +{{ number_stepper(model='qty', min=1, disabled_var='isLoading') }} +``` + +### Number Stepper Parameters +| Parameter | Default | Description | +|-----------|---------|-------------| +| `model` | required | Alpine.js x-model variable | +| `min` | `1` | Minimum allowed value | +| `max` | `none` | Maximum allowed value (can be Alpine.js expression) | +| `step` | `1` | Increment/decrement step | +| `size` | `'md'` | Size variant: `'sm'`, `'md'`, `'lg'` | +| `disabled_var` | `none` | Alpine.js variable for disabled state | +| `name` | `none` | Input name for form submission | +| `id` | `none` | Input id attribute | +| `label` | `'Quantity'` | Accessible label for screen readers | + +--- + +## Tabs + +Tab navigation components for switching between content sections. + +### 🗂️ Navigation Tabs (with icons) +```html +{% from 'shared/macros/tabs.html' import tabs_nav, tab_button %} + +{% call tabs_nav() %} + {{ tab_button('dashboard', 'Dashboard', icon='home') }} + {{ tab_button('settings', 'Settings', icon='cog') }} + {{ tab_button('profile', 'Profile', icon='user') }} +{% endcall %} + + +
+ Dashboard content... +
+``` + +### 🗂️ Inline Tabs (with count badges) +```html +{% from 'shared/macros/tabs.html' import tabs_inline, tab_button %} + +
+ {% call tabs_inline() %} + {{ tab_button('all', 'All Items', count_var='allItems.length') }} + {{ tab_button('active', 'Active', count_var='activeItems.length') }} + {{ tab_button('archived', 'Archived', count_var='archivedItems.length') }} + {% endcall %} +
Search...
+
+``` + +### 🗂️ Tabs with Custom Click Handlers +```html +{% call tabs_nav() %} + {{ tab_button('database', 'Database Logs', + tab_var='logSource', + icon='database', + onclick="logSource = 'database'; loadDatabaseLogs()") }} + {{ tab_button('file', 'File Logs', + tab_var='logSource', + icon='document', + onclick="logSource = 'file'; loadFileLogs()") }} +{% endcall %} +``` + +### Tab Button Parameters +| Parameter | Default | Description | +|-----------|---------|-------------| +| `id` | required | Tab identifier for comparison | +| `label` | required | Display text | +| `tab_var` | `'activeTab'` | Alpine.js variable for active state | +| `icon` | `none` | Optional icon name | +| `count_var` | `none` | Alpine.js variable for count badge | +| `onclick` | `none` | Custom click handler (overrides default) | + ## Grid Layouts ### 2 Columns (Desktop) @@ -223,6 +325,138 @@ 7. Helper text: `text-xs text-gray-600` 8. Error text: `text-xs text-red-600` +--- + +## E-commerce Components (Shop Frontend) + +Reusable macros for shop/storefront functionality. Located in `app/templates/shared/macros/shop/`. + +### 🛍️ Product Card +```html +{% from 'shared/macros/shop/product-card.html' import product_card %} + +{# Basic product card #} +{{ product_card(product_var='product') }} + +{# With size and options #} +{{ product_card( + product_var='item', + size='lg', + show_rating=true, + show_quick_add=true, + show_wishlist=true +) }} +``` + +**Size variants:** `sm` (compact), `md` (default), `lg` (featured) + +**Features:** +- Sale badge (when `sale_price` exists) +- "New" badge (when `is_new` is true) +- Out of stock overlay +- Star ratings with review count +- Wishlist toggle button +- Quick add to cart + +### 🛍️ Product Grid +```html +{% from 'shared/macros/shop/product-grid.html' import product_grid %} + +{# Basic grid #} +{{ product_grid(products_var='products', loading_var='loading') }} + +{# With empty state #} +{{ product_grid( + products_var='searchResults', + loading_var='searching', + empty_message='No products found', + empty_icon='search' +) }} +``` + +**Features:** +- Responsive columns (auto-adjusts or fixed) +- Loading skeleton placeholders +- Empty state with customizable icon/message + +### 🛒 Add to Cart +```html +{% from 'shared/macros/shop/add-to-cart.html' import add_to_cart_button, add_to_cart_form, buy_now_button %} + +{# Simple button #} +{{ add_to_cart_button(action='addToCart()') }} + +{# Complete form with quantity #} +{{ add_to_cart_form(product_var='product', size='md') }} + +{# Buy now button #} +{{ buy_now_button(action='buyNow()') }} +``` + +**Macros available:** +- `add_to_cart_button()` - Simple button with loading state +- `add_to_cart_form()` - Form with quantity selector + button +- `buy_now_button()` - Direct checkout button +- `shop_quantity_selector()` - Stock-aware quantity input + +### 🛒 Mini Cart (Header) +```html +{% from 'shared/macros/shop/mini-cart.html' import mini_cart, mini_cart_icon %} + +{# Complete mini cart with dropdown #} +{{ mini_cart(cart_var='cart', show_var='showCart') }} + +{# Just the icon with badge #} +{{ mini_cart_icon(cart_var='cart', size='md') }} +``` + +**Macros available:** +- `mini_cart()` - Combined icon + dropdown +- `mini_cart_icon()` - Icon with item count badge +- `mini_cart_dropdown()` - Dropdown panel +- `cart_item()` - Individual item display +- `cart_summary()` - Subtotal, shipping, total + +### E-commerce Alpine.js State +```javascript +// Required state variables for e-commerce components +{ + // Products + products: [], + loading: true, + + // Cart + cart: { + items: [], + item_count: 0, + subtotal: 0, + total: 0 + }, + showCart: false, + + // Add to cart + quantity: 1, + addingToCart: false, + addedToCart: false, + + // Wishlist + toggleWishlist(product) { + product.in_wishlist = !product.in_wishlist; + }, + + // Cart actions + addToCart() { + this.addingToCart = true; + // API call... + }, + removeFromCart(itemId) { + this.cart.items = this.cart.items.filter(i => i.id !== itemId); + } +} +``` + +--- + ## Reference Page Visit `/admin/components` for full component library with live examples! diff --git a/docs/frontend/shop/ecommerce-components-proposal.md b/docs/frontend/shop/ecommerce-components-proposal.md new file mode 100644 index 00000000..b8b5ad7d --- /dev/null +++ b/docs/frontend/shop/ecommerce-components-proposal.md @@ -0,0 +1,622 @@ +# E-commerce Components Proposal + +**Version:** 1.0 +**Created:** December 2025 +**Status:** Proposal +**Author:** Development Team + +--- + +## Overview + +This document proposes a comprehensive set of reusable Jinja macro components for the shop frontend. These components will standardize the e-commerce experience across all vendor shops while supporting vendor-specific theming via CSS variables. + +--- + +## Design Principles + +1. **Vendor Theming** - Use CSS variables (`var(--color-primary)`) for brand colors +2. **Mobile First** - Responsive design starting from mobile +3. **Performance** - Lazy loading, optimized images, minimal JS +4. **Accessibility** - WCAG 2.1 AA compliant +5. **Consistency** - Same UX patterns across all shops + +--- + +## Proposed Components + +### Priority 1: Core Shopping Components + +#### 1.1 Product Card +**File:** `app/templates/shared/macros/shop/product-card.html` + +A versatile product card for grids, carousels, and lists. + +```jinja +{{ product_card( + product=product, + show_vendor=false, + show_rating=true, + show_quick_add=true, + size='md', {# sm, md, lg #} + layout='vertical' {# vertical, horizontal #} +) }} +``` + +**Features:** +- Product image with lazy loading +- Sale badge / "New" badge +- Product title (truncated) +- Price (with strikethrough for sales) +- Star rating +- Quick "Add to Cart" button +- Wishlist heart icon +- Hover effects + +**Variants:** +| Size | Use Case | +|------|----------| +| `sm` | Mobile grids, sidebars | +| `md` | Standard product grids | +| `lg` | Featured products, hero sections | + +--- + +#### 1.2 Product Grid +**File:** `app/templates/shared/macros/shop/product-grid.html` + +Responsive grid layout for product listings. + +```jinja +{{ product_grid( + products=products, + columns={'sm': 2, 'md': 3, 'lg': 4}, + gap='md', + show_empty_state=true +) }} +``` + +**Features:** +- Responsive column configuration +- Loading skeleton state +- Empty state with CTA +- "Load more" or pagination integration + +--- + +#### 1.3 Add to Cart Button +**File:** `app/templates/shared/macros/shop/add-to-cart.html` + +Standardized add-to-cart functionality. + +```jinja +{{ add_to_cart_button( + product_id='product.id', + variant_id='selectedVariant?.id', + quantity_model='quantity', + show_quantity=true, + size='md', + full_width=false +) }} +``` + +**Features:** +- Loading state during add +- Success animation +- Out of stock state +- Quantity selector integration (uses `number_stepper`) +- "Added!" feedback + +--- + +#### 1.4 Quantity Selector (Shop Version) +**File:** Already exists as `number_stepper` in `inputs.html` + +Shop-specific wrapper with stock validation: + +```jinja +{{ shop_quantity_selector( + model='quantity', + max='product.stock', + disabled_var='addingToCart', + out_of_stock_var='product.stock === 0' +) }} +``` + +--- + +### Priority 2: Cart Components + +#### 2.1 Mini Cart (Header Dropdown) +**File:** `app/templates/shared/macros/shop/mini-cart.html` + +Dropdown cart preview in the header. + +```jinja +{{ mini_cart( + cart_var='cart', + max_items=3, + show_checkout_button=true +) }} +``` + +**Features:** +- Cart item count badge +- Item thumbnails +- Quick quantity adjust +- Remove item button +- Subtotal display +- "View Cart" and "Checkout" CTAs + +--- + +#### 2.2 Cart Item Row +**File:** `app/templates/shared/macros/shop/cart-item.html` + +Individual cart line item. + +```jinja +{{ cart_item( + item='item', + index='index', + show_image=true, + editable=true, + compact=false +) }} +``` + +**Features:** +- Product thumbnail +- Product name + variant info +- Unit price +- Quantity stepper (using `number_stepper` with `size='sm'`) +- Line total +- Remove button +- Stock warning if low + +--- + +#### 2.3 Cart Summary +**File:** `app/templates/shared/macros/shop/cart-summary.html` + +Order summary sidebar/section. + +```jinja +{{ cart_summary( + cart_var='cart', + show_promo_code=true, + show_shipping_estimate=true, + checkout_url='/checkout' +) }} +``` + +**Features:** +- Subtotal +- Discount code input +- Applied discounts +- Estimated shipping +- Tax display +- Order total +- Checkout CTA +- Secure payment badges + +--- + +### Priority 3: Product Detail Components + +#### 3.1 Product Gallery +**File:** `app/templates/shared/macros/shop/product-gallery.html` + +Image gallery with thumbnails and zoom. + +```jinja +{{ product_gallery( + images='product.images', + show_thumbnails=true, + enable_zoom=true, + enable_fullscreen=true +) }} +``` + +**Features:** +- Main image display +- Thumbnail navigation +- Image zoom on hover +- Fullscreen lightbox +- Swipe support on mobile +- Video support + +--- + +#### 3.2 Variant Selector +**File:** `app/templates/shared/macros/shop/variant-selector.html` + +Product variant selection (size, color, etc.). + +```jinja +{{ variant_selector( + variants='product.variants', + selected_var='selectedVariant', + type='buttons' {# buttons, dropdown, swatches #} +) }} +``` + +**Variant Types:** +- **Buttons** - Size selection (S, M, L, XL) +- **Dropdown** - Many options +- **Swatches** - Color selection with preview + +**Features:** +- Out of stock variants disabled +- Low stock warning +- Price change on selection +- Image change on color selection + +--- + +#### 3.3 Product Info Block +**File:** `app/templates/shared/macros/shop/product-info.html` + +Product details section. + +```jinja +{{ product_info( + product='product', + show_sku=true, + show_stock=true, + show_vendor=false +) }} +``` + +**Features:** +- Product title (H1) +- Price display (sale price handling) +- Rating stars + review count +- Short description +- SKU display +- Stock status +- Vendor name (marketplace) + +--- + +#### 3.4 Product Tabs +**File:** `app/templates/shared/macros/shop/product-tabs.html` + +Tabbed product information. + +```jinja +{{ product_tabs( + product='product', + tabs=['description', 'specifications', 'reviews', 'shipping'] +) }} +``` + +**Tab Options:** +- Description (HTML content) +- Specifications (key-value table) +- Reviews (review list + form) +- Shipping info +- Returns policy + +--- + +### Priority 4: Navigation & Discovery + +#### 4.1 Category Navigation +**File:** `app/templates/shared/macros/shop/category-nav.html` + +Category browsing sidebar/menu. + +```jinja +{{ category_nav( + categories='categories', + current_category='currentCategory', + show_count=true, + collapsible=true +) }} +``` + +**Features:** +- Nested category tree +- Product counts +- Active state highlighting +- Collapsible sections +- Mobile drawer version + +--- + +#### 4.2 Breadcrumbs +**File:** `app/templates/shared/macros/shop/breadcrumbs.html` + +Navigation breadcrumb trail. + +```jinja +{{ shop_breadcrumbs( + items=[ + {'label': 'Home', 'url': '/'}, + {'label': 'Electronics', 'url': '/category/electronics'}, + {'label': 'Headphones', 'url': none} + ] +) }} +``` + +--- + +#### 4.3 Search Bar +**File:** `app/templates/shared/macros/shop/search-bar.html` + +Product search with autocomplete. + +```jinja +{{ search_bar( + placeholder='Search products...', + show_suggestions=true, + show_categories=true +) }} +``` + +**Features:** +- Autocomplete suggestions +- Recent searches +- Category filter +- Voice search (optional) +- Mobile fullscreen mode + +--- + +#### 4.4 Filter Sidebar +**File:** `app/templates/shared/macros/shop/filter-sidebar.html` + +Product filtering panel. + +```jinja +{{ filter_sidebar( + filters='availableFilters', + active_filters='activeFilters', + show_price_range=true, + show_rating_filter=true +) }} +``` + +**Filter Types:** +- Checkbox (brand, size) +- Color swatches +- Price range slider +- Rating stars +- In-stock toggle + +--- + +### Priority 5: Social Proof & Trust + +#### 5.1 Star Rating +**File:** `app/templates/shared/macros/shop/star-rating.html` + +Reusable star rating display. + +```jinja +{{ star_rating( + rating=4.5, + max=5, + size='md', + show_count=true, + count=127 +) }} +``` + +--- + +#### 5.2 Review Card +**File:** `app/templates/shared/macros/shop/review-card.html` + +Individual product review. + +```jinja +{{ review_card( + review='review', + show_verified_badge=true, + show_helpful_buttons=true +) }} +``` + +**Features:** +- Reviewer name/avatar +- Star rating +- Review date +- Review text +- Verified purchase badge +- Helpful/Not helpful buttons +- Review images + +--- + +#### 5.3 Trust Badges +**File:** `app/templates/shared/macros/shop/trust-badges.html` + +Trust and security indicators. + +```jinja +{{ trust_badges( + badges=['secure_payment', 'free_shipping', 'returns', 'support'] +) }} +``` + +**Badge Options:** +- Secure payment +- Free shipping over X +- Easy returns +- 24/7 support +- Money back guarantee +- SSL secured + +--- + +### Priority 6: Promotional Components + +#### 6.1 Sale Banner +**File:** `app/templates/shared/macros/shop/sale-banner.html` + +Promotional banner with countdown. + +```jinja +{{ sale_banner( + title='Summer Sale', + subtitle='Up to 50% off', + end_date='2025-08-31', + show_countdown=true, + cta_text='Shop Now', + cta_url='/sale' +) }} +``` + +--- + +#### 6.2 Product Badge +**File:** `app/templates/shared/macros/shop/product-badge.html` + +Product overlay badges. + +```jinja +{{ product_badge(type='sale', value='-20%') }} +{{ product_badge(type='new') }} +{{ product_badge(type='bestseller') }} +{{ product_badge(type='low_stock', value='Only 3 left') }} +``` + +--- + +#### 6.3 Recently Viewed +**File:** `app/templates/shared/macros/shop/recently-viewed.html` + +Recently viewed products carousel. + +```jinja +{{ recently_viewed( + products='recentlyViewed', + max_items=6, + title='Recently Viewed' +) }} +``` + +--- + +## Implementation Phases + +### Phase 1: Core Components (Week 1-2) +- [ ] Product Card +- [ ] Product Grid +- [ ] Add to Cart Button +- [ ] Mini Cart +- [ ] Cart Item Row +- [ ] Cart Summary + +### Phase 2: Product Detail (Week 3-4) +- [ ] Product Gallery +- [ ] Variant Selector +- [ ] Product Info Block +- [ ] Product Tabs +- [ ] Star Rating + +### Phase 3: Navigation (Week 5-6) +- [ ] Category Navigation +- [ ] Breadcrumbs +- [ ] Search Bar +- [ ] Filter Sidebar + +### Phase 4: Social & Promo (Week 7-8) +- [ ] Review Card +- [ ] Trust Badges +- [ ] Sale Banner +- [ ] Product Badge +- [ ] Recently Viewed + +--- + +## CSS Variables for Theming + +All shop components will use these CSS variables set by the vendor theme: + +```css +:root { + /* Primary brand colors */ + --color-primary: #7c3aed; + --color-primary-hover: #6d28d9; + --color-primary-light: #8b5cf6; + + /* Secondary colors */ + --color-secondary: #f3f4f6; + --color-secondary-hover: #e5e7eb; + + /* Text colors */ + --color-text-primary: #111827; + --color-text-secondary: #6b7280; + + /* Status colors */ + --color-success: #10b981; + --color-warning: #f59e0b; + --color-error: #ef4444; + --color-sale: #dc2626; + + /* UI colors */ + --color-border: #e5e7eb; + --color-background: #ffffff; + --color-surface: #f9fafb; + + /* Spacing */ + --spacing-unit: 0.25rem; + + /* Border radius */ + --radius-sm: 0.25rem; + --radius-md: 0.375rem; + --radius-lg: 0.5rem; + --radius-full: 9999px; +} +``` + +--- + +## File Structure + +``` +app/templates/shared/macros/shop/ +├── product-card.html +├── product-grid.html +├── add-to-cart.html +├── mini-cart.html +├── cart-item.html +├── cart-summary.html +├── product-gallery.html +├── variant-selector.html +├── product-info.html +├── product-tabs.html +├── category-nav.html +├── breadcrumbs.html +├── search-bar.html +├── filter-sidebar.html +├── star-rating.html +├── review-card.html +├── trust-badges.html +├── sale-banner.html +├── product-badge.html +└── recently-viewed.html +``` + +--- + +## Next Steps + +1. **Review & Approve** - Team reviews this proposal +2. **Prioritize** - Confirm component priority order +3. **Design Mockups** - Create visual designs for key components +4. **Implementation** - Build components in priority order +5. **Documentation** - Add to component reference page +6. **Testing** - Test across vendors and themes + +--- + +## Related Documentation + +- [Component Standards](../shared/component-standards.md) +- [Shop Architecture](architecture.md) +- [Theme System](../../architecture/theme-system/overview.md) +- [UI Components Quick Reference](../shared/ui-components-quick-reference.md) diff --git a/mkdocs.yml b/mkdocs.yml index 0ec413f1..602f46e0 100644 --- a/mkdocs.yml +++ b/mkdocs.yml @@ -86,6 +86,7 @@ nav: - CDN Fallback Strategy: frontend/cdn-fallback-strategy.md - Tailwind CSS Build: frontend/tailwind-css.md - Shared Components: + - Component Standards: frontend/shared/component-standards.md - Jinja Macros Library: frontend/shared/jinja-macros.md - UI Components: frontend/shared/ui-components.md - UI Components Quick Reference: frontend/shared/ui-components-quick-reference.md @@ -102,6 +103,7 @@ nav: - Shop Frontend: - Architecture: frontend/shop/architecture.md - Page Templates: frontend/shop/page-templates.md + - E-commerce Components Proposal: frontend/shop/ecommerce-components-proposal.md - Authentication Pages: frontend/shop/authentication-pages.md - Navigation Flow: frontend/shop/navigation-flow.md diff --git a/scripts/validate_architecture.py b/scripts/validate_architecture.py index a9e5cb44..29759768 100755 --- a/scripts/validate_architecture.py +++ b/scripts/validate_architecture.py @@ -397,6 +397,10 @@ class ArchitectureValidator: if not is_components_page and not is_macro: self._check_icon_helper_usage(file_path, content, lines) + # FE-008: Check for raw number inputs (should use number_stepper) + if not is_components_page and not is_macro: + self._check_number_stepper_macro_usage(file_path, content, lines) + # Only check admin templates for extends if "/admin/" not in file_path_str and "\\admin\\" not in file_path_str: return @@ -630,12 +634,19 @@ class ArchitectureValidator: # Look for dropdown patterns: @click.outside or @click.away with menu positioning for i, line in enumerate(lines, 1): - if ("@click.outside=" in line or "@click.away=" in line) and "false" in line: - # Check context for dropdown menu indicators - context_lines = "\n".join(lines[max(0, i-3):min(len(lines), i+10)]) - # Look for dropdown menu styling - if "absolute" in context_lines and ("right-0" in context_lines or "left-0" in context_lines): - if "py-2" in context_lines or "py-1" in context_lines: + # Match @click.away="something = false" or @click.outside="..." + if "@click.away=" in line or "@click.outside=" in line: + # Skip if this is a modal (modals also use @click.away but have different structure) + context_lines = "\n".join(lines[max(0, i-5):min(len(lines), i+10)]) + + # Skip if it looks like a modal (fixed inset-0) + if "fixed inset-0" in context_lines: + continue + + # Look for dropdown menu styling: absolute positioning with z-index + if "absolute" in context_lines and ("z-10" in context_lines or "z-20" in context_lines or "z-50" in context_lines): + # Additional indicators: shadow, border, bg-white/dark, rounded + if "shadow" in context_lines or "border" in context_lines: self._add_violation( rule_id="FE-006", rule_name="Use dropdowns macro", @@ -678,6 +689,56 @@ class ArchitectureValidator: ) return + def _check_number_stepper_macro_usage(self, file_path: Path, content: str, lines: list[str]): + """FE-008: Check for raw number inputs that should use number_stepper macro + + Detects that should use the number_stepper macro for + consistent styling and dark mode support. + + Exceptions: + - ID fields (placeholder contains 'id' or 'ID') + - Files already importing number_stepper + - Lines with noqa: FE-008 comment + """ + # Check if already using the number_stepper macro + uses_macro = any("number_stepper" in line for line in lines) + if uses_macro: + return + + # Check for file-level noqa comment + has_noqa = any("noqa: fe-008" in line.lower() for line in lines) + if has_noqa: + return + + # Look for raw number inputs + for i, line in enumerate(lines, 1): + if 'type="number"' in line or "type='number'" in line: + # Skip if line has noqa comment + if "noqa" in line.lower(): + continue + + # Skip if it looks like an ID field (check surrounding lines for context) + context_lines = "\n".join(lines[max(0, i-3):min(len(lines), i+2)]).lower() + if "user id" in context_lines or "placeholder" in context_lines and "id" in context_lines: + continue + + # Skip if it's in a comment + stripped = line.strip() + if stripped.startswith("{#") or stripped.startswith("