{# Partial HTMX — contenu swapé dans #list-content lors des navigations liste #}
{# Variables requises : identiques à admin/list #}
{% set_global can_create = false %}
{% if current_user %}
{% if current_user.is_superuser %}
{% set_global can_create = true %}
{% else %}
{% for groupe in current_user.groupes %}
{% for perm in groupe.permissions %}
{% if perm.resource_key == resource.key and perm.can_create %}
{% set_global can_create = true %}
{% endif %}
{% endfor %}
{% endfor %}
{% endif %}
{% endif %}
{% set has_filters = resource.display.list_filter | length > 0 %}
<div id="list-content">
{# ── Barre de recherche ── #}
{% block list_search %}
<div class="admin-search__row">
<form method="GET" action="" class="admin-search__form"
hx-get=""
hx-target="#list-content"
hx-swap="innerHTML"
hx-push-url="true">
<div class="admin-search__wrapper">
<input
type="text"
name="search"
value="{{ search }}"
placeholder="{% if admin_list_search_placeholder %}{{ admin_list_search_placeholder }}{% else %}Search...{% endif %}"
class="admin-search__input"
hx-get=""
hx-trigger="input changed delay:400ms"
hx-target="#list-content"
hx-swap="innerHTML"
hx-push-url="true"
hx-include="closest form"
>
{% if sort_by %}<input type="hidden" name="sort_by" value="{{ sort_by }}">{% endif %}
{% if sort_dir %}<input type="hidden" name="sort_dir" value="{{ sort_dir }}">{% endif %}
{% for col, val in active_filters %}<input type="hidden" name="filter_{{ col }}" value="{{ val }}">{% endfor %}
<button type="submit" class="btn btn-secondary admin-search__btn">
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" width="14" height="14">
<circle cx="11" cy="11" r="8"/><line x1="21" y1="21" x2="16.65" y2="16.65"/>
</svg>
</button>
{% if search %}
<a href="?{% if sort_by %}sort_by={{ sort_by }}&sort_dir={{ sort_dir }}{% endif %}{{ filter_qs }}"
hx-get="?{% if sort_by %}sort_by={{ sort_by }}&sort_dir={{ sort_dir }}{% endif %}{{ filter_qs }}"
hx-target="#list-content" hx-swap="innerHTML" hx-push-url="true"
class="btn btn-secondary">✕</a>
{% endif %}
</div>
</form>
{% if has_filters %}
<button class="btn btn-ghost btn-icon admin-search__filter-toggle" id="mobileFilterToggle" type="button" aria-label="Filtres">
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" width="18" height="18">
<line x1="3" y1="6" x2="21" y2="6"/><line x1="3" y1="12" x2="15" y2="12"/><line x1="3" y1="18" x2="9" y2="18"/>
</svg>
{% if active_filters %}<span class="admin-search__filter-badge">{{ active_filters | length }}</span>{% endif %}
</button>
{% endif %}
</div>
{% endblock list_search %}
{# ── Barre d'actions bulk ── #}
{% set_global can_bulk = false %}
{% if current_user %}
{% if current_user.is_superuser %}
{% set_global can_bulk = true %}
{% else %}
{% for groupe in current_user.groupes %}
{% for perm in groupe.permissions %}
{% if perm.resource_key == resource.key and perm.can_delete %}
{% set_global can_bulk = true %}
{% endif %}
{% endfor %}
{% endfor %}
{% endif %}
{% endif %}
{% block list_group_action %}
{% if can_bulk %}
<form id="bulk-form" method="POST" action="{{ resource_base }}/bulk">
{% csrf %}
<input type="hidden" name="bulk_action" id="bulk-action-input" value="">
<input type="hidden" name="ids" id="bulk-ids-input" value="">
<div id="bulk-action-bar" class="admin-group-action__bar">
<span class="admin-group-action__info"><span id="bulk-count">0</span> sélectionné(s)</span>
<div class="admin-group-action__btns">
{% if group_actions | length > 0 %}
<div class="admin-group-action__selects">
{% for ga in group_actions %}
<select name="ga_{{ ga.field }}" class="admin-group-action__select">
<option value="">{{ ga.label }} — sans changement</option>
{% for choice in ga.choices %}
<option value="{{ choice[0] }}">{{ ga.label }} — {{ choice[1] }}</option>
{% endfor %}
</select>
{% endfor %}
<button type="button" class="btn btn-sm btn-primary" data-bulk-action="group_set">Modifier</button>
</div>
{% endif %}
{% if can_update %}
<button type="button" class="btn btn-sm btn-secondary" data-bulk-action="bulk-edit">Modifier en groupe</button>
{% endif %}
<button type="button" class="btn btn-sm btn-danger" data-bulk-action="delete">Supprimer</button>
</div>
</div>
</form>
{% endif %}
{% endblock list_group_action %}
<div class="{% if has_filters %}admin-list__layout{% endif %}">
{# ── Colonne principale : table + pagination ── #}
<div>
{% block list_table %}
<div class="admin-card">
{% if entries is defined and entries | length > 0 %}
<div class="admin-table__wrapper">
<table class="admin-table">
<colgroup>
{% if can_bulk %}<col class="admin-table__col-bulk">{% endif %}
<col class="admin-table__col-id">
{% for col in visible_columns %}
<col{% if loop.index0 > 0 %} class="admin-table__col-secondary"{% endif %}>
{% endfor %}
<col class="admin-table__col-expand">
<col class="admin-table__col-actions">
</colgroup>
<thead>
<tr>
{% if can_bulk %}<th class="admin-table__th-bulk"><input type="checkbox" id="bulk-check-all" title="Tout sélectionner"></th>{% endif %}
<th>
<a href="?sort_by=id&sort_dir={% if sort_by == "id" %}{{ sort_dir_toggle }}{% else %}asc{% endif %}{% if search %}&search={{ search | urlencode }}{% endif %}{{ filter_qs }}&page=1"
hx-get="?sort_by=id&sort_dir={% if sort_by == "id" %}{{ sort_dir_toggle }}{% else %}asc{% endif %}{% if search %}&search={{ search | urlencode }}{% endif %}{{ filter_qs }}&page=1"
hx-target="#list-content" hx-swap="innerHTML" hx-push-url="true"
class="admin-table__sort-link">
{% if admin_list_th_id %}{{ admin_list_th_id }}{% endif %}
{% if sort_by == "id" %}<span class="admin-table__sort-indicator">{% if sort_dir == "asc" %}▲{% else %}▼{% endif %}</span>{% endif %}
</a>
</th>
{% for col in visible_columns %}
<th {% if loop.index0 > 0 %} class="admin-table__col-secondary"{% endif %}>
<a href="?sort_by={{ col }}&sort_dir={% if sort_by == col %}{{ sort_dir_toggle }}{% else %}asc{% endif %}{% if search %}&search={{ search | urlencode }}{% endif %}{{ filter_qs }}&page=1"
hx-get="?sort_by={{ col }}&sort_dir={% if sort_by == col %}{{ sort_dir_toggle }}{% else %}asc{% endif %}{% if search %}&search={{ search | urlencode }}{% endif %}{{ filter_qs }}&page=1"
hx-target="#list-content" hx-swap="innerHTML" hx-push-url="true"
class="admin-table__sort-link">
{% if column_labels[col] %}{{ column_labels[col] }}{% else %}{{ col | replace(from="_", to=" ") }}{% endif %}
{% if sort_by == col %}<span class="admin-table__sort-indicator">{% if sort_dir == "asc" %}▲{% else %}▼{% endif %}</span>{% endif %}
</a>
</th>
{% endfor %}
<th class="admin-table__th-expand"></th>
<th class="admin-table__th-right">{% if admin_list_th_actions %}{{ admin_list_th_actions }}{% endif %}</th>
</tr>
</thead>
<tbody>
{% for entry in entries %}
<tr>
{% if can_bulk %}<td class="admin-table__td-bulk"><input type="checkbox" class="admin-table__bulk-check" value="{{ entry.id }}"></td>{% endif %}
<td><span class="admin-badge--id">#{{ entry.id }}</span></td>
{% for col in visible_columns %}
{% set value = entry[col] %}
<td class="admin-table__td-data{% if loop.index0 > 0 %} admin-table__col-secondary{% endif %}" data-label="{% if column_labels[col] %}{{ column_labels[col] }}{% else %}{{ col | replace(from="_", to=" ") }}{% endif %}">
{% if value == true %}
<span class="admin-badge admin-badge--green">{% if admin_list_bool_true %}{{ admin_list_bool_true }}{% endif %}</span>
{% elif value == false %}
<span class="admin-badge admin-badge--neutral">{% if admin_list_bool_false %}{{ admin_list_bool_false }}{% endif %}</span>
{% elif value %}
<span class="admin-table__td-content">{% if rich_fields and col in rich_fields %}{{ value | plaintext }}{% else %}{{ value }}{% endif %}</span>
{% else %}
<span class="admin-text--muted">—</span>
{% endif %}
</td>
{% endfor %}
<td class="admin-table__td-expand">
<button class="admin-table__expand-btn" type="button" aria-expanded="false" aria-label="Détails">
<svg class="admin-table__expand-icon" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" width="14" height="14">
<polyline points="9 18 15 12 9 6"/>
</svg>
</button>
</td>
<td class="admin-table__td-actions">
{% set resource_key = resource.key %}
{% set entry_id = entry.id %}
{% include "admin/kebab.html" %}
</td>
</tr>
<tr class="admin-table__row-detail">
<td colspan="999">
<div class="admin-table__detail-grid">
{% for col in visible_columns %}
{% if loop.index0 > 0 %}
{% set value = entry[col] %}
<div class="admin-table__detail-item">
<span class="admin-table__detail-label">{% if column_labels[col] %}{{ column_labels[col] }}{% else %}{{ col | replace(from="_", to=" ") }}{% endif %}</span>
<span class="admin-table__detail-value">
{% if value == true %}<span class="admin-badge admin-badge--green">{% if admin_list_bool_true %}{{ admin_list_bool_true }}{% endif %}</span>
{% elif value == false %}<span class="admin-badge admin-badge--neutral">{% if admin_list_bool_false %}{{ admin_list_bool_false }}{% endif %}</span>
{% elif value %}{% if rich_fields and col in rich_fields %}{{ value | plaintext }}{% else %}{{ value }}{% endif %}
{% else %}<span class="admin-text--muted">—</span>{% endif %}
</span>
</div>
{% endif %}
{% endfor %}
</div>
</td>
</tr>
{% endfor %}
</tbody>
</table>
</div>
{% else %}
<div class="admin-empty-state">
<svg class="admin-empty-state__icon" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.5">
<path d="M9 5H7a2 2 0 0 0-2 2v12a2 2 0 0 0 2 2h10a2 2 0 0 0 2-2V7a2 2 0 0 0-2-2h-2"/>
<rect x="9" y="3" width="6" height="4" rx="1"/>
</svg>
<p class="admin-empty-state__title">{% if admin_list_empty_title %}{{ admin_list_empty_title }}{% endif %}</p>
<p class="admin-empty-state__desc">{% if admin_list_empty_desc %}{{ admin_list_empty_desc }}{% endif %}</p>
{% if can_create %}<a href="{{ resource_base }}/create" class="btn btn-primary mt-sm">{% if admin_list_btn_create_first %}{{ admin_list_btn_create_first }}{% endif %}</a>{% endif %}
</div>
{% endif %}
</div>
{% endblock list_table %}
{% block list_pagination %}
{% if page_count is defined and page_count > 1 %}
<div class="admin-pagination">
{% if has_prev %}
<a href="?page={{ prev_page }}{% if sort_by %}&sort_by={{ sort_by }}&sort_dir={{ sort_dir }}{% endif %}{% if search %}&search={{ search | urlencode }}{% endif %}{{ filter_qs }}"
hx-get="?page={{ prev_page }}{% if sort_by %}&sort_by={{ sort_by }}&sort_dir={{ sort_dir }}{% endif %}{% if search %}&search={{ search | urlencode }}{% endif %}{{ filter_qs }}"
hx-target="#list-content" hx-swap="innerHTML" hx-push-url="true"
class="btn btn-sm btn-secondary">«</a>
{% else %}
<span class="btn btn-sm btn-secondary disabled">«</span>
{% endif %}
<span class="admin-pagination__info">{{ page }} / {{ page_count }}</span>
{% if has_next %}
<a href="?page={{ next_page }}{% if sort_by %}&sort_by={{ sort_by }}&sort_dir={{ sort_dir }}{% endif %}{% if search %}&search={{ search | urlencode }}{% endif %}{{ filter_qs }}"
hx-get="?page={{ next_page }}{% if sort_by %}&sort_by={{ sort_by }}&sort_dir={{ sort_dir }}{% endif %}{% if search %}&search={{ search | urlencode }}{% endif %}{{ filter_qs }}"
hx-target="#list-content" hx-swap="innerHTML" hx-push-url="true"
class="btn btn-sm btn-secondary">»</a>
{% else %}
<span class="btn btn-sm btn-secondary disabled">»</span>
{% endif %}
</div>
{% endif %}
{% endblock list_pagination %}
</div>
{# ── Sidebar filtres ── #}
{% block list_filters %}
{% if has_filters %}
<div class="admin-filter__overlay" id="filterOverlay"></div>
<aside class="admin-filter__sidebar" id="filterSidebar" data-resource="{{ resource.key }}">
<div class="admin-filter__header">
<span class="admin-filter__title">Filtres</span>
<button class="admin-filter__toggle" id="filterToggle" title="Masquer les filtres">
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" width="16" height="16">
<polyline points="9 18 15 12 9 6"/>
</svg>
</button>
</div>
<div class="admin-filter__body">
{% for filter_entry in resource.display.list_filter %}
{% set col = filter_entry[0] %}
{% set label = filter_entry[1] %}
{% set values = filter_values[col] %}
{% set meta = filter_meta[col] %}
{% if values and values | length > 0 %}
<div class="admin-filter__group" data-filter-col="{{ col }}">
<button class="admin-filter__group-title" type="button" aria-expanded="true">
{{ label }}
<svg class="admin-filter__chevron" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" width="12" height="12">
<polyline points="6 9 12 15 18 9"/>
</svg>
</button>
<div class="admin-filter__group-body">
{% if active_filters[col] %}
<a href="?{% for k, v in active_filters %}{% if k != col and v %}filter_{{ k }}={{ v | urlencode }}&{% endif %}{% endfor %}{% if sort_by %}sort_by={{ sort_by }}&sort_dir={{ sort_dir }}&{% endif %}{% if search %}search={{ search | urlencode }}&{% endif %}page=1"
hx-get="?{% for k, v in active_filters %}{% if k != col and v %}filter_{{ k }}={{ v | urlencode }}&{% endif %}{% endfor %}{% if sort_by %}sort_by={{ sort_by }}&sort_dir={{ sort_dir }}&{% endif %}{% if search %}search={{ search | urlencode }}&{% endif %}page=1"
hx-target="#list-content" hx-swap="innerHTML" hx-push-url="true"
class="admin-filter__option admin-filter__option--clear">✕ Tout</a>
{% endif %}
{% for val in values %}
{% set is_active = active_filters[col] == val %}
<a href="?filter_{{ col }}={{ val | urlencode }}{% for k, v in active_filters %}{% if k != col and v %}&filter_{{ k }}={{ v | urlencode }}{% endif %}{% endfor %}{% if sort_by %}&sort_by={{ sort_by }}&sort_dir={{ sort_dir }}{% endif %}{% if search %}&search={{ search | urlencode }}{% endif %}&page=1"
hx-get="?filter_{{ col }}={{ val | urlencode }}{% for k, v in active_filters %}{% if k != col and v %}&filter_{{ k }}={{ v | urlencode }}{% endif %}{% endfor %}{% if sort_by %}&sort_by={{ sort_by }}&sort_dir={{ sort_dir }}{% endif %}{% if search %}&search={{ search | urlencode }}{% endif %}&page=1"
hx-target="#list-content" hx-swap="innerHTML" hx-push-url="true"
class="admin-filter__option {% if is_active %}admin-filter__option--active{% endif %}">
{{ val | replace(from="_", to=" ") }}
</a>
{% endfor %}
{% if meta.total_pages > 1 %}
<div class="admin-filter__pagination">
{% if meta.has_prev %}
<a href="?{{ meta.prev_qs }}&page=1"
hx-get="?{{ meta.prev_qs }}&page=1"
hx-target="#list-content" hx-swap="innerHTML" hx-push-url="true"
class="admin-filter__page-btn">‹</a>
{% else %}
<span class="admin-filter__page-btn disabled">‹</span>
{% endif %}
<span class="admin-filter__page-info">{{ meta.current_page + 1 }} / {{ meta.total_pages }}</span>
{% if meta.has_next %}
<a href="?{{ meta.next_qs }}&page=1"
hx-get="?{{ meta.next_qs }}&page=1"
hx-target="#list-content" hx-swap="innerHTML" hx-push-url="true"
class="admin-filter__page-btn">›</a>
{% else %}
<span class="admin-filter__page-btn disabled">›</span>
{% endif %}
</div>
{% endif %}
</div>
</div>
{% endif %}
{% endfor %}
</div>
</aside>
{% endif %}
{% endblock list_filters %}
</div>{# fin admin-list__layout #}
</div>