Implement avatar rendering logic: add custom tags, template, and styles; update base.html integration and revamp member filter and list designs.

This commit is contained in:
2026-01-10 22:59:11 +01:00
parent 03f8a5eb35
commit 26155de246
11 changed files with 541 additions and 156 deletions

View File

@@ -1 +1,194 @@
{% extends "backend/base.html" %}
{% extends "backend/base.html" %}
{% load i18n %}
{% load form_field %}
{% load avatar %}
{% load pagination %}
{% block content %}
<h1 class="page-title">Members</h1>
<div class="lg:hidden collapse collapse-plus bg-base-100 border-neutral border">
<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-content">
<form class="flex flex-col gap-2">
{% for field in filter.form %}
{% form_field field show_label=False size="small" %}
{% endfor %}
<div class="flex flex-row w-full gap-2">
<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" %}">
<i class="fa-solid fa-times"></i>{% translate "Clear" %}
</a>
{% endif %}
</div>
</form>
</div>
</div>
<div class="action_bar">
<div class="filter hidden lg:flex">
<form>
{% 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" %}">
<i class="fa-solid fa-times"></i>{% translate "Clear" %}
</a>
{% endif %}
</div>
</form>
</div>
<div class="add">
<a class="btn btn-accent btn-sm grow hidden lg:flex" href="">
<i class="fa-solid fa-file-upload"></i>{% translate "Load members from file" %}
</a>
<a class="btn btn-neutral btn-outline btn-sm grow" href="">
<i class="fa-solid fa-plus"></i>{% translate "Add member" %}
</a>
</div>
</div>
<div class="hidden lg:flex mt-4">
<table class="table table-zebra">
<thead>
<tr>
<th>{% translate "Member" %}</th>
<th>{% translate "Birthday" %}</th>
<th>{% translate "License" %}</th>
<th>{% translate "Phone number(s)" %}</th>
<th></th>
</tr>
</thead>
<tbody>
{% if object_list|length == 0 %}
<tr>
<td colspan="5">{% translate "No members found" %}</td>
</tr>
{% else %}
{% for member in object_list %}
<tr class="hover:bg-base-300">
<td>
<a href="">
<div class="flex flex-row items-center gap-3">
<div>
{% avatar first_name=member.user.first_name last_name=member.user.last_name %}
</div>
<div class="flex flex-col grow">
<div class="font-bold">{{ member.user.get_full_name }}</div>
<div class="text-sm opacity-50">{{ member.user.email }}</div>
</div>
{% if member.user.is_superuser %}
<div class="badge badge-sm badge-accent"><i class="fa-solid fa-user-shield"></i>{% translate "Admin" %}</div>
{% endif %}
{% if not member.user.is_active %}
<div class="badge badge-neutral badge-sm">{% translate "Inactive"%}</div>
{% endif %}
</div>
</a>
</td>
<td>{{ member.birthday|date:"d M Y"|default:"-" }}</td>
<td>{{ member.license|default:"-" }}</td>
<td>
<div class="flex flex-col gap-1">
{% if member.phone_number %}
<a href="{{ member.phone_number.as_rfc3966 }}" class="btn btn-info btn-xs">
<i class="fa-solid fa-phone"></i>{{ member.phone_number }}
</a>
{% endif %}
{% if member.emergency_phone_number %}
<a href="{{ member.emergency_phone_number.as_rfc3966 }}" class="btn btn-error btn-xs">
<i class="fa-solid fa-star-of-life"></i>{{ member.emergency_phone_number }}
</a>
{% endif %}
</div>
</td>
<td>
<div class="flex flex-row gap-2">
<a class="btn btn-outline btn-sm" href="">
<i class="fa-solid fa-eye"></i>{% translate "Details" %}
</a>
<a class="btn btn-outline btn-error btn-sm" href="">
<i class="fa-solid fa-trash"></i>{% translate "Delete" %}
</a>
</div>
</td>
</tr>
{% endfor %}
{% endif %}
</tbody>
</table>
</div>
<div class="lg:hidden">
{% if object_list|length == 0 %}
<div class="text-center w-full">{% translate "No members found" %}</div>
{% else %}
<div class="flex flex-col gap-1">
{% for member in object_list %}
<a class="border border-base-300 rounded-lg p-2 flex flex-row gap-2 items-center" href="">
<div>
{% avatar first_name=member.user.first_name last_name=member.user.last_name width="sm" %}
</div>
<div class="grow">
<div class="font-semibold text-sm">{{ member.user.get_full_name }} {% if member.license %}#{{ member.license }}{% endif %}</div>
<div class="opacity-50 text-xs">{{ member.birthday|date:"d M Y"|default:"" }}</div>
</div>
<div>
<i class="fa-solid fa-chevron-right"></i>
</div>
</a>
{% endfor %}
</div>
{% endif %}
</div>
{% 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 %}">&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 %}"><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>
{% if page_obj.has_next %}
<a class="join-item btn" href="?{% url_replace request "page" page_obj.next_page_number %}"><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 %}"><span
class="hidden lg:inline"> {% translate "last" %}</span> &raquo;</a>
{% endif %}
</div>
</div>
{% endif %}
{% endblock content %}