diff --git a/apps/ui/src/components/views/overview-view.tsx b/apps/ui/src/components/views/overview-view.tsx
index b280f7dd..68ae60f5 100644
--- a/apps/ui/src/components/views/overview-view.tsx
+++ b/apps/ui/src/components/views/overview-view.tsx
@@ -5,19 +5,31 @@
* 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 { isElectron } from '@/lib/electron';
+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,
@@ -26,8 +38,222 @@ import {
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 { addProject, setCurrentProject } = useAppStore();
+
+ // Modal state
+ const [showNewProjectModal, setShowNewProjectModal] = useState(false);
+ const [showWorkspacePicker, setShowWorkspacePicker] = useState(false);
+ const [isCreating, setIsCreating] = useState(false);
+
+ 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);
+ }
+ }
+ }, []);
+
+ 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;
+ }
+
+ const project = {
+ id: `project-${Date.now()}`,
+ name,
+ path,
+ lastOpened: new Date().toISOString(),
+ };
+
+ addProject(project);
+ setCurrentProject(project);
+
+ 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',
+ });
+ }
+ },
+ [addProject, setCurrentProject, navigate]
+ );
+
+ 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);
+ await initializeProject(projectPath);
+
+ await api.writeFile(
+ `${projectPath}/.automaker/app_spec.txt`,
+ `
{overview ? `${overview.aggregate.projectCounts.total} projects` : 'Loading...'}
@@ -66,6 +292,18 @@ export function OverviewView() {