// Endpoint detail drawer function NodeDetail({ node, onClose, onReboot, onMaint }) { // Local state must be declared unconditionally (Rules of Hooks). const [shotBust, setShotBust] = React.useState(0); const [shotBusy, setShotBusy] = React.useState(false); const [shotErr, setShotErr] = React.useState(null); if (!node) return
; const statusLabel = { ok: 'Up', warn: 'Warning', crit: 'Down', off: 'Offline', maint: 'Maintenance', unknown: 'Pending' }[node.status] || node.status; const shot = node.screenshot || { exists: false, capturedAt: 0 }; const shotSrc = shot.exists ? `/api/screenshots/${node.id}.jpg?v=${shotBust || shot.capturedAt}` : null; const shotAge = shot.capturedAt ? window.fmtRel(shot.capturedAt) : null; async function recaptureShot() { setShotBusy(true); setShotErr(null); try { const meta = await window.adminFetch(`/api/admin/screenshots/${node.id}`, { method: 'POST' }); setShotBust(meta.capturedAt || Date.now()); if (meta.error) setShotErr(meta.error); } catch (e) { setShotErr(e.message || 'capture failed'); } finally { setShotBusy(false); } } return (