import { apiGet } from '../api.js'; import { canOpenKioskSettings, openDeviceSettings, openNetworkSettings, openWifiSettings } from '../kiosk-settings.js'; import { syncNotifications } from '../notifications.js'; export async function renderDashboard(root, { experience } = {}) { const currentExperience = experience || { moduleKey: 'mixed', moduleLabel: 'FTTH', moduleMetricLabel: 'Missions FTTH', moduleInProgressLabel: 'FTTH en cours', moduleActionLabel: 'Ouvrir FTTH', moduleOpenActionLabel: 'Voir fiches', navItems: [], }; root.innerHTML = `
tableau de bord
Vue d'ensemble terrain

Retrouvez vos volumes de tickets, vos priorités et les accès rapides juste après la connexion.

Raccourcis
Accès direct aux écrans opérationnels
${renderShortcutActions(currentExperience)}
${renderKioskSettingsPanel()}
Alertes prioritaires
Éléments qui demandent une attention rapide
Dernières assignations
Aperçu des tickets disponibles sans quitter le dashboard
`; const alertBox = root.querySelector('#dashboardAlert'); const heroStats = root.querySelector('#dashboardHeroStats'); const metricsBox = root.querySelector('#dashboardMetrics'); const priorityBox = root.querySelector('#dashboardPriorityList'); const recentBox = root.querySelector('#dashboardRecentList'); const kioskButtons = Array.from(root.querySelectorAll('[data-kiosk-settings]')); for (const button of kioskButtons) { button.addEventListener('click', async () => { const action = String(button.dataset.kioskSettings || ''); const handlers = { wifi: openWifiSettings, network: openNetworkSettings, device: openDeviceSettings, }; const handler = handlers[action]; if (!handler) { return; } try { await handler(); } catch (error) { alertBox.innerHTML = '
Impossible d’ouvrir les paramètres de la tablette depuis ce terminal.
'; } }); } async function load() { alertBox.innerHTML = ''; heroStats.innerHTML = '
ChargementSynchronisation…
'; metricsBox.innerHTML = '
Chargement du tableau de bord…
'; priorityBox.innerHTML = '
Analyse des priorités…
'; recentBox.innerHTML = '
Chargement des assignations…
'; const shouldLoadIncidents = currentExperience.moduleKey === 'backbones' || currentExperience.moduleKey === 'mixed'; const shouldLoadTickets = currentExperience.moduleKey !== 'backbones'; const [incidentsResult, ftthResult] = await Promise.allSettled([ shouldLoadIncidents ? apiGet('/api/mobile/incidents/assigned') : Promise.resolve({ ok: true, incidents: [] }), shouldLoadTickets ? apiGet('/api/mobile/ftth/assigned') : Promise.resolve({ ok: true, tickets: [] }), ]); const incidents = incidentsResult.status === 'fulfilled' ? (incidentsResult.value?.incidents || []) : []; const tickets = ftthResult.status === 'fulfilled' ? (ftthResult.value?.tickets || []) : []; const urgentCount = incidents.filter((item) => String(item.priority || '').toLowerCase() === 'urgent').length; const inProgressFtth = tickets.filter((item) => { const status = String(item.status || '').toLowerCase(); return status && status !== 'traité' && status !== 'validé' && status !== 'clôturé'; }).length; syncNotifications({ incidents, tickets }); heroStats.innerHTML = renderHeroStats(currentExperience, incidents.length, tickets.length, urgentCount, inProgressFtth); metricsBox.innerHTML = renderMetricCards(currentExperience, incidents.length, tickets.length, urgentCount, inProgressFtth); const priorityItems = buildPriorityItems(currentExperience, incidents.length, tickets.length, urgentCount, inProgressFtth); priorityBox.innerHTML = priorityItems.map((text) => `
${escapeHtml(text)}
`).join(''); const recentIncidents = incidents.slice(0, 2).map((item) => ({ title: item.ticket_id || `INC-${item.id}`, subtitle: item.title || 'Incident terrain', meta: item.status_label || item.priority || 'À traiter', href: `#/treatment?id=${Number(item.id || 0)}`, icon: 'fa-triangle-exclamation', })); const recentTickets = tickets.slice(0, 2).map((item) => ({ title: item.client_name || item.ref_code || `FTTH-${item.id}`, subtitle: item.nature_intervention || item.client_address || `Mission ${currentExperience.moduleLabel}`, meta: item.priority || item.status || 'À ouvrir', href: `#/ftth-report?id=${Number(item.id || 0)}`, icon: 'fa-network-wired', })); const recentItems = currentExperience.moduleKey === 'backbones' ? recentIncidents : currentExperience.moduleKey === 'mixed' ? [...recentIncidents, ...recentTickets] : recentTickets; recentBox.innerHTML = recentItems.length ? recentItems.map((item) => ` ${escapeHtml(item.title)} ${escapeHtml(item.subtitle)} ${escapeHtml(item.meta)} `).join('') : '
Aucune assignation récente à afficher.
'; if ((shouldLoadIncidents && incidentsResult.status === 'rejected') || (shouldLoadTickets && ftthResult.status === 'rejected')) { alertBox.innerHTML = '
Certaines données n’ont pas pu être synchronisées. Le dashboard affiche les éléments disponibles.
'; } } root.querySelector('#btnRefreshDashboard').addEventListener('click', load); await load(); } function renderMetricCard(label, value, icon, badgeClass, href, actionLabel) { return `
${escapeHtml(label)}
${value}
${escapeHtml(actionLabel)}
`; } function renderShortcutActions(experience) { const actions = experience.navItems.filter((item) => item.path !== '/dashboard'); return actions.map((item, index) => { const classes = index === 0 ? 'btn btn-primary mobile-cta' : 'btn btn-outline-primary mobile-cta'; return `${escapeHtml(item.label)}`; }).join(''); } function renderKioskSettingsPanel() { if (!canOpenKioskSettings()) { return ''; } return `
Accès tablette autorisé en mode borne
`; } function renderHeroStats(experience, incidentsCount, ticketsCount, urgentCount, inProgressCount) { if (experience.moduleKey === 'backbones') { return `
Incidents ${incidentsCount}
Urgents ${urgentCount}
`; } return `
${escapeHtml(experience.moduleMetricLabel)} ${ticketsCount}
En cours ${inProgressCount}
`; } function renderMetricCards(experience, incidentsCount, ticketsCount, urgentCount, inProgressCount) { if (experience.moduleKey === 'backbones') { return ` ${renderMetricCard('Incidents assignés', incidentsCount, 'fa-triangle-exclamation', 'text-bg-warning', '#/assigned', 'Ouvrir incidents')} ${renderMetricCard('Incidents urgents', urgentCount, 'fa-bolt', 'text-bg-danger', '#/assigned', 'Traiter urgences')} `; } return ` ${renderMetricCard(experience.moduleMetricLabel, ticketsCount, 'fa-network-wired', 'text-bg-info', '#/ftth', experience.moduleActionLabel)} ${renderMetricCard(experience.moduleInProgressLabel, inProgressCount, 'fa-file-waveform', 'text-bg-primary', '#/ftth', experience.moduleOpenActionLabel)} `; } function buildPriorityItems(experience, incidentsCount, ticketsCount, urgentCount, inProgressCount) { if (experience.moduleKey === 'backbones') { return [ urgentCount ? `${urgentCount} incident(s) urgent(s) nécessitent une prise en charge.` : '', !incidentsCount ? 'Aucun incident en attente pour le moment.' : '', ].filter(Boolean); } return [ inProgressCount ? `${inProgressCount} mission(s) ${experience.moduleLabel.toLowerCase()} restent à compléter.` : '', !ticketsCount ? `Aucune mission ${experience.moduleLabel.toLowerCase()} en attente pour le moment.` : '', ].filter(Boolean); } function escapeHtml(value) { return String(value ?? '') .replaceAll('&', '&') .replaceAll('<', '<') .replaceAll('>', '>') .replaceAll('"', '"') .replaceAll("'", '''); }