import React, { useState, useCallback } from 'react' import { Link, useNavigate } from 'react-router-dom' import { useQuery, useMutation, useQueryClient } from '@tanstack/react-query' import { chatbotsAPI } from '@/services/api' import { useAuthStore } from '@/store/authStore' import { Button, Card, Badge, StatusDot, EmptyState, Modal, Spinner } from '@/components/ui' import { formatDate, getFileIcon, cn } from '@/lib/utils' import type { Chatbot } from '@/types' import { Bot, Plus, MoreHorizontal, Globe, Lock, Trash2, Settings, Upload, Eye, ExternalLink, Download, BarChart2 } from 'lucide-react' // BUG-05 FIX: Toast queue system using array + auto-dismiss interface ToastItem { id: string message: string } export const DashboardPage: React.FC = () => { const { user } = useAuthStore() const navigate = useNavigate() const queryClient = useQueryClient() const [deleteId, setDeleteId] = useState(null) const [toasts, setToasts] = useState([]) // BUG-05 FIX: Queue-based toast - no overwrites const showToast = useCallback((message: string) => { const id = crypto.randomUUID() setToasts(prev => [...prev, { id, message }]) setTimeout(() => { setToasts(prev => prev.filter(t => t.id !== id)) }, 3000) }, []) const removeToast = useCallback((id: string) => { setToasts(prev => prev.filter(t => t.id !== id)) }, []) const { data: chatbots = [], isLoading } = useQuery({ queryKey: ['chatbots'], queryFn: chatbotsAPI.list, }) const deleteMutation = useMutation({ mutationFn: chatbotsAPI.delete, onSuccess: () => { queryClient.invalidateQueries({ queryKey: ['chatbots'] }) setDeleteId(null) showToast('Chatbot deleted') }, }) const publishMutation = useMutation({ mutationFn: chatbotsAPI.publish, onSuccess: () => { queryClient.invalidateQueries({ queryKey: ['chatbots'] }) showToast('Chatbot published to marketplace!') }, }) const unpublishMutation = useMutation({ mutationFn: chatbotsAPI.unpublish, onSuccess: () => { queryClient.invalidateQueries({ queryKey: ['chatbots'] }) showToast('Chatbot unpublished') }, }) // IMP-11: Confirmation before publish/unpublish const [confirmAction, setConfirmAction] = useState<{ type: 'publish' | 'unpublish'; id: string } | null>(null) const handleConfirmAction = () => { if (!confirmAction) return if (confirmAction.type === 'publish') { publishMutation.mutate(confirmAction.id) } else { unpublishMutation.mutate(confirmAction.id) } setConfirmAction(null) } return (

Dashboard

Manage your AI chatbots

{isLoading ? (
) : chatbots.length === 0 ? ( } title="No chatbots yet" description="Create your first AI chatbot powered by your documents. It's free to build and test." action={ } /> ) : ( // R-02 FIX: Better responsive grid breakpoints
{chatbots.map((chatbot) => ( navigate(`/chatbots/${chatbot.id}/edit`)} onPreview={() => navigate(`/chatbots/${chatbot.id}/preview`)} onPublish={() => setConfirmAction({ type: 'publish', id: chatbot.id })} onUnpublish={() => setConfirmAction({ type: 'unpublish', id: chatbot.id })} onDelete={() => setDeleteId(chatbot.id)} onAnalytics={() => navigate(`/chatbots/${chatbot.id}/analytics`)} /> ))} {/* Add new card */} navigate('/chatbots/new')} >

New Chatbot

)} {/* Delete confirmation */} setDeleteId(null)} title="Delete Chatbot" >

Are you sure you want to delete this chatbot? All documents and conversation history will be permanently removed.

{/* IMP-11: Publish/Unpublish confirmation */} setConfirmAction(null)} title={confirmAction?.type === 'publish' ? 'Publish Chatbot' : 'Unpublish Chatbot'} >

{confirmAction?.type === 'publish' ? 'This will make your chatbot publicly visible on the marketplace. Are you sure?' : 'This will remove your chatbot from the marketplace. Users will no longer be able to access it.'}

{/* BUG-05 FIX: Toast queue - renders all active toasts */}
{toasts.map((toast) => (
{toast.message}
))}
) } const ChatbotCard: React.FC<{ chatbot: Chatbot onEdit: () => void onPreview: () => void onPublish: () => void onUnpublish: () => void onDelete: () => void onAnalytics: () => void }> = ({ chatbot, onEdit, onPreview, onPublish, onUnpublish, onDelete, onAnalytics }) => { const [menuOpen, setMenuOpen] = useState(false) return (
{chatbot.logo_url ? ( {chatbot.name} ) : (
)}

{chatbot.name}

{chatbot.is_published ? 'Published' : 'Preview'}
{/* R-02 FIX: Menu dropdown with viewport-aware positioning */}
{menuOpen && ( <>
setMenuOpen(false)} />
{chatbot.is_published ? ( ) : ( )}
)}
{chatbot.description && (

{chatbot.description}

)} {/* Stats */}
📄 {chatbot.document_count} docs 💬 {chatbot.conversation_count} chats {chatbot.category && 🏷 {chatbot.category}}
{/* Actions */}
{chatbot.is_published ? ( ) : ( )}
) }