1import { useState, useEffect } from 'react'
2import { Folder, GitBranch, GitBranchPlus, GitMerge, CheckCircle2 } from 'lucide-react'
3import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from "@/components/ui/select";
4
5interface Task {
6 id: number;
7 title: string;
8 date: string;
9 status?: 'Open' | 'Merged' | 'Done';
10 changes: string;
11 statusColor?: 'green' | 'purple' | 'gray';
12}
13
14interface ProcessingTaskState {
15 id: number | null;
16 status: string;
17}
18
19const initialTasks: Task[] = [
20 {
21 id: 4,
22 title: 'Look at my config in sudo_documentation and import terminal sizes from terminal_emulator',
23 date: '',
24 changes: '+93 -58',
25 },
26 {
27 id: 1,
28 title: 'Scan the entire repository and flag any variables, parameters, or properties whose n...',
29 date: 'May 2 · monorepo',
30 status: 'Open',
31 changes: '+57 -27',
32 statusColor: 'green'
33 },
34 {
35 id: 2,
36 title: 'Convert non-critical components to React.lazy with Suspense fallbacks',
37 date: '2 hours ago · monorepo',
38 status: 'Merged',
39 changes: '+134 -45',
40 statusColor: 'purple'
41 },
42 {
43 id: 3,
44 title: 'Create a CI workflow that runs ESLint on every PR and blocks on violations...',
45 date: '2 hours ago · monorepo',
46 changes: '+89 -33',
47 }
48];
49
50const archivedTasksData: Task[] = [
51 {
52 id: 5,
53 title: 'Refactor old class components to functional components with hooks',
54 date: 'Apr 28 · monorepo',
55 status: 'Done',
56 changes: '+250 -112',
57 statusColor: 'gray'
58 },
59 {
60 id: 6,
61 title: 'Update all deprecated dependencies and resolve peer dependency warnings',
62 date: 'Apr 25 · monorepo',
63 status: 'Done',
64 changes: '+15 -9',
65 statusColor: 'gray'
66 }
67];
68
69const ShimmerStyle = () => (
70 <style>{`
71 @keyframes shimmer {
72 0% { background-position: -200% 0; }
73 100% { background-position: 200% 0; }
74 }
75 .shimmer-text {
76 background-image: linear-gradient(to right, #a0a0a0 20%, #e5e7eb 50%, #a0a0a0 80%);
77 background-size: 200% auto;
78 color: transparent;
79 background-clip: text;
80 -webkit-background-clip: text;
81 animation: shimmer 2s linear infinite;
82 }
83 `}</style>
84);
85
86const BackgroundCodingAgent = () => {
87 const [input, setInput] = useState('')
88 const [activeTab, setActiveTab] = useState('Tasks')
89 const [selectedRepo, setSelectedRepo] = useState('monorepo')
90 const [selectedBranch, setSelectedBranch] = useState('main')
91
92 const [tasks, setTasks] = useState<Task[]>(initialTasks);
93 const [archivedTasks, setArchivedTasks] = useState<Task[]>(archivedTasksData);
94
95 const [isProcessing, setIsProcessing] = useState(false);
96 const [processingTask, setProcessingTask] = useState<ProcessingTaskState>({ id: null, status: '' });
97
98 const displayedTasks = activeTab === 'Tasks' ? tasks : archivedTasks;
99
100 const delay = (ms: number) => new Promise(res => setTimeout(res, ms));
101
102 useEffect(() => {
103 const runSimulation = async () => {
104 const targetTaskId = 4;
105 const taskToProcess = tasks.find(t => t.id === targetTaskId);
106 if (!taskToProcess || taskToProcess.status) return;
107
108 setIsProcessing(true);
109
110 const statusUpdates = [
111 { text: 'Analyzing request...', duration: 1500 },
112 { text: 'Scanning files...', duration: 2000 },
113 { text: 'Implementing changes...', duration: 2500 },
114 { text: 'Fixing linting issues...', duration: 1800 },
115 { text: 'Running tests...', duration: 2000 },
116 { text: 'Pushing to branch...', duration: 1500 },
117 ];
118
119 for (const update of statusUpdates) {
120 setProcessingTask({ id: targetTaskId, status: update.text });
121 await delay(update.duration);
122 }
123
124 setTasks(prevTasks =>
125 prevTasks.map(task =>
126 task.id === targetTaskId
127 ? {
128 ...task,
129 status: 'Open',
130 statusColor: 'green',
131 date: 'Just now · monorepo',
132 }
133 : task
134 )
135 );
136
137 setProcessingTask({ id: null, status: '' });
138 setIsProcessing(false);
139 };
140
141 runSimulation();
142 }, []);
143
144 return (
145 <div className='min-h-screen bg-white flex items-center justify-center p-4 font-inter'>
146 <ShimmerStyle />
147 <div className='relative w-full max-w-5xl rounded-3xl p-8 space-y-6'>
148 <div className='text-center'>
149 <h2 className='text-2xl font-semibold text-gray-900'>What should we code next?</h2>
150 </div>
151
152 <div className='space-y-4'>
153 <div className='border border-gray-200 rounded-3xl p-4'>
154 <textarea
155 value={input}
156 onChange={(e: React.ChangeEvent<HTMLTextAreaElement>) => setInput(e.target.value)}
157 placeholder='In my current project, find a bug in the last 5 commits and fix it!'
158 className='w-full bg-transparent resize-none outline-none text-sm text-gray-700 placeholder-gray-400 font-inter'
159 rows={3}
160 />
161
162 <div className='flex items-center justify-between mt-4 pt-4'>
163 <div className='flex gap-2'>
164 <Select value={selectedRepo} onValueChange={setSelectedRepo}>
165 <SelectTrigger className='flex items-center justify-center gap-2 px-3 py-1 rounded-full border border-gray-300 bg-white hover:bg-gray-50 text-sm text-gray-700 transition w-auto focus:ring-0'>
166 <Folder className='w-4 h-4' />
167 <SelectValue placeholder="Select repo" />
168 </SelectTrigger>
169 <SelectContent>
170 <SelectItem value="monorepo">monorepo</SelectItem>
171 <SelectItem value="frontend-app">frontend-app</SelectItem>
172 <SelectItem value="backend-api">backend-api</SelectItem>
173 </SelectContent>
174 </Select>
175 <Select value={selectedBranch} onValueChange={setSelectedBranch}>
176 <SelectTrigger className='flex items-center justify-center gap-2 px-3 py-1 rounded-full border border-gray-300 bg-white hover:bg-gray-50 text-sm text-gray-700 transition w-auto focus:ring-0'>
177 <GitBranch className='w-4 h-4' />
178 <SelectValue placeholder="Select branch" />
179 </SelectTrigger>
180 <SelectContent>
181 <SelectItem value="main">main</SelectItem>
182 <SelectItem value="develop">develop</SelectItem>
183 <SelectItem value="feat/new-ui">feat/new-ui</SelectItem>
184 </SelectContent>
185 </Select>
186 </div>
187 <div className='flex gap-2'>
188 <button className='px-4 py-1 rounded-full border border-gray-300 bg-white hover:bg-gray-50 text-sm text-gray-700 transition'>
189 Ask
190 </button>
191 <button
192 disabled={isProcessing}
193 className='px-4 py-1 rounded-full bg-black text-white text-sm hover:bg-gray-800 transition font-medium disabled:bg-gray-400 disabled:cursor-not-allowed'
194 >
195 Code
196 </button>
197 </div>
198 </div>
199 </div>
200 </div>
201
202 <div className='space-y-3'>
203 <div className='flex gap-4 border-b border-gray-200'>
204 <button
205 onClick={() => setActiveTab('Tasks')}
206 className={`px-3 py-2 text-sm font-medium transition border-b-2 ${activeTab === 'Tasks'
207 ? 'text-gray-900 border-gray-900'
208 : 'text-gray-400 hover:text-gray-700 border-transparent'
209 }`}
210 >
211 Tasks
212 </button>
213 <button
214 onClick={() => setActiveTab('Archive')}
215 className={`px-3 py-2 text-sm font-medium transition border-b-2 ${activeTab === 'Archive'
216 ? 'text-gray-900 border-gray-900'
217 : 'text-gray-400 hover:text-gray-700 border-transparent'
218 }`}
219 >
220 Archive
221 </button>
222 </div>
223
224 <div className='space-y-3'>
225 {displayedTasks.map((task) => (
226 <div key={task.id} className='flex items-center justify-between p-3 hover:bg-gray-50 rounded-lg transition group cursor-pointer border border-transparent hover:border-gray-200'>
227 <div className='flex-1 space-y-1 pr-4'>
228 <p className='text-sm text-gray-700 group-hover:text-gray-900'>{task.title}</p>
229 <p className='text-xs text-gray-400'>{task.date}</p>
230 </div>
231 <div className='flex items-center gap-3'>
232 {task.id === processingTask.id ? (
233 <span className='text-sm font-medium shimmer-text'>
234 {processingTask.status}
235 </span>
236 ) : task.status && (
237 <span className={`flex items-center gap-2 px-3 py-1.5 rounded-full text-xs font-medium ${task.statusColor === 'green' ? 'bg-green-100 text-green-700' :
238 task.statusColor === 'purple' ? 'bg-purple-100 text-purple-700' :
239 'bg-gray-100 text-gray-600'
240 }`}>
241 {task.status === 'Open' && <GitBranchPlus className='w-4 h-4' />}
242 {task.status === 'Merged' && <GitMerge className='w-4 h-4' />}
243 {task.status === 'Done' && <CheckCircle2 className='w-4 h-4' />}
244 {task.status}
245 </span>
246 )}
247 {task.id !== processingTask.id && (
248 <span className='text-xs font-mono font-semibold whitespace-nowrap'>
249 {(() => {
250 const parts = task.changes.split(' ');
251 const additions = parts.find(p => p.startsWith('+'));
252 const deletions = parts.find(p => p.startsWith('-'));
253 return (
254 <>
255 {additions && <span className="text-green-600">{additions}</span>}
256 {deletions && <span className={`text-red-600 ${additions ? 'ml-2' : ''}`}>{deletions}</span>}
257 </>
258 );
259 })()}
260 </span>
261 )}
262 </div>
263 </div>
264 ))}
265 </div>
266 </div>
267 </div>
268 </div>
269 )
270}
271
272export default BackgroundCodingAgent;