Les pages /aide, /settings et /logs référeçaient encore Tasker (payant) pour
la détection de présence. Mise à jour de la procédure (Wi-Fi ou géofence +
action Trigger URL POST), des libellés et messages, et du test qui vérifiait
la présence du mot « Tasker ».
💘 Generated with Crush
Assisted-by: Crush:glm-5.2
270 lines
9.6 KiB
HTML
270 lines
9.6 KiB
HTML
{% extends "base.html" %}
|
|
{% block content %}
|
|
<div class="stats-head">
|
|
<div>
|
|
<span class="stats-title">Réglages</span>
|
|
<div class="stats-meta">Rappels de pointage sur ton téléphone</div>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="settings-card">
|
|
<span class="hist-label">Plages horaires</span>
|
|
<p class="settings-p">
|
|
Bornes de présence obligatoire matin/après-midi et heure de reprise par défaut de
|
|
l'après-midi, utilisées pour les avertissements de saisie et les préconisations de
|
|
fin de demi-journée. Les heures visées (arrivée le matin, départ le soir) guident
|
|
les préconisations : le matin est planifié depuis ton arrivée visée, et le rattrapage
|
|
est casé avant ton départ visé tant que c'est possible. Propres à ton compte —
|
|
ne change rien pour les autres.
|
|
</p>
|
|
|
|
{% if plages_saved %}
|
|
<div class="settings-saved">Enregistré.</div>
|
|
{% endif %}
|
|
{% if plages_error %}
|
|
<div class="settings-error">Horaires invalides (format HH:MM, matin_debut < matin_fin < aprem_debut < aprem_fin).</div>
|
|
{% endif %}
|
|
|
|
<form method="post" action="/settings/plages" class="settings-form">
|
|
<label class="login-label" for="matin_debut">Présence matin</label>
|
|
<div class="settings-range">
|
|
<input id="matin_debut" class="login-input time-range-input" type="time" name="matin_debut" value="{{ plages.matin_debut }}">
|
|
<span class="settings-range-sep">→</span>
|
|
<input id="matin_fin" class="login-input time-range-input" type="time" name="matin_fin" value="{{ plages.matin_fin }}">
|
|
</div>
|
|
<label class="login-label" for="aprem_debut">Présence après-midi</label>
|
|
<div class="settings-range">
|
|
<input id="aprem_debut" class="login-input time-range-input" type="time" name="aprem_debut" value="{{ plages.aprem_debut }}">
|
|
<span class="settings-range-sep">→</span>
|
|
<input id="aprem_fin" class="login-input time-range-input" type="time" name="aprem_fin" value="{{ plages.aprem_fin }}">
|
|
</div>
|
|
<label class="login-label" for="pause_dejeuner_fin">Reprise par défaut de l'après-midi</label>
|
|
<input id="pause_dejeuner_fin" class="login-input time-range-input" type="time" name="pause_dejeuner_fin" value="{{ plages.pause_dejeuner_fin }}">
|
|
<label class="login-label" for="arrivee_visee">Heures visées arrivée / départ (optionnel)</label>
|
|
<div class="settings-range">
|
|
<input id="arrivee_visee" class="login-input time-range-input" type="time" name="arrivee_visee" value="{{ plages.arrivee_visee }}">
|
|
<span class="settings-range-sep">→</span>
|
|
<input id="depart_vise" class="login-input time-range-input" type="time" name="depart_vise" value="{{ plages.depart_vise }}" aria-label="Départ visé le soir">
|
|
</div>
|
|
<button class="login-btn" type="submit">Enregistrer</button>
|
|
</form>
|
|
</div>
|
|
|
|
<div class="settings-card">
|
|
<span class="hist-label">Notifications (ntfy) <a href="/aide" class="settings-help-link">Aide</a></span>
|
|
<p class="settings-p">
|
|
Crée un topic sur ton serveur <a href="https://ntfy.sh" target="_blank" rel="noopener">ntfy</a>
|
|
(public ou auto-hébergé), un identifiant secret que toi seul connais, abonne-toi dessus avec
|
|
l'app ntfy sur ton téléphone, puis renseigne les deux champs ci-dessous. Guide complet sur
|
|
la page <a href="/aide">Aide</a>.
|
|
</p>
|
|
|
|
{% if saved %}
|
|
<div class="settings-saved">Enregistré.</div>
|
|
{% endif %}
|
|
|
|
<form method="post" action="/settings" class="settings-form">
|
|
<label class="login-label" for="ntfy_server">Serveur ntfy</label>
|
|
<input
|
|
id="ntfy_server"
|
|
class="login-input"
|
|
type="text"
|
|
name="ntfy_server"
|
|
value="{{ ntfy_server }}"
|
|
placeholder="https://ntfy.sh"
|
|
autocomplete="off"
|
|
>
|
|
<label class="login-label" for="ntfy_topic">Topic ntfy</label>
|
|
<input
|
|
id="ntfy_topic"
|
|
class="login-input"
|
|
type="text"
|
|
name="ntfy_topic"
|
|
value="{{ ntfy_topic }}"
|
|
placeholder="ex: antoine-pointeuse-x7f2"
|
|
autocomplete="off"
|
|
>
|
|
<label class="login-label" for="ntfy_token">Jeton d'accès (optionnel, serveur protégé)</label>
|
|
<input
|
|
id="ntfy_token"
|
|
class="login-input"
|
|
type="password"
|
|
name="ntfy_token"
|
|
value="{{ ntfy_token }}"
|
|
placeholder="ex: tk_xxxxxxxxxxxxxxxx"
|
|
autocomplete="off"
|
|
>
|
|
<label class="login-label" for="rappel_debut_h">Plage horaire des rappels</label>
|
|
<div class="settings-range">
|
|
<input
|
|
id="rappel_debut_h"
|
|
class="login-input settings-range-input"
|
|
type="number" min="0" max="23" name="rappel_debut_h"
|
|
value="{{ rappel_debut_h }}"
|
|
>
|
|
<span class="settings-range-sep">h → </span>
|
|
<input
|
|
id="rappel_fin_h"
|
|
class="login-input settings-range-input"
|
|
type="number" min="1" max="24" name="rappel_fin_h"
|
|
value="{{ rappel_fin_h }}"
|
|
>
|
|
<span class="settings-range-sep">h, jours ouvrés</span>
|
|
</div>
|
|
<button class="login-btn" type="submit">Enregistrer</button>
|
|
</form>
|
|
<div class="settings-test-row">
|
|
<button
|
|
class="login-btn settings-test-btn"
|
|
hx-post="/settings/test-ntfy"
|
|
hx-target="#ntfy-test-result"
|
|
hx-swap="innerHTML"
|
|
hx-disabled-elt="this"
|
|
>Tester la notification</button>
|
|
<span id="ntfy-test-result"></span>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="settings-card">
|
|
<span class="hist-label">Détection de présence (Automation) <a href="/aide" class="settings-help-link">Aide</a></span>
|
|
<p class="settings-p">
|
|
Crée une automatisation <a href="https://f-droid.org/en/packages/com.jens.automation2/" target="_blank" rel="noopener">Automation</a>
|
|
déclenchée par le Wi-Fi du bureau (ou une géofence) : à la <b>connexion</b>/entrée, appelle l'URL « arrivée » ;
|
|
à la <b>déconnexion</b>/sortie, l'URL « départ ». Tant que tu es détecté présent sans
|
|
avoir pointé l'entrée du matin, ou parti sans avoir pointé la sortie, tu reçois un rappel
|
|
selon l'intervalle réglé ci-dessus, dans la plage horaire configurée (jours ouvrés).
|
|
Étapes détaillées sur la page <a href="/aide">Aide</a>.
|
|
</p>
|
|
<div id="presence-urls">
|
|
<div class="settings-urlrow">
|
|
<span class="settings-urllabel">Arrivée</span>
|
|
<code class="settings-url">{{ arrivee_url }}</code>
|
|
</div>
|
|
<div class="settings-urlrow">
|
|
<span class="settings-urllabel">Départ</span>
|
|
<code class="settings-url">{{ depart_url }}</code>
|
|
</div>
|
|
</div>
|
|
<p class="settings-hint">
|
|
Ces URLs sont secrètes (elles contiennent ton token) — ne les partage pas.
|
|
En cas de compromission, régénère le token : les anciennes URLs cesseront de fonctionner
|
|
et tu devras mettre à jour Tasker avec les nouvelles.
|
|
</p>
|
|
<button
|
|
class="login-btn settings-regen-btn"
|
|
hx-post="/settings/regenerate-presence-token"
|
|
hx-target="#presence-urls"
|
|
hx-swap="outerHTML"
|
|
hx-confirm="Régénérer le token ? Les URLs actuelles cesseront immédiatement de fonctionner, tu devras mettre à jour Tasker."
|
|
hx-disabled-elt="this"
|
|
>Régénérer le token</button>
|
|
</div>
|
|
|
|
<style>
|
|
.settings-card {
|
|
background: var(--surface);
|
|
border: 1.5px solid var(--rule);
|
|
padding: 1.25rem 1.5rem;
|
|
display: flex;
|
|
flex-direction: column;
|
|
gap: .75rem;
|
|
}
|
|
.settings-p { font-size: 13px; color: var(--text); line-height: 1.6; }
|
|
.settings-p a { color: var(--accent); }
|
|
.settings-form { display: flex; flex-direction: column; gap: .5rem; max-width: 420px; }
|
|
.settings-saved {
|
|
background: var(--pos-bg);
|
|
border-left: 3px solid var(--pos);
|
|
color: var(--pos);
|
|
font-size: 12px;
|
|
padding: .5rem .8rem;
|
|
font-family: var(--mono);
|
|
}
|
|
.settings-error {
|
|
background: var(--neg-bg);
|
|
border-left: 3px solid var(--neg);
|
|
color: var(--neg);
|
|
font-size: 12px;
|
|
padding: .5rem .8rem;
|
|
font-family: var(--mono);
|
|
}
|
|
.settings-urlrow { display: flex; align-items: center; gap: .75rem; flex-wrap: wrap; }
|
|
.settings-urllabel {
|
|
font-family: var(--mono);
|
|
font-size: 10px;
|
|
letter-spacing: .08em;
|
|
text-transform: uppercase;
|
|
color: var(--muted);
|
|
width: 70px;
|
|
flex-shrink: 0;
|
|
}
|
|
.settings-url {
|
|
font-family: var(--mono);
|
|
font-size: 12px;
|
|
color: var(--text);
|
|
background: var(--ground);
|
|
border: 1px solid var(--rule);
|
|
padding: .4rem .6rem;
|
|
word-break: break-all;
|
|
}
|
|
.settings-hint { font-size: 11px; color: var(--muted); }
|
|
.settings-range { display: flex; align-items: center; gap: .4rem; flex-wrap: wrap; }
|
|
.settings-range-input { width: 70px; text-align: center; }
|
|
.settings-range-sep { font-size: 12px; color: var(--muted); font-family: var(--mono); }
|
|
.time-range-input {
|
|
width: 128px;
|
|
text-align: left;
|
|
padding-right: .4rem;
|
|
}
|
|
.time-range-input::-webkit-calendar-picker-indicator {
|
|
margin-left: .5rem;
|
|
}
|
|
.settings-help-link {
|
|
font-family: var(--mono);
|
|
font-size: 10px;
|
|
letter-spacing: .04em;
|
|
color: var(--accent);
|
|
text-decoration: none;
|
|
border: 1px solid var(--rule);
|
|
padding: .1rem .4rem;
|
|
margin-left: .5rem;
|
|
vertical-align: middle;
|
|
transition: border-color .15s, background .15s;
|
|
}
|
|
.settings-help-link:hover {
|
|
border-color: var(--accent);
|
|
background: rgba(47,95,160,.06);
|
|
}
|
|
.settings-test-row {
|
|
display: flex;
|
|
align-items: center;
|
|
gap: .75rem;
|
|
flex-wrap: wrap;
|
|
}
|
|
.settings-test-btn {
|
|
flex-shrink: 0;
|
|
}
|
|
.settings-regen-btn {
|
|
align-self: flex-start;
|
|
border-color: var(--neg);
|
|
color: var(--neg);
|
|
}
|
|
.settings-regen-btn:hover:not([disabled]) {
|
|
background: var(--neg-bg);
|
|
}
|
|
#ntfy-test-result {
|
|
flex: 1;
|
|
min-width: 0;
|
|
}
|
|
#ntfy-test-result:empty { display: none; }
|
|
#ntfy-test-result:not(:empty) {
|
|
font-family: var(--mono);
|
|
font-size: 12px;
|
|
padding: .5rem .8rem;
|
|
border-left: 3px solid var(--muted);
|
|
background: var(--ground);
|
|
line-height: 1.45;
|
|
}
|
|
</style>
|
|
{% endblock %}
|