Skip to content

Commit c810c58

Browse files
refactor: migrate to supabase
1 parent d9d4201 commit c810c58

File tree

19 files changed

+5247
-866
lines changed

19 files changed

+5247
-866
lines changed

async-code-web/app/page.tsx

Lines changed: 480 additions & 356 deletions
Large diffs are not rendered by default.
Lines changed: 247 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,247 @@
1+
"use client";
2+
3+
import { useState, useEffect } from "react";
4+
import { useParams } from "next/navigation";
5+
import { ArrowLeft, Clock, CheckCircle, XCircle, AlertCircle, Eye, Plus, Github, FolderGit2 } from "lucide-react";
6+
import Link from "next/link";
7+
import { Button } from "@/components/ui/button";
8+
import { Card, CardContent, CardDescription, CardHeader, CardTitle } from "@/components/ui/card";
9+
import { Badge } from "@/components/ui/badge";
10+
import { ProtectedRoute } from "@/components/protected-route";
11+
import { useAuth } from "@/contexts/auth-context";
12+
import { ApiService } from "@/lib/api-service";
13+
import { Task, Project } from "@/types";
14+
15+
interface TaskWithProject extends Task {
16+
project?: Project
17+
}
18+
19+
export default function ProjectTasksPage() {
20+
const { user } = useAuth();
21+
const params = useParams();
22+
const projectId = parseInt(params.id as string);
23+
24+
const [project, setProject] = useState<Project | null>(null);
25+
const [tasks, setTasks] = useState<TaskWithProject[]>([]);
26+
const [loading, setLoading] = useState(true);
27+
28+
useEffect(() => {
29+
if (user?.id && projectId) {
30+
loadProject();
31+
loadTasks();
32+
}
33+
}, [user?.id, projectId]);
34+
35+
const loadProject = async () => {
36+
if (!user?.id) return;
37+
38+
try {
39+
const projectData = await ApiService.getProject(user.id, projectId);
40+
setProject(projectData);
41+
} catch (error) {
42+
console.error('Error loading project:', error);
43+
}
44+
};
45+
46+
const loadTasks = async () => {
47+
if (!user?.id) return;
48+
49+
try {
50+
setLoading(true);
51+
const taskData = await ApiService.getTasks(user.id, projectId);
52+
setTasks(taskData);
53+
} catch (error) {
54+
console.error('Error loading tasks:', error);
55+
} finally {
56+
setLoading(false);
57+
}
58+
};
59+
60+
const getStatusVariant = (status: string) => {
61+
switch (status) {
62+
case "pending": return "secondary";
63+
case "running": return "default";
64+
case "completed": return "default";
65+
case "failed": return "destructive";
66+
default: return "outline";
67+
}
68+
};
69+
70+
const getStatusIcon = (status: string) => {
71+
switch (status) {
72+
case "pending": return <Clock className="w-4 h-4" />;
73+
case "running": return <AlertCircle className="w-4 h-4" />;
74+
case "completed": return <CheckCircle className="w-4 h-4" />;
75+
case "failed": return <XCircle className="w-4 h-4" />;
76+
default: return null;
77+
}
78+
};
79+
80+
if (loading) {
81+
return (
82+
<ProtectedRoute>
83+
<div className="min-h-screen bg-gradient-to-br from-slate-50 to-slate-100 flex items-center justify-center">
84+
<div className="text-center">
85+
<div className="animate-spin rounded-full h-8 w-8 border-b-2 border-slate-900 mx-auto"></div>
86+
<p className="text-slate-600 mt-2">Loading tasks...</p>
87+
</div>
88+
</div>
89+
</ProtectedRoute>
90+
);
91+
}
92+
93+
if (!project) {
94+
return (
95+
<ProtectedRoute>
96+
<div className="min-h-screen bg-gradient-to-br from-slate-50 to-slate-100 flex items-center justify-center">
97+
<div className="text-center">
98+
<XCircle className="w-16 h-16 text-slate-300 mx-auto mb-4" />
99+
<h3 className="text-xl font-semibold text-slate-900 mb-2">Project Not Found</h3>
100+
<p className="text-slate-600 mb-6">The project you're looking for doesn't exist or you don't have access to it.</p>
101+
<Link href="/projects">
102+
<Button>Back to Projects</Button>
103+
</Link>
104+
</div>
105+
</div>
106+
</ProtectedRoute>
107+
);
108+
}
109+
110+
return (
111+
<ProtectedRoute>
112+
<div className="min-h-screen bg-gradient-to-br from-slate-50 to-slate-100">
113+
{/* Header */}
114+
<header className="border-b bg-white/80 backdrop-blur-sm sticky top-0 z-50">
115+
<div className="container mx-auto px-6 py-4">
116+
<div className="flex items-center justify-between">
117+
<div className="flex items-center gap-4">
118+
<Link href="/projects" className="text-slate-600 hover:text-slate-900 flex items-center gap-2">
119+
<ArrowLeft className="w-4 h-4" />
120+
Back to Projects
121+
</Link>
122+
<div>
123+
<h1 className="text-xl font-semibold text-slate-900 flex items-center gap-2">
124+
<FolderGit2 className="w-5 h-5" />
125+
{project.name} Tasks
126+
</h1>
127+
<p className="text-sm text-slate-500 flex items-center gap-1">
128+
<Github className="w-3 h-3" />
129+
{project.repo_owner}/{project.repo_name}
130+
</p>
131+
</div>
132+
</div>
133+
<Link href={`/?project=${projectId}`}>
134+
<Button className="gap-2">
135+
<Plus className="w-4 h-4" />
136+
New Task
137+
</Button>
138+
</Link>
139+
</div>
140+
</div>
141+
</header>
142+
143+
{/* Main Content */}
144+
<main className="container mx-auto px-6 py-8 max-w-4xl">
145+
{/* Project Info */}
146+
<Card className="mb-6">
147+
<CardContent className="pt-6">
148+
<div className="grid grid-cols-1 md:grid-cols-3 gap-4">
149+
<div className="text-center p-4 bg-slate-50 rounded-lg">
150+
<div className="text-2xl font-bold text-slate-900">{tasks.length}</div>
151+
<div className="text-sm text-slate-500">Total Tasks</div>
152+
</div>
153+
<div className="text-center p-4 bg-green-50 rounded-lg">
154+
<div className="text-2xl font-bold text-green-700">
155+
{tasks.filter(t => t.status === 'completed').length}
156+
</div>
157+
<div className="text-sm text-green-600">Completed</div>
158+
</div>
159+
<div className="text-center p-4 bg-blue-50 rounded-lg">
160+
<div className="text-2xl font-bold text-blue-700">
161+
{tasks.filter(t => t.status === 'running' || t.status === 'pending').length}
162+
</div>
163+
<div className="text-sm text-blue-600">Active</div>
164+
</div>
165+
</div>
166+
</CardContent>
167+
</Card>
168+
169+
{/* Tasks List */}
170+
<Card>
171+
<CardHeader>
172+
<CardTitle>All Tasks</CardTitle>
173+
<CardDescription>
174+
Automation tasks for this project
175+
</CardDescription>
176+
</CardHeader>
177+
<CardContent>
178+
{tasks.length === 0 ? (
179+
<div className="text-center py-12">
180+
<AlertCircle className="w-16 h-16 text-slate-300 mx-auto mb-4" />
181+
<h3 className="text-xl font-semibold text-slate-900 mb-2">No tasks yet</h3>
182+
<p className="text-slate-600 mb-6">Start your first automation task for this project</p>
183+
<Link href={`/?project=${projectId}`}>
184+
<Button size="lg" className="gap-2">
185+
<Plus className="w-4 h-4" />
186+
Create First Task
187+
</Button>
188+
</Link>
189+
</div>
190+
) : (
191+
<div className="space-y-4">
192+
{tasks.map((task) => (
193+
<div key={task.id} className="flex items-center justify-between p-4 border rounded-lg hover:bg-slate-50">
194+
<div className="flex-1 min-w-0">
195+
<div className="flex items-center gap-3 mb-2">
196+
<Badge variant={getStatusVariant(task.status || '')} className="gap-1">
197+
{getStatusIcon(task.status || '')}
198+
{task.status}
199+
</Badge>
200+
<span className="text-sm text-slate-500">
201+
Task #{task.id}
202+
</span>
203+
<span className="text-sm text-slate-500">
204+
{task.agent?.toUpperCase()}
205+
</span>
206+
</div>
207+
<p className="text-sm font-medium text-slate-900 truncate mb-1">
208+
{task.chat_messages?.[0]?.content || 'No prompt available'}
209+
</p>
210+
<div className="flex items-center gap-4 text-xs text-slate-500">
211+
<span>Created: {new Date(task.created_at || '').toLocaleString()}</span>
212+
{task.completed_at && (
213+
<span>Completed: {new Date(task.completed_at).toLocaleString()}</span>
214+
)}
215+
{task.commit_hash && (
216+
<span>Commit: {task.commit_hash.substring(0, 8)}</span>
217+
)}
218+
{task.pr_number && (
219+
<span>PR: #{task.pr_number}</span>
220+
)}
221+
</div>
222+
</div>
223+
<div className="flex items-center gap-2">
224+
{task.pr_url && (
225+
<Button variant="outline" size="sm" asChild>
226+
<a href={task.pr_url} target="_blank" rel="noopener noreferrer">
227+
<Github className="w-3 h-3" />
228+
</a>
229+
</Button>
230+
)}
231+
<Link href={`/tasks/${task.id}`}>
232+
<Button variant="outline" size="sm">
233+
<Eye className="w-4 h-4" />
234+
</Button>
235+
</Link>
236+
</div>
237+
</div>
238+
))}
239+
</div>
240+
)}
241+
</CardContent>
242+
</Card>
243+
</main>
244+
</div>
245+
</ProtectedRoute>
246+
);
247+
}

0 commit comments

Comments
 (0)