From 7bc9e67e964bf4520829c863bf0b3d28065e3c49 Mon Sep 17 00:00:00 2001 From: Claude VM Date: Wed, 8 Apr 2026 10:40:31 +0300 Subject: [PATCH] feat(monitor): add eTerra session indicator + login form The eTerra connect/disconnect UI and session status were missing from the deployed monitor page. Also, since ETERRA env vars are empty in the container, the connect flow now accepts username/password from the UI. This unblocks county sync which requires an active eTerra session. Co-Authored-By: Claude Opus 4.6 (1M context) --- src/app/(modules)/monitor/page.tsx | 137 +++++++++++++++++++++++++++++ 1 file changed, 137 insertions(+) diff --git a/src/app/(modules)/monitor/page.tsx b/src/app/(modules)/monitor/page.tsx index 1908fe1..e5b81f4 100644 --- a/src/app/(modules)/monitor/page.tsx +++ b/src/app/(modules)/monitor/page.tsx @@ -11,6 +11,16 @@ type MonitorData = { config?: { martinUrl?: string; pmtilesUrl?: string; n8nWebhook?: string }; }; +type EterraSessionStatus = { + connected: boolean; + username?: string; + connectedAt?: string; + activeJobCount: number; + eterraAvailable?: boolean; + eterraMaintenance?: boolean; + eterraHealthMessage?: string; +}; + export default function MonitorPage() { const [data, setData] = useState(null); const [loading, setLoading] = useState(true); @@ -18,6 +28,11 @@ export default function MonitorPage() { const [logs, setLogs] = useState<{ time: string; type: "info" | "ok" | "error" | "wait"; msg: string }[]>([]); const [counties, setCounties] = useState([]); const [selectedCounty, setSelectedCounty] = useState(""); + const [eterraSession, setEterraSession] = useState({ connected: false, activeJobCount: 0 }); + const [eterraConnecting, setEterraConnecting] = useState(false); + const [showLoginForm, setShowLoginForm] = useState(false); + const [eterraUser, setEterraUser] = useState(""); + const [eterraPwd, setEterraPwd] = useState(""); const rebuildPrevRef = useRef(null); const pollRef = useRef | null>(null); @@ -50,6 +65,61 @@ export default function MonitorPage() { .catch(() => {}); }, []); + // eTerra session status — poll every 30s + const fetchEterraSession = useCallback(async () => { + try { + const res = await fetch("/api/eterra/session"); + if (res.ok) setEterraSession(await res.json() as EterraSessionStatus); + } catch { /* noop */ } + }, []); + + useEffect(() => { + void fetchEterraSession(); + const interval = setInterval(() => void fetchEterraSession(), 30_000); + return () => clearInterval(interval); + }, [fetchEterraSession]); + + const handleEterraConnect = async () => { + setEterraConnecting(true); + try { + const payload: Record = { action: "connect" }; + if (eterraUser.trim()) payload.username = eterraUser.trim(); + if (eterraPwd.trim()) payload.password = eterraPwd.trim(); + const res = await fetch("/api/eterra/session", { + method: "POST", + headers: { "Content-Type": "application/json" }, + body: JSON.stringify(payload), + }); + const d = await res.json() as { success?: boolean; error?: string }; + if (d.success) { + await fetchEterraSession(); + addLog("ok", "eTerra conectat"); + setShowLoginForm(false); + setEterraPwd(""); + } else { + addLog("error", `eTerra: ${d.error ?? "Eroare conectare"}`); + } + } catch { + addLog("error", "eTerra: eroare retea"); + } + setEterraConnecting(false); + }; + + const handleEterraDisconnect = async () => { + const res = await fetch("/api/eterra/session", { + method: "POST", + headers: { "Content-Type": "application/json" }, + body: JSON.stringify({ action: "disconnect" }), + }); + const d = await res.json() as { success?: boolean; error?: string }; + if (d.success) { + setEterraSession({ connected: false, activeJobCount: 0 }); + addLog("info", "eTerra deconectat"); + } else { + addLog("error", `Deconectare: ${d.error ?? "Eroare"}`); + } + }; + // Cleanup poll on unmount useEffect(() => { return () => { if (pollRef.current) clearInterval(pollRef.current); }; @@ -232,6 +302,73 @@ export default function MonitorPage() { {/* Actions + Log */} + {/* eTerra session indicator */} +
+
+
+ + + {eterraSession.connected + ? `eTerra: ${eterraSession.username}` + : "eTerra: deconectat"} + + {eterraSession.connected && eterraSession.activeJobCount > 0 && ( + ({eterraSession.activeJobCount} job activ) + )} +
+ {eterraSession.connected ? ( + + ) : ( + + )} + {eterraSession.eterraMaintenance && ( + Mentenanta + )} +
+ {showLoginForm && !eterraSession.connected && ( +
+
+ + setEterraUser(e.target.value)} + className="h-8 w-40 rounded-md border border-border bg-background px-2 text-sm" + placeholder="user@ancpi" + /> +
+
+ + setEterraPwd(e.target.value)} + onKeyDown={(e) => { if (e.key === "Enter") handleEterraConnect(); }} + className="h-8 w-40 rounded-md border border-border bg-background px-2 text-sm" + placeholder="parola" + /> +
+ +
+ )} +
+