"use client"; import UptimeMiniBar from "@app/components/UptimeMiniBar"; import ConfirmDeleteDialog from "@app/components/ConfirmDeleteDialog"; import HealthCheckCredenza, { HealthCheckRow } from "@app/components/HealthCheckCredenza"; import { Badge } from "@app/components/ui/badge"; import { Button } from "@app/components/ui/button"; import { DataTable, ExtendedColumnDef } from "@app/components/ui/data-table"; import { DropdownMenu, DropdownMenuContent, DropdownMenuItem, DropdownMenuTrigger } from "@app/components/ui/dropdown-menu"; import { Switch } from "@app/components/ui/switch"; import { toast } from "@app/hooks/useToast"; import { useEnvContext } from "@app/hooks/useEnvContext"; import { createApiClient, formatAxiosError } from "@app/lib/api"; import { orgQueries } from "@app/lib/queries"; import { useQuery, useQueryClient } from "@tanstack/react-query"; import { ArrowUpDown, ArrowUpRight, MoreHorizontal } from "lucide-react"; import { useTranslations } from "next-intl"; import { useState } from "react"; import Link from "next/link"; import { PaidFeaturesAlert } from "@app/components/PaidFeaturesAlert"; import { usePaidStatus } from "@app/hooks/usePaidStatus"; import { tierMatrix } from "@server/lib/billing/tierMatrix"; type StandaloneHealthChecksTableProps = { orgId: string; }; function formatTarget(row: HealthCheckRow): string { if (!row.hcHostname) return "-"; if (row.hcMode === "tcp") { if (!row.hcPort) return row.hcHostname; return `${row.hcHostname}:${row.hcPort}`; } // HTTP / default const scheme = row.hcScheme ?? "http"; const host = row.hcHostname; const port = row.hcPort ? `:${row.hcPort}` : ""; const path = row.hcPath ?? "/"; return `${scheme}://${host}${port}${path}`; } const healthLabel: Record = { healthy: "Healthy", unhealthy: "Unhealthy", unknown: "Unknown" }; const healthVariant: Record< HealthCheckRow["hcHealth"], "green" | "red" | "secondary" > = { healthy: "green", unhealthy: "red", unknown: "secondary" }; export default function HealthChecksTable({ orgId }: StandaloneHealthChecksTableProps) { const t = useTranslations(); const api = createApiClient(useEnvContext()); const queryClient = useQueryClient(); const { isPaidUser } = usePaidStatus(); const isPaid = isPaidUser(tierMatrix.standaloneHealthChecks); const [credenzaOpen, setCredenzaOpen] = useState(false); const [deleteOpen, setDeleteOpen] = useState(false); const [selected, setSelected] = useState(null); const [togglingId, setTogglingId] = useState(null); const { data: rows = [], isLoading, refetch, isRefetching } = useQuery({ ...orgQueries.standaloneHealthChecks({ orgId }), refetchInterval: 10_000 }); const invalidate = () => queryClient.invalidateQueries( orgQueries.standaloneHealthChecks({ orgId }) ); const handleToggleEnabled = async ( row: HealthCheckRow, enabled: boolean ) => { setTogglingId(row.targetHealthCheckId); try { await api.post( `/org/${orgId}/health-check/${row.targetHealthCheckId}`, { hcEnabled: enabled } ); await invalidate(); } catch (e) { toast({ title: t("error"), description: formatAxiosError(e), variant: "destructive" }); } finally { setTogglingId(null); } }; const handleDelete = async () => { if (!selected) return; try { await api.delete( `/org/${orgId}/health-check/${selected.targetHealthCheckId}` ); await invalidate(); toast({ title: t("standaloneHcDeleted") }); } catch (e) { toast({ title: t("error"), description: formatAxiosError(e), variant: "destructive" }); } finally { setDeleteOpen(false); setSelected(null); } }; const columns: ExtendedColumnDef[] = [ { accessorKey: "name", enableHiding: false, friendlyName: t("name"), header: ({ column }) => ( ), cell: ({ row }) => ( {row.original.name ? row.original.name : "-"} ) }, { id: "mode", friendlyName: t("standaloneHcColumnMode"), header: () => ( {t("standaloneHcColumnMode")} ), cell: ({ row }) => ( {row.original.hcMode?.toUpperCase() ?? "-"} ) }, { id: "target", friendlyName: t("standaloneHcColumnTarget"), header: () => ( {t("standaloneHcColumnTarget")} ), cell: ({ row }) => {formatTarget(row.original)} }, { id: "resource", friendlyName: "Resource", header: () => ( Resource ), cell: ({ row }) => { const r = row.original; if (!r.resourceId || !r.resourceName || !r.resourceNiceId) { return -; } return ( ); } }, { id: "health", friendlyName: t("standaloneHcColumnHealth"), header: () => ( {t("standaloneHcColumnHealth")} ), cell: ({ row }) => { const health = row.original.hcHealth; if (health === "healthy") { return (
{healthLabel.healthy}
); } else if (health === "unhealthy") { return (
{healthLabel.unhealthy}
); } else { return (
{healthLabel.unknown}
); } } }, { id: "uptime", friendlyName: "Uptime", header: () => {t("uptime30d")}, cell: ({ row }) => { return ( ); } }, { accessorKey: "hcEnabled", friendlyName: t("alertingColumnEnabled"), header: () => ( {t("alertingColumnEnabled")} ), cell: ({ row }) => { const r = row.original; return ( handleToggleEnabled(r, v)} /> ); } }, { id: "rowActions", enableHiding: false, header: () => , cell: ({ row }) => { const r = row.original; return (
{ setSelected(r); setDeleteOpen(true); }} > {t("delete")}
); } } ]; return ( <> {selected && deleteOpen && ( { setDeleteOpen(val); if (!val) setSelected(null); }} dialog={

{t("standaloneHcDeleteQuestion")}

} buttonText={t("delete")} onConfirm={handleDelete} string={selected.name} title={t("standaloneHcDeleteTitle")} /> )} { setCredenzaOpen(val); if (!val) setSelected(null); }} orgId={orgId} initialValues={selected} onSaved={invalidate} /> { setSelected(null); setCredenzaOpen(true); }} addButtonDisabled={!isPaid} onRefresh={() => refetch()} isRefreshing={isRefetching || isLoading} addButtonText={t("standaloneHcAddButton")} enableColumnVisibility stickyLeftColumn="name" stickyRightColumn="rowActions" /> ); }