fix: use table_header_custom for custom headers in subscription pages

The table_header() macro doesn't support caller() - it takes a columns list.
Using {% call table_header() %} caused a Jinja2 error:
  "macro 'table_header' was invoked with two values for the special caller argument"

Changes:
- Add table_header_custom() macro that supports caller() for custom headers
- Update subscriptions.html, subscription-tiers.html, billing-history.html
- Add TPL-008 architecture rule to detect this pattern
- Renumber TPL-009 (block names) and TPL-010 (Alpine vars)

🤖 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-25 22:25:33 +01:00
parent e21abd4c32
commit 6bd4b71588
6 changed files with 103 additions and 15 deletions

View File

@@ -813,7 +813,10 @@ class ArchitectureValidator:
# TPL-007: Check empty state implementation
self._check_template_empty_state(file_path, content, lines)
# TPL-008: Check for invalid block names
# TPL-008: Check for call table_header() pattern (should be table_header_custom)
self._check_table_header_call_pattern(file_path, content, lines)
# TPL-009: Check for invalid block names
if is_admin:
self._check_valid_block_names(file_path, content, lines)
@@ -1058,11 +1061,41 @@ class ArchitectureValidator:
suggestion='Add <template x-if="items.length === 0">No items found</template>',
)
def _check_table_header_call_pattern(
self, file_path: Path, content: str, lines: list[str]
):
"""TPL-008: Check that {% call table_header() %} is not used.
The table_header() macro doesn't support caller() - it takes a columns list.
Using {% call table_header() %} causes a Jinja2 error:
"macro 'table_header' was invoked with two values for the special caller argument"
Use table_header_custom() instead for custom headers with caller().
"""
if "noqa: tpl-008" in content.lower():
return
import re
pattern = re.compile(r"{%\s*call\s+table_header\s*\(\s*\)\s*%}")
for i, line in enumerate(lines, 1):
if pattern.search(line):
self._add_violation(
rule_id="TPL-008",
rule_name="Use table_header_custom for custom headers",
severity=Severity.ERROR,
file_path=file_path,
line_number=i,
message="{% call table_header() %} causes Jinja2 error - table_header doesn't support caller()",
context=line.strip()[:80],
suggestion="Use {% call table_header_custom() %} for custom headers with th_sortable or custom <th> elements",
)
def _check_valid_block_names(
self, file_path: Path, content: str, lines: list[str]
):
"""TPL-008: Check that templates use valid block names from base template"""
if "noqa: tpl-008" in content.lower():
"""TPL-009: Check that templates use valid block names from base template"""
if "noqa: tpl-009" in content.lower():
return
# Skip base templates
@@ -1092,7 +1125,7 @@ class ArchitectureValidator:
block_name = match.group(1)
if block_name in invalid_blocks:
self._add_violation(
rule_id="TPL-008",
rule_id="TPL-009",
rule_name="Use valid block names from base templates",
severity=Severity.ERROR,
file_path=file_path,
@@ -1105,8 +1138,8 @@ class ArchitectureValidator:
def _check_alpine_template_vars(
self, file_path: Path, content: str, lines: list[str], js_content: str
):
"""TPL-009: Check that Alpine variables used in templates are defined in JS"""
if "noqa: tpl-009" in content.lower():
"""TPL-010: Check that Alpine variables used in templates are defined in JS"""
if "noqa: tpl-010" in content.lower():
return
import re
@@ -1141,7 +1174,7 @@ class ArchitectureValidator:
for i, line in enumerate(lines, 1):
if var in line and ("error_state" in line or "action_dropdown" in line):
self._add_violation(
rule_id="TPL-009",
rule_id="TPL-010",
rule_name="Alpine variables must be defined in JS component",
severity=Severity.ERROR,
file_path=file_path,
@@ -2913,11 +2946,14 @@ class ArchitectureValidator:
if not is_base_or_partial and not is_macro and not is_components_page:
self._check_number_stepper_macro_usage(file_path, content, lines)
# TPL-008: Check for invalid block names
# TPL-008: Check for call table_header() pattern
self._check_table_header_call_pattern(file_path, content, lines)
# TPL-009: Check for invalid block names
if not is_base_or_partial:
self._check_valid_block_names(file_path, content, lines)
# TPL-009: Check Alpine variables are defined in JS
# TPL-010: Check Alpine variables are defined in JS
if not is_base_or_partial and not is_macro and not is_components_page:
# Try to find corresponding JS file
# Template: app/templates/admin/messages.html -> JS: static/admin/js/messages.js