"use client"; import { BotMessageSquareIcon, GitBranchIcon, PowerIcon } from "lucide-react"; import { useRouter } from "next/navigation"; import { useMemo } from "react"; import { DashboardCrudPage, createDashboardStatusColumn, createDashboardStatusToggleAction, type DashboardCrudColumn, type DashboardCrudFilter, } from "@/components/dashboard/crud"; import { Badge } from "@/components/ui/badge"; import { createAIAgent, deleteAIAgent, fetchAIAgents, updateAIAgent, updateAIAgentSort, updateAIAgentStatus, type AIAgent, type CreateAIAgentPayload, } from "@/lib/api/admin"; import { IMConversationServiceMode, Status } from "@/lib/generated/enums"; import { useI18n } from "@/i18n/provider"; import { EditDialog } from "./_components/edit"; type TFunction = (key: string, values?: Record) => string; function getStatusOptions(t: TFunction) { return [ { value: "all", label: t("aiAgent.allStatuses") }, { value: String(Status.Ok), label: t("aiAgent.enabled") }, { value: String(Status.Disabled), label: t("aiAgent.disabled") }, { value: String(Status.Deleted), label: t("status.deleted") }, ]; } function getStatusLabel(value: string, t: TFunction) { return ( getStatusOptions(t).find((item) => item.value === value)?.label ?? t("aiAgent.allStatuses") ); } function getServiceModeLabel(mode: number, t: TFunction) { switch (mode) { case IMConversationServiceMode.AIOnly: return t("aiAgent.serviceAiOnly"); case IMConversationServiceMode.HumanOnly: return t("aiAgent.serviceHumanOnly"); case IMConversationServiceMode.AIFirst: return t("aiAgent.serviceAiFirst"); default: return "-"; } } function getNextStatus(item: AIAgent) { return item.status === Status.Ok ? Status.Disabled : Status.Ok; } export default function DashboardAIAgentsPage() { const t = useI18n(); const router = useRouter(); const statusOptions = useMemo(() => getStatusOptions(t), [t]); const filters = useMemo( () => [ { name: "name", label: t("aiAgent.filterName"), placeholder: t("aiAgent.filterName"), defaultValue: "", trim: true, className: "w-full sm:w-56", }, { name: "status", label: t("aiAgent.allStatuses"), type: "select", defaultValue: "all", allValue: "all", options: statusOptions, className: "w-full sm:w-52", }, ], [statusOptions, t], ); const columns = useMemo[]>( () => [ { key: "agent", label: "Agent", render: (item) => (
{item.name}
), }, { key: "aiConfig", label: t("aiAgent.columnAiConfig"), render: (item) => item.aiConfigName || "-", }, { key: "serviceMode", label: t("aiAgent.columnServiceMode"), render: (item) => getServiceModeLabel(item.serviceMode, t), }, { key: "knowledge", label: t("aiAgent.columnKnowledge"), render: (item) => { const knowledgeIds = item.knowledgeIds ?? []; const knowledgeBaseNames = item.knowledgeBaseNames ?? []; return (
{knowledgeIds.length === 0 ? ( {t("aiAgent.notConfigured")} ) : ( knowledgeBaseNames.map((name, index) => ( {name} )) )}
); }, }, { key: "skills", label: t("aiAgent.columnSkills"), render: (item) => { const skills = item.skills ?? []; return (
{skills.length === 0 ? ( {t("aiAgent.ragOnly")} ) : ( skills.map((skill) => ( {skill.name} )) )}
); }, }, { key: "capabilities", label: t("aiAgent.columnCapabilities"), render: (item) => { const skills = item.skills ?? []; const directTools = item.directTools ?? []; const directToolServerCodes = Array.from( new Set(directTools.map((tool) => tool.serverCode).filter(Boolean)), ); return (
{skills.length} Skills {directTools.length} Tools
{directToolServerCodes.length === 0 ? ( {t("aiAgent.noMcpServer")} ) : ( directToolServerCodes.map((serverCode) => ( {serverCode} )) )}
); }, }, createDashboardStatusColumn({ label: t("aiAgent.columnStatus"), getStatus: (item) => item.status, getLabel: (status) => getStatusLabel(String(status), t), getBadgeVariant: (status) => status === Status.Ok ? "default" : "secondary", isEnabled: (status) => status === Status.Ok, toggle: { getNextStatus, updateStatus: (item, nextStatus) => updateAIAgentStatus(item.id, nextStatus), successMessage: (item, nextStatus) => t("aiAgent.statusChanged", { name: item.name, status: nextStatus === Status.Ok ? t("aiAgent.enabled") : t("aiAgent.stop"), }), errorMessage: t("aiAgent.statusUpdateFailed"), ariaLabel: (item) => t("aiAgent.toggleStatus", { name: item.name }), }, }), ], [t], ); return ( filters={filters} columns={columns} fetchList={(query) => fetchAIAgents({ name: typeof query.name === "string" ? query.name : undefined, status: typeof query.status === "string" ? query.status : undefined, page: Number(query.page), limit: Number(query.limit), }) } getItemId={(item) => item.id} createItem={createAIAgent} updateItem={(item, payload) => updateAIAgent({ id: item.id, ...payload })} onEditItem={(item) => router.push(`/dashboard/ai-agents/config?agentId=${item.id}`)} deleteItem={(item) => deleteAIAgent(item.id)} rowActions={[ { key: "workflow", icon: , label: t("aiAgent.configure"), run: ({ item }) => { router.push(`/dashboard/ai-agents/config?agentId=${item.id}`); }, }, createDashboardStatusToggleAction({ icon: , label: (item) => item.status === Status.Ok ? t("aiAgent.stop") : t("aiAgent.enabled"), getNextStatus, updateStatus: (item, nextStatus) => updateAIAgentStatus(item.id, nextStatus), successMessage: (item, nextStatus) => t("aiAgent.statusChanged", { name: item.name, status: nextStatus === Status.Ok ? t("aiAgent.enabled") : t("aiAgent.stop"), }), errorMessage: t("aiAgent.statusUpdateFailed"), }), ]} sort={{ enabled: true, onReorder: (items) => updateAIAgentSort(items.map((item) => item.id)), successMessage: t("aiAgent.sortUpdated"), errorMessage: t("aiAgent.sortUpdateFailed"), handleLabel: t("aiAgent.dragSort", { name: "" }), }} renderEditDialog={({ open, saving, itemId, onOpenChange, onSubmit }) => ( )} labels={{ refresh: t("aiAgent.refresh"), create: t("aiAgent.new"), query: t("aiAgent.query"), loading: t("aiAgent.loadingRows"), empty: t("aiAgent.emptyRows"), actions: t("aiAgent.columnActions"), edit: t("aiAgent.configure"), delete: t("aiAgent.delete"), processing: t("aiAgent.processing"), moreActions: (item) => t("aiAgent.moreActions", { name: item.name }), loadFailed: t("aiAgent.loadFailed"), saveFailed: t("aiAgent.saveFailed"), deleteFailed: t("aiAgent.deleteFailed"), created: (payload) => t("aiAgent.created", { name: payload.name }), updated: (_item, payload) => t("aiAgent.updated", { name: payload.name }), deleted: (item) => t("aiAgent.deleted", { name: item.name }), }} /> ); }