'use client'; import { useEffect, useMemo, useState } from 'react'; import Link from 'next/link'; import { AlertTriangle, CheckCircle2, Cloud, DatabaseBackup, ExternalLink, Gauge, HardDrive, RefreshCw, ShieldCheck, Timer, Wifi } from 'lucide-react'; import { Badge, Button } from '@/components/ui/Primitives'; import { SectionCard } from '@/components/hermes-shell'; import { api, type HermesOpsInstance, type HermesOpsSnapshot } from '@/lib/api'; function boolTone(value: boolean): 'success' | 'error' { return value ? 'success' : 'error'; } function boolText(value: boolean) { return value ? 'OK' : 'Needs attention'; } function formatDate(value: string | null) { if (!value) return 'unknown'; const date = new Date(value); if (Number.isNaN(date.getTime())) return value; return new Intl.DateTimeFormat('en', { month: 'short', day: 'numeric', hour: 'numeric', minute: '2-digit', }).format(date); } function StatusRow({ label, value, ok }: { label: string; value: string; ok: boolean }) { return (
{label} {value}
); } function InstanceCard({ instance }: { instance: HermesOpsInstance }) { const score = [ instance.gateway.active, instance.gateway.enabled, instance.dashboard.active, instance.backup.timer.active, instance.backup.repo.clean, instance.google.workspaceToken, ].filter(Boolean).length; return (

{instance.label}

{instance.hermesHome}

= 4 ? 'warning' : 'error'}>{score}/6 healthy
Backup repo

HEAD {instance.backup.repo.head ?? 'unknown'}

Last commit {formatDate(instance.backup.repo.lastCommitAt)}

{instance.backup.repo.clean ? 'Clean working tree' : 'Uncommitted changes present'}

Restore payload

{instance.backup.restoredFileCount ?? 'unknown'} tracked files

{instance.backup.restoredCronJobs ?? 'unknown'} cron job definitions

{instance.backup.repo.size ?? 'size unknown'}

); } export function HermesOpsPanel() { const [snapshot, setSnapshot] = useState(null); const [loading, setLoading] = useState(true); const [error, setError] = useState(null); const load = async () => { setLoading(true); setError(null); try { setSnapshot(await api.getHermesOps()); } catch (err) { setError(err instanceof Error ? err.message : 'Unable to load Hermes operations status'); } finally { setLoading(false); } }; useEffect(() => { void load(); const id = window.setInterval(() => void load(), 60_000); return () => window.clearInterval(id); }, []); const allHealthy = useMemo(() => snapshot ? snapshot.warnings.length === 0 : false, [snapshot]); return ( {snapshot ? {allHealthy ? 'All green' : `${snapshot.warnings.length} warning(s)`} : null} )} > {error ? (
{error}
) : null} {snapshot ? (
Tailscale IP

{snapshot.tailscaleIp ?? 'unknown'}

Emergency Drive

{snapshot.emergencyDriveUpload.active ? 'active' : 'inactive'}

Next Drive bundle

{snapshot.emergencyDriveUpload.nextRun ?? 'unknown'}

Generated

{formatDate(snapshot.generatedAt)}

{snapshot.warnings.length ? (
Recovery warnings
{snapshot.warnings.map((warning) => (
{warning}
))}
) : (
Vijay and Bheem recovery paths are healthy.
)}
{snapshot.instances.map((instance) => ( ))}
Disaster recovery details live in{' '} Hermes settings {' '}and the tracked runbook in docs/hermes-disaster-recovery.md.
) : (
Loading live Hermes operations status...
)}
); }