Projette les cibles en temps réel et corrige le fuseau horaire
Calcule les heures travaillées et cibles d'entrée/sortie même quand une demi-journée est en cours (pas encore de pointage de sortie), au lieu d'attendre la fin de journée. Ajoute un endpoint /refresh pour rafraîchir l'affichage sans repointer. Désambiguïse les libellés de semaine sur plusieurs années et force TZ=Europe/Paris dans le conteneur. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
@ -204,6 +204,117 @@
|
||||
})();
|
||||
</script>
|
||||
|
||||
<script>
|
||||
// Alerte départ — surveille l'heure cible du jour courant
|
||||
(function () {
|
||||
var _alerted = false;
|
||||
var _lastCible = null;
|
||||
|
||||
function todayStr() {
|
||||
var d = new Date();
|
||||
return d.getFullYear() + '-' +
|
||||
String(d.getMonth() + 1).padStart(2, '0') + '-' +
|
||||
String(d.getDate()).padStart(2, '0');
|
||||
}
|
||||
|
||||
function getCible() {
|
||||
var el = document.querySelector('#calc-' + todayStr() + '-cible .cible-num');
|
||||
if (!el) return null;
|
||||
var txt = el.textContent.trim();
|
||||
// Formats: "17:13" ou "16:15→17:00" — on veut l'heure de DÉPART (dernière)
|
||||
var time = txt.indexOf('→') >= 0 ? txt.split('→').pop().trim() : txt;
|
||||
return /^\d{2}:\d{2}$/.test(time) ? time : null;
|
||||
}
|
||||
|
||||
function nowMin() {
|
||||
var d = new Date();
|
||||
return d.getHours() * 60 + d.getMinutes();
|
||||
}
|
||||
|
||||
function toMin(t) {
|
||||
var p = t.split(':');
|
||||
return parseInt(p[0], 10) * 60 + parseInt(p[1], 10);
|
||||
}
|
||||
|
||||
function getSolde() {
|
||||
var el = document.querySelector('#soldes-section .solde-item:last-child .solde-num');
|
||||
return el ? el.textContent.trim() : null;
|
||||
}
|
||||
|
||||
function showAlert(time) {
|
||||
if (document.getElementById('alerte-depart')) return;
|
||||
var solde = getSolde();
|
||||
var soldeHtml = '';
|
||||
if (solde && solde.charAt(0) === '+' && solde !== '+0h00') {
|
||||
soldeHtml = '<div class="alerte-supp">' + solde + ' de supp.</div>';
|
||||
}
|
||||
var notifBody = 'Heure de départ : ' + time;
|
||||
if (solde && solde.charAt(0) === '+') notifBody += ' · ' + solde + ' de supp.';
|
||||
|
||||
var el = document.createElement('div');
|
||||
el.id = 'alerte-depart';
|
||||
el.innerHTML =
|
||||
'<div class="alerte-inner">' +
|
||||
'<span class="alerte-icon">⏰</span>' +
|
||||
'<div class="alerte-body">' +
|
||||
'<div class="alerte-label">Il faut partir</div>' +
|
||||
'<div class="alerte-time">' + time + '</div>' +
|
||||
soldeHtml +
|
||||
'</div>' +
|
||||
'<button class="alerte-close" title="Fermer" ' +
|
||||
'onclick="document.getElementById(\'alerte-depart\').remove();' +
|
||||
'window._alerteDismissed=true;">✕</button>' +
|
||||
'</div>';
|
||||
document.body.appendChild(el);
|
||||
|
||||
if ('Notification' in window && Notification.permission === 'granted') {
|
||||
new Notification('⏰ Il faut partir !', { body: notifBody, tag: 'depart' });
|
||||
}
|
||||
}
|
||||
|
||||
function check() {
|
||||
if (window._alerteDismissed) return;
|
||||
var cible = getCible();
|
||||
if (!cible) return;
|
||||
if (cible !== _lastCible) {
|
||||
_lastCible = cible;
|
||||
if (toMin(cible) > nowMin()) _alerted = false;
|
||||
}
|
||||
if (!_alerted && nowMin() >= toMin(cible)) {
|
||||
_alerted = true;
|
||||
showAlert(cible);
|
||||
}
|
||||
}
|
||||
|
||||
if ('Notification' in window && Notification.permission === 'default') {
|
||||
Notification.requestPermission();
|
||||
}
|
||||
|
||||
check();
|
||||
setInterval(check, 30000);
|
||||
})();
|
||||
</script>
|
||||
|
||||
<script>
|
||||
// Rafraîchit automatiquement les compteurs (travaillé/delta/soldes) du jour courant
|
||||
(function () {
|
||||
function todayStr() {
|
||||
var d = new Date();
|
||||
return d.getFullYear() + '-' +
|
||||
String(d.getMonth() + 1).padStart(2, '0') + '-' +
|
||||
String(d.getDate()).padStart(2, '0');
|
||||
}
|
||||
|
||||
function refresh() {
|
||||
var date = todayStr();
|
||||
if (!document.getElementById('row-' + date)) return;
|
||||
htmx.ajax('GET', '/refresh/' + date, { swap: 'none' });
|
||||
}
|
||||
|
||||
setInterval(refresh, 60000);
|
||||
})();
|
||||
</script>
|
||||
|
||||
<script>
|
||||
// Live warn on time inputs that violate minimum directives
|
||||
document.addEventListener('input', function(e) {
|
||||
|
||||
Reference in New Issue
Block a user