"use client"; import { MoreHorizontalIcon, PanelLeftCloseIcon, PanelLeftOpenIcon, PlusIcon, SearchIcon, Trash2Icon, UserCogIcon } from "lucide-react"; import { useCallback, useEffect, useState, type KeyboardEvent } from "react"; import { toast } from "sonner"; import { DashboardTableShell, DashboardTableStateRow, DashboardToolbar, } from "@/components/dashboard-page"; import { ListPagination } from "@/components/list-pagination"; import { Badge } from "@/components/ui/badge"; import { Button } from "@/components/ui/button"; import { ButtonGroup } from "@/components/ui/button-group"; import { OptionCombobox } from "@/components/option-combobox"; import { DropdownMenu, DropdownMenuContent, DropdownMenuItem, DropdownMenuTrigger, } from "@/components/ui/dropdown-menu"; import { Input } from "@/components/ui/input"; import { Table, TableBody, TableCell, TableHead, TableHeader, TableRow, } from "@/components/ui/table"; import { createAgentProfile, deleteAgentProfile, fetchAgentProfiles, updateAgentProfile, type AdminAgentProfile, type AdminAgentTeam, type CreateAdminAgentProfilePayload, type PageResult, } from "@/lib/api/admin"; import { useI18n } from "@/i18n/provider"; import { ServiceStatus } from "@/lib/generated/enums"; import { formatDateTime } from "@/lib/utils"; import { EditDialog } from "./_components/edit"; import { AgentTeamSidebar } from "./_components/team-sidebar"; type TFunction = (key: string, values?: Record) => string; function getServiceStatusOptions(t: TFunction) { return [ { value: "all", label: t("agentProfile.allStatuses") }, { value: String(ServiceStatus.Idle), label: t("agentProfile.statusIdle") }, { value: String(ServiceStatus.Busy), label: t("agentProfile.statusBusy") }, ]; } function getStatusLabel(value: number, t: TFunction) { return getServiceStatusOptions(t).find((item) => item.value === String(value))?.label ?? String(value); } export default function DashboardAgentsPage() { const t = useI18n(); const serviceStatusOptions = getServiceStatusOptions(t); const [selectedTeam, setSelectedTeam] = useState(null); const [teams, setTeams] = useState([]); const [sidebarCollapsed, setSidebarCollapsed] = useState(false); const [agentCodeInput, setAgentCodeInput] = useState(""); const [displayNameInput, setDisplayNameInput] = useState(""); const [statusFilterInput, setStatusFilterInput] = useState("all"); const [agentCode, setAgentCode] = useState(""); const [displayName, setDisplayName] = useState(""); const [statusFilter, setStatusFilter] = useState("all"); const [page, setPage] = useState(1); const [limit, setLimit] = useState(20); const [loading, setLoading] = useState(true); const [saving, setSaving] = useState(false); const [actionLoadingId, setActionLoadingId] = useState(null); const [dialogOpen, setDialogOpen] = useState(false); const [editingItem, setEditingItem] = useState( null, ); const [result, setResult] = useState>({ results: [], page: { page: 1, limit: 20, total: 0 }, }); const loadData = useCallback(async () => { setLoading(true); try { const data = await fetchAgentProfiles({ teamId: selectedTeam?.id, agentCode: agentCode.trim() || undefined, displayName: displayName.trim() || undefined, serviceStatus: statusFilter === "all" ? undefined : statusFilter, page, limit, }); setResult(data); } catch (error) { toast.error(error instanceof Error ? error.message : t("agentProfile.loadFailed")); } finally { setLoading(false); } }, [agentCode, displayName, limit, page, selectedTeam?.id, statusFilter, t]); useEffect(() => { void loadData(); }, [loadData]); useEffect(() => { setPage(1); }, [selectedTeam?.id]); useEffect(() => { if (teams.length === 0) { if (selectedTeam) { setSelectedTeam(null); } return; } if (!selectedTeam) { setSelectedTeam(teams[0]); return; } const matchedTeam = teams.find((item) => item.id === selectedTeam.id); if (!matchedTeam) { setSelectedTeam(teams[0]); return; } if (matchedTeam !== selectedTeam) { setSelectedTeam(matchedTeam); } }, [selectedTeam, teams]); function applyFilters() { setAgentCode(agentCodeInput); setDisplayName(displayNameInput); setStatusFilter(statusFilterInput); setPage(1); } function handleFilterKeyDown(event: KeyboardEvent) { if (event.key !== "Enter") { return; } event.preventDefault(); applyFilters(); } function handlePageChange(nextPage: number) { if (nextPage < 1 || nextPage === page) { return; } setPage(nextPage); } function openCreateDialog() { setEditingItem(null); setDialogOpen(true); } function openEditDialog(item: AdminAgentProfile) { setEditingItem(item); setDialogOpen(true); } function handleDialogOpenChange(open: boolean) { if (saving) { return; } if (!open) { setEditingItem(null); } setDialogOpen(open); } async function handleSubmit(payload: CreateAdminAgentProfilePayload) { if (saving) { return; } setSaving(true); try { if (editingItem) { await updateAgentProfile({ id: editingItem.id, ...payload }); toast.success(t("agentProfile.updated", { name: editingItem.displayName })); } else { await createAgentProfile(payload); toast.success(t("agentProfile.created", { name: payload.displayName })); } setDialogOpen(false); setEditingItem(null); await loadData(); } catch (error) { toast.error(error instanceof Error ? error.message : t("agentProfile.saveFailed")); } finally { setSaving(false); } } async function handleDelete(item: AdminAgentProfile) { setActionLoadingId(item.id); try { await deleteAgentProfile(item.id); toast.success(t("agentProfile.deleted", { name: item.displayName })); await loadData(); } catch (error) { toast.error(error instanceof Error ? error.message : t("agentProfile.deleteFailed")); } finally { setActionLoadingId(null); } } return ( <>
{t("agentProfile.new")} } >
setDisplayNameInput(event.target.value) } onKeyDown={handleFilterKeyDown} placeholder={t("agentProfile.filterDisplayName")} className="pl-9" />
setAgentCodeInput(event.target.value)} onKeyDown={handleFilterKeyDown} placeholder={t("agentProfile.filterAgentCode")} className="w-full sm:w-44" />
setStatusFilterInput(value ?? "all")} />
{ setLimit(nextLimit); setPage(1); }} /> } > {t("agentProfile.columnAgent")} {t("agentProfile.columnRules")} {t("agentProfile.columnDispatch")} {t("agentProfile.columnRecent")} {t("agentProfile.columnActions")} {result.results.map((item) => (
{item.avatar ? ( {item.displayName} ) : ( )}
{item.displayName}
{item.nickname || item.username || t("agentProfile.userFallback", { id: item.userId })}
{t("agentProfile.agentCode", { code: item.agentCode })}
{getStatusLabel(item.serviceStatus, t)}
{t("agentProfile.capacityPriority", { capacity: item.maxConcurrentCount, priority: item.priorityLevel, })}
{item.autoAssignEnabled ? t("agentProfile.autoAssign") : t("agentProfile.noAutoAssign")} {item.receiveOfflineMessage ? t("agentProfile.offlineReceive") : t("agentProfile.noOfflineReceive")}
{t("agentProfile.onlineAt", { time: formatDateTime(item.lastOnlineAt) })}
{t("agentProfile.statusAt", { time: formatDateTime(item.lastStatusAt) })}
} aria-label={t("agentProfile.moreActions", { name: item.displayName })} > void handleDelete(item)} className="text-destructive focus:text-destructive" > {actionLoadingId === item.id ? t("agentProfile.deleting") : t("agentProfile.delete")}
))} {loading || result.results.length === 0 ? ( ) : null}
); }