Skip to content

Commit 99082d2

Browse files
author
Gerome El-assaad
committed
Add task-related components
1 parent fdff566 commit 99082d2

4 files changed

Lines changed: 321 additions & 0 deletions

File tree

components/page-header.tsx

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
'use client'
2+
3+
import { Button } from '@/components/ui/button'
4+
import { Menu } from 'lucide-react'
5+
import { ReactNode } from 'react'
6+
7+
interface PageHeaderProps {
8+
showMobileMenu?: boolean
9+
onToggleMobileMenu?: () => void
10+
isMobileSidebarOpen?: boolean
11+
actions?: ReactNode
12+
}
13+
14+
export function PageHeader({
15+
showMobileMenu = false,
16+
onToggleMobileMenu,
17+
isMobileSidebarOpen,
18+
actions
19+
}: PageHeaderProps) {
20+
return (
21+
<header className="sticky top-0 z-50 w-full border-b bg-background/95 backdrop-blur supports-[backdrop-filter]:bg-background/60">
22+
<div className="container flex h-14 items-center">
23+
<div className="flex items-center gap-2">
24+
{showMobileMenu && (
25+
<Button
26+
variant="ghost"
27+
size="sm"
28+
className="md:hidden h-8 w-8 p-0"
29+
onClick={onToggleMobileMenu}
30+
>
31+
<Menu className="h-4 w-4" />
32+
</Button>
33+
)}
34+
35+
<div className="flex items-center space-x-2">
36+
<h1 className="text-lg font-semibold">CodinIT</h1>
37+
</div>
38+
</div>
39+
40+
<div className="flex flex-1 items-center justify-end space-x-2">
41+
{actions}
42+
</div>
43+
</div>
44+
</header>
45+
)
46+
}

components/task-details.tsx

Lines changed: 146 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,146 @@
1+
'use client'
2+
3+
import { Task } from '@/lib/tasks'
4+
import { Card, CardContent, CardHeader, CardTitle } from '@/components/ui/card'
5+
import { Badge } from '@/components/ui/badge'
6+
import { Progress } from '@/components/ui/progress'
7+
import { ScrollArea } from '@/components/ui/scroll-area'
8+
9+
interface TaskDetailsProps {
10+
task: Task
11+
}
12+
13+
export function TaskDetails({ task }: TaskDetailsProps) {
14+
const getStatusColor = (status: Task['status']) => {
15+
switch (status) {
16+
case 'completed':
17+
return 'bg-green-500'
18+
case 'error':
19+
return 'bg-red-500'
20+
case 'processing':
21+
return 'bg-blue-500'
22+
default:
23+
return 'bg-gray-500'
24+
}
25+
}
26+
27+
return (
28+
<div className="space-y-6">
29+
{/* Task Info Card */}
30+
<Card>
31+
<CardHeader>
32+
<div className="flex items-start justify-between">
33+
<div className="space-y-2">
34+
<CardTitle className="text-xl">Task Details</CardTitle>
35+
<div className="flex items-center gap-2">
36+
<Badge className={getStatusColor(task.status)}>
37+
{task.status}
38+
</Badge>
39+
<span className="text-sm text-muted-foreground">
40+
Progress: {task.progress}%
41+
</span>
42+
</div>
43+
</div>
44+
</div>
45+
</CardHeader>
46+
<CardContent className="space-y-4">
47+
<div>
48+
<h4 className="font-medium mb-1">Prompt</h4>
49+
<p className="text-sm text-muted-foreground">{task.prompt}</p>
50+
</div>
51+
52+
<Progress value={task.progress} className="w-full" />
53+
54+
{task.repo_url && (
55+
<div>
56+
<h4 className="font-medium mb-1">Repository URL</h4>
57+
<p className="text-sm text-muted-foreground">{task.repo_url}</p>
58+
</div>
59+
)}
60+
61+
{task.selected_agent && (
62+
<div>
63+
<h4 className="font-medium mb-1">Selected Agent</h4>
64+
<p className="text-sm text-muted-foreground">{task.selected_agent}</p>
65+
</div>
66+
)}
67+
68+
{task.sandbox_url && (
69+
<div>
70+
<h4 className="font-medium mb-1">Sandbox URL</h4>
71+
<a
72+
href={task.sandbox_url}
73+
target="_blank"
74+
rel="noopener noreferrer"
75+
className="text-sm text-blue-600 hover:underline"
76+
>
77+
{task.sandbox_url}
78+
</a>
79+
</div>
80+
)}
81+
82+
<div className="grid grid-cols-2 gap-4 text-sm">
83+
<div>
84+
<span className="font-medium">Created:</span>{' '}
85+
<span className="text-muted-foreground">
86+
{new Date(task.created_at).toLocaleString()}
87+
</span>
88+
</div>
89+
<div>
90+
<span className="font-medium">Updated:</span>{' '}
91+
<span className="text-muted-foreground">
92+
{new Date(task.updated_at).toLocaleString()}
93+
</span>
94+
</div>
95+
</div>
96+
</CardContent>
97+
</Card>
98+
99+
{/* Logs Card */}
100+
<Card>
101+
<CardHeader>
102+
<CardTitle>Task Logs</CardTitle>
103+
</CardHeader>
104+
<CardContent>
105+
<ScrollArea className="h-96">
106+
<div className="space-y-2">
107+
{task.logs.length > 0 ? (
108+
task.logs.map((log) => (
109+
<div
110+
key={log.id}
111+
className="flex items-start gap-2 text-sm p-2 rounded-md bg-muted/50"
112+
>
113+
<Badge
114+
variant="outline"
115+
className={`text-xs ${
116+
log.type === 'error'
117+
? 'border-red-200 text-red-700'
118+
: log.type === 'success'
119+
? 'border-green-200 text-green-700'
120+
: log.type === 'command'
121+
? 'border-blue-200 text-blue-700'
122+
: 'border-gray-200 text-gray-700'
123+
}`}
124+
>
125+
{log.type}
126+
</Badge>
127+
<div className="flex-1 space-y-1">
128+
<p className="text-muted-foreground">{log.message}</p>
129+
<p className="text-xs text-muted-foreground">
130+
{new Date(log.timestamp).toLocaleString()}
131+
</p>
132+
</div>
133+
</div>
134+
))
135+
) : (
136+
<p className="text-muted-foreground text-center py-8">
137+
No logs available yet
138+
</p>
139+
)}
140+
</div>
141+
</ScrollArea>
142+
</CardContent>
143+
</Card>
144+
</div>
145+
)
146+
}

components/task-page-client.tsx

Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,66 @@
1+
'use client'
2+
3+
import { useTask } from '@/hooks/use-task'
4+
import { TaskDetails } from '@/components/task-details'
5+
import { TaskPageHeader } from '@/components/task-page-header'
6+
import { Card, CardContent } from '@/components/ui/card'
7+
8+
interface TaskPageClientProps {
9+
taskId: string
10+
}
11+
12+
export function TaskPageClient({ taskId }: TaskPageClientProps) {
13+
const { task, isLoading, error } = useTask(taskId)
14+
15+
if (isLoading) {
16+
return (
17+
<div className="flex-1 bg-background">
18+
<div className="mx-auto p-3">
19+
<div className="max-w-4xl mx-auto">
20+
<div className="flex-1 p-6 overflow-y-auto">
21+
<div className="max-w-4xl mx-auto space-y-6">
22+
{/* Task Info Skeleton - 339px height */}
23+
<Card className="h-[339px]">
24+
<CardContent className="space-y-4"></CardContent>
25+
</Card>
26+
27+
{/* Logs Skeleton - 512px height */}
28+
<Card className="h-[512px]">
29+
<CardContent></CardContent>
30+
</Card>
31+
</div>
32+
</div>
33+
</div>
34+
</div>
35+
</div>
36+
)
37+
}
38+
39+
if (error || !task) {
40+
return (
41+
<div className="flex-1 bg-background">
42+
<div className="mx-auto p-3">
43+
<div className="flex items-center justify-center h-64">
44+
<div className="text-center">
45+
<h2 className="text-lg font-semibold mb-2">Task Not Found</h2>
46+
<p className="text-muted-foreground">{error || 'The requested task could not be found.'}</p>
47+
</div>
48+
</div>
49+
</div>
50+
</div>
51+
)
52+
}
53+
54+
return (
55+
<div className="flex-1 bg-background">
56+
<div className="mx-auto p-3">
57+
<TaskPageHeader task={task} />
58+
59+
{/* Task details */}
60+
<div className="max-w-4xl mx-auto">
61+
<TaskDetails task={task} />
62+
</div>
63+
</div>
64+
</div>
65+
)
66+
}

components/task-page-header.tsx

Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,63 @@
1+
'use client'
2+
3+
import { Task } from '@/lib/tasks'
4+
import { Button } from '@/components/ui/button'
5+
import { Badge } from '@/components/ui/badge'
6+
import { MoreHorizontal, ExternalLink } from 'lucide-react'
7+
8+
interface TaskPageHeaderProps {
9+
task: Task
10+
}
11+
12+
export function TaskPageHeader({ task }: TaskPageHeaderProps) {
13+
const getStatusColor = (status: Task['status']) => {
14+
switch (status) {
15+
case 'completed':
16+
return 'bg-green-500'
17+
case 'error':
18+
return 'bg-red-500'
19+
case 'processing':
20+
return 'bg-blue-500'
21+
default:
22+
return 'bg-gray-500'
23+
}
24+
}
25+
26+
return (
27+
<div className="border-b bg-background px-6 py-4">
28+
<div className="flex items-center justify-between">
29+
<div className="space-y-1">
30+
<div className="flex items-center gap-2">
31+
<h1 className="text-xl font-semibold">Task {task.id.slice(0, 8)}</h1>
32+
<Badge className={getStatusColor(task.status)}>
33+
{task.status}
34+
</Badge>
35+
</div>
36+
<p className="text-sm text-muted-foreground max-w-2xl">
37+
{task.prompt}
38+
</p>
39+
</div>
40+
41+
<div className="flex items-center gap-2">
42+
{task.sandbox_url && (
43+
<Button variant="outline" size="sm" asChild>
44+
<a
45+
href={task.sandbox_url}
46+
target="_blank"
47+
rel="noopener noreferrer"
48+
className="flex items-center gap-2"
49+
>
50+
<ExternalLink className="h-4 w-4" />
51+
View Sandbox
52+
</a>
53+
</Button>
54+
)}
55+
56+
<Button variant="ghost" size="sm" className="h-8 w-8 p-0">
57+
<MoreHorizontal className="h-4 w-4" />
58+
</Button>
59+
</div>
60+
</div>
61+
</div>
62+
)
63+
}

0 commit comments

Comments
 (0)