(() => { const elements = [...document.querySelectorAll('[data-presence-asset]')]; if (!elements.length) return; function formattedDate(value) { return value ? (window.AssetManagerTime?.formatUtc(value, value) || value) : '–'; } function updateElement(element, item) { const state = ['online', 'offline'].includes(item.state) ? item.state : 'unknown'; element.classList.remove('presence-online', 'presence-offline', 'presence-unknown'); element.classList.add(`presence-${state}`); element.dataset.filterValue = element.dataset[`label${state[0].toUpperCase()}${state.slice(1)}`] || state; const label = element.querySelector('[data-presence-label]'); if (label) { label.textContent = element.dataset[`label${state[0].toUpperCase()}${state.slice(1)}`] || state; } const lastSeenText = formattedDate(item.last_seen_at); const lastSeenPrefix = element.dataset.lastSeenLabel || 'Last seen'; element.title = `${label?.textContent || state} · ${lastSeenPrefix}: ${lastSeenText}`; const meta = document.querySelector(`[data-presence-meta="${CSS.escape(String(item.asset_id))}"]`); if (meta) { const stateElement = meta.querySelector('[data-presence-meta-state]'); const updatedElement = meta.querySelector('[data-presence-updated]'); const lastOnlineElement = meta.querySelector('[data-presence-last-online]'); const lastSeenElement = meta.querySelector('[data-presence-last-seen]'); if (stateElement) stateElement.textContent = label?.textContent || state; if (updatedElement) updatedElement.textContent = formattedDate(item.updated_at); if (lastOnlineElement) lastOnlineElement.textContent = formattedDate(item.last_online_at); if (lastSeenElement) lastSeenElement.textContent = formattedDate(item.last_seen_at); } } async function refreshPresence() { try { const response = await fetch('/api/assets/presence', {cache: 'no-store'}); if (!response.ok) return; const payload = await response.json(); const byId = new Map((payload.items || []).map(item => [String(item.asset_id), item])); elements.forEach(element => { const item = byId.get(String(element.dataset.presenceAsset)); if (item) updateElement(element, item); }); document.querySelectorAll('table.data-table').forEach(table => { table.assetRebuildFilterCache?.(); table.assetApplyFilters?.(); }); } catch (_) { // Keep the last known state visible when the API is temporarily unavailable. } } refreshPresence(); window.setInterval(refreshPresence, 5000); })();