loko/streetup/notifications/templates/notifications/inbox_mobile.html
2026-07-22 14:48:40 +02:00

241 lines
9.1 KiB
HTML

{% extends "mobile/mobile_base.html" %}
{% load i18n %}
{% block title %}{% translate "Notifications" %}{% endblock title %}
{% block extra_head %}
<style>
.notif-card {
border-left: 4px solid transparent;
}
.notif-card.unread {
border-left-color: var(--brand-color, #2f3e8b);
background-color: #f8faff;
}
.notif-meta {
font-size: 0.78rem;
}
.notif-type-pill {
font-size: 0.72rem;
}
</style>
{% endblock extra_head %}
{% block content %}
<div class="d-flex justify-content-between align-items-center mb-3">
<h1 class="h5 mb-0">
{% translate "Notifications" %}
{% if unread_count > 0 %}
<span class="badge rounded-pill ms-1" style="background-color: var(--brand-color, #2f3e8b);">{{ unread_count }}</span>
{% endif %}
</h1>
<a href="{% url 'mobile:mobile_index' %}" class="btn btn-outline-secondary btn-sm">
<i class="bi bi-house me-1"></i>{% translate "Accueil" %}
</a>
</div>
<div class="card border-0 shadow-sm mb-3">
<div class="card-body py-2">
<form method="get" class="row g-2 align-items-center">
<div class="col-12">
<select name="type" class="form-select form-select-sm" onchange="this.form.submit()">
<option value="">{% translate "Tous les types" %}</option>
{% for val, label in notification_types %}
<option value="{{ val }}" {% if filter_type == val %}selected{% endif %}>{{ label }}</option>
{% endfor %}
</select>
</div>
<div class="col-12 d-flex justify-content-between align-items-center">
<div class="form-check form-switch mb-0">
<input class="form-check-input" type="checkbox" id="showArchived"
name="archived" value="1"
{% if show_archived %}checked{% endif %}
onchange="this.form.submit()">
<label class="form-check-label" for="showArchived">{% translate "Archivées" %}</label>
</div>
{% if filter_type or show_archived %}
<a href="{% url 'mobile:notifications_mobile' %}" class="btn btn-link btn-sm text-muted p-0">
{% translate "Réinitialiser" %}
</a>
{% endif %}
</div>
</form>
</div>
</div>
{% if unread_count > 0 %}
<div class="mb-3">
<button type="button" class="btn btn-sm btn-outline-secondary w-100" id="mark-all-read-btn">
<i class="bi bi-check2-all me-1"></i>{% translate "Tout marquer comme lu" %}
</button>
</div>
{% endif %}
{% if notifications %}
<div class="d-flex flex-column gap-2">
{% for item in notifications %}
{% with notif=item.notification %}
<div class="card notif-card {% if not item.is_read and not item.is_archived %}unread{% endif %}"
id="notif-row-{{ item.recipient_id }}-{{ item.recipient_type }}">
<div class="card-body py-2">
<div class="d-flex justify-content-between gap-2">
<div class="flex-grow-1">
<div class="d-flex align-items-center gap-2 mb-1">
<i class="bi {{ notif.type_icon }} text-{{ notif.type_color }}"></i>
<strong class="small">{{ notif.title }}</strong>
{% if not item.is_read and not item.is_archived %}
<span class="badge bg-primary notif-type-pill">{% translate "Nouveau" %}</span>
{% endif %}
</div>
{% if notif.content %}
<p class="text-muted small mb-2">{{ notif.content|linebreaksbr }}</p>
{% endif %}
<div class="d-flex flex-wrap gap-2 align-items-center notif-meta text-muted">
<span>{{ notif.created_at|date:"d/m/Y H:i" }}</span>
{% if item.team_name %}
<span class="badge bg-secondary notif-type-pill">{{ item.team_name }}</span>
{% endif %}
{% if item.mobile_action_url %}
<a href="{{ item.mobile_action_url }}" class="btn btn-sm btn-outline-primary py-0 px-2">
{{ notif.action_label|default:item.mobile_action_url }}
</a>
{% endif %}
</div>
</div>
<div class="d-flex flex-column align-items-end gap-2">
{% if not item.is_read %}
<button type="button"
class="btn btn-sm btn-outline-success mark-read-btn"
data-pk="{{ item.recipient_id }}"
data-type="{{ item.recipient_type }}">
{% if notif.is_single_claim %}
{% translate "Prendre" %}
{% else %}
{% translate "Lu" %}
{% endif %}
</button>
{% endif %}
{% if item.recipient_type == 'personal' and not item.is_archived %}
<button type="button"
class="btn btn-sm btn-outline-secondary archive-btn"
data-pk="{{ item.recipient_id }}"
aria-label="{% translate 'Archiver' %}">
<i class="bi bi-archive"></i>
</button>
{% endif %}
</div>
</div>
</div>
</div>
{% endwith %}
{% endfor %}
</div>
{% else %}
<div class="text-center py-5 text-muted">
<i class="bi bi-bell-slash" style="font-size: 2.5rem;"></i>
<p class="mt-2 mb-0">
{% if show_archived %}
{% translate "Aucune notification archivée." %}
{% else %}
{% translate "Vous n'avez aucune notification." %}
{% endif %}
</p>
</div>
{% endif %}
{% endblock content %}
{% block extra_scripts %}
<script>
(() => {
const csrfToken = "{{ csrf_token }}";
const markReadBase = "{% url 'notifications:mark_read' 0 %}";
const archiveBase = "{% url 'notifications:archive' 0 %}";
const markAllUrl = "{% url 'notifications:mark_all_read' %}";
function updateBadge(count) {
const badges = document.querySelectorAll('#mobile-navbar-notif-badge, #navbar-notif-badge');
badges.forEach((badge) => {
if (!badge) return;
if (count > 0) {
badge.textContent = count;
badge.style.display = '';
} else {
badge.style.display = 'none';
}
});
}
async function postAction(url, body = null) {
const options = {
method: 'POST',
headers: {
'X-CSRFToken': csrfToken,
'X-Requested-With': 'XMLHttpRequest',
},
};
if (body) {
options.headers['Content-Type'] = 'application/x-www-form-urlencoded';
options.body = body;
}
const response = await fetch(url, options);
if (!response.ok) {
throw new Error('request_failed');
}
return response.json();
}
document.querySelectorAll('.mark-read-btn').forEach((button) => {
button.addEventListener('click', async () => {
button.disabled = true;
const pk = button.dataset.pk;
const type = button.dataset.type;
try {
const data = await postAction(markReadBase.replace('0', pk), `type=${encodeURIComponent(type)}`);
const row = document.getElementById(`notif-row-${pk}-${type}`);
if (row) {
row.classList.remove('unread');
}
button.remove();
updateBadge(data.unread_count);
} catch (error) {
button.disabled = false;
}
});
});
document.querySelectorAll('.archive-btn').forEach((button) => {
button.addEventListener('click', async () => {
button.disabled = true;
const pk = button.dataset.pk;
try {
const data = await postAction(archiveBase.replace('0', pk));
const row = document.getElementById(`notif-row-${pk}-personal`);
if (row) {
row.remove();
}
updateBadge(data.unread_count);
} catch (error) {
button.disabled = false;
}
});
});
const markAllButton = document.getElementById('mark-all-read-btn');
if (markAllButton) {
markAllButton.addEventListener('click', async () => {
markAllButton.disabled = true;
try {
const data = await postAction(markAllUrl);
updateBadge(data.unread_count || 0);
window.location.reload();
} catch (error) {
markAllButton.disabled = false;
}
});
}
})();
</script>
{% endblock extra_scripts %}