{% extends "layouts/main.html" %} {% block title %}{{ page_title }}{% include "layouts/_app_title_suffix.html" %}{% endblock %} {% block page_title %}{{ page_title }}{% endblock %} {# Pivot view — interactive cross-tab table with Alpine pvPivotToolbar. Controls: - Row groupby : multi-select chips (add from dropdown → click chip to remove) - Column groupby: same - Measures : multi-select chips - Swap axes : flip row/col groupby with one button - Search : free-text filter - View-switcher: jump to list / kanban / graph / pivot for same model On every control change the component calls /api/pivot/data and rewrites `tableHtml` by turning the JSON response into a DOM table — avoids full-page reload while keeping the interactivity at zero extra dependencies. #} {% block content %}
{# ── Toolbar row ── #}
{% if view_switcher %} {% include "_view_switcher.html" %} {% endif %} {# Row groupby #}
Rows
{# Swap button #} {# Col groupby #}
Cols
{# Measures #}
Measures
{# Search #}
{# Spinner + subtitle #}
{# ── Pivot table area ── #}
{# Server-rendered initial table #}
{% for level in header_levels %} {% if loop.first %} {% endif %} {% for h in level %} {% endfor %} {% if loop.first %} {% endif %} {% endfor %} {% if header_levels or measure_count > 1 %} {% for m in measure_label_row %} {% endfor %} {% endif %} {% for row in body_rows %} {% for label in row.labels %} {% endfor %} {% for cell in row.cells %} {% endfor %} {% endfor %} {% if body_rows | length == 0 %} {% endif %} {% if body_rows | length > 0 %} {% for cell in col_totals %} {% endfor %} {% endif %}
{{ row_axis_titles | join(' / ') }} {{ h.label }} {{ grand_header.label }}
{{ m.label }}
{{ label }} {{ cell.display }}
No data to pivot. Adjust the controls above.
Total {{ cell.display }}
{# Client-rendered table after first fetch #}
{% endblock %} {% block scripts %} {% endblock %}