import React, { useCallback, useEffect, useMemo, useState } from 'react'; import { Bot, FolderOpen, Play, RefreshCw, Search, SlidersHorizontal, Trash2, X } from 'lucide-react'; import { getApiUrl } from '../services/runtimeConfig'; import { motion } from 'framer-motion'; import { supabase } from '../services/supabase'; import { useAuth } from '../context/useAuth'; import AubixIcon from './AubixIcon'; import StatusBadge from './common/StatusBadge'; interface Project { id: string; name: string; description: string | null; status: string; created_at: string; } interface Task { id: string; project_id: string; status: string; } interface DashboardProps { onNewProject: () => void; onOpenProject: (projectId: string) => void; } const Dashboard: React.FC = ({ onNewProject, onOpenProject }) => { const { user } = useAuth(); const [projects, setProjects] = useState([]); const [tasks, setTasks] = useState([]); const [loading, setLoading] = useState(false); const [error, setError] = useState(null); const [searchTerm, setSearchTerm] = useState(''); const [statusFilter, setStatusFilter] = useState('all'); const [progressFilter, setProgressFilter] = useState('all'); const [sortBy, setSortBy] = useState('newest'); const [aiPrompt, setAiPrompt] = useState(''); const [isGenerating, setIsGenerating] = useState(false); const [magicFiles, setMagicFiles] = useState([]); const loadDashboard = useCallback(async () => { if (!user) return; setLoading(true); setError(null); const { data: projectData, error: projectError } = await supabase .from('projects') .select('id,name,description,status,created_at') .eq('owner_id', user.id) .order('created_at', { ascending: false }); if (projectError) { setError(projectError.message); setLoading(false); return; } const projectIds = (projectData ?? []).map((project) => project.id); let taskData: Task[] = []; if (projectIds.length) { const { data, error: taskError } = await supabase .from('tasks') .select('id,project_id,status') .in('project_id', projectIds); if (taskError) { setError(taskError.message); } else { taskData = data ?? []; } } setProjects(projectData ?? []); setTasks(taskData); setLoading(false); }, [user]); useEffect(() => { loadDashboard(); }, [loadDashboard]); const handleDeleteProject = async (id: string, name: string) => { if (!window.confirm(`Are you sure you want to delete "${name}"? This action cannot be undone.`)) return; const { error: deleteError } = await supabase.from('projects').delete().eq('id', id); if (deleteError) { setError(`Error deleting project: ${deleteError.message}`); } else { loadDashboard(); } }; const handleMagicGenerate = async () => { if (!aiPrompt.trim()) return; setIsGenerating(true); try { const formData = new FormData(); formData.append('prompt', aiPrompt); magicFiles.forEach(file => { formData.append('files', file); }); const apiUrl = getApiUrl(); const response = await fetch(`${apiUrl}/generator/generate-project`, { method: 'POST', body: formData }); const data = await response.json(); if (!response.ok) throw new Error(data.detail || 'AI Generation failed'); onNewProject(data); // Open NewProject wizard with pre-filled data setAiPrompt(''); setMagicFiles([]); } catch (err: any) { console.error('Magic Generate Error:', err); setError(`AI Error: ${err.message}`); } finally { setIsGenerating(false); } }; const taskCounts = useMemo(() => { return tasks.reduce>((acc, task) => { if (!acc[task.project_id]) acc[task.project_id] = { done: 0, total: 0 }; acc[task.project_id].total += 1; if (task.status === 'done') acc[task.project_id].done += 1; return acc; }, {}); }, [tasks]); const filteredProjects = useMemo(() => { const normalizedSearch = searchTerm.trim().toLowerCase(); return projects .filter((project) => { if (statusFilter !== 'all' && project.status !== statusFilter) return false; if (normalizedSearch) { const searchableText = `${project.name} ${project.description ?? ''}`.toLowerCase(); if (!searchableText.includes(normalizedSearch)) return false; } const counts = taskCounts[project.id] ?? { done: 0, total: 0 }; const progress = counts.total > 0 ? counts.done / counts.total : 0; if (progressFilter === 'not_started') return counts.done === 0; if (progressFilter === 'in_progress') return progress > 0 && progress < 1; if (progressFilter === 'completed') return counts.total > 0 && progress === 1; if (progressFilter === 'no_tasks') return counts.total === 0; return true; }) .sort((a, b) => { if (sortBy === 'name') return a.name.localeCompare(b.name); if (sortBy === 'oldest') return new Date(a.created_at).getTime() - new Date(b.created_at).getTime(); if (sortBy === 'progress') { const aCounts = taskCounts[a.id] ?? { done: 0, total: 0 }; const bCounts = taskCounts[b.id] ?? { done: 0, total: 0 }; const aProgress = aCounts.total > 0 ? aCounts.done / aCounts.total : 0; const bProgress = bCounts.total > 0 ? bCounts.done / bCounts.total : 0; return bProgress - aProgress; } return new Date(b.created_at).getTime() - new Date(a.created_at).getTime(); }); }, [progressFilter, projects, searchTerm, sortBy, statusFilter, taskCounts]); const hasActiveFilters = Boolean(searchTerm.trim()) || statusFilter !== 'all' || progressFilter !== 'all' || sortBy !== 'newest'; const clearFilters = () => { setSearchTerm(''); setStatusFilter('all'); setProgressFilter('all'); setSortBy('newest'); }; return ( <>

Project Dashboard

Monitor and manage your autonomous AI agent workflows.

{/* AI Magic Bar (Aubix) */}
setAiPrompt(e.target.value)} onKeyDown={(e) => e.key === 'Enter' && handleMagicGenerate()} style={{ width: '100%', padding: '12px 20px', paddingRight: '140px', background: 'rgba(0,0,0,0.2)', border: '1px solid var(--border)', borderRadius: 'var(--radius-md)', color: 'white', fontSize: '1rem', outline: 'none' }} />
{magicFiles.length > 0 && (
{magicFiles.map((f, i) => ( {f.name} setMagicFiles(prev => prev.filter((_, idx) => idx !== i))} /> ))}
)}
{error &&
{error}
} {projects.length > 0 && (
setSearchTerm(event.target.value)} placeholder="Search projects..." aria-label="Search projects" />
{filteredProjects.length}/{projects.length} shown {hasActiveFilters && ( )}
)} {!loading && projects.length === 0 && (

No projects yet

Create a project to start assigning agents and tasks.

)} {!loading && projects.length > 0 && filteredProjects.length === 0 && (

No matching projects

Adjust the search or filters to show more projects.

)}
{filteredProjects.map((project) => { const counts = taskCounts[project.id] ?? { done: 0, total: 0 }; return ( onOpenProject(project.id)} onDelete={() => handleDeleteProject(project.id, project.name)} /> ); })}
); }; const ProjectCard: React.FC<{ name: string; status: string; tasksDone: number; tasksTotal: number; onOpen: () => void; onDelete: () => void }> = ({ name, status, tasksDone, tasksTotal, onOpen, onDelete }) => { const progress = tasksTotal > 0 ? (tasksDone / tasksTotal) * 100 : 0; return (

{name}

{/* Description removed as requested for a cleaner layout */}
Tasks Progress {tasksDone}/{tasksTotal}
); }; export default Dashboard;