Skip to content

Commit 56e7c49

Browse files
committed
feat: add BackgroundJobsSidebar and Sidebar components for background job management
1 parent 7978975 commit 56e7c49

File tree

2 files changed

+121
-0
lines changed

2 files changed

+121
-0
lines changed
Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,65 @@
1+
import { Sidebar } from './ui/sidebar'
2+
import { useHasMounted } from '@/hooks/useHasMounted'
3+
import { useBackgroundJobs } from '@/hooks/useBackgroundJobs'
4+
import { Clock } from 'lucide-react'
5+
import { BackgroundJobItem } from './BackgroundJobItem'
6+
7+
interface BackgroundJobsSidebarProps {
8+
isOpen: boolean
9+
onClose: () => void
10+
onLoadResponse?: (jobId: string, response: string) => void
11+
onCancelJob?: (jobId: string) => void
12+
}
13+
14+
export function BackgroundJobsSidebar({
15+
isOpen,
16+
onClose,
17+
onLoadResponse,
18+
onCancelJob,
19+
}: BackgroundJobsSidebarProps) {
20+
const { jobs, updateJob, removeJob } = useBackgroundJobs()
21+
const hasMounted = useHasMounted()
22+
23+
if (!hasMounted) {
24+
return null
25+
}
26+
27+
return (
28+
<Sidebar
29+
isOpen={isOpen}
30+
onClose={onClose}
31+
title="Background Jobs"
32+
className="w-96"
33+
>
34+
<div className="space-y-4">
35+
{jobs.length === 0 ? (
36+
<div
37+
className="text-center text-muted-foreground py-8"
38+
aria-live="polite"
39+
>
40+
<Clock
41+
className="h-8 w-8 mx-auto mb-2 opacity-50"
42+
aria-hidden="true"
43+
/>
44+
<p>No background jobs</p>
45+
</div>
46+
) : (
47+
<ul aria-label="Background jobs" className="grid gap-2">
48+
{jobs.map((job) => (
49+
<li>
50+
<BackgroundJobItem
51+
key={job.id}
52+
job={job}
53+
onLoadResponse={onLoadResponse}
54+
onCancelJob={onCancelJob}
55+
updateJob={updateJob}
56+
removeJob={removeJob}
57+
/>
58+
</li>
59+
))}
60+
</ul>
61+
)}
62+
</div>
63+
</Sidebar>
64+
)
65+
}

src/components/ui/sidebar.tsx

Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
import * as React from 'react'
2+
import { cn } from '@/lib/utils'
3+
import { Button } from './button'
4+
import { X } from 'lucide-react'
5+
6+
interface SidebarProps {
7+
children: React.ReactNode
8+
isOpen: boolean
9+
onClose: () => void
10+
title: string
11+
className?: string
12+
}
13+
14+
export function Sidebar({
15+
children,
16+
isOpen,
17+
onClose,
18+
title,
19+
className
20+
}: SidebarProps) {
21+
return (
22+
<>
23+
{/* Overlay */}
24+
{isOpen && (
25+
<div
26+
className="fixed inset-0 bg-black/50 z-40"
27+
onClick={onClose}
28+
/>
29+
)}
30+
31+
{/* Sidebar */}
32+
<div
33+
className={cn(
34+
'fixed right-0 top-0 h-full w-80 bg-background border-l shadow-lg transform transition-transform duration-200 ease-in-out z-50',
35+
isOpen ? 'translate-x-0' : 'translate-x-full',
36+
className
37+
)}
38+
>
39+
<div className="flex items-center justify-between p-4 border-b">
40+
<h2 className="text-lg font-semibold">{title}</h2>
41+
<Button
42+
variant="ghost"
43+
size="icon"
44+
onClick={onClose}
45+
className="h-8 w-8"
46+
>
47+
<X className="h-4 w-4" />
48+
</Button>
49+
</div>
50+
<div className="p-4 overflow-y-auto h-[calc(100vh-4rem)]">
51+
{children}
52+
</div>
53+
</div>
54+
</>
55+
)
56+
}

0 commit comments

Comments
 (0)