-
-
- {task.pr_url && task.pr_number && (
-
- )}
-
- #{task.id} • {getAgentIcon(task.agent || '')} {task.agent?.toUpperCase()}
-
+ <>
+
+ {tasks.map((task) => (
+
+
+
+
+ {task.pr_url && task.pr_number && (
+
+ )}
+
+ #{task.id} • {getAgentIcon(task.agent || '')} {task.agent?.toUpperCase()}
+
+
+
+ {(task.chat_messages as any[])?.[0]?.content?.substring(0, 50) || ''}...
+
+
+ {task.project ? (
+ <>
+
+ {task.project.repo_name}
+ >
+ ) : (
+ <>
+
+ Custom
+ >
+ )}
+ •
+ {new Date(task.created_at || '').toLocaleDateString()}
+
+
+
+ {task.status === "completed" && (
+
+ )}
+ {task.status === "running" && (
+
+ )}
+
+
+
+
+
+
-
- {(task.chat_messages as any[])?.[0]?.content?.substring(0, 50) || ''}...
-
-
- {task.project ? (
+ ))}
+
+
+ {/* Load More Button */}
+ {hasMoreTasks && (
+
+
+ {isLoadingMore ? (
<>
-
- {task.project.repo_name}
+
+ Loading...
>
) : (
<>
-
- Custom
+
+ Load More
>
)}
- •
- {new Date(task.created_at || '').toLocaleDateString()}
-
+
-
- {task.status === "completed" && (
-
- )}
- {task.status === "running" && (
-
- )}
-
-
-
-
-
-
-
- ))
+ )}
+ >
)}
diff --git a/async-code-web/app/tasks/page.tsx b/async-code-web/app/tasks/page.tsx
deleted file mode 100644
index 46ffc6f..0000000
--- a/async-code-web/app/tasks/page.tsx
+++ /dev/null
@@ -1,481 +0,0 @@
-"use client";
-
-import { useState, useEffect } from "react";
-import { ArrowLeft, Github, Clock, CheckCircle, XCircle, AlertCircle, Eye, Code2, Filter, Search, Calendar, FolderGit2, ExternalLink, RefreshCw } from "lucide-react";
-import Link from "next/link";
-import { Button } from "@/components/ui/button";
-import { Input } from "@/components/ui/input";
-import { Card, CardContent, CardDescription, CardHeader, CardTitle } from "@/components/ui/card";
-import { Badge } from "@/components/ui/badge";
-import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from "@/components/ui/select";
-import { ProtectedRoute } from "@/components/protected-route";
-import { TaskStatusBadge } from "@/components/task-status-badge";
-import { PRStatusBadge } from "@/components/pr-status-badge";
-import { useAuth } from "@/contexts/auth-context";
-import { ApiService } from "@/lib/api-service";
-import { SupabaseService } from "@/lib/supabase-service";
-import { Task, Project } from "@/types";
-
-interface TaskWithProject extends Task {
- project?: Project
-}
-
-export default function TasksPage() {
- const { user } = useAuth();
- const [tasks, setTasks] = useState
([]);
- const [projects, setProjects] = useState([]);
- const [loading, setLoading] = useState(true);
- const [searchQuery, setSearchQuery] = useState("");
- const [statusFilter, setStatusFilter] = useState("all");
- const [projectFilter, setProjectFilter] = useState("all");
- const [sortBy, setSortBy] = useState("created_at");
-
- useEffect(() => {
- if (user?.id) {
- loadData();
- }
- }, [user?.id]);
-
- // Poll for status updates of running tasks
- useEffect(() => {
- if (!user?.id) return;
-
- const runningTasks = tasks.filter(task => task.status === "running" || task.status === "pending");
- if (runningTasks.length === 0) return;
-
- const interval = setInterval(async () => {
- try {
- const updatedTasks = await Promise.all(
- runningTasks.map(task => SupabaseService.getTask(task.id))
- );
-
- setTasks(prevTasks =>
- prevTasks.map(task => {
- const updated = updatedTasks.find(t => t && t.id === task.id);
- if (updated) {
- return { ...task, ...updated };
- }
- return task;
- })
- );
- } catch (error) {
- console.error('Error polling task status:', error);
- }
- }, 3000);
-
- return () => clearInterval(interval);
- }, [tasks, user?.id]);
-
- const loadData = async () => {
- if (!user?.id) return;
-
- try {
- setLoading(true);
- const [taskData, projectData] = await Promise.all([
- SupabaseService.getTasks(),
- ApiService.getProjects(user.id)
- ]);
-
- // Enhance tasks with project data
- const tasksWithProjects = taskData.map((task: any) => ({
- ...task,
- project: projectData.find(p => p.id === task.project_id)
- }));
-
- setTasks(tasksWithProjects);
- setProjects(projectData);
- } catch (error) {
- console.error('Error loading data:', error);
- } finally {
- setLoading(false);
- }
- };
-
- const getStatusVariant = (status: string) => {
- switch (status) {
- case "pending": return "secondary";
- case "running": return "default";
- case "completed": return "default";
- case "failed": return "destructive";
- default: return "outline";
- }
- };
-
- const getStatusIcon = (status: string) => {
- switch (status) {
- case "pending": return ;
- case "running": return ;
- case "completed": return ;
- case "failed": return ;
- default: return null;
- }
- };
-
- const getPromptFromTask = (task: TaskWithProject): string => {
- if (task.chat_messages &&
- Array.isArray(task.chat_messages) &&
- task.chat_messages.length > 0) {
- const firstMessage = task.chat_messages[0];
- if (firstMessage &&
- typeof firstMessage === 'object' &&
- firstMessage !== null &&
- 'content' in firstMessage &&
- typeof firstMessage.content === 'string') {
- return firstMessage.content;
- }
- }
- return 'No prompt available';
- };
-
- // Filter and sort tasks
- const filteredTasks = tasks
- .filter(task => {
- // Search filter
- if (searchQuery) {
- const prompt = getPromptFromTask(task).toLowerCase();
- const projectName = task.project?.name?.toLowerCase() || '';
- const repoUrl = task.repo_url?.toLowerCase() || '';
- const query = searchQuery.toLowerCase();
-
- if (!prompt.includes(query) && !projectName.includes(query) && !repoUrl.includes(query)) {
- return false;
- }
- }
-
- // Status filter
- if (statusFilter !== "all" && task.status !== statusFilter) {
- return false;
- }
-
- // Project filter
- if (projectFilter !== "all") {
- if (projectFilter === "no-project" && task.project_id) {
- return false;
- }
- if (projectFilter !== "no-project" && task.project_id?.toString() !== projectFilter) {
- return false;
- }
- }
-
- return true;
- })
- .sort((a, b) => {
- switch (sortBy) {
- case "created_at":
- return new Date(b.created_at || 0).getTime() - new Date(a.created_at || 0).getTime();
- case "status":
- return (a.status || '').localeCompare(b.status || '');
- case "project":
- return (a.project?.name || '').localeCompare(b.project?.name || '');
- default:
- return 0;
- }
- });
-
- const statusCounts = {
- all: tasks.length,
- pending: tasks.filter(t => t.status === "pending").length,
- running: tasks.filter(t => t.status === "running").length,
- completed: tasks.filter(t => t.status === "completed").length,
- failed: tasks.filter(t => t.status === "failed").length,
- };
-
- if (loading) {
- return (
-
-
-
- );
- }
-
- return (
-
-
- {/* Header */}
-
-
-
-
-
-
- Back to Dashboard
-
-
-
All Tasks
-
- {filteredTasks.length} of {tasks.length} tasks
-
-
-
-
-
-
- Refresh
-
-
-
-
- New Task
-
-
-
-
-
-
-
- {/* Main Content */}
-
- {/* Status Overview Cards */}
-
-
-
-
-
Total
-
{statusCounts.all}
-
-
-
-
-
-
-
-
Pending
-
{statusCounts.pending}
-
-
-
-
-
-
-
-
Running
-
{statusCounts.running}
-
-
-
-
-
-
-
-
Completed
-
{statusCounts.completed}
-
-
-
-
-
-
-
-
Failed
-
{statusCounts.failed}
-
-
-
-
-
-
- {/* Filters and Search */}
-
-
-
-
- Filters
-
-
-
-
-
-
Search
-
-
- setSearchQuery(e.target.value)}
- className="pl-10"
- />
-
-
-
- Status
-
-
-
-
-
- All Statuses
- Pending
- Running
- Completed
- Failed
-
-
-
-
- Project
-
-
-
-
-
- All Projects
- No Project
- {projects.map((project) => (
-
- {project.name}
-
- ))}
-
-
-
-
- Sort By
-
-
-
-
-
- Date Created
- Status
- Project
-
-
-
-
-
-
-
- {/* Tasks List */}
- {filteredTasks.length === 0 ? (
-
-
-
- No tasks found
-
- {tasks.length === 0
- ? "You haven't created any tasks yet. Start by creating your first task."
- : "No tasks match your current filters. Try adjusting your search criteria."
- }
-
-
-
-
- Create Your First Task
-
-
-
-
- ) : (
-
- {filteredTasks.map((task) => (
-
-
-
-
-
-
-
-
-
-
-
- {getPromptFromTask(task)}
-
- {task.pr_url && task.pr_number && (
-
- )}
-
-
-
-
- Task #{task.id}
-
-
•
-
- {task.agent?.toUpperCase()}
-
- {task.project && (
- <>
-
•
-
-
- {task.project.repo_owner}/{task.project.repo_name}
-
- >
- )}
-
-
-
-
-
-
- {task.repo_url}
-
-
-
-
- {new Date(task.created_at || '').toLocaleDateString()}
-
-
-
-
-
- {task.error && (
-
- Error: {task.error}
-
- )}
-
- {task.status === "running" && (
-
-
-
AI is working on this task...
-
- )}
-
-
-
-
-
-
- View
-
-
- {task.pr_url && task.pr_number && (
-
- )}
-
-
-
-
- ))}
-
- )}
-
-
-
- );
-}
\ No newline at end of file
diff --git a/async-code-web/lib/supabase-service.ts b/async-code-web/lib/supabase-service.ts
index 0d985d2..8a6ea20 100644
--- a/async-code-web/lib/supabase-service.ts
+++ b/async-code-web/lib/supabase-service.ts
@@ -87,7 +87,10 @@ export class SupabaseService {
}
// Task operations
- static async getTasks(projectId?: number): Promise {
+ static async getTasks(projectId?: number, options?: {
+ limit?: number
+ offset?: number
+ }): Promise {
// Get current authenticated user
const { data: { user } } = await this.supabase.auth.getUser()
if (!user) throw new Error('No authenticated user')
@@ -109,6 +112,13 @@ export class SupabaseService {
query = query.eq('project_id', projectId)
}
+ // Add pagination if options provided
+ if (options?.limit) {
+ const start = options.offset || 0
+ const end = start + options.limit - 1
+ query = query.range(start, end)
+ }
+
const { data, error } = await query.order('created_at', { ascending: false })
if (error) throw error