import { useState, useEffect, useRef, useCallback } from 'react'; import { useNavigate } from 'react-router-dom'; import { Tabs, TabsList, TabsTrigger, TabsContent } from '@/components/ui/tabs'; import { Button } from '@/components/ui/button'; import { Switch } from '@/components/ui/switch'; import { Label } from '@/components/ui/label'; import { Dialog, DialogContent, DialogHeader, DialogTitle, DialogDescription, DialogFooter, } from '@/components/ui/dialog'; import BotForm, { BotFormHandle, } from '@/app/home/bots/components/bot-form/BotForm'; import { BotLogListComponent } from '@/app/home/bots/components/bot-log/view/BotLogListComponent'; import BotSessionMonitor from '@/app/home/bots/components/bot-session/BotSessionMonitor'; import type { BotSessionMonitorHandle } from '@/app/home/bots/components/bot-session/BotSessionMonitor'; import { httpClient } from '@/app/infra/http/HttpClient'; import { useSidebarData } from '@/app/home/components/home-sidebar/SidebarDataContext'; import { useTranslation } from 'react-i18next'; import { Settings, FileText, Users, RefreshCw, Trash2 } from 'lucide-react'; import { cn } from '@/lib/utils'; import { toast } from 'sonner'; import { useCurrentWorkspace } from '@/app/infra/http'; import { Bot } from '@/app/infra/entities/api'; import EntityBasicInfoDialog, { EntityBasicInfoValues, } from '@/app/home/components/entity-basic-info/EntityBasicInfoDialog'; import AdapterEventDebugDialog from './components/bot-form/AdapterEventDebugDialog'; import EntityTitleEditButton from '@/app/home/components/entity-basic-info/EntityTitleEditButton'; export default function BotDetailContent({ id }: { id: string }) { const isCreateMode = id === 'new'; const navigate = useNavigate(); const { t } = useTranslation(); const currentWorkspace = useCurrentWorkspace(); const canManage = currentWorkspace?.permissions.includes('resource.manage') ?? false; const canViewMonitoring = currentWorkspace?.permissions.includes('resource.view') ?? false; const { refreshBots, bots, setDetailEntityName } = useSidebarData(); // Set breadcrumb entity name useEffect(() => { if (isCreateMode) { setDetailEntityName(t('bots.createBot')); } else { const bot = bots.find((b) => b.id === id); setDetailEntityName(bot?.name ?? id); } return () => setDetailEntityName(null); }, [id, isCreateMode, bots, setDetailEntityName, t]); const [activeTab, setActiveTab] = useState('config'); const [adapterLabel, setAdapterLabel] = useState(''); const [showDeleteConfirm, setShowDeleteConfirm] = useState(false); const [basicInfoOpen, setBasicInfoOpen] = useState(false); const [bot, setBot] = useState(null); const [isRefreshingSessions, setIsRefreshingSessions] = useState(false); const sessionMonitorRef = useRef(null); const botFormRef = useRef(null); // Track whether the form has unsaved changes const [formDirty, setFormDirty] = useState(false); // Enable state managed here so the header switch works const [botEnabled, setBotEnabled] = useState(true); const [enableLoaded, setEnableLoaded] = useState(false); // Fetch bot enable state useEffect(() => { if (!isCreateMode) { httpClient.getBot(id).then((res) => { setBot(res.bot); setBotEnabled(res.bot.enable ?? true); setEnableLoaded(true); }); } }, [id, isCreateMode]); const handleEnableToggle = useCallback( async (checked: boolean) => { const prev = botEnabled; setBotEnabled(checked); try { await httpClient.updateBot(id, { enable: checked }); setBot((current) => current ? { ...current, enable: checked } : current, ); refreshBots(); } catch { setBotEnabled(prev); toast.error(t('bots.setBotEnableError')); } }, [id, botEnabled, refreshBots, t], ); function handleFormSubmit() { // Re-sync enable state after form save (form may update enable too) httpClient.getBot(id).then((res) => { setBot(res.bot); setBotEnabled(res.bot.enable ?? true); }); refreshBots(); } function handleBotDeleted() { refreshBots(); navigate('/home/bots'); } function handleNewBotCreated(newBotId: string) { refreshBots(); navigate(`/home/bots?id=${encodeURIComponent(newBotId)}`); } async function saveBasicInfo(values: EntityBasicInfoValues) { try { await httpClient.updateBot(id, { name: values.name, description: values.description, }); setBot((current) => (current ? { ...current, ...values } : current)); botFormRef.current?.syncBasicInfo(values); await refreshBots(); toast.success(t('bots.saveSuccess')); } catch (error) { const message = typeof error === 'object' && error && 'msg' in error ? String((error as { msg?: string }).msg || '') : ''; toast.error(t('bots.saveError') + message); throw error; } } function confirmDelete() { httpClient .deleteBot(id) .then(() => { setShowDeleteConfirm(false); toast.success(t('bots.deleteSuccess')); handleBotDeleted(); }) .catch((err) => { toast.error(t('bots.deleteError') + err.msg); }); } // ==================== Create Mode ==================== if (isCreateMode) { return (
{/* Header */}

{t('bots.createBot')}

{canManage && ( )}
{/* Content */}
); } // ==================== Edit Mode ==================== return ( <>
{/* Sticky Header: title + enable switch + save button */}

{bot?.name || t('bots.editBot')}

{canManage && ( setBasicInfoOpen(true)} /> )}
{enableLoaded && (
)}
{canManage && (
)}
{/* Horizontal Tabs */}
{t('bots.configuration')} {canViewMonitoring && ( {t('bots.logs')} )} {canViewMonitoring && ( {t('bots.sessionMonitor.title')} )} {activeTab === 'sessions' && ( )}
{/* Tab: Configuration */}
{/* Tab: Logs */} {canViewMonitoring && ( )} {/* Tab: Sessions */} {canViewMonitoring && ( )}
{/* Delete confirmation dialog */} {t('common.confirmDelete')} {t('bots.deleteConfirmation')}
{t('bots.deleteConfirmation')}
); }