59 lines
2.6 KiB
JavaScript
59 lines
2.6 KiB
JavaScript
(() => {
|
||
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);
|
||
})();
|