docs: add architecture rules and docs for e-commerce components
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 <noreply@anthropic.com>
This commit is contained in:
@@ -116,6 +116,108 @@
|
||||
</span>
|
||||
```
|
||||
|
||||
## 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 %}
|
||||
|
||||
<!-- Tab content panels -->
|
||||
<div x-show="activeTab === 'dashboard'" x-transition>
|
||||
Dashboard content...
|
||||
</div>
|
||||
```
|
||||
|
||||
### 🗂️ Inline Tabs (with count badges)
|
||||
```html
|
||||
{% from 'shared/macros/tabs.html' import tabs_inline, tab_button %}
|
||||
|
||||
<div class="flex justify-between gap-4">
|
||||
{% 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 %}
|
||||
<div>Search...</div>
|
||||
</div>
|
||||
```
|
||||
|
||||
### 🗂️ 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!
|
||||
|
||||
Reference in New Issue
Block a user