"use client"; import { Column, ColumnDef } from "@tanstack/react-table"; import { SitesDataTable } from "./SitesDataTable"; import { DropdownMenu, DropdownMenuContent, DropdownMenuItem, DropdownMenuTrigger } from "@app/components/ui/dropdown-menu"; import { Button } from "@app/components/ui/button"; import { ArrowRight, ArrowUpDown, Check, MoreHorizontal, X } from "lucide-react"; import Link from "next/link"; import { useRouter } from "next/navigation"; import { AxiosResponse } from "axios"; import { useState, useEffect } from "react"; import ConfirmDeleteDialog from "@app/components/ConfirmDeleteDialog"; import { toast } from "@app/hooks/useToast"; import { formatAxiosError } from "@app/lib/api"; import { createApiClient } from "@app/lib/api"; import { useEnvContext } from "@app/hooks/useEnvContext"; import { useTranslations } from "next-intl"; import { parseDataSize } from "@app/lib/dataSize"; import { Badge } from "@app/components/ui/badge"; import { InfoPopup } from "@app/components/ui/info-popup"; export type SiteRow = { id: number; nice: string; name: string; mbIn: string; mbOut: string; orgId: string; type: "newt" | "wireguard"; newtVersion?: string; newtUpdateAvailable?: boolean; online: boolean; address?: string; }; type SitesTableProps = { sites: SiteRow[]; orgId: string; }; export default function SitesTable({ sites, orgId }: SitesTableProps) { const router = useRouter(); const [isDeleteModalOpen, setIsDeleteModalOpen] = useState(false); const [selectedSite, setSelectedSite] = useState(null); const [rows, setRows] = useState(sites); const [isRefreshing, setIsRefreshing] = useState(false); const api = createApiClient(useEnvContext()); const t = useTranslations(); const { env } = useEnvContext(); // Update local state when props change (e.g., after refresh) useEffect(() => { setRows(sites); }, [sites]); const refreshData = async () => { console.log("Data refreshed"); setIsRefreshing(true); try { await new Promise((resolve) => setTimeout(resolve, 200)); router.refresh(); } catch (error) { toast({ title: t("error"), description: t("refreshError"), variant: "destructive" }); } finally { setIsRefreshing(false); } }; const deleteSite = (siteId: number) => { api.delete(`/site/${siteId}`) .catch((e) => { console.error(t("siteErrorDelete"), e); toast({ variant: "destructive", title: t("siteErrorDelete"), description: formatAxiosError(e, t("siteErrorDelete")) }); }) .then(() => { router.refresh(); setIsDeleteModalOpen(false); const newRows = rows.filter((row) => row.id !== siteId); setRows(newRows); }); }; const columns: ColumnDef[] = [ { accessorKey: "name", header: ({ column }) => { return ( ); } }, { accessorKey: "online", header: ({ column }) => { return ( ); }, cell: ({ row }) => { const originalRow = row.original; if ( originalRow.type == "newt" || originalRow.type == "wireguard" ) { if (originalRow.online) { return (
{t("online")}
); } else { return (
{t("offline")}
); } } else { return -; } } }, { accessorKey: "nice", header: ({ column }) => { return ( ); }, cell: ({ row }) => { return (
{row.original.nice}
); } }, { accessorKey: "mbIn", header: ({ column }) => { return ( ); }, sortingFn: (rowA, rowB) => parseDataSize(rowA.original.mbIn) - parseDataSize(rowB.original.mbIn) }, { accessorKey: "mbOut", header: ({ column }) => { return ( ); }, sortingFn: (rowA, rowB) => parseDataSize(rowA.original.mbOut) - parseDataSize(rowB.original.mbOut) }, { accessorKey: "type", header: ({ column }) => { return ( ); }, cell: ({ row }) => { const originalRow = row.original; if (originalRow.type === "newt") { return (
Newt {originalRow.newtVersion && ( v{originalRow.newtVersion} )}
{originalRow.newtUpdateAvailable && ( )}
); } if (originalRow.type === "wireguard") { return (
WireGuard
); } if (originalRow.type === "local") { return (
{t("local")}
); } } }, ...(env.flags.enableClients ? [{ accessorKey: "address", header: ({ column }: { column: Column }) => { return ( ); } }] : []), { id: "actions", cell: ({ row }) => { const siteRow = row.original; return (
{t("viewSettings")} { setSelectedSite(siteRow); setIsDeleteModalOpen(true); }} > {t("delete")}
); } } ]; return ( <> {selectedSite && ( { setIsDeleteModalOpen(val); setSelectedSite(null); }} dialog={

{t("siteQuestionRemove", { selectedSite: selectedSite?.name || selectedSite?.id })}

{t("siteMessageRemove")}

{t("siteMessageConfirm")}

} buttonText={t("siteConfirmDelete")} onConfirm={async () => deleteSite(selectedSite!.id)} string={selectedSite.name} title={t("siteDelete")} /> )} router.push(`/${orgId}/settings/sites/create`) } onRefresh={refreshData} isRefreshing={isRefreshing} /> ); }