"use client"; import { Button } from "@app/components/ui/button"; import { ControlledDataTable, type ExtendedColumnDef } from "@app/components/ui/controlled-data-table"; import { useNavigationContext } from "@app/hooks/useNavigationContext"; import { toast } from "@app/hooks/useToast"; import { getNextSortOrder, getSortDirection } from "@app/lib/sortColumn"; import type { AdminOrgRow } from "@server/routers/org"; import { useEnvContext } from "@app/hooks/useEnvContext"; import { createApiClient, formatAxiosError } from "@app/lib/api"; import { type PaginationState } from "@tanstack/react-table"; import { ArrowDown01Icon, ArrowUp10Icon, ArrowUpRight, ChevronsUpDownIcon } from "lucide-react"; import moment from "moment"; import { useTranslations } from "next-intl"; import Link from "next/link"; import { useRouter } from "next/navigation"; import { useMemo, useState, useTransition } from "react"; import { useDebouncedCallback } from "use-debounce"; import ConfirmDeleteDialog from "./ConfirmDeleteDialog"; import CopyToClipboard from "./CopyToClipboard"; type OrgTableProps = { orgs: AdminOrgRow[]; pagination: PaginationState; rowCount: number; }; export default function OrgsTable({ orgs, pagination, rowCount }: OrgTableProps) { const router = useRouter(); const t = useTranslations(); const { navigate: filter, isNavigating: isFiltering, searchParams } = useNavigationContext(); const [isRefreshing, startTransition] = useTransition(); const [isDeleteModalOpen, setIsDeleteModalOpen] = useState(false); const [selectedOrg, setSelectedOrg] = useState(); const api = createApiClient(useEnvContext()); function refreshData() { startTransition(async () => { try { router.refresh(); } catch (error) { toast({ title: t("error"), description: t("refreshError"), variant: "destructive" }); } }); } function toggleSort(column: string) { const newSearch = getNextSortOrder(column, searchParams); filter({ searchParams: newSearch }); } function sortableHeader(column: string, label: string) { const sortOrder = getSortDirection(column, searchParams); const Icon = sortOrder === "asc" ? ArrowDown01Icon : sortOrder === "desc" ? ArrowUp10Icon : ChevronsUpDownIcon; return ( ); } const columns = useMemo[]>(() => { return [ { accessorKey: "name", friendlyName: t("name"), enableHiding: false, header: () => sortableHeader("name", t("name")) }, { accessorKey: "orgId", friendlyName: t("orgId"), header: () => {t("orgId")}, cell: ({ row }) => ( ) }, { accessorKey: "createdAt", friendlyName: t("createdAt"), header: () => sortableHeader("createdAt", t("createdAt")), cell: ({ row }) => { const createdAt = row.original.createdAt; return ( {createdAt ? moment(createdAt).format("lll") : "-"} ); } }, { accessorKey: "owner", friendlyName: t("accessRoleOwner"), header: () => ( {t("accessRoleOwner")} ), cell: ({ row }) => { const owner = row.original.owner; return owner ? ( ) : ( - ); } }, { accessorKey: "subnet", friendlyName: t("subnet"), header: () => {t("subnet")}, cell: ({ row }) => {row.original.subnet || "-"} }, { accessorKey: "utilitySubnet", friendlyName: t("utilitySubnet"), header: () => {t("utilitySubnet")}, cell: ({ row }) => ( {row.original.utilitySubnet || "-"} ) }, { accessorKey: "userCount", friendlyName: t("users"), header: () => {t("users")}, cell: ({ row }) => {row.original.userCount} }, { accessorKey: "siteCount", friendlyName: t("sites"), header: () => {t("sites")}, cell: ({ row }) => {row.original.siteCount} }, { accessorKey: "resourceCount", friendlyName: t("resources"), header: () => {t("resources")}, cell: ({ row }) => {row.original.resourceCount} }, { id: "actions", enableHiding: false, header: () => , cell: ({ row }) => { const orgRow = row.original; return (
); } } ]; }, [t, searchParams]); const handlePaginationChange = (newPage: PaginationState) => { searchParams.set("page", (newPage.pageIndex + 1).toString()); searchParams.set("pageSize", newPage.pageSize.toString()); filter({ searchParams }); }; const handleSearchChange = useDebouncedCallback((query: string) => { searchParams.set("query", query); searchParams.delete("page"); filter({ searchParams }); }, 300); async function deleteOrg(orgId: string) { try { const res = await api.delete(`/admin/org/${orgId}`); toast({ title: t("orgDeleted"), description: t("orgDeletedMessage") }); } catch (err) { console.error(err); toast({ variant: "destructive", title: t("orgErrorDelete"), description: formatAxiosError(err, t("orgErrorDeleteMessage")) }); } finally { router.refresh(); } } return ( <> {selectedOrg && ( { setIsDeleteModalOpen(val); setSelectedOrg(null); }} dialog={

{t("orgQuestionRemove")}

{t("orgMessageRemove")}

} buttonText={t("orgDeleteConfirm")} onConfirm={async () => { startTransition(() => deleteOrg(selectedOrg.orgId)); }} string={selectedOrg.name} title={t("orgDelete")} /> )} ); }