"use client"; import { Badge } from "@workspace/ui/components/badge"; import { Button } from "@workspace/ui/components/button"; import { Switch } from "@workspace/ui/components/switch"; import { ConfirmButton } from "@workspace/ui/composed/confirm-button"; import { ProTable, type ProTableActions, } from "@workspace/ui/composed/pro-table/pro-table"; import { createNode, deleteNode, filterNodeList, resetSortWithNode, toggleNodeStatus, updateNode, } from "@workspace/ui/services/admin/server"; import { useRef, useState } from "react"; import { useTranslation } from "react-i18next"; import { toast } from "sonner"; import { useNode } from "@/stores/node"; import { useServer } from "@/stores/server"; import NodeForm from "./node-form"; export default function Nodes() { const { t } = useTranslation("nodes"); const ref = useRef(null); const [loading, setLoading] = useState(false); // Use our zustand store for server data const { getServerName, getServerAddress, getProtocolPort } = useServer(); const { fetchNodes, fetchTags } = useNode(); return ( action={ref} actions={{ render: (row) => [ { setLoading(true); try { const body: API.UpdateNodeRequest = { ...row, ...values, } as any; await updateNode(body); toast.success(t("updated", "Updated")); ref.current?.refresh(); fetchNodes(); fetchTags(); setLoading(false); return true; } catch { setLoading(false); return false; } }} title={t("drawerEditTitle", "Edit Node")} trigger={t("edit", "Edit")} />, { await deleteNode({ id: row.id } as any); toast.success(t("deleted", "Deleted")); ref.current?.refresh(); fetchNodes(); fetchTags(); }} title={t("confirmDeleteTitle", "Delete this node?")} trigger={ } />, , ], batchRender(rows) { return [ { await Promise.all( rows.map((r) => deleteNode({ id: r.id } as any)) ); toast.success(t("deleted", "Deleted")); ref.current?.refresh(); fetchNodes(); fetchTags(); }} title={t("confirmDeleteTitle", "Delete this node?")} trigger={ } />, ]; }, }} columns={[ { id: "enabled", header: t("enabled", "Enabled"), cell: ({ row }) => ( { await toggleNodeStatus({ id: row.original.id, enable: v }); toast.success( v ? t("enabled_on", "Enabled") : t("enabled_off", "Disabled") ); ref.current?.refresh(); fetchNodes(); fetchTags(); }} /> ), }, { accessorKey: "name", header: t("name", "Name") }, { id: "address_port", header: `${t("address", "Address")}:${t("port", "Port")}`, cell: ({ row }) => `${row.original.address || "—"}:${row.original.port || "—"}`, }, { id: "server_id", header: t("server", "Server"), cell: ({ row }) => `${getServerName(row.original.server_id)}:${getServerAddress(row.original.server_id)}`, }, { id: "protocol", header: ` ${t("protocol", "Protocol")}:${t("port", "Port")}`, cell: ({ row }) => `${row.original.protocol}:${getProtocolPort(row.original.server_id, row.original.protocol)}`, }, { accessorKey: "tags", header: t("tags", "Tags"), cell: ({ row }) => (
{(row.original.tags || []).length === 0 ? "—" : row.original.tags.map((tg) => ( {tg} ))}
), }, ]} header={{ title: t("pageTitle", "Nodes"), toolbar: ( { setLoading(true); try { const body: API.CreateNodeRequest = { name: values.name, server_id: Number(values.server_id!), protocol: values.protocol, address: values.address, port: Number(values.port!), tags: values.tags || [], enabled: false, }; await createNode(body); toast.success(t("created", "Created")); ref.current?.refresh(); fetchNodes(); fetchTags(); setLoading(false); return true; } catch { setLoading(false); return false; } }} title={t("drawerCreateTitle", "Create Node")} trigger={t("create", "Create")} /> ), }} onSort={async (source, target, items) => { const sourceIndex = items.findIndex( (item) => String(item.id) === source ); const targetIndex = items.findIndex( (item) => String(item.id) === target ); const originalSorts = items.map((item) => item.sort); const [movedItem] = items.splice(sourceIndex, 1); items.splice(targetIndex, 0, movedItem!); const updatedItems = items.map((item, index) => { const originalSort = originalSorts[index]; const newSort = originalSort !== undefined ? originalSort : item.sort; return { ...item, sort: newSort }; }); const changedItems = updatedItems.filter( (item, index) => item.sort !== items[index]?.sort ); if (changedItems.length > 0) { resetSortWithNode({ sort: changedItems.map((item) => ({ id: item.id, sort: item.sort, })) as API.SortItem[], }); toast.success(t("sorted_success", "Sorted successfully")); } return updatedItems; }} params={[{ key: "search" }]} request={async (pagination, filter) => { const { data } = await filterNodeList({ page: pagination.page, size: pagination.size, search: filter?.search || undefined, }); const list = (data?.data?.list || []) as API.Node[]; const total = Number(data?.data?.total || list.length); return { list, total }; }} /> ); }