TypeScript 97.5%
SQL 1.4%
Python 0.8%
1"use client";2import * as React from "react";3import { useRouter } from "next/navigation";4import { Archive, Check } from "lucide-react";5import { archiveProject, selectProject } from "@/actions/projects";6import { Button, type ButtonProps } from "@/components/ui/button";7import { Dialog, DialogContent, DialogDescription, DialogFooter, DialogHeader, DialogTitle } from "@/components/ui/dialog";8import { Alert } from "@/components/ui/alert";9import { Input } from "@/components/ui/input";10import { Field, Label } from "@/components/ui/label";1112/** Makes the project the current one (cookie) and refreshes the dashboard. */13export function SelectProjectButton({ projectId, size = "xs", variant = "outline", children }: { projectId: string } & Pick<ButtonProps, "size" | "variant" | "children">) {14 const router = useRouter();15 const [pending, startTransition] = React.useTransition();16 return (17 <Button18 size={size}19 variant={variant}20 loading={pending}21 onClick={() =>22 startTransition(async () => {23 await selectProject(projectId);24 router.refresh();25 })26 }27 >28 {children ?? (29 <>30 <Check /> Select31 </>32 )}33 </Button>34 );35}3637export function ArchiveProjectButton({ projectId, name, disabled, disabledReason }: { projectId: string; name: string; disabled?: boolean; disabledReason?: string }) {38 const router = useRouter();39 const [open, setOpen] = React.useState(false);40 const [confirmName, setConfirmName] = React.useState("");41 const [error, setError] = React.useState<string | null>(null);42 const [pending, startTransition] = React.useTransition();4344 return (45 <>46 <div className="flex flex-col items-start gap-1.5">47 <Button variant="danger" size="sm" disabled={disabled} onClick={() => setOpen(true)}>48 <Archive /> Archive project49 </Button>50 {disabled && disabledReason ? <p className="text-[12px] text-fg-subtle">{disabledReason}</p> : null}51 </div>52 <Dialog53 open={open}54 onOpenChange={(o) => {55 if (!pending) {56 setOpen(o);57 setError(null);58 setConfirmName("");59 }60 }}61 >62 <DialogContent size="sm">63 <DialogHeader>64 <DialogTitle>Archive {name}?</DialogTitle>65 <DialogDescription>66 Archived projects disappear from the dashboard and their API keys stop authenticating. Request history is kept for your plan's retention window. Contact support to restore an archived project.67 </DialogDescription>68 </DialogHeader>69 {error ? <Alert variant="danger">{error}</Alert> : null}70 <Field>71 <Label htmlFor="archive-confirm">72 Type <span className="font-mono">{name}</span> to confirm73 </Label>74 <Input id="archive-confirm" value={confirmName} onChange={(e) => setConfirmName(e.target.value)} autoComplete="off" autoFocus />75 </Field>76 <DialogFooter>77 <Button variant="outline" onClick={() => setOpen(false)} disabled={pending}>78 Cancel79 </Button>80 <Button81 variant="danger"82 disabled={confirmName.trim() !== name}83 loading={pending}84 onClick={() =>85 startTransition(async () => {86 const res = await archiveProject(projectId);87 if (!res.ok) {88 setError(res.error);89 return;90 }91 setOpen(false);92 router.push("/dashboard/projects");93 router.refresh();94 })95 }96 >97 Archive project98 </Button>99 </DialogFooter>100 </DialogContent>101 </Dialog>102 </>103 );104}105