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>
This commit is contained in:
2025-12-22 22:39:34 +01:00
parent 1274135091
commit 6f8434f200
27 changed files with 1966 additions and 303 deletions

View File

@@ -2698,6 +2698,138 @@ class ArchitectureValidator:
# JS-004: Check Alpine components set currentPage
self._check_alpine_current_page(file_path, content, lines)
# JS-010: Check PlatformSettings usage for pagination
self._check_platform_settings_usage(file_path, content, lines)
# JS-011: Check standard pagination structure
self._check_pagination_structure(file_path, content, lines)
# JS-012: Check for double /api/v1 prefix in API calls
self._check_api_prefix_usage(file_path, content, lines)
def _check_platform_settings_usage(
self, file_path: Path, content: str, lines: list[str]
):
"""
JS-010: Check that pages with pagination use PlatformSettings.getRowsPerPage()
"""
# Skip excluded files
excluded_files = ["init-alpine.js", "init-api-client.js", "settings.js"]
if file_path.name in excluded_files:
return
# Check if this file has pagination (look for pagination object)
has_pagination = re.search(r"pagination:\s*\{", content)
if not has_pagination:
return
# Check if file uses PlatformSettings
uses_platform_settings = "PlatformSettings" in content
if not uses_platform_settings:
# Find the line where pagination is defined
for i, line in enumerate(lines, 1):
if "pagination:" in line and "{" in line:
self._add_violation(
rule_id="JS-010",
rule_name="Use PlatformSettings for pagination",
severity=Severity.ERROR,
file_path=file_path,
line_number=i,
message="Page with pagination must use PlatformSettings.getRowsPerPage()",
context=line.strip()[:80],
suggestion="Add: if (window.PlatformSettings) { this.pagination.per_page = await window.PlatformSettings.getRowsPerPage(); }",
)
break
def _check_pagination_structure(
self, file_path: Path, content: str, lines: list[str]
):
"""
JS-011: Check that pagination uses standard structure (pagination.per_page not page_size)
"""
# Skip excluded files
excluded_files = ["init-alpine.js"]
if file_path.name in excluded_files:
return
# Check if this file has pagination
has_pagination = re.search(r"pagination:\s*\{", content)
if not has_pagination:
return
# Check for wrong property names in pagination object definition
# Look for pagination object with wrong property names
wrong_patterns = [
(r"page_size\s*:", "page_size", "per_page"),
(r"pageSize\s*:", "pageSize", "per_page"),
(r"total_pages\s*:", "total_pages", "pages"),
(r"totalPages\s*:", "totalPages (in pagination object)", "pages"),
]
# Find the pagination object definition and check its properties
in_pagination_block = False
brace_count = 0
for i, line in enumerate(lines, 1):
# Detect start of pagination object
if "pagination:" in line and "{" in line:
in_pagination_block = True
brace_count = line.count("{") - line.count("}")
continue
if in_pagination_block:
brace_count += line.count("{") - line.count("}")
# Check for wrong property names only inside the pagination block
for pattern, wrong_name, correct_name in wrong_patterns:
if re.search(pattern, line):
self._add_violation(
rule_id="JS-011",
rule_name="Use standard pagination structure",
severity=Severity.ERROR,
file_path=file_path,
line_number=i,
message=f"Use '{correct_name}' instead of '{wrong_name}' in pagination",
context=line.strip()[:80],
suggestion=f"Rename '{wrong_name}' to '{correct_name}'",
)
# Exit pagination block when braces close
if brace_count <= 0:
in_pagination_block = False
def _check_api_prefix_usage(
self, file_path: Path, content: str, lines: list[str]
):
"""
JS-012: Check that apiClient calls don't include /api/v1 prefix
(apiClient adds this prefix automatically)
"""
# Skip excluded files
excluded_files = ["init-api-client.js", "api-client.js"]
if file_path.name in excluded_files:
return
for i, line in enumerate(lines, 1):
# Skip comments
stripped = line.strip()
if stripped.startswith("//") or stripped.startswith("*"):
continue
# Check for apiClient calls with /api/v1 prefix
if re.search(r"apiClient\.(get|post|put|delete|patch)\s*\(\s*['\"`]/api/v1", line):
self._add_violation(
rule_id="JS-012",
rule_name="Do not include /api/v1 prefix",
severity=Severity.ERROR,
file_path=file_path,
line_number=i,
message="apiClient already adds /api/v1 prefix - remove it from the endpoint",
context=line.strip()[:80],
suggestion="Change '/api/v1/admin/...' to '/admin/...'",
)
def _validate_templates(self, target_path: Path):
"""Validate template patterns"""
print("📄 Validating templates...")