Ajoute un journal par utilisateur des notifications ntfy et de la présence Tasker
Chaque utilisateur a désormais son propre fichier logs.json dans son
dossier users/<id>/, alimenté automatiquement quand un rappel ntfy est
envoyé (ou échoue) et quand Tasker signale une arrivée ou un départ.
Une nouvelle page /logs, accessible depuis la nav, affiche ces entrées
avec filtres (Tout / Notifications / Présence), et le journal tourne à
500 entrées pour rester compact.
💘 Generated with Crush
Assisted-by: Crush:glm-5.2
This commit is contained in:
@ -1032,6 +1032,7 @@
|
||||
{% if current_user is defined and current_user %}
|
||||
<a href="/" class="nav-link">Semaine courante</a>
|
||||
<a href="/stats" class="nav-link">Statistiques</a>
|
||||
<a href="/logs" class="nav-link">Journaux</a>
|
||||
<a href="/settings" class="nav-link">Réglages</a>
|
||||
<span class="nav-user">{{ current_user }}</span>
|
||||
<a href="/logout" class="nav-link nav-logout">Déconnexion</a>
|
||||
|
||||
141
app/templates/logs.html
Normal file
141
app/templates/logs.html
Normal file
@ -0,0 +1,141 @@
|
||||
{% extends "base.html" %}
|
||||
{% block content %}
|
||||
<div class="stats-head">
|
||||
<div>
|
||||
<span class="stats-title">Journaux</span>
|
||||
<div class="stats-meta">
|
||||
Notifications ntfy et détections de présence (Tasker) — {{ current_user }}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="logs-tabs" role="tablist">
|
||||
{% set counts = {'all': logs|length, 'notif': logs | selectattr('type', 'equalto', 'notif') | list | length, 'presence': logs | selectattr('type', 'equalto', 'presence') | list | length} %}
|
||||
<a class="ltab{% if filter == 'all' %} active{% endif %}" href="/logs" role="tab">
|
||||
Tout <span class="ltab-count">{{ counts.all }}</span>
|
||||
</a>
|
||||
<a class="ltab{% if filter == 'notif' %} active{% endif %}" href="/logs?filter=notif" role="tab">
|
||||
Notifications <span class="ltab-count">{{ counts.notif }}</span>
|
||||
</a>
|
||||
<a class="ltab{% if filter == 'presence' %} active{% endif %}" href="/logs?filter=presence" role="tab">
|
||||
Présence <span class="ltab-count">{{ counts.presence }}</span>
|
||||
</a>
|
||||
</div>
|
||||
|
||||
{% set visible = logs if filter == 'all' else logs | selectattr('type', 'equalto', filter) | list %}
|
||||
|
||||
{% if visible %}
|
||||
<div class="logs-list">
|
||||
{% for entry in visible %}
|
||||
<div class="log-row log-{{ entry.type }}{% if entry.event == 'échec' %} log-failed{% endif %}">
|
||||
<div class="log-ts">{{ entry.ts }}</div>
|
||||
<div class="log-type">{{ 'Notification' if entry.type == 'notif' else 'Présence' }}</div>
|
||||
<div class="log-body">
|
||||
<span class="log-event">{{ entry.event }}</span>
|
||||
{% if entry.message %}<span class="log-msg">{{ entry.message }}</span>{% endif %}
|
||||
</div>
|
||||
</div>
|
||||
{% endfor %}
|
||||
</div>
|
||||
{% else %}
|
||||
<div class="logs-empty">Aucun événement enregistré pour le moment.</div>
|
||||
{% endif %}
|
||||
|
||||
<style>
|
||||
.logs-tabs {
|
||||
display: flex;
|
||||
gap: .4rem;
|
||||
border-bottom: 1.5px solid var(--rule);
|
||||
padding-bottom: .6rem;
|
||||
flex-wrap: wrap;
|
||||
}
|
||||
.ltab {
|
||||
font-family: var(--mono);
|
||||
font-size: 11px;
|
||||
letter-spacing: .04em;
|
||||
color: var(--muted);
|
||||
text-decoration: none;
|
||||
border: 1.5px solid var(--rule);
|
||||
padding: .35rem .7rem;
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
gap: .45rem;
|
||||
transition: border-color .15s, color .15s, background .15s;
|
||||
}
|
||||
.ltab:hover { border-color: var(--text); color: var(--text); }
|
||||
.ltab.active { border-color: var(--text); color: var(--text); background: var(--surface); }
|
||||
.ltab-count {
|
||||
font-size: 10px;
|
||||
background: var(--ground);
|
||||
border: 1px solid var(--rule);
|
||||
padding: .05rem .35rem;
|
||||
color: var(--muted);
|
||||
}
|
||||
|
||||
.logs-list {
|
||||
border: 1.5px solid var(--rule);
|
||||
background: var(--surface);
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
.log-row {
|
||||
display: grid;
|
||||
grid-template-columns: 180px 130px 1fr;
|
||||
gap: 1rem;
|
||||
padding: .55rem 1rem;
|
||||
border-bottom: 1px solid var(--rule-l);
|
||||
font-size: 13px;
|
||||
align-items: baseline;
|
||||
}
|
||||
.log-row:last-child { border-bottom: none; }
|
||||
.log-row:hover { background: var(--ground); }
|
||||
.log-ts {
|
||||
font-family: var(--mono);
|
||||
font-size: 11px;
|
||||
color: var(--muted);
|
||||
white-space: nowrap;
|
||||
}
|
||||
.log-type {
|
||||
font-family: var(--mono);
|
||||
font-size: 10px;
|
||||
letter-spacing: .06em;
|
||||
text-transform: uppercase;
|
||||
color: var(--muted);
|
||||
white-space: nowrap;
|
||||
}
|
||||
.log-notif .log-type { color: var(--accent); }
|
||||
.log-presence .log-type { color: var(--accent-2); }
|
||||
.log-body { display: flex; flex-direction: column; gap: .15rem; }
|
||||
.log-event {
|
||||
font-weight: 500;
|
||||
color: var(--text);
|
||||
}
|
||||
.log-msg {
|
||||
font-size: 12px;
|
||||
color: var(--muted);
|
||||
line-height: 1.45;
|
||||
}
|
||||
.log-failed {
|
||||
background: var(--neg-bg);
|
||||
}
|
||||
.log-failed .log-event { color: var(--neg); }
|
||||
.log-failed:hover { background: var(--neg-bg); }
|
||||
|
||||
.logs-empty {
|
||||
border: 1.5px solid var(--rule);
|
||||
background: var(--surface);
|
||||
padding: 2rem;
|
||||
text-align: center;
|
||||
color: var(--muted);
|
||||
font-size: 13px;
|
||||
}
|
||||
|
||||
@media (max-width: 640px) {
|
||||
.log-row {
|
||||
grid-template-columns: 1fr;
|
||||
gap: .2rem;
|
||||
}
|
||||
.log-ts { font-size: 10px; }
|
||||
}
|
||||
</style>
|
||||
{% endblock %}
|
||||
Reference in New Issue
Block a user