/**
* OverviewView - Multi-project dashboard showing status across all projects
*
* Provides a unified view of all projects with active features, running agents,
* recent completions, and alerts. Quick navigation to any project or feature.
*/
import { useState, useCallback } from 'react';
import { useNavigate } from '@tanstack/react-router';
import { createLogger } from '@automaker/utils/logger';
import { useMultiProjectStatus } from '@/hooks/use-multi-project-status';
import { useAppStore } from '@/store/app-store';
import { isElectron, getElectronAPI } from '@/lib/electron';
import { isMac } from '@/lib/utils';
import { initializeProject } from '@/lib/project-init';
import { getHttpApiClient } from '@/lib/http-api-client';
import { toast } from 'sonner';
import { Spinner } from '@/components/ui/spinner';
import { Button } from '@/components/ui/button';
import { Card, CardContent, CardHeader, CardTitle } from '@/components/ui/card';
import { NewProjectModal } from '@/components/dialogs/new-project-modal';
import { WorkspacePickerModal } from '@/components/dialogs/workspace-picker-modal';
import { ProjectStatusCard } from './overview/project-status-card';
import { RecentActivityFeed } from './overview/recent-activity-feed';
import { RunningAgentsPanel } from './overview/running-agents-panel';
import type { StarterTemplate } from '@/lib/templates';
import {
LayoutDashboard,
RefreshCw,
Folder,
FolderOpen,
Plus,
Activity,
CheckCircle2,
XCircle,
Clock,
Bot,
Bell,
} from 'lucide-react';
const logger = createLogger('OverviewView');
export function OverviewView() {
const navigate = useNavigate();
const { overview, isLoading, error, refresh } = useMultiProjectStatus(15000); // Refresh every 15s
const { upsertAndSetCurrentProject } = useAppStore();
// Modal state
const [showNewProjectModal, setShowNewProjectModal] = useState(false);
const [showWorkspacePicker, setShowWorkspacePicker] = useState(false);
const [isCreating, setIsCreating] = useState(false);
const initializeAndOpenProject = useCallback(
async (path: string, name: string) => {
try {
const initResult = await initializeProject(path);
if (!initResult.success) {
toast.error('Failed to initialize project', {
description: initResult.error || 'Unknown error occurred',
});
return;
}
upsertAndSetCurrentProject(path, name);
toast.success('Project opened', { description: `Opened ${name}` });
navigate({ to: '/board' });
} catch (error) {
logger.error('[Overview] Failed to open project:', error);
toast.error('Failed to open project', {
description: error instanceof Error ? error.message : 'Unknown error',
});
}
},
[upsertAndSetCurrentProject, navigate]
);
const handleOpenProject = useCallback(async () => {
try {
const httpClient = getHttpApiClient();
const configResult = await httpClient.workspace.getConfig();
if (configResult.success && configResult.configured) {
setShowWorkspacePicker(true);
} else {
const api = getElectronAPI();
const result = await api.openDirectory();
if (!result.canceled && result.filePaths[0]) {
const path = result.filePaths[0];
const name = path.split(/[/\\]/).filter(Boolean).pop() || 'Untitled Project';
await initializeAndOpenProject(path, name);
}
}
} catch (error) {
logger.error('[Overview] Failed to check workspace config:', error);
const api = getElectronAPI();
const result = await api.openDirectory();
if (!result.canceled && result.filePaths[0]) {
const path = result.filePaths[0];
const name = path.split(/[/\\]/).filter(Boolean).pop() || 'Untitled Project';
await initializeAndOpenProject(path, name);
}
}
}, [initializeAndOpenProject]);
const handleWorkspaceSelect = useCallback(
async (path: string, name: string) => {
setShowWorkspacePicker(false);
await initializeAndOpenProject(path, name);
},
[initializeAndOpenProject]
);
const handleCreateBlankProject = useCallback(
async (projectName: string, parentDir: string) => {
setIsCreating(true);
try {
const api = getElectronAPI();
const projectPath = `${parentDir}/${projectName}`;
await api.mkdir(projectPath);
const initResult = await initializeProject(projectPath);
if (!initResult.success) {
toast.error('Failed to initialize project', {
description: initResult.error || 'Unknown error occurred',
});
return;
}
await api.writeFile(
`${projectPath}/.automaker/app_spec.txt`,
`
{overview ? `${overview.aggregate.projectCounts.total} projects` : 'Loading...'}
Loading project overview...
{error}
{overview.aggregate.projectCounts.total}
Projects
{overview.aggregate.featureCounts.running}
Running
{overview.aggregate.featureCounts.pending}
Pending
{overview.aggregate.featureCounts.completed}
Completed
{overview.aggregate.featureCounts.failed}
Failed
{overview.aggregate.projectsWithAutoModeRunning}
Auto-mode
Create or open a project to get started
Use the sidebar to create or open a project