import { useQuery } from "@tanstack/react-query"; import { Link, useSearch } from "@tanstack/react-router"; import { Badge } from "@workspace/ui/components/badge"; import { Button } from "@workspace/ui/components/button"; import { Dialog, DialogContent, DialogHeader, DialogTitle, DialogTrigger, } from "@workspace/ui/components/dialog"; import { DropdownMenu, DropdownMenuContent, DropdownMenuItem, DropdownMenuTrigger, } from "@workspace/ui/components/dropdown-menu"; import { Input } from "@workspace/ui/components/input"; import { ScrollArea } from "@workspace/ui/components/scroll-area"; import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue, } from "@workspace/ui/components/select"; import { Sheet, SheetContent, SheetHeader, SheetTitle, SheetTrigger, } from "@workspace/ui/components/sheet"; import { Switch } from "@workspace/ui/components/switch"; import { Tabs, TabsContent, TabsList, TabsTrigger, } from "@workspace/ui/components/tabs"; import { Combobox } from "@workspace/ui/composed/combobox"; import { ConfirmButton } from "@workspace/ui/composed/confirm-button"; import { ProTable, type ProTableActions, } from "@workspace/ui/composed/pro-table/pro-table"; import { // getUserGroupList, previewUserNodes, } from "@workspace/ui/services/admin/group"; import { createUser, deleteUser, getUserDetail, getUserList, updateUserBasicInfo, } from "@workspace/ui/services/admin/user"; import { useCallback, useRef, useState } from "react"; import { useTranslation } from "react-i18next"; import { toast } from "sonner"; import { Display } from "@/components/display"; import { useSubscribe } from "@/stores/subscribe"; import { formatDate } from "@/utils/common"; import FamilyManagement from "./family"; import { UserDetail } from "./user-detail"; import UserForm from "./user-form"; import { UserInviteStatsSheet } from "./user-invite-stats-sheet"; import { AuthMethodsForm } from "./user-profile/auth-methods-form"; import { BasicInfoForm } from "./user-profile/basic-info-form"; import { NotifySettingsForm } from "./user-profile/notify-settings-form"; import UserSubscription from "./user-subscription"; // import EditUserGroupDialog from "./edit-user-group-dialog"; export default function User() { const { t } = useTranslation("user"); const [loading, setLoading] = useState(false); const ref = useRef(null); const sp = useSearch({ strict: false }) as Record; const { subscribes } = useSubscribe(); const searchRef = useRef({ type: sp.user_id ? "user_id" : "email", value: sp.search || sp.user_id || "", }); const handleSearch = useCallback((type: string, value: string) => { searchRef.current = { type, value }; ref.current?.refresh(); }, []); // const { data: userGroupsData } = useQuery({ // queryKey: ["userGroups"], // queryFn: async () => { // const { data } = await getUserGroupList({ page: 1, size: 1000 }); // return data.data?.list || []; // }, // }); const initialFilters = { search: sp.search || undefined, user_id: sp.user_id || undefined, subscribe_id: sp.subscribe_id || undefined, user_subscribe_id: sp.user_subscribe_id || undefined, short_code: sp.short_code || undefined, // user_group_id: sp.user_group_id || undefined, }; return ( action={ref} actions={{ render: (row) => [ ref.current?.refresh()} userId={row.id} />, , ref.current?.refresh()} userId={row.id} />, , { await deleteUser({ id: row.id }); toast.success(t("deleteSuccess", "Deleted successfully")); ref.current?.refresh(); }} title={t("confirmDelete", "Confirm Delete")} trigger={ } />, {t("orderList", "Order List")} {t("loginLogs", "Login Logs")} {t("balanceLogs", "Balance Logs")} {t("commissionLogs", "Commission Logs")} {t("giftLogs", "Gift Logs")} , ], }} columns={[ { id: "enable", accessorKey: "enable", header: t("enable", "Enable"), cell: ({ row }) => ( { const { auth_methods: _auth_methods, user_devices: _user_devices, enable_balance_notify: _enable_balance_notify, enable_login_notify: _enable_login_notify, enable_subscribe_notify: _enable_subscribe_notify, enable_trade_notify: _enable_trade_notify, updated_at: _updated_at, created_at: _created_at, id, ...rest } = row.original; await updateUserBasicInfo({ user_id: id, ...rest, enable: checked, } as unknown as API.UpdateUserBasiceInfoRequest); toast.success(t("updateSuccess", "Updated successfully")); ref.current?.refresh(); }} /> ), }, { id: "id", accessorKey: "id", header: "ID", }, { id: "deleted_at", accessorKey: "deleted_at", header: t("isDeleted", "Deleted"), cell: ({ row }) => { const deletedAt = row.getValue("deleted_at") as number | undefined; return deletedAt ? ( {t("deleted", "Deleted")} ) : ( {t("normal", "Normal")} ); }, }, { id: "auth_methods", accessorKey: "auth_methods", header: t("userName", "Username"), cell: ({ row }) => { const method = row.original.auth_methods?.[0]; const identifier = method?.auth_identifier || ""; const isDevice = method?.auth_type === "device"; const deviceNo = (row.original.user_devices?.[0] as any)?.device_no; const display = isDevice ? deviceNo || identifier : identifier; return (
{method?.auth_type} {display}
); }, }, { id: "balance", accessorKey: "balance", header: t("balance", "Balance"), cell: ({ row }) => ( ), }, { id: "gift_amount", accessorKey: "gift_amount", header: t("giftAmount", "Gift Amount"), cell: ({ row }) => ( ), }, { id: "commission", accessorKey: "commission", header: t("commission", "Commission"), cell: ({ row }) => ( ), }, { id: "refer_code", accessorKey: "refer_code", header: t("inviteCode", "Invite Code"), cell: ({ row }) => row.getValue("refer_code") || "--", }, { id: "referer_id", accessorKey: "referer_id", header: t("referer", "Referer"), cell: ({ row }) => , }, { id: "created_at", accessorKey: "created_at", header: t("createdAt", "Created At"), cell: ({ row }) => formatDate(row.getValue("created_at")), }, ]} header={{ title: ( ), toolbar: ( key="create" loading={loading} onSubmit={async (values) => { setLoading(true); try { await createUser(values); toast.success(t("createSuccess", "Created successfully")); ref.current?.refresh(); setLoading(false); return true; } catch { setLoading(false); return false; } }} title={t("createUser", "Create User")} trigger={t("create", "Create")} /> ), }} initialFilters={initialFilters} key={initialFilters.user_id} params={[ { key: "subscribe_id", placeholder: t("subscription", "Subscription"), options: [ { label: t("all", "All"), value: "" }, ...(subscribes?.map((item) => ({ label: item.name!, value: String(item.id!), })) || []), ], }, { key: "search", placeholder: "Search", }, { key: "user_id", placeholder: t("userId", "User ID"), }, { key: "user_subscribe_id", placeholder: t("subscriptionId", "Subscription ID"), }, { key: "short_code", placeholder: t("shortCode", "Short Code"), }, ]} request={async (pagination, filter) => { const { data } = await getUserList({ ...pagination, ...filter, }); return { list: data.data?.list || [], total: data.data?.total || 0, }; }} /> ); } function ProfileSheet({ userId, onUpdated, }: { userId: number; onUpdated?: () => void; }) { const { t } = useTranslation("user"); const [open, setOpen] = useState(false); const { data: user, refetch } = useQuery({ enabled: open, queryKey: ["user", userId], queryFn: async () => { const { data } = await getUserDetail({ id: userId }); return data.data as API.User; }, }); const refetchAll = async () => { await refetch(); onUpdated?.(); return Promise.resolve(); }; return ( {t("userProfile", "User Profile")} · ID: {userId} {user && ( {t("basicInfoTitle", "Basic Info")} {t("notifySettingsTitle", "Notify Settings")} {t("authMethodsTitle", "Auth Methods")} )} ); } function SubscriptionSheet({ userId }: { userId: number }) { const { t } = useTranslation("user"); const [open, setOpen] = useState(false); return ( {t("subscriptionList", "Subscription List")} · ID: {userId}
); } function InviteStatsMenuItem({ userId }: { userId: number }) { const { t } = useTranslation("user"); const [open, setOpen] = useState(false); return ( <> { e.preventDefault(); setOpen(true); }} > {t("inviteStats", "Invite Statistics")} ); } function DeviceGroupSheet({ userId, onChanged, }: { userId: number; onChanged?: () => void; }) { const { t } = useTranslation("user"); const [open, setOpen] = useState(false); return ( {t("deviceGroup", "Device Group")} · ID: {userId}
); } function UserSearchBar({ initialType, initialValue, onSearch, subscribes, }: { initialType: string; initialValue: string; onSearch: (type: string, value: string) => void; subscribes?: API.SubscribeItem[]; }) { const { t } = useTranslation("user"); const [searchType, setSearchType] = useState(initialType); const [searchValue, setSearchValue] = useState(initialValue); return (
{searchType === "subscribe_id" ? ( { setSearchValue(value); onSearch("subscribe_id", value); }} options={subscribes?.map((item) => ({ label: item.name!, value: String(item.id!), }))} placeholder={t("subscription", "Subscription")} value={searchValue} /> ) : ( <> setSearchValue(e.target.value)} onKeyDown={(e) => e.key === "Enter" && onSearch(searchType, searchValue) } placeholder={t("searchInputPlaceholder", "Enter search term")} value={searchValue} /> {searchValue && ( )} )}
); } function PreviewNodesDialog({ userId }: { userId: number }) { const { t } = useTranslation("user"); const [open, setOpen] = useState(false); const { data: previewData, isLoading } = useQuery({ enabled: open, queryKey: ["previewUserNodes", userId], queryFn: async () => { const { data } = await previewUserNodes({ user_id: userId }); return data.data; }, }); return ( {t("previewNodes", "Preview Nodes")} · ID: {userId} {isLoading ? (
{t("loading", "Loading...")}
) : previewData ? (
{t("availableNodes", "Available Nodes")}: {" "} {previewData.node_groups?.reduce( (sum, group) => sum + (group.nodes?.length || 0), 0 ) || 0}
{previewData.node_groups && previewData.node_groups.length > 0 ? (
{previewData.node_groups.map((group) => (

{group.name || (group.id === -1 ? t("subscriptionNodes", "Subscription Nodes") : group.id === 0 ? t("publicNodes", "Public Nodes") : `${t("nodeGroup", "Node Group")} ${group.id}`)}

{group.nodes && group.nodes.length > 0 ? ( {group.nodes.map((node) => ( ))}
ID {t("name", "Name")} {t("address", "Address")}
{node.id} {node.name} {node.address}:{node.port}
) : null}
))}
) : (
{t("noNodesAvailable", "No nodes available")}
)}
) : null}
); }