Files
orion/.architecture-rules/frontend.yaml
Samir Boulahtit 6f8434f200 feat: add PlatformSettings for pagination and vendor filter improvements
Platform Settings:
- Add PlatformSettings utility in init-alpine.js with 5-min cache
- Add Display tab in /admin/settings for rows_per_page config
- Integrate PlatformSettings.getRowsPerPage() in all paginated pages
- Standardize default per_page to 20 across all admin pages
- Add documentation at docs/frontend/shared/platform-settings.md

Architecture Rules:
- Add JS-010: enforce PlatformSettings usage for pagination
- Add JS-011: enforce standard pagination structure
- Add JS-012: detect double /api/v1 prefix in apiClient calls
- Implement all rules in validate_architecture.py

Vendor Filter (Tom Select):
- Add vendor filter to marketplace-products, vendor-products,
  customers, inventory, and vendor-themes pages
- Add selectedVendor display panel with clear button
- Add localStorage persistence for vendor selection
- Fix double /api/v1 prefix in vendor-selector.js

Bug Fixes:
- Remove duplicate PlatformSettings from utils.js
- Fix customers.js pagination structure (page_size → per_page)
- Fix code-quality-violations.js pagination structure

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2025-12-22 22:39:34 +01:00

706 lines
23 KiB
YAML

# Architecture Rules - Frontend Rules
# Combined rules for JavaScript, Templates, Components, and Styling
# ============================================================================
# JAVASCRIPT ARCHITECTURE RULES
# ============================================================================
javascript_rules:
- id: "JS-001"
name: "Use centralized logger, not console"
severity: "error"
description: |
Use window.LogConfig.createLogger() for consistent logging.
Never use console.log, console.error, console.warn directly.
pattern:
file_pattern: "static/**/js/**/*.js"
anti_patterns:
- "console\\.log"
- "console\\.error"
- "console\\.warn"
exceptions:
- "// eslint-disable"
- "console.log('✅"
auto_exclude_files:
- "init-*.js"
- id: "JS-002"
name: "Use lowercase apiClient for API calls"
severity: "error"
description: |
Use lowercase 'apiClient' consistently, not 'ApiClient' or 'API_CLIENT'
pattern:
file_pattern: "static/**/js/**/*.js"
anti_patterns:
- "ApiClient\\."
- "API_CLIENT\\."
required_pattern: "apiClient\\."
- id: "JS-003"
name: "Alpine components must spread ...data()"
severity: "error"
description: |
All Alpine.js components must inherit base layout data using spread operator
pattern:
file_pattern: "static/**/js/**/*.js"
required_in_alpine_components:
- "\\.\\.\\.data\\(\\)"
- id: "JS-004"
name: "Alpine components must set currentPage"
severity: "error"
description: |
All Alpine.js page components must set a currentPage identifier
pattern:
file_pattern: "static/**/js/**/*.js"
required_in_alpine_components:
- "currentPage:"
- id: "JS-005"
name: "Initialization methods must include guard"
severity: "error"
description: |
Init methods should prevent duplicate initialization with guard
pattern:
file_pattern: "static/**/js/**/*.js"
recommended_pattern: |
if (window._pageInitialized) return;
window._pageInitialized = true;
- id: "JS-006"
name: "All async operations must have try/catch with error logging"
severity: "error"
description: |
All API calls and async operations must have error handling
pattern:
file_pattern: "static/**/js/**/*.js"
check: "async_error_handling"
- id: "JS-007"
name: "Set loading state before async operations"
severity: "warning"
description: |
Loading state should be set before and cleared after async operations
pattern:
file_pattern: "static/**/js/**/*.js"
recommended_pattern: |
loading = true;
try {
// operation
} finally {
loading = false;
}
- id: "JS-008"
name: "Use apiClient for API calls, not raw fetch()"
severity: "error"
description: |
All API calls must use the apiClient helper instead of raw fetch().
The apiClient automatically:
- Adds Authorization header with JWT token from cookies
- Sets Content-Type headers
- Handles error responses consistently
- Provides logging integration
WRONG (raw fetch):
const response = await fetch('/api/v1/admin/products/123');
RIGHT (apiClient):
const response = await apiClient.get('/admin/products/123');
const result = await apiClient.post('/admin/products', data);
await apiClient.delete('/admin/products/123');
pattern:
file_pattern: "static/**/js/**/*.js"
anti_patterns:
- "fetch\\('/api/"
- 'fetch\\("/api/'
- "fetch\\(`/api/"
exceptions:
- "init-api-client.js"
- id: "JS-009"
name: "Use Utils.showToast() for notifications, not alert() or window.showToast"
severity: "error"
description: |
All user notifications must use Utils.showToast() from static/shared/js/utils.js.
Never use browser alert() dialogs or undefined window.showToast.
Utils.showToast() provides:
- Consistent styling (Tailwind-based toast in bottom-right corner)
- Automatic fade-out after duration
- Color-coded types (success=green, error=red, warning=yellow, info=blue)
WRONG (browser dialog):
alert('Product saved successfully');
RIGHT (Utils helper):
Utils.showToast('Product saved successfully', 'success');
Utils.showToast('Failed to save product', 'error');
pattern:
file_pattern: "static/**/js/**/*.js"
anti_patterns:
- "alert\\("
- "window\\.showToast"
exceptions:
- "utils.js"
- id: "JS-010"
name: "Use PlatformSettings for pagination rows per page"
severity: "error"
description: |
All pages with tables MUST use window.PlatformSettings.getRowsPerPage()
to load the platform-configured rows per page setting. This ensures
consistent pagination behavior across the entire admin and vendor interface.
The setting is configured at /admin/settings under the Display tab.
Settings are cached client-side for 5 minutes to minimize API calls.
Required pattern in init() method:
async init() {
// Guard against multiple initialization
if (window._pageNameInitialized) return;
window._pageNameInitialized = true;
// REQUIRED: Load platform settings for pagination
if (window.PlatformSettings) {
this.pagination.per_page = await window.PlatformSettings.getRowsPerPage();
}
await this.loadData();
}
WRONG (hardcoded pagination):
pagination: {
page: 1,
per_page: 50, // Hardcoded!
total: 0
}
RIGHT (platform settings):
pagination: {
page: 1,
per_page: 20, // Default, overridden by PlatformSettings
total: 0
}
async init() {
if (window.PlatformSettings) {
this.pagination.per_page = await window.PlatformSettings.getRowsPerPage();
}
}
Documentation: docs/frontend/shared/platform-settings.md
pattern:
file_pattern: "static/admin/js/**/*.js"
required_in_pages_with_pagination:
- "PlatformSettings\\.getRowsPerPage"
- "window\\.PlatformSettings"
exceptions:
- "init-alpine.js"
- "init-api-client.js"
- "settings.js"
- id: "JS-011"
name: "Use standard pagination object structure"
severity: "error"
description: |
All pages with tables MUST use the standard nested pagination object
structure. This ensures compatibility with the pagination macro and
consistent behavior across all pages.
REQUIRED structure:
pagination: {
page: 1,
per_page: 20,
total: 0,
pages: 0
}
WRONG (flat structure):
page: 1,
limit: 20,
total: 0,
skip: 0
WRONG (different property names):
pagination: {
currentPage: 1,
itemsPerPage: 20
}
Required computed properties:
- totalPages
- startIndex
- endIndex
- pageNumbers
Required methods:
- previousPage()
- nextPage()
- goToPage(pageNum)
Documentation: docs/frontend/shared/pagination.md
pattern:
file_pattern: "static/**/js/**/*.js"
required_in_pages_with_pagination:
- "pagination:"
- "pagination\\.page"
- "pagination\\.per_page"
anti_patterns_in_pagination_pages:
- "^\\s*page:\\s*\\d"
- "^\\s*limit:\\s*\\d"
- "^\\s*skip:\\s*"
exceptions:
- "init-alpine.js"
- id: "JS-012"
name: "Do not include /api/v1 prefix in API endpoints"
severity: "error"
description: |
When using apiClient.get(), apiClient.post(), etc., do NOT include
the /api/v1 prefix in the endpoint path. The apiClient automatically
prepends this prefix.
CORRECT:
apiClient.get('/admin/vendors')
apiClient.post('/admin/products')
const apiEndpoint = '/admin/vendors'
WRONG (causes double prefix /api/v1/api/v1/...):
apiClient.get('/api/v1/admin/vendors')
const apiEndpoint = '/api/v1/admin/vendors'
Exception: Direct fetch() calls without apiClient should use full path.
Documentation: docs/frontend/shared/api-client.md
pattern:
file_pattern: "static/**/js/**/*.js"
anti_patterns:
- "apiClient\\.(get|post|put|delete|patch)\\s*\\(\\s*['\"`]/api/v1"
- "apiEndpoint.*=.*['\"`]/api/v1"
exceptions:
- "init-api-client.js"
# ============================================================================
# TEMPLATE RULES (Jinja2)
# ============================================================================
template_rules:
- id: "TPL-001"
name: "Admin templates must extend admin/base.html"
severity: "error"
description: |
All admin templates must extend the base template for consistency.
Auto-excluded files:
- login.html - Standalone login page (no sidebar/navigation)
- errors/*.html - Error pages extend errors/base.html instead
- test-*.html - Test/development templates
Standalone template markers (place in first 5 lines):
- {# standalone #} - Mark template as intentionally standalone
- {# noqa: TPL-001 #} - Standard noqa style to suppress error
- <!-- standalone --> - HTML comment style
pattern:
file_pattern: "app/templates/admin/**/*.html"
required_patterns:
- "{% extends ['\"]admin/base\\.html['\"] %}"
auto_exclude_files:
- "login.html"
- "errors/"
- "test-"
standalone_markers:
- "{# standalone #}"
- "{# noqa: tpl-001 #}"
- "<!-- standalone -->"
exceptions:
- "base.html"
- "partials/"
- id: "TPL-002"
name: "Vendor templates must extend vendor/base.html"
severity: "error"
description: "All vendor templates must extend the base template"
pattern:
file_pattern: "app/templates/vendor/**/*.html"
required_patterns:
- "{% extends ['\"]vendor/base\\.html['\"] %}"
exceptions:
- "base.html"
- "partials/"
- id: "TPL-003"
name: "Shop templates must extend shop/base.html"
severity: "error"
description: "All shop templates must extend the base template"
pattern:
file_pattern: "app/templates/shop/**/*.html"
required_patterns:
- "{% extends ['\"]shop/base\\.html['\"] %}"
exceptions:
- "base.html"
- "partials/"
- id: "TPL-004"
name: "Use x-text for dynamic text content (prevents XSS)"
severity: "warning"
description: |
Use x-text directive for dynamic content to prevent XSS vulnerabilities
pattern:
file_pattern: "app/templates/**/*.html"
recommended_pattern: '<p x-text="item.name"></p>'
- id: "TPL-005"
name: "Use x-html ONLY for safe content"
severity: "error"
description: |
Use x-html only for trusted content like icons, never for user-generated content
pattern:
file_pattern: "app/templates/**/*.html"
safe_usage:
- 'x-html="\\$icon\\('
- id: "TPL-006"
name: "Implement loading state for data loads"
severity: "warning"
description: |
All templates that load data should show loading state
pattern:
file_pattern: "app/templates/**/*.html"
recommended_pattern: '<div x-show="loading">Loading...</div>'
- id: "TPL-007"
name: "Implement empty state when no data"
severity: "warning"
description: |
Show empty state when lists have no items
pattern:
file_pattern: "app/templates/**/*.html"
recommended_pattern: '<template x-if="items.length === 0">No items</template>'
- id: "TPL-008"
name: "Use valid block names from base templates"
severity: "error"
description: |
Templates must use block names that exist in their base template.
Using undefined blocks silently fails (content is not rendered).
Admin base blocks: title, extra_head, alpine_data, content, extra_scripts
Vendor base blocks: title, extra_head, alpine_data, content, extra_scripts
Shop base blocks: title, description, extra_head, alpine_data, content, extra_scripts
WRONG: {% block page_scripts %}...{% endblock %} (undefined)
RIGHT: {% block extra_scripts %}...{% endblock %}
pattern:
file_pattern: "app/templates/admin/**/*.html"
valid_blocks:
- "title"
- "extra_head"
- "alpine_data"
- "content"
- "extra_scripts"
forbidden_patterns:
- "{% block page_scripts %}"
- "{% block scripts %}"
- "{% block js %}"
- "{% block footer_scripts %}"
exceptions:
- "base.html"
# ============================================================================
# FRONTEND COMPONENT RULES
# ============================================================================
frontend_component_rules:
- id: "FE-001"
name: "Use pagination macro instead of inline HTML"
severity: "warning"
description: |
Use the shared pagination macro instead of duplicating pagination HTML.
Import from shared/macros/pagination.html.
RIGHT (use macro):
{% from 'shared/macros/pagination.html' import pagination %}
{{ pagination() }}
pattern:
file_pattern: "app/templates/**/*.html"
anti_patterns:
- 'aria-label="Table navigation"'
- "previousPage\\(\\).*nextPage\\(\\)"
exceptions:
- "shared/macros/pagination.html"
- "components.html"
- id: "FE-002"
name: "Use $icon() helper instead of inline SVGs"
severity: "warning"
description: |
Use the Alpine.js $icon() helper for consistent iconography.
Do not use inline <svg> elements.
RIGHT (icon helper):
<span x-html="$icon('arrow-left', 'w-4 h-4')"></span>
pattern:
file_pattern: "app/templates/**/*.html"
anti_patterns:
- "<svg.*viewBox.*>.*</svg>"
exceptions:
- "base.html"
- "components.html"
- "shared/macros/"
- id: "FE-003"
name: "Use table macros for consistent table styling"
severity: "info"
description: |
Use the shared table macros for consistent table styling.
Import from shared/macros/tables.html.
pattern:
file_pattern: "app/templates/**/*.html"
encouraged_patterns:
- "{% from 'shared/macros/tables.html' import"
- id: "FE-004"
name: "Use form macros for consistent form styling"
severity: "info"
description: |
Use the shared form macros for consistent input styling and validation.
Import from shared/macros/forms.html.
pattern:
file_pattern: "app/templates/**/*.html"
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 <input type="number">.
This ensures consistent styling, proper dark mode support, and hides
native browser spinners that render inconsistently.
RIGHT (use macro):
{% from 'shared/macros/inputs.html' import number_stepper %}
{{ number_stepper(model='quantity', min=1, max=99) }}
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"
- "components.html"
- 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.
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.
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.
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.
pattern:
file_pattern: "app/templates/shop/**/*.html"
encouraged_patterns:
- "{% from 'shared/macros/shop/mini-cart.html' import"
- id: "FE-013"
name: "Use product_gallery macro for image galleries"
severity: "info"
description: |
Use the shared product_gallery macros for product image displays.
Import from shared/macros/shop/product-gallery.html.
pattern:
file_pattern: "app/templates/shop/**/*.html"
encouraged_patterns:
- "{% from 'shared/macros/shop/product-gallery.html' import"
- id: "FE-014"
name: "Use variant_selector macros for product options"
severity: "info"
description: |
Use the shared variant_selector macros for product variant selection.
Import from shared/macros/shop/variant-selector.html.
pattern:
file_pattern: "app/templates/shop/**/*.html"
encouraged_patterns:
- "{% from 'shared/macros/shop/variant-selector.html' import"
- id: "FE-015"
name: "Use product_info macros for product details"
severity: "info"
description: |
Use the shared product_info macros for product detail sections.
Import from shared/macros/shop/product-info.html.
pattern:
file_pattern: "app/templates/shop/**/*.html"
encouraged_patterns:
- "{% from 'shared/macros/shop/product-info.html' import"
- id: "FE-016"
name: "Use product_tabs macro for product content tabs"
severity: "info"
description: |
Use the shared product_tabs macros for tabbed product information.
Import from shared/macros/shop/product-tabs.html.
pattern:
file_pattern: "app/templates/shop/**/*.html"
encouraged_patterns:
- "{% from 'shared/macros/shop/product-tabs.html' import"
- id: "FE-017"
name: "Use category_nav macros for category navigation"
severity: "info"
description: |
Use the shared category_nav macros for category navigation sidebars and menus.
Import from shared/macros/shop/category-nav.html.
pattern:
file_pattern: "app/templates/shop/**/*.html"
encouraged_patterns:
- "{% from 'shared/macros/shop/category-nav.html' import"
- id: "FE-018"
name: "Use breadcrumbs macros for breadcrumb navigation"
severity: "info"
description: |
Use the shared breadcrumbs macros for navigation trails.
Import from shared/macros/shop/breadcrumbs.html.
pattern:
file_pattern: "app/templates/shop/**/*.html"
encouraged_patterns:
- "{% from 'shared/macros/shop/breadcrumbs.html' import"
- id: "FE-019"
name: "Use search_bar macros for product search"
severity: "info"
description: |
Use the shared search_bar macros for product search functionality.
Import from shared/macros/shop/search-bar.html.
pattern:
file_pattern: "app/templates/shop/**/*.html"
encouraged_patterns:
- "{% from 'shared/macros/shop/search-bar.html' import"
- id: "FE-020"
name: "Use filter_sidebar macros for product filtering"
severity: "info"
description: |
Use the shared filter_sidebar macros for product filtering panels.
Import from shared/macros/shop/filter-sidebar.html.
pattern:
file_pattern: "app/templates/shop/**/*.html"
encouraged_patterns:
- "{% from 'shared/macros/shop/filter-sidebar.html' import"
- id: "FE-021"
name: "Use star_rating macros for rating displays"
severity: "info"
description: |
Use the shared star_rating macros for all rating displays and inputs.
Import from shared/macros/shop/star-rating.html.
pattern:
file_pattern: "app/templates/shop/**/*.html"
encouraged_patterns:
- "{% from 'shared/macros/shop/star-rating.html' import"
- id: "FE-022"
name: "Use review macros for review displays"
severity: "info"
description: |
Use the shared review macros for product reviews.
Import from shared/macros/shop/reviews.html.
pattern:
file_pattern: "app/templates/shop/**/*.html"
encouraged_patterns:
- "{% from 'shared/macros/shop/reviews.html' import"
- id: "FE-023"
name: "Use trust_badges macros for trust signals"
severity: "info"
description: |
Use the shared trust_badges macros for security and trust indicators.
Import from shared/macros/shop/trust-badges.html.
pattern:
file_pattern: "app/templates/shop/**/*.html"
encouraged_patterns:
- "{% from 'shared/macros/shop/trust-badges.html' import"
# ============================================================================
# FRONTEND STYLING RULES
# ============================================================================
styling_rules:
- id: "CSS-001"
name: "Use Tailwind utility classes"
severity: "warning"
description: |
Prefer Tailwind utility classes over custom CSS
pattern:
file_pattern: "app/templates/**/*.html"
encouraged: true
- id: "CSS-002"
name: "Support dark mode with dark: prefix"
severity: "warning"
description: |
All color classes should include dark mode variants
pattern:
file_pattern: "app/templates/**/*.html"
recommended_pattern: 'class="bg-white dark:bg-gray-800"'
- id: "CSS-003"
name: "Shop templates use vendor theme CSS variables"
severity: "error"
description: |
Shop templates must use CSS variables for vendor-specific theming
pattern:
file_pattern: "app/templates/shop/**/*.html"
required_pattern: 'var\\(--color-primary\\)'
- id: "CSS-004"
name: "Mobile-first responsive design"
severity: "warning"
description: |
Use mobile-first responsive classes
pattern:
file_pattern: "app/templates/**/*.html"
recommended_pattern: 'class="grid-cols-1 md:grid-cols-2 lg:grid-cols-4"'