Redesign backend layout: dark sidebar + slim topbar

Moves from a floating-card sidebar to a full-height dark sidebar
(bg-neutral) with the club logo at the top and logged-in user pinned
to the bottom. The topbar becomes a slim bar containing only the
mobile hamburger, notifications, and logout — no duplicate logo.

- base.html: DaisyUI drawer with h-screen/overflow-hidden so the
  sidebar is sticky and the content column scrolls independently.
  The #content div is now the white card (bg-base-100 rounded-xl)
  so HTMX innerHTML swaps stay inside it.
- member_filter.html: replaces the .action_bar component with a
  clean flex row (title + Add button) above a plain filter form.
  Table and mobile card list are unchanged.
- styles.css: adds .sidebar-nav scoped overrides for menu link
  hover/active colours on the dark sidebar; reduces h1.page-title
  bottom margin from mb-12 to mb-4.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-06-07 17:13:33 +02:00
parent e169d83311
commit fd1d3fcc95
3 changed files with 213 additions and 177 deletions

View File

@@ -12,13 +12,68 @@
{% include "backend/partials/messages.html" %}
{% endif %}
<h1 class="page-title">{% translate "Members" %}</h1>
<!-- ── Page header: title + primary action ──────────────────────────── -->
<div class="flex items-start justify-between gap-4 mb-5">
<h1 class="text-2xl font-bold tracking-tight">{% translate "Members" %}</h1>
<div class="lg:hidden collapse collapse-plus bg-base-100 border-neutral border">
<div class="flex items-center gap-2 shrink-0">
{% flag "TF_MASS_UPLOAD" %}
<a class="btn btn-ghost btn-sm hidden lg:flex"
href="{% url "backend:members:load" %}"
hx-get="{% url "backend:members:load" %}"
hx-target="#content">
<i class="fa-solid fa-file-upload"></i>
{% translate "Load from file" %}
</a>
{% endflag %}
<a class="btn btn-neutral btn-outline btn-sm"
href="{% url "backend:members:add" %}"
hx-get="{% url "backend:members:add" %}"
hx-target="#content">
<i class="fa-solid fa-plus"></i>
{% translate "Add member" %}
</a>
</div>
</div>
<!-- ── Desktop filter row ───────────────────────────────────────────── -->
<!-- Visible on lg+. Sits between the page header and the table. -->
<form class="hidden lg:flex flex-wrap items-end gap-2 mb-4"
hx-get="{% url "backend:members:list" %}"
hx-target="#content">
{% for field in filter.form %}
{% form_field field show_label=False size="extra-small" %}
{% endfor %}
<button type="submit" class="btn btn-outline btn-xs">
<i class="fa-solid fa-filter"></i>{% translate "Filter" %}
</button>
{% if filter.is_bound %}
<a class="btn btn-outline btn-error btn-xs"
href="{% url "backend:members:list" %}"
hx-get="{% url "backend:members:list" %}"
hx-target="#content">
<i class="fa-solid fa-times"></i>{% translate "Clear" %}
</a>
{% endif %}
</form>
<!-- ── Mobile filter (collapsible) ─────────────────────────────────── -->
<div class="lg:hidden collapse collapse-plus bg-base-100 border border-base-300 mb-4">
<input type="checkbox"/>
<div class="collapse-title text-sm font-semibold"><i class="fa-solid fa-filter mr-2"></i>{% translate "Filter" %}{% if filter.is_bound %}<span class="ml-2 badge badge-sm badge-neutral">active</span>{% endif %}</div>
<div class="collapse-title text-sm font-semibold">
<i class="fa-solid fa-filter mr-2"></i>
{% translate "Filter" %}
{% if filter.is_bound %}
<span class="ml-2 badge badge-sm badge-neutral">active</span>
{% endif %}
</div>
<div class="collapse-content">
<form class="flex flex-col gap-2" hx-get="{% url "backend:members:list" %}" hx-target="#content">
<form class="flex flex-col gap-2"
hx-get="{% url "backend:members:list" %}"
hx-target="#content">
{% for field in filter.form %}
{% form_field field show_label=False size="small" %}
{% endfor %}
@@ -27,9 +82,11 @@
<button type="submit" class="btn btn-sm btn-outline btn-neutral grow">
<i class="fa-solid fa-filter"></i>{% translate "Filter" %}
</button>
{% if filter.is_bound %}
<a class="btn btn-outline btn-error btn-sm grow" href="{% url "backend:members:list" %}" hx-get="{% url "backend:members:list" %}" hx-target="#content">
<a class="btn btn-outline btn-error btn-sm grow"
href="{% url "backend:members:list" %}"
hx-get="{% url "backend:members:list" %}"
hx-target="#content">
<i class="fa-solid fa-times"></i>{% translate "Clear" %}
</a>
{% endif %}
@@ -38,41 +95,8 @@
</div>
</div>
<div class="action_bar">
<div class="filter hidden lg:flex">
<form hx-get="{% url "backend:members:list" %}" hx-target="#content">
{% for field in filter.form %}
{% form_field field show_label=False size="extra-small" %}
{% endfor %}
<div class="flex flex-row w-full gap-2 lg:w-fit">
<button type="submit">
<i class="fa-solid fa-filter"></i>{% translate "Filter" %}
</button>
{% if filter.is_bound %}
<a class="btn btn-outline btn-error btn-xs grow" href="{% url "backend:members:list" %}" hx-get="{% url "backend:members:list" %}" hx-target="#content">
<i class="fa-solid fa-times"></i>{% translate "Clear" %}
</a>
{% endif %}
</div>
</form>
</div>
<div class="add">
{% flag "TF_MASS_UPLOAD" %}
<a class="btn btn-accent btn-sm grow hidden lg:flex" href="{% url "backend:members:load" %}" hx-get="{% url "backend:members:load" %}" hx-target="#content">
<i class="fa-solid fa-file-upload"></i>{% translate "Load members from file" %}
</a>
{% endflag %}
<a class="btn btn-neutral btn-outline btn-sm grow" href="{% url "backend:members:add" %}" hx-get="{% url "backend:members:add" %}" hx-target="#content">
<i class="fa-solid fa-plus"></i>{% translate "Add member" %}
</a>
</div>
</div>
<div class="hidden lg:flex mt-4">
<!-- ── Desktop table ────────────────────────────────────────────────── -->
<div class="hidden lg:block">
<table class="table table-zebra">
<thead>
<tr>
@@ -148,6 +172,7 @@
</table>
</div>
<!-- ── Mobile card list ─────────────────────────────────────────────── -->
<div class="lg:hidden">
{% if object_list|length == 0 %}
<div class="text-center w-full">{% translate "No members found" %}</div>
@@ -183,31 +208,26 @@
{% endif %}
</div>
<!-- ── Pagination ───────────────────────────────────────────────────── -->
{% if is_paginated %}
<div class="flex justify-center mt-4">
<div class="join">
{% if page_obj.has_previous %}
<a class="join-item btn" href="?{% url_replace request "page" 1 %}" hx-get="?{% url_replace request "page" 1 %}" hx-target="#content">&laquo;<span
class="hidden lg:inline"> {% translate "first" %}</span></a>
<a class="join-item btn"
href="?{% url_replace request "page" page_obj.previous_page_number %}" hx-get="?{% url_replace request "page" page_obj.previous_page_number %}" hx-target="#content"><span
class="hidden lg:inline">{% translate "previous" %}</span><span
class="lg:hidden">&lt;</span></a>
<a class="join-item btn" href="?{% url_replace request "page" 1 %}" hx-get="?{% url_replace request "page" 1 %}" hx-target="#content">&laquo;<span class="hidden lg:inline"> {% translate "first" %}</span></a>
<a class="join-item btn" href="?{% url_replace request "page" page_obj.previous_page_number %}" hx-get="?{% url_replace request "page" page_obj.previous_page_number %}" hx-target="#content"><span class="hidden lg:inline">{% translate "previous" %}</span><span class="lg:hidden">&lt;</span></a>
{% endif %}
<button class="join-item btn btn-disabled">
{% blocktranslate with page=page_obj.number num_pages=page_obj.paginator.num_pages %}page {{ page }}
of {{ num_pages }}{% endblocktranslate %}</button>
{% blocktranslate with page=page_obj.number num_pages=page_obj.paginator.num_pages %}page {{ page }} of {{ num_pages }}{% endblocktranslate %}
</button>
{% if page_obj.has_next %}
<a class="join-item btn" href="?{% url_replace request "page" page_obj.next_page_number %}" hx-get="?{% url_replace request "page" page_obj.next_page_number %}" hx-target="#content"><span
class="hidden lg:inline">{% translate "next" %}</span><span
class="lg:hidden">&gt;</span></a>
<a class="join-item btn" href="?{% url_replace request "page" page_obj.paginator.num_pages %}" hx-get="?{% url_replace request "page" page_obj.paginator.num_pages %}" hx-target="#content"><span
class="hidden lg:inline"> {% translate "last" %}</span> &raquo;</a>
<a class="join-item btn" href="?{% url_replace request "page" page_obj.next_page_number %}" hx-get="?{% url_replace request "page" page_obj.next_page_number %}" hx-target="#content"><span class="hidden lg:inline">{% translate "next" %}</span><span class="lg:hidden">&gt;</span></a>
<a class="join-item btn" href="?{% url_replace request "page" page_obj.paginator.num_pages %}" hx-get="?{% url_replace request "page" page_obj.paginator.num_pages %}" hx-target="#content"><span class="hidden lg:inline"> {% translate "last" %}</span> &raquo;</a>
{% endif %}
</div>
</div>
{% endif %}
{% endpartialdef content %}
{% endblock content %}
{% endblock content %}