import sys path = "/home/guru/gururmm/dashboard/src/pages/Alerts.tsx" with open(path, "r", encoding="utf-8") as f: content = f.read() # ─── Insert WatchdogAlertsSection component before export function Alerts() ─── insert_before = "export function Alerts() {" watchdog_section = ( "// ─── WatchdogAlertsSection ─────────────────────────────────────────────────\n" "\n" "type WdogStatus = \"active\" | \"acknowledged\" | \"resolved\";\n" "\n" "function wdogStatus(alert: WatchdogAlert): WdogStatus {\n" " if (alert.resolved_at) return \"resolved\";\n" " if (alert.acknowledged_at) return \"acknowledged\";\n" " return \"active\";\n" "}\n" "\n" "function WatchdogAlertsSection() {\n" " const queryClient = useQueryClient();\n" " const { toast } = useToast();\n" " const [showAll, setShowAll] = useState(false);\n" " const [expandedLogId, setExpandedLogId] = useState(null);\n" "\n" " const { data: allAlerts = [], isLoading } = useQuery({\n" " queryKey: [\"watchdog-alerts\"],\n" " queryFn: () => watchdogAlertsApi.list().then((r) => r.data),\n" " refetchInterval: 30000,\n" " });\n" "\n" " const alerts = showAll\n" " ? allAlerts\n" " : allAlerts.filter((a) => !a.resolved_at);\n" "\n" " const acknowledgeMutation = useMutation({\n" " mutationFn: (id: string) => watchdogAlertsApi.acknowledge(id),\n" " onSuccess: () => {\n" " queryClient.invalidateQueries({ queryKey: [\"watchdog-alerts\"] });\n" " toast({ type: \"success\", title: \"Alert acknowledged\" });\n" " },\n" " onError: (err: Error) =>\n" " toast({ type: \"error\", title: \"Could not acknowledge\", message: err.message }),\n" " });\n" "\n" " const resolveMutation = useMutation({\n" " mutationFn: (id: string) => watchdogAlertsApi.resolve(id),\n" " onSuccess: () => {\n" " queryClient.invalidateQueries({ queryKey: [\"watchdog-alerts\"] });\n" " toast({ type: \"success\", title: \"Alert resolved\" });\n" " },\n" " onError: (err: Error) =>\n" " toast({ type: \"error\", title: \"Could not resolve\", message: err.message }),\n" " });\n" "\n" " const deleteMutation = useMutation({\n" " mutationFn: (id: string) => watchdogAlertsApi.delete(id),\n" " onSuccess: () => {\n" " queryClient.invalidateQueries({ queryKey: [\"watchdog-alerts\"] });\n" " toast({ type: \"success\", title: \"Alert deleted\" });\n" " },\n" " onError: (err: Error) =>\n" " toast({ type: \"error\", title: \"Could not delete\", message: err.message }),\n" " });\n" "\n" " const isMutating =\n" " acknowledgeMutation.isPending ||\n" " resolveMutation.isPending ||\n" " deleteMutation.isPending;\n" "\n" " const activeCount = allAlerts.filter((a) => wdogStatus(a) === \"active\").length;\n" "\n" " return (\n" " \n" " \n" "
\n" " \n" " \n" " Watchdog Alerts\n" " {activeCount > 0 && (\n" " \n" " {activeCount} active\n" " \n" " )}\n" " \n" " setShowAll((v) => !v)}\n" " >\n" " {showAll ? \"Active only\" : \"Show all\"}\n" " \n" "
\n" "
\n" " \n" " {isLoading && (\n" "

Loading...

\n" " )}\n" " {!isLoading && alerts.length === 0 && (\n" "

\n" " {showAll ? \"No watchdog alerts recorded.\" : \"No active watchdog alerts.\"}\n" "

\n" " )}\n" "
\n" " {alerts.map((alert) => {\n" " const status = wdogStatus(alert);\n" " const logExpanded = expandedLogId === alert.id;\n" " const statusColor =\n" " status === \"active\"\n" " ? \"text-red-600 dark:text-red-400\"\n" " : status === \"acknowledged\"\n" " ? \"text-amber-600 dark:text-amber-400\"\n" " : \"text-[hsl(var(--muted-foreground))]\";\n" "\n" " return (\n" " \n" "
\n" "
\n" "
\n" " \n" " {status}\n" " \n" " \n" " \xb7 {alert.restart_attempts} restart attempt\n" " {alert.restart_attempts !== 1 ? \"s\" : \"\"}\n" " \n" "
\n" "

\n" " Agent:{\" \"}\n" " \n" " {alert.agent_id.slice(0, 8)}…\n" " \n" " {\" \"}\xb7{\" \"}\n" " Triggered: {formatRelative(alert.triggered_at)}\n" " {alert.acknowledged_at && (\n" " <>\n" " {\" \"}\xb7{\" \"}Ack’d:{\" \"}\n" " {formatRelative(alert.acknowledged_at)}\n" " \n" " )}\n" " {alert.resolved_at && (\n" " <>\n" " {\" \"}\xb7{\" \"}Resolved:{\" \"}\n" " {formatRelative(alert.resolved_at)}\n" " \n" " )}\n" "

\n" " {alert.last_error && (\n" "

\n" " Error: {alert.last_error}\n" "

\n" " )}\n" "
\n" "\n" "
\n" " {status === \"active\" && (\n" " acknowledgeMutation.mutate(alert.id)}\n" " >\n" " Acknowledge\n" " \n" " )}\n" " {status !== \"resolved\" && (\n" " resolveMutation.mutate(alert.id)}\n" " >\n" " Resolve\n" " \n" " )}\n" " deleteMutation.mutate(alert.id)}\n" " >\n" " Delete\n" " \n" "
\n" "
\n" "\n" " {alert.log_tail && (\n" "
\n" " \n" " setExpandedLogId(logExpanded ? null : alert.id)\n" " }\n" " >\n" " {logExpanded ? (\n" " \n" " ) : (\n" " \n" " )}\n" " Agent log tail\n" " \n" " {logExpanded && (\n" "
\n"
    "                        {alert.log_tail}\n"
    "                      
\n" " )}\n" "
\n" " )}\n" "
\n" " );\n" " })}\n" " \n" "
\n" "
\n" " );\n" "}\n" "\n" ) if insert_before in content: content = content.replace(insert_before, watchdog_section + insert_before, 1) print("OK: WatchdogAlertsSection inserted") else: print("ERROR: could not find 'export function Alerts() {'") sys.exit(1) # ─── Add at the bottom of the Alerts return div ─── # The closing of the Alerts function return div old_end = " \n );\n}\n\n// Re-export types" new_end = " \n \n );\n}\n\n// Re-export types" if old_end in content: content = content.replace(old_end, new_end, 1) print("OK: rendered in Alerts return") else: print("ERROR: closing div anchor not found") # Debug idx = content.find("// Re-export types") if idx >= 0: print("Context:", repr(content[max(0,idx-200):idx+30])) with open(path, "w", encoding="utf-8") as f: f.write(content) print(f"Done. Lines: {len(content.splitlines())}") "\n", 'type WatchdogStatus = "active" | "acknowledged" | "resolved";\n', "\n", "function watchdogStatus(alert: WatchdogAlert): WatchdogStatus {\n", ' if (alert.resolved_at) return "resolved";\n', ' if (alert.acknowledged_at) return "acknowledged";\n', ' return "active";\n', "}\n", "\n", "function WatchdogAlertsPanel({ agentId }: { agentId: string }) {\n", " const queryClient = useQueryClient();\n", " const { toast } = useToast();\n", ' const [statusFilter, setStatusFilter] = useState<"" | WatchdogStatus>("");\n', " const [expandedLogId, setExpandedLogId] = useState(null);\n", "\n", " const { data: alerts = [], isLoading } = useQuery({\n", ' queryKey: ["watchdog-alerts", agentId],\n', " queryFn: () => watchdogAlertsApi.listForAgent(agentId).then((r) => r.data),\n", " refetchInterval: 30000,\n", " });\n", "\n", " const filtered = alerts.filter(\n", " (a) => !statusFilter || watchdogStatus(a) === statusFilter\n", " );\n", "\n", " const acknowledgeMutation = useMutation({\n", " mutationFn: (id: string) => watchdogAlertsApi.acknowledge(id),\n", " onSuccess: () => {\n", ' queryClient.invalidateQueries({ queryKey: ["watchdog-alerts", agentId] });\n', ' toast({ type: "success", title: "Alert acknowledged" });\n', " },\n", " onError: (err: Error) =>\n", ' toast({ type: "error", title: "Could not acknowledge", message: err.message }),\n', " });\n", "\n", " const resolveMutation = useMutation({\n", " mutationFn: (id: string) => watchdogAlertsApi.resolve(id),\n", " onSuccess: () => {\n", ' queryClient.invalidateQueries({ queryKey: ["watchdog-alerts", agentId] });\n', ' toast({ type: "success", title: "Alert resolved" });\n', " },\n", " onError: (err: Error) =>\n", ' toast({ type: "error", title: "Could not resolve", message: err.message }),\n', " });\n", "\n", " const deleteMutation = useMutation({\n", " mutationFn: (id: string) => watchdogAlertsApi.delete(id),\n", " onSuccess: () => {\n", ' queryClient.invalidateQueries({ queryKey: ["watchdog-alerts", agentId] });\n', ' toast({ type: "success", title: "Alert deleted" });\n', " },\n", " onError: (err: Error) =>\n", ' toast({ type: "error", title: "Could not delete", message: err.message }),\n', " });\n", "\n", " const isMutating =\n", " acknowledgeMutation.isPending ||\n", " resolveMutation.isPending ||\n", " deleteMutation.isPending;\n", "\n", ' const activeCount = alerts.filter((a) => watchdogStatus(a) === "active").length;\n', "\n", " return (\n", " \n", " \n", ' \n', ' \n', " Watchdog Alerts\n", " {activeCount > 0 && (\n", ' \n', " {activeCount} active\n", " \n", " )}\n", " \n", " \n", ' \n', '
\n', " setStatusFilter(e.target.value as "" | WatchdogStatus)}\n', ' className="w-auto min-w-[10rem]"\n', ' aria-label="Filter by status"\n', " >\n", ' \n', ' \n', ' \n', ' \n', " \n", "
\n", "\n", " {isLoading && (\n", '

\n', " Loading...\n", "

\n", " )}\n", "\n", " {!isLoading && filtered.length === 0 && (\n", '

\n', " {statusFilter\n", ' ? "No alerts match the current filter."\n', ' : "No watchdog alerts recorded."}\n', "

\n", " )}\n", "\n", " {filtered.map((alert) => {\n", " const status = watchdogStatus(alert);\n", " const logExpanded = expandedLogId === alert.id;\n", " const statusColor =\n", ' status === "active"\n', ' ? "text-red-600 dark:text-red-400"\n', ' : status === "acknowledged"\n', ' ? "text-amber-600 dark:text-amber-400"\n', ' : "text-[hsl(var(--muted-foreground))]";\n', "\n", " return (\n", " \n", '
\n', '
\n', '
\n', " \n", " {status}\n", " \n", ' \n', " \xb7 {alert.restart_attempts} restart attempt\n", ' {alert.restart_attempts !== 1 ? "s" : ""}\n', " \n", "
\n", '

\n', " Triggered: {new Date(alert.triggered_at).toLocaleString()}\n", " {alert.acknowledged_at && (\n", " <>\n", ' {" "}\n', " \xb7 Acknowledged:{\" \"}\n", " {new Date(alert.acknowledged_at).toLocaleString()}\n", " \n", " )}\n", " {alert.resolved_at && (\n", " <>\n", ' {" "}\n', " \xb7 Resolved:{\" \"}\n", " {new Date(alert.resolved_at).toLocaleString()}\n", " \n", " )}\n", "

\n", " {alert.last_error && (\n", '

\n', ' Error: {alert.last_error}\n', "

\n", " )}\n", "
\n", "\n", '
\n', ' {status === "active" && (\n', " acknowledgeMutation.mutate(alert.id)}\n", " >\n", " Acknowledge\n", " \n", " )}\n", ' {status !== "resolved" && (\n', " resolveMutation.mutate(alert.id)}\n", " >\n", " Resolve\n", " \n", " )}\n", " deleteMutation.mutate(alert.id)}\n", " >\n", " Delete\n", " \n", "
\n", "
\n", "\n", " {alert.log_tail && (\n", "
\n", " \n", " setExpandedLogId(logExpanded ? null : alert.id)\n", " }\n", " >\n", " {logExpanded ? (\n", ' \n', " ) : (\n", ' \n', " )}\n", " Agent log tail\n", " \n", " {logExpanded && (\n", '
\n',
    "                      {alert.log_tail}\n",
    "                    
\n", " )}\n", "
\n", " )}\n", " \n", " );\n", " })}\n", "
\n", "
\n", " );\n", "}\n", "\n", ] file_lines[anchor_idx:anchor_idx] = C print(f"OK: inserted {len(C)} lines before AgentAlertsPanel anchor") content2 = "".join(file_lines) old_tab = ( ' \n' " \n" " " ) new_tab = ( ' \n' " \n" '
\n' " \n" "
\n" "
" ) if old_tab in content2: content2 = content2.replace(old_tab, new_tab, 1) print("OK: TabPanel updated") else: print("ERROR: TabPanel anchor not found") idx = content2.find('tabId="alerts"') if idx >= 0: print("Context:", repr(content2[idx:idx+200])) with open(path, "w", encoding="utf-8") as f: f.write(content2) print(f"Done. Lines: {len(content2.splitlines())}")