mirror of
https://github.com/AutoMaker-Org/automaker.git
synced 2026-02-04 09:13:08 +00:00
Implement initial project structure and features for Automaker application, including environment setup, auto mode services, and session management. Update port configurations to 3007 and add new UI components for enhanced user interaction.
This commit is contained in:
@@ -15,10 +15,16 @@ import {
|
||||
import { Card, CardContent, CardDescription, CardHeader, CardTitle } from "@/components/ui/card";
|
||||
import { useAppStore } from "@/store/app-store";
|
||||
import { getElectronAPI } from "@/lib/electron";
|
||||
import { FolderOpen, Plus, Sparkles, Folder, Clock } from "lucide-react";
|
||||
import { FolderOpen, Plus, Cpu, Folder, Clock, Sparkles, MessageSquare, ChevronDown } from "lucide-react";
|
||||
import {
|
||||
DropdownMenu,
|
||||
DropdownMenuContent,
|
||||
DropdownMenuItem,
|
||||
DropdownMenuTrigger,
|
||||
} from "@/components/ui/dropdown-menu";
|
||||
|
||||
export function WelcomeView() {
|
||||
const { projects, addProject, setCurrentProject } = useAppStore();
|
||||
const { projects, addProject, setCurrentProject, setCurrentView } = useAppStore();
|
||||
const [showNewProjectDialog, setShowNewProjectDialog] = useState(false);
|
||||
const [newProjectName, setNewProjectName] = useState("");
|
||||
const [newProjectPath, setNewProjectPath] = useState("");
|
||||
@@ -50,6 +56,10 @@ export function WelcomeView() {
|
||||
setShowNewProjectDialog(true);
|
||||
};
|
||||
|
||||
const handleInteractiveMode = () => {
|
||||
setCurrentView("interview");
|
||||
};
|
||||
|
||||
const handleSelectDirectory = async () => {
|
||||
const api = getElectronAPI();
|
||||
const result = await api.openDirectory();
|
||||
@@ -132,144 +142,222 @@ export function WelcomeView() {
|
||||
.slice(0, 5);
|
||||
|
||||
return (
|
||||
<div className="flex-1 flex flex-col items-center justify-center p-8" data-testid="welcome-view">
|
||||
{/* Hero Section */}
|
||||
<div className="text-center mb-12">
|
||||
<div className="inline-flex items-center justify-center w-20 h-20 rounded-2xl bg-primary/10 mb-6">
|
||||
<Sparkles className="w-10 h-10 text-primary" />
|
||||
</div>
|
||||
<h1 className="text-4xl font-bold mb-4">Welcome to Automaker</h1>
|
||||
<p className="text-lg text-muted-foreground max-w-md">
|
||||
Your autonomous AI development studio. Build software with intelligent orchestration.
|
||||
</p>
|
||||
</div>
|
||||
|
||||
{/* Action Cards */}
|
||||
<div className="grid grid-cols-1 md:grid-cols-2 gap-6 max-w-2xl w-full mb-12">
|
||||
<Card
|
||||
className="cursor-pointer hover:border-primary/50 transition-colors"
|
||||
onClick={handleNewProject}
|
||||
data-testid="new-project-card"
|
||||
>
|
||||
<CardHeader>
|
||||
<div className="w-12 h-12 rounded-lg bg-primary/10 flex items-center justify-center mb-2">
|
||||
<Plus className="w-6 h-6 text-primary" />
|
||||
<div className="flex-1 flex flex-col content-bg" data-testid="welcome-view">
|
||||
{/* Header Section */}
|
||||
<div className="flex-shrink-0 border-b border-white/10 bg-zinc-950/50 backdrop-blur-md">
|
||||
<div className="px-8 py-6">
|
||||
<div className="flex items-center gap-3 mb-2">
|
||||
<div className="w-10 h-10 rounded-xl bg-gradient-to-br from-brand-500 to-purple-600 shadow-lg shadow-brand-500/20 flex items-center justify-center">
|
||||
<Cpu className="w-5 h-5 text-white" />
|
||||
</div>
|
||||
<CardTitle>New Project</CardTitle>
|
||||
<CardDescription>
|
||||
Create a new project from scratch or use interactive mode
|
||||
</CardDescription>
|
||||
</CardHeader>
|
||||
<CardContent>
|
||||
<Button className="w-full" data-testid="create-new-project">
|
||||
<Plus className="w-4 h-4 mr-2" />
|
||||
Create Project
|
||||
</Button>
|
||||
</CardContent>
|
||||
</Card>
|
||||
|
||||
<Card
|
||||
className="cursor-pointer hover:border-primary/50 transition-colors"
|
||||
onClick={handleOpenProject}
|
||||
data-testid="open-project-card"
|
||||
>
|
||||
<CardHeader>
|
||||
<div className="w-12 h-12 rounded-lg bg-primary/10 flex items-center justify-center mb-2">
|
||||
<FolderOpen className="w-6 h-6 text-primary" />
|
||||
<div>
|
||||
<h1 className="text-2xl font-bold text-white">Welcome to Automaker</h1>
|
||||
<p className="text-sm text-zinc-400">Your autonomous AI development studio</p>
|
||||
</div>
|
||||
<CardTitle>Open Project</CardTitle>
|
||||
<CardDescription>
|
||||
Open an existing project folder to continue working
|
||||
</CardDescription>
|
||||
</CardHeader>
|
||||
<CardContent>
|
||||
<Button variant="secondary" className="w-full" data-testid="open-existing-project">
|
||||
<FolderOpen className="w-4 h-4 mr-2" />
|
||||
Browse Folder
|
||||
</Button>
|
||||
</CardContent>
|
||||
</Card>
|
||||
</div>
|
||||
|
||||
{/* Recent Projects */}
|
||||
{recentProjects.length > 0 && (
|
||||
<div className="max-w-2xl w-full">
|
||||
<h2 className="text-lg font-semibold mb-4 flex items-center gap-2">
|
||||
<Clock className="w-5 h-5" />
|
||||
Recent Projects
|
||||
</h2>
|
||||
<div className="space-y-2">
|
||||
{recentProjects.map((project) => (
|
||||
<Card
|
||||
key={project.id}
|
||||
className="cursor-pointer hover:border-primary/50 transition-colors"
|
||||
onClick={() => setCurrentProject(project)}
|
||||
data-testid={`recent-project-${project.id}`}
|
||||
>
|
||||
<CardContent className="flex items-center gap-4 p-4">
|
||||
<div className="w-10 h-10 rounded-lg bg-muted flex items-center justify-center">
|
||||
<Folder className="w-5 h-5 text-muted-foreground" />
|
||||
</div>
|
||||
<div className="flex-1 min-w-0">
|
||||
<p className="font-medium truncate">{project.name}</p>
|
||||
<p className="text-sm text-muted-foreground truncate">{project.path}</p>
|
||||
</div>
|
||||
{project.lastOpened && (
|
||||
<p className="text-xs text-muted-foreground whitespace-nowrap">
|
||||
{new Date(project.lastOpened).toLocaleDateString()}
|
||||
</p>
|
||||
)}
|
||||
</CardContent>
|
||||
</Card>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
|
||||
{/* Content Area */}
|
||||
<div className="flex-1 overflow-y-auto p-8">
|
||||
<div className="max-w-6xl mx-auto">
|
||||
{/* Quick Actions */}
|
||||
<div className="grid grid-cols-1 md:grid-cols-2 gap-6 mb-12">
|
||||
<div
|
||||
className="group relative overflow-hidden rounded-xl border border-white/10 bg-zinc-900/50 backdrop-blur-md hover:bg-zinc-900/70 hover:border-white/20 transition-all duration-200"
|
||||
data-testid="new-project-card"
|
||||
>
|
||||
<div className="absolute inset-0 bg-gradient-to-br from-brand-500/5 to-purple-600/5 opacity-0 group-hover:opacity-100 transition-opacity"></div>
|
||||
<div className="relative p-6">
|
||||
<div className="flex items-start gap-4 mb-4">
|
||||
<div className="w-12 h-12 rounded-lg bg-gradient-to-br from-brand-500 to-purple-600 shadow-lg shadow-brand-500/20 flex items-center justify-center group-hover:scale-110 transition-transform">
|
||||
<Plus className="w-6 h-6 text-white" />
|
||||
</div>
|
||||
<div className="flex-1">
|
||||
<h3 className="text-lg font-semibold text-white mb-1">New Project</h3>
|
||||
<p className="text-sm text-zinc-400">
|
||||
Create a new project from scratch with AI-powered development
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
<DropdownMenu>
|
||||
<DropdownMenuTrigger asChild>
|
||||
<Button
|
||||
className="w-full bg-gradient-to-r from-brand-500 to-purple-600 hover:from-brand-600 hover:to-purple-700 text-white border-0"
|
||||
data-testid="create-new-project"
|
||||
>
|
||||
<Plus className="w-4 h-4 mr-2" />
|
||||
Create Project
|
||||
<ChevronDown className="w-4 h-4 ml-2" />
|
||||
</Button>
|
||||
</DropdownMenuTrigger>
|
||||
<DropdownMenuContent align="end" className="w-56">
|
||||
<DropdownMenuItem
|
||||
onClick={handleNewProject}
|
||||
data-testid="quick-setup-option"
|
||||
>
|
||||
<Plus className="w-4 h-4 mr-2" />
|
||||
Quick Setup
|
||||
</DropdownMenuItem>
|
||||
<DropdownMenuItem
|
||||
onClick={handleInteractiveMode}
|
||||
data-testid="interactive-mode-option"
|
||||
>
|
||||
<MessageSquare className="w-4 h-4 mr-2" />
|
||||
Interactive Mode
|
||||
</DropdownMenuItem>
|
||||
</DropdownMenuContent>
|
||||
</DropdownMenu>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div
|
||||
className="group relative overflow-hidden rounded-xl border border-white/10 bg-zinc-900/50 backdrop-blur-md hover:bg-zinc-900/70 hover:border-white/20 transition-all duration-200 cursor-pointer"
|
||||
onClick={handleOpenProject}
|
||||
data-testid="open-project-card"
|
||||
>
|
||||
<div className="absolute inset-0 bg-gradient-to-br from-blue-500/5 to-cyan-600/5 opacity-0 group-hover:opacity-100 transition-opacity"></div>
|
||||
<div className="relative p-6">
|
||||
<div className="flex items-start gap-4 mb-4">
|
||||
<div className="w-12 h-12 rounded-lg bg-zinc-800 border border-white/10 flex items-center justify-center group-hover:scale-110 transition-transform">
|
||||
<FolderOpen className="w-6 h-6 text-zinc-400 group-hover:text-white transition-colors" />
|
||||
</div>
|
||||
<div className="flex-1">
|
||||
<h3 className="text-lg font-semibold text-white mb-1">Open Project</h3>
|
||||
<p className="text-sm text-zinc-400">
|
||||
Open an existing project folder to continue working
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
<Button
|
||||
variant="secondary"
|
||||
className="w-full bg-white/5 hover:bg-white/10 text-white border border-white/10 hover:border-white/20"
|
||||
data-testid="open-existing-project"
|
||||
>
|
||||
<FolderOpen className="w-4 h-4 mr-2" />
|
||||
Browse Folder
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Recent Projects */}
|
||||
{recentProjects.length > 0 && (
|
||||
<div>
|
||||
<div className="flex items-center gap-2 mb-4">
|
||||
<Clock className="w-5 h-5 text-zinc-400" />
|
||||
<h2 className="text-lg font-semibold text-white">Recent Projects</h2>
|
||||
</div>
|
||||
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-4">
|
||||
{recentProjects.map((project) => (
|
||||
<div
|
||||
key={project.id}
|
||||
className="group relative overflow-hidden rounded-xl border border-white/10 bg-zinc-900/50 backdrop-blur-md hover:bg-zinc-900/70 hover:border-brand-500/50 transition-all duration-200 cursor-pointer"
|
||||
onClick={() => setCurrentProject(project)}
|
||||
data-testid={`recent-project-${project.id}`}
|
||||
>
|
||||
<div className="absolute inset-0 bg-gradient-to-br from-brand-500/0 to-purple-600/0 group-hover:from-brand-500/5 group-hover:to-purple-600/5 transition-all"></div>
|
||||
<div className="relative p-4">
|
||||
<div className="flex items-start gap-3">
|
||||
<div className="w-10 h-10 rounded-lg bg-zinc-800 border border-white/10 flex items-center justify-center group-hover:border-brand-500/50 transition-colors">
|
||||
<Folder className="w-5 h-5 text-zinc-400 group-hover:text-brand-500 transition-colors" />
|
||||
</div>
|
||||
<div className="flex-1 min-w-0">
|
||||
<p className="font-medium text-white truncate group-hover:text-brand-500 transition-colors">
|
||||
{project.name}
|
||||
</p>
|
||||
<p className="text-xs text-zinc-500 truncate mt-0.5">{project.path}</p>
|
||||
{project.lastOpened && (
|
||||
<p className="text-xs text-zinc-600 mt-1">
|
||||
{new Date(project.lastOpened).toLocaleDateString()}
|
||||
</p>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{/* Empty State for No Projects */}
|
||||
{recentProjects.length === 0 && (
|
||||
<div className="flex flex-col items-center justify-center py-12 text-center">
|
||||
<div className="w-16 h-16 rounded-2xl bg-zinc-900/50 border border-white/10 flex items-center justify-center mb-4">
|
||||
<Sparkles className="w-8 h-8 text-zinc-600" />
|
||||
</div>
|
||||
<h3 className="text-lg font-semibold text-white mb-2">No projects yet</h3>
|
||||
<p className="text-sm text-zinc-400 max-w-md">
|
||||
Get started by creating a new project or opening an existing one
|
||||
</p>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* New Project Dialog */}
|
||||
<Dialog open={showNewProjectDialog} onOpenChange={setShowNewProjectDialog}>
|
||||
<DialogContent data-testid="new-project-dialog">
|
||||
<DialogContent
|
||||
className="bg-zinc-900 border-white/10"
|
||||
data-testid="new-project-dialog"
|
||||
>
|
||||
<DialogHeader>
|
||||
<DialogTitle>Create New Project</DialogTitle>
|
||||
<DialogDescription>
|
||||
<DialogTitle className="text-white">Create New Project</DialogTitle>
|
||||
<DialogDescription className="text-zinc-400">
|
||||
Set up a new project directory with initial configuration files.
|
||||
</DialogDescription>
|
||||
</DialogHeader>
|
||||
<div className="space-y-4 py-4">
|
||||
<div className="space-y-2">
|
||||
<Label htmlFor="project-name">Project Name</Label>
|
||||
<Label htmlFor="project-name" className="text-zinc-300">
|
||||
Project Name
|
||||
</Label>
|
||||
<Input
|
||||
id="project-name"
|
||||
placeholder="my-awesome-project"
|
||||
value={newProjectName}
|
||||
onChange={(e) => setNewProjectName(e.target.value)}
|
||||
className="bg-zinc-950/50 border-white/10 text-white placeholder:text-zinc-500"
|
||||
data-testid="project-name-input"
|
||||
/>
|
||||
</div>
|
||||
<div className="space-y-2">
|
||||
<Label htmlFor="project-path">Parent Directory</Label>
|
||||
<Label htmlFor="project-path" className="text-zinc-300">
|
||||
Parent Directory
|
||||
</Label>
|
||||
<div className="flex gap-2">
|
||||
<Input
|
||||
id="project-path"
|
||||
placeholder="/path/to/projects"
|
||||
value={newProjectPath}
|
||||
onChange={(e) => setNewProjectPath(e.target.value)}
|
||||
className="flex-1"
|
||||
className="flex-1 bg-zinc-950/50 border-white/10 text-white placeholder:text-zinc-500"
|
||||
data-testid="project-path-input"
|
||||
/>
|
||||
<Button variant="secondary" onClick={handleSelectDirectory} data-testid="browse-directory">
|
||||
<Button
|
||||
variant="secondary"
|
||||
onClick={handleSelectDirectory}
|
||||
className="bg-white/5 hover:bg-white/10 text-white border border-white/10"
|
||||
data-testid="browse-directory"
|
||||
>
|
||||
Browse
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<DialogFooter>
|
||||
<Button variant="ghost" onClick={() => setShowNewProjectDialog(false)}>
|
||||
<Button
|
||||
variant="ghost"
|
||||
onClick={() => setShowNewProjectDialog(false)}
|
||||
className="text-zinc-400 hover:text-white hover:bg-white/5"
|
||||
>
|
||||
Cancel
|
||||
</Button>
|
||||
<Button
|
||||
onClick={handleCreateProject}
|
||||
disabled={!newProjectName || !newProjectPath || isCreating}
|
||||
className="bg-gradient-to-r from-brand-500 to-purple-600 hover:from-brand-600 hover:to-purple-700 text-white border-0"
|
||||
data-testid="confirm-create-project"
|
||||
>
|
||||
{isCreating ? "Creating..." : "Create Project"}
|
||||
|
||||
Reference in New Issue
Block a user