function formatDate(dateStr) { const d = new Date(dateStr); return d.toLocaleDateString('fr-FR', { year: 'numeric', month: 'long', day: 'numeric' }); } function showError(message) { document.getElementById('error').innerHTML = `
`; } function statTile(value, label) { return `Aucun calendrier configuré.
'}Aucune personne suivie pour le moment.
'; container.querySelectorAll('.add-feed-form').forEach((form) => { form.addEventListener('submit', (e) => onAddFeed(e, parseInt(form.dataset.personId, 10))); }); container.querySelectorAll('.remove-feed-btn').forEach((btn) => { btn.addEventListener('click', () => onRemoveFeed(parseInt(btn.dataset.feedId, 10))); }); } async function onAddFeed(e, personId) { e.preventDefault(); const form = e.target; const label = form.label.value.trim(); const icsUrl = form.icsUrl.value.trim(); try { await api.post(`/api/calendar/people/${personId}/feeds`, { label, icsUrl }); await loadCalendarPeople(); } catch (err) { showError(err.message); } } async function onRemoveFeed(feedId) { try { await api.del(`/api/calendar/feeds/${feedId}`); await loadCalendarPeople(); } catch (err) { showError(err.message); } } async function loadSlotSettingsForm() { const settings = await api.get('/api/calendar/settings'); const form = document.getElementById('slot-settings-form'); form.weekdayStart.value = settings.weekdayStart; form.weekdayEnd.value = settings.weekdayEnd; form.weekendStart.value = settings.weekendStart; form.weekendEnd.value = settings.weekendEnd; form.marginMinutes.value = settings.marginMinutes; } async function onSaveSlotSettings(e) { e.preventDefault(); const form = e.target; const statusEl = document.getElementById('slot-settings-status'); statusEl.textContent = ''; try { await api.patch('/api/calendar/settings', { weekdayStart: form.weekdayStart.value, weekdayEnd: form.weekdayEnd.value, weekendStart: form.weekendStart.value, weekendEnd: form.weekendEnd.value, marginMinutes: parseInt(form.marginMinutes.value, 10), }); statusEl.textContent = 'Horaires enregistrés.'; } catch (err) { showError(err.message); } } (async function init() { const me = await initNav('admin'); if (!me.isAdmin) { window.location.href = '/index.html'; return; } try { const stats = await api.get('/api/admin/stats'); const container = document.getElementById('content'); container.innerHTML = `Vue d'ensemble de l'activité et des données de l'application.
La date de dernière connexion est approximative (dernière synchronisation du profil, pas un journal de connexion précis).
Horaires utilisés pour calculer les disponibilités sur `/calendar.html`.
Chaque personne peut avoir plusieurs calendriers (Google, Outlook, Apple...). L'application ne conserve jamais le contenu de ces calendriers — seul un statut disponible/occupé par créneau est déduit et enregistré.
Chargement…