diff --git a/.architecture-rules/frontend.yaml b/.architecture-rules/frontend.yaml index cb08b41a..ab912b25 100644 --- a/.architecture-rules/frontend.yaml +++ b/.architecture-rules/frontend.yaml @@ -530,6 +530,76 @@ template_rules: exceptions: - "base.html" + - id: "TPL-013" + name: "Use new pagination macro API" + severity: "error" + description: | + The pagination macro was simplified to only accept show_condition. + It now relies on standardized Alpine.js component properties. + + OLD (deprecated - will break): + {{ pagination( + current_page='pagination.page', + total_pages='totalPages', + ... + ) }} + + NEW (correct): + {{ pagination(show_condition="!loading && pagination.total > 0") }} + + Required Alpine.js component properties: + - pagination.page, pagination.total + - totalPages, pageNumbers + - startIndex, endIndex + - previousPage(), nextPage(), goToPage() + pattern: + file_pattern: "app/templates/**/*.html" + anti_patterns: + - "pagination\\s*\\([^)]*current_page\\s*=" + - "pagination\\s*\\([^)]*total_pages\\s*=" + - "pagination\\s*\\([^)]*page_numbers\\s*=" + exceptions: + - "shared/macros/pagination.html" + + - id: "TPL-014" + name: "Use new modal_simple macro API with call block" + severity: "error" + description: | + The modal_simple macro now uses {% call %}...{% endcall %} syntax. + Content (including buttons) goes inside the call block. + + OLD (deprecated - will break): + {{ modal_simple( + show_var='showModal', + title='Title', + icon='exclamation', + confirm_text='OK', + confirm_fn='doSomething()' + ) }} + + + NEW (correct): + {% call modal_simple('modalId', 'Title', show_var='showModal') %} +
+

Modal content here

+
+ + +
+
+ {% endcall %} + + Parameters: modal_simple(id, title, show_var='isModalOpen', size='md') + pattern: + file_pattern: "app/templates/**/*.html" + anti_patterns: + - "\\{\\{\\s*modal_simple\\s*\\(" + - "modal_simple\\s*\\([^)]*icon\\s*=" + - "modal_simple\\s*\\([^)]*confirm_text\\s*=" + - "modal_simple\\s*\\([^)]*confirm_fn\\s*=" + exceptions: + - "shared/macros/modals.html" + # ============================================================================ # FRONTEND COMPONENT RULES # ============================================================================ diff --git a/scripts/validate_architecture.py b/scripts/validate_architecture.py index d1c78f43..92a34c4d 100755 --- a/scripts/validate_architecture.py +++ b/scripts/validate_architecture.py @@ -1217,6 +1217,95 @@ class ArchitectureValidator: suggestion="Change outer attribute to single quotes: @click='copyCode(`...`)'", ) + def _check_pagination_macro_api( + self, file_path: Path, content: str, lines: list[str] + ): + """ + TPL-013: Check for old pagination macro API. + + The pagination macro was simplified to only accept show_condition. + It now relies on standardized Alpine.js component properties. + + OLD (deprecated): {{ pagination(current_page=..., total_pages=...) }} + NEW (correct): {{ pagination(show_condition="!loading && pagination.total > 0") }} + """ + # Skip the macro definition file + if "shared/macros/pagination.html" in str(file_path): + return + + if "noqa: tpl-013" in content.lower(): + return + + # Old API patterns that indicate deprecated usage + old_patterns = [ + (r"pagination\s*\([^)]*current_page\s*=", "current_page"), + (r"pagination\s*\([^)]*total_pages\s*=", "total_pages"), + (r"pagination\s*\([^)]*page_numbers\s*=", "page_numbers"), + (r"pagination\s*\([^)]*start_index\s*=", "start_index"), + (r"pagination\s*\([^)]*end_index\s*=", "end_index"), + ] + + for i, line in enumerate(lines, 1): + for pattern, param_name in old_patterns: + if re.search(pattern, line): + self._add_violation( + rule_id="TPL-013", + rule_name="Use new pagination macro API", + severity=Severity.ERROR, + file_path=file_path, + line_number=i, + message=f"Old pagination API with '{param_name}' parameter", + context=line.strip()[:80], + suggestion="Use: {{ pagination(show_condition=\"!loading && pagination.total > 0\") }}", + ) + break # Only report once per line + + def _check_modal_simple_macro_api( + self, file_path: Path, content: str, lines: list[str] + ): + """ + TPL-014: Check for old modal_simple macro API. + + The modal_simple macro now uses {% call %}...{% endcall %} syntax. + Content (including buttons) goes inside the call block. + + OLD (deprecated): {{ modal_simple(show_var=..., icon=..., confirm_fn=...) }} + NEW (correct): {% call modal_simple('id', 'Title', show_var='...') %}...{% endcall %} + """ + # Skip the macro definition file + if "shared/macros/modals.html" in str(file_path): + return + + if "noqa: tpl-014" in content.lower(): + return + + # Old API patterns - using {{ }} instead of {% call %} + # Also checking for old parameters that don't exist anymore + old_patterns = [ + (r"\{\{\s*modal_simple\s*\(", "{{ modal_simple( instead of {% call modal_simple("), + (r"modal_simple\s*\([^)]*icon\s*=", "icon parameter"), + (r"modal_simple\s*\([^)]*icon_color\s*=", "icon_color parameter"), + (r"modal_simple\s*\([^)]*confirm_text\s*=", "confirm_text parameter"), + (r"modal_simple\s*\([^)]*confirm_fn\s*=", "confirm_fn parameter"), + (r"modal_simple\s*\([^)]*confirm_class\s*=", "confirm_class parameter"), + (r"modal_simple\s*\([^)]*loading_var\s*=", "loading_var parameter"), + ] + + for i, line in enumerate(lines, 1): + for pattern, issue in old_patterns: + if re.search(pattern, line): + self._add_violation( + rule_id="TPL-014", + rule_name="Use new modal_simple macro API with call block", + severity=Severity.ERROR, + file_path=file_path, + line_number=i, + message=f"Old modal_simple API: {issue}", + context=line.strip()[:80], + suggestion="Use: {{% call modal_simple('id', 'Title', show_var='...') %}}...{{% endcall %}}", + ) + break # Only report once per line + def _check_alpine_template_vars( self, file_path: Path, content: str, lines: list[str], js_content: str ): @@ -3191,6 +3280,12 @@ class ArchitectureValidator: # TPL-012: Check for escaped quotes in Alpine template literals self._check_escaped_quotes_in_alpine(file_path, content, lines) + # TPL-013: Check for old pagination macro API + self._check_pagination_macro_api(file_path, content, lines) + + # TPL-014: Check for old modal_simple macro API + self._check_modal_simple_macro_api(file_path, content, lines) + # Skip base/partials for TPL-001 check if is_base_or_partial: continue