|
2 | 2 |
|
3 | 3 | import { useCallback, useEffect, useMemo, useState } from "react"; |
4 | 4 | import { useRouter } from "next/navigation"; |
5 | | -import { ChevronsUpDown, RefreshCw } from "lucide-react"; |
| 5 | +import { ChevronsUpDown, RefreshCw, Trash2 } from "lucide-react"; |
6 | 6 | import { cn } from "@/lib/utils"; |
7 | 7 | import { wrappedTheme } from "@/lib/theme"; |
8 | 8 | import { Command, CommandEmpty, CommandGroup, CommandInput, CommandItem, CommandList } from "@/components/ui/command"; |
@@ -204,6 +204,39 @@ export default function ReposClient({ |
204 | 204 | } |
205 | 205 | } |
206 | 206 |
|
| 207 | + const [disconnecting, setDisconnecting] = useState<string | null>(null); |
| 208 | + |
| 209 | + async function disconnectRepo(repoId: string, repoName: string) { |
| 210 | + if (!confirm(`Disconnect "${repoName}"? This will remove it from your profile but won't delete any analysis data.`)) { |
| 211 | + return; |
| 212 | + } |
| 213 | + |
| 214 | + setDisconnecting(repoId); |
| 215 | + try { |
| 216 | + const response = await fetch("/api/repos/disconnect", { |
| 217 | + method: "POST", |
| 218 | + headers: { "Content-Type": "application/json" }, |
| 219 | + body: JSON.stringify({ repo_id: repoId }), |
| 220 | + }); |
| 221 | + |
| 222 | + if (!response.ok) { |
| 223 | + const body = await response.json(); |
| 224 | + throw new Error(body.error || "Could not disconnect repo"); |
| 225 | + } |
| 226 | + |
| 227 | + toast({ title: "Repo disconnected", description: `${repoName} has been removed.` }); |
| 228 | + router.refresh(); |
| 229 | + } catch (error) { |
| 230 | + toast({ |
| 231 | + variant: "destructive", |
| 232 | + title: "Disconnect failed", |
| 233 | + description: error instanceof Error ? error.message : "Please try again", |
| 234 | + }); |
| 235 | + } finally { |
| 236 | + setDisconnecting(null); |
| 237 | + } |
| 238 | + } |
| 239 | + |
207 | 240 | const connectedRepos = initialConnected.map((repo) => ({ |
208 | 241 | ...repo, |
209 | 242 | latestJobId: latestJobByRepoId[repo.repo_id] ?? null, |
@@ -300,27 +333,40 @@ export default function ReposClient({ |
300 | 333 | <span className="text-sm font-semibold text-zinc-900">{repo.full_name}</span> |
301 | 334 | {repo.latestJobId && <span className="text-[11px] text-zinc-500">Analyzed</span>} |
302 | 335 | </div> |
303 | | - {/* Only show action buttons in vibes mode */} |
304 | | - {mode === "vibes" && ( |
305 | | - <div className="flex items-center gap-2"> |
306 | | - {repo.latestJobId ? ( |
| 336 | + {/* Action buttons differ by mode */} |
| 337 | + <div className="flex items-center gap-2"> |
| 338 | + {mode === "vibes" && ( |
| 339 | + <> |
| 340 | + {repo.latestJobId ? ( |
| 341 | + <button |
| 342 | + type="button" |
| 343 | + className={cn(wrappedTheme.secondaryButton, "px-3 py-1 text-sm font-semibold")} |
| 344 | + onClick={() => router.push(`/analysis/${repo.latestJobId}`)} |
| 345 | + > |
| 346 | + View vibe |
| 347 | + </button> |
| 348 | + ) : null} |
307 | 349 | <button |
308 | 350 | type="button" |
309 | | - className={cn(wrappedTheme.secondaryButton, "px-3 py-1 text-sm font-semibold")} |
310 | | - onClick={() => router.push(`/analysis/${repo.latestJobId}`)} |
| 351 | + className="rounded-full border border-zinc-300/80 bg-white/70 px-3 py-1 text-sm font-semibold text-zinc-950 shadow-sm hover:bg-white" |
| 352 | + onClick={() => startAnalysis(repo.repo_id, repo.full_name)} |
311 | 353 | > |
312 | | - View vibe |
| 354 | + {repo.latestJobId ? "Re-run" : "Start vibe"} |
313 | 355 | </button> |
314 | | - ) : null} |
| 356 | + </> |
| 357 | + )} |
| 358 | + {mode === "settings" && ( |
315 | 359 | <button |
316 | 360 | type="button" |
317 | | - className="rounded-full border border-zinc-300/80 bg-white/70 px-3 py-1 text-sm font-semibold text-zinc-950 shadow-sm hover:bg-white" |
318 | | - onClick={() => startAnalysis(repo.repo_id, repo.full_name)} |
| 361 | + className="flex items-center gap-1.5 rounded-full border border-red-200 bg-white px-3 py-1 text-sm font-semibold text-red-600 transition hover:bg-red-50 hover:border-red-300 disabled:opacity-50" |
| 362 | + onClick={() => disconnectRepo(repo.repo_id, repo.full_name)} |
| 363 | + disabled={disconnecting === repo.repo_id} |
319 | 364 | > |
320 | | - {repo.latestJobId ? "Re-run" : "Start vibe"} |
| 365 | + <Trash2 className="h-3.5 w-3.5" /> |
| 366 | + {disconnecting === repo.repo_id ? "Removing…" : "Remove"} |
321 | 367 | </button> |
322 | | - </div> |
323 | | - )} |
| 368 | + )} |
| 369 | + </div> |
324 | 370 | </div> |
325 | 371 | ))} |
326 | 372 | </div> |
|
0 commit comments