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