import { FormEvent, useState } from "react"; import { Activity, Pencil, PlugZap, Plus, RefreshCw, Trash2, X } from "lucide-react"; import { api } from "../api/client"; import { Button } from "../components/Button"; import type { Monitor } from "../types/api"; interface NetworkChecksPageProps { token: string; monitors: Monitor[]; onChanged: () => Promise; } type NetworkCheckType = "ping" | "tcp"; export function NetworkChecksPage({ token, monitors, onChanged }: NetworkChecksPageProps) { const networkChecks = monitors.filter((monitor) => monitor.monitor_type === "ping" || monitor.monitor_type === "tcp"); const [checkType, setCheckType] = useState("ping"); const [name, setName] = useState(""); const [host, setHost] = useState(""); const [port, setPort] = useState(443); const [timeoutSeconds, setTimeoutSeconds] = useState(5); const [intervalSeconds, setIntervalSeconds] = useState(60); const [failureThreshold, setFailureThreshold] = useState(3); const [alertEnabled, setAlertEnabled] = useState(true); const [editingMonitorId, setEditingMonitorId] = useState(null); const [submitting, setSubmitting] = useState(false); const [deletingId, setDeletingId] = useState(null); const [error, setError] = useState(null); async function handleSubmit(event: FormEvent) { event.preventDefault(); setSubmitting(true); setError(null); try { if (editingMonitorId) { await api.updateMonitor(token, editingMonitorId, { name, target: checkType === "tcp" ? `${host}:${port}` : host, interval_seconds: intervalSeconds, config: checkType === "tcp" ? { host, port, timeout_seconds: timeoutSeconds } : { timeout_seconds: timeoutSeconds }, }); } else if (checkType === "tcp") { await api.createTcpMonitor(token, { name, host, port, timeout_seconds: timeoutSeconds, interval_seconds: intervalSeconds, create_asset: true, alert_enabled: alertEnabled, alert_severity: "warning", failure_threshold: failureThreshold, }); } else { await api.createPingMonitor(token, { name, host, timeout_seconds: timeoutSeconds, interval_seconds: intervalSeconds, create_asset: true, alert_enabled: alertEnabled, alert_severity: "warning", failure_threshold: failureThreshold, }); } resetForm(); await onChanged(); } catch (err) { setError(err instanceof Error ? err.message : "Could not save network check"); } finally { setSubmitting(false); } } function startEdit(monitor: Monitor) { const nextType = monitor.monitor_type === "tcp" ? "tcp" : "ping"; setEditingMonitorId(monitor.id); setCheckType(nextType); setName(monitor.name); setHost(nextType === "tcp" ? String(monitor.config?.host ?? monitor.target.split(":")[0] ?? "") : monitor.target); setPort(Number(monitor.config?.port ?? 443)); setTimeoutSeconds(Number(monitor.config?.timeout_seconds ?? 5)); setIntervalSeconds(monitor.interval_seconds); setAlertEnabled(true); setFailureThreshold(3); setError(null); } function resetForm() { setEditingMonitorId(null); setCheckType("ping"); setName(""); setHost(""); setPort(443); setTimeoutSeconds(5); setIntervalSeconds(60); setFailureThreshold(3); setAlertEnabled(true); } async function deleteMonitor(monitorId: number) { setDeletingId(monitorId); setError(null); try { await api.deleteMonitor(token, monitorId); await onChanged(); } catch (err) { setError(err instanceof Error ? err.message : "Could not delete network check"); } finally { setDeletingId(null); } } return (

Network Checks

ICMP ping checks and TCP port availability checks.

{checkType === "tcp" ? : }

{editingMonitorId ? "Edit Network Check" : "Add Network Check"}

{checkType === "tcp" ? ( ) : null}
{!editingMonitorId ? (
Alert on repeated failures setAlertEnabled(event.target.checked)} type="checkbox" />
) : null} {!editingMonitorId ? ( ) : null} {error ?
{error}
: null}
{editingMonitorId ? ( ) : null}

Configured Network Checks

{networkChecks.length ? ( networkChecks.map((monitor) => (
{monitor.name}
{monitor.target}
{monitor.monitor_type}
{monitor.last_checked_at ? new Date(monitor.last_checked_at).toLocaleTimeString() : "Not checked"}
)) ) : (
No network checks yet.
)}
); } function modeClass(active: boolean) { return `h-8 rounded-md text-sm transition disabled:opacity-70 ${active ? "bg-slate-800 text-white" : "text-slate-400 hover:bg-slate-900 hover:text-white"}`; } function Status({ status }: { status: string }) { const classes = status === "up" ? "border-teal-500/40 bg-teal-950/40 text-teal-200" : status === "down" ? "border-red-500/40 bg-red-950/40 text-red-200" : status === "warning" ? "border-amber-500/40 bg-amber-950/40 text-amber-200" : "border-slate-600 bg-slate-900 text-slate-300"; return {status}; }