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:
Cody Seibert
2025-12-08 21:11:00 -05:00
parent 3c8e786f29
commit 9392422d35
67 changed files with 16275 additions and 696 deletions

View File

@@ -3,19 +3,39 @@
import { useSortable } from "@dnd-kit/sortable";
import { CSS } from "@dnd-kit/utilities";
import { cn } from "@/lib/utils";
import { Card, CardContent, CardDescription, CardHeader, CardTitle } from "@/components/ui/card";
import {
Card,
CardContent,
CardDescription,
CardHeader,
CardTitle,
} from "@/components/ui/card";
import { Button } from "@/components/ui/button";
import { Feature } from "@/store/app-store";
import { GripVertical, Edit, Play, CheckCircle2, Circle } from "lucide-react";
import { GripVertical, Edit, CheckCircle2, Circle, Loader2, Trash2, Eye, PlayCircle } from "lucide-react";
interface KanbanCardProps {
feature: Feature;
onEdit: () => void;
onDelete: () => void;
onViewOutput?: () => void;
onVerify?: () => void;
isCurrentAutoTask?: boolean;
}
export function KanbanCard({ feature, onEdit }: KanbanCardProps) {
const { attributes, listeners, setNodeRef, transform, transition, isDragging } = useSortable({
export function KanbanCard({ feature, onEdit, onDelete, onViewOutput, onVerify, isCurrentAutoTask }: KanbanCardProps) {
// Disable dragging if the feature is in progress or verified
const isDraggable = feature.status === "backlog";
const {
attributes,
listeners,
setNodeRef,
transform,
transition,
isDragging,
} = useSortable({
id: feature.id,
disabled: !isDraggable,
});
const style = {
@@ -28,24 +48,38 @@ export function KanbanCard({ feature, onEdit }: KanbanCardProps) {
ref={setNodeRef}
style={style}
className={cn(
"cursor-grab active:cursor-grabbing transition-all",
isDragging && "opacity-50 scale-105 shadow-lg"
"cursor-grab active:cursor-grabbing transition-all backdrop-blur-sm border-white/10 relative",
isDragging && "opacity-50 scale-105 shadow-lg",
isCurrentAutoTask && "border-purple-500 border-2 shadow-purple-500/50 shadow-lg animate-pulse"
)}
data-testid={`kanban-card-${feature.id}`}
{...attributes}
>
<CardHeader className="p-3 pb-2">
{isCurrentAutoTask && (
<div className="absolute top-2 right-2 flex items-center gap-1 bg-purple-500/20 border border-purple-500 rounded px-2 py-0.5">
<Loader2 className="w-4 h-4 text-purple-400 animate-spin" />
<span className="text-xs text-purple-400 font-medium">Running...</span>
</div>
)}
<div className="flex items-start gap-2">
<div
{...listeners}
className="mt-0.5 cursor-grab touch-none"
className={cn(
"mt-0.5 touch-none",
isDraggable ? "cursor-grab" : "cursor-not-allowed opacity-50"
)}
data-testid={`drag-handle-${feature.id}`}
>
<GripVertical className="w-4 h-4 text-muted-foreground" />
</div>
<div className="flex-1 min-w-0">
<CardTitle className="text-sm leading-tight">{feature.description}</CardTitle>
<CardDescription className="text-xs mt-1">{feature.category}</CardDescription>
<CardTitle className="text-sm leading-tight">
{feature.description}
</CardTitle>
<CardDescription className="text-xs mt-1">
{feature.category}
</CardDescription>
</div>
</div>
</CardHeader>
@@ -54,8 +88,11 @@ export function KanbanCard({ feature, onEdit }: KanbanCardProps) {
{feature.steps.length > 0 && (
<div className="mb-3 space-y-1">
{feature.steps.slice(0, 3).map((step, index) => (
<div key={index} className="flex items-start gap-2 text-xs text-muted-foreground">
{feature.passes ? (
<div
key={index}
className="flex items-start gap-2 text-xs text-muted-foreground"
>
{feature.status === "verified" ? (
<CheckCircle2 className="w-3 h-3 mt-0.5 text-green-500 shrink-0" />
) : (
<Circle className="w-3 h-3 mt-0.5 shrink-0" />
@@ -73,27 +110,84 @@ export function KanbanCard({ feature, onEdit }: KanbanCardProps) {
{/* Actions */}
<div className="flex gap-2">
<Button
variant="ghost"
size="sm"
className="flex-1 h-7 text-xs"
onClick={(e) => {
e.stopPropagation();
onEdit();
}}
data-testid={`edit-feature-${feature.id}`}
>
<Edit className="w-3 h-3 mr-1" />
Edit
</Button>
<Button
variant="ghost"
size="sm"
className="h-7 text-xs text-primary hover:text-primary"
data-testid={`run-feature-${feature.id}`}
>
<Play className="w-3 h-3" />
</Button>
{isCurrentAutoTask && onViewOutput && (
<Button
variant="default"
size="sm"
className="flex-1 h-7 text-xs bg-purple-600 hover:bg-purple-700"
onClick={(e) => {
e.stopPropagation();
onViewOutput();
}}
data-testid={`view-output-${feature.id}`}
>
<Eye className="w-3 h-3 mr-1" />
View Output
</Button>
)}
{!isCurrentAutoTask && feature.status === "in_progress" && (
<>
{onVerify && (
<Button
variant="default"
size="sm"
className="flex-1 h-7 text-xs bg-green-600 hover:bg-green-700"
onClick={(e) => {
e.stopPropagation();
onVerify();
}}
data-testid={`verify-feature-${feature.id}`}
>
<PlayCircle className="w-3 h-3 mr-1" />
Verify
</Button>
)}
{onViewOutput && (
<Button
variant="ghost"
size="sm"
className="h-7 text-xs"
onClick={(e) => {
e.stopPropagation();
onViewOutput();
}}
data-testid={`view-output-inprogress-${feature.id}`}
>
<Eye className="w-3 h-3 mr-1" />
Output
</Button>
)}
</>
)}
{!isCurrentAutoTask && feature.status !== "in_progress" && (
<>
<Button
variant="ghost"
size="sm"
className="flex-1 h-7 text-xs"
onClick={(e) => {
e.stopPropagation();
onEdit();
}}
data-testid={`edit-feature-${feature.id}`}
>
<Edit className="w-3 h-3 mr-1" />
Edit
</Button>
<Button
variant="ghost"
size="sm"
className="h-7 text-xs text-destructive hover:text-destructive hover:bg-destructive/10"
onClick={(e) => {
e.stopPropagation();
onDelete();
}}
data-testid={`delete-feature-${feature.id}`}
>
<Trash2 className="w-3 h-3" />
</Button>
</>
)}
</div>
</CardContent>
</Card>