blob: 6903c5e48c1efddcb1ade566ae3e910a2ddcb6cf [file] [log] [blame]
gio5f2f1002025-03-20 18:38:48 +04001import { ChangeEvent, useCallback, useEffect, useState } from "react";
2import { Project, useProjectId, useStateStore } from "./lib/state";
3import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from "./components/ui/select";
gio5f2f1002025-03-20 18:38:48 +04004import { Input } from "./components/ui/input";
5import { Button } from "./components/ui/button";
6import { Dialog, DialogContent, DialogTrigger } from "./components/ui/dialog";
7import { useToast } from "@/hooks/use-toast";
giobc47f9f2025-05-12 08:31:07 +00008import { Separator } from "./components/ui/separator";
9import { Plus } from "lucide-react";
gio5f2f1002025-03-20 18:38:48 +040010
gio880de162025-05-11 07:26:00 +000011export function ProjectSelect() {
giod0026612025-05-08 13:00:36 +000012 const { toast } = useToast();
13 const store = useStateStore();
14 const [projects, setProjects] = useState<Project[]>([]);
gio7461e502025-05-12 10:11:55 +000015 const projectId = useProjectId();
16
giod0026612025-05-08 13:00:36 +000017 const refreshProjects = useCallback(async () => {
18 try {
19 const resp = await fetch("/api/project");
gio7461e502025-05-12 10:11:55 +000020 const projectList = await resp.json();
21 const sortedProjects = [...projectList].sort((a, b) =>
22 a.name.localeCompare(b.name, undefined, { sensitivity: "base" }),
23 );
24 setProjects(sortedProjects);
giod0026612025-05-08 13:00:36 +000025 } catch (e) {
26 console.log(e);
27 }
28 }, [setProjects]);
gio7461e502025-05-12 10:11:55 +000029
30 useEffect(() => {
31 if (projects.length > 0 && (projectId == null || !projects.some((p) => p.id === projectId))) {
32 store.setProject(projects[0].id);
33 }
34 }, [projectId, projects, store]);
giod0026612025-05-08 13:00:36 +000035 useEffect(() => {
36 refreshProjects();
37 }, [refreshProjects]);
gio7461e502025-05-12 10:11:55 +000038
giod0026612025-05-08 13:00:36 +000039 const [createNewOpen, setCreateNewOpen] = useState(false);
40 const onSelect = useCallback(
41 (projectId: string) => {
42 if (projectId === "create-new") {
43 setCreateNewOpen(true);
44 } else {
45 store.setProject(projectId);
46 }
47 },
48 [store],
49 );
giod0026612025-05-08 13:00:36 +000050 const [name, setName] = useState<string | undefined>(undefined);
51 const updateName = useCallback(
52 (e: ChangeEvent<HTMLInputElement>) => {
53 setName(e.target.value);
54 },
55 [setName],
56 );
57 const createNew = useCallback(() => {
58 console.log(name);
59 if (!name) {
60 return;
61 }
62 fetch("/api/project", {
63 method: "POST",
64 headers: {
65 "Content-Type": "application/json",
66 },
67 body: JSON.stringify({
68 name: name,
69 }),
70 })
71 .then(async (resp) => {
72 if (!resp.ok) {
73 return;
74 }
75 const { id } = await resp.json();
76 await refreshProjects();
77 store.setProject(id as string);
78 setCreateNewOpen(false);
79 toast({
80 title: `Created project: ${name}`,
81 });
82 })
83 .catch((e) => {
84 console.log(e);
85 toast({
86 variant: "destructive",
87 title: `Failed to create project: ${name}`,
88 });
89 });
90 }, [name, setCreateNewOpen, toast, store, refreshProjects]);
91 return (
gio4b9b58a2025-05-12 11:46:08 +000092 <Select onValueChange={onSelect} value={projectId}>
giobc47f9f2025-05-12 08:31:07 +000093 <SelectTrigger className="w-[200px] !border-none !shadow-none !focus:ring-0 !focus:ring-offset-0">
gio4b9b58a2025-05-12 11:46:08 +000094 <SelectValue placeholder="Choose Project" defaultValue={projectId} />
gio880de162025-05-11 07:26:00 +000095 </SelectTrigger>
96 <SelectContent>
giobc47f9f2025-05-12 08:31:07 +000097 {projects.map((p) => (
98 <SelectItem key={p.id} value={p.id}>
99 {p.name}
100 </SelectItem>
101 ))}
102 <Separator />
gio880de162025-05-11 07:26:00 +0000103 <SelectItem value={"create-new"}>
104 <Dialog open={createNewOpen} onOpenChange={setCreateNewOpen}>
giobc47f9f2025-05-12 08:31:07 +0000105 <DialogTrigger className="flex flex-row items-center">
106 <Plus />
107 Create New
108 </DialogTrigger>
gio880de162025-05-11 07:26:00 +0000109 <DialogContent>
110 <Input type="text" placeholder="Name" onChange={updateName} />
111 <Button onClick={createNew}>Create New</Button>
112 </DialogContent>
113 </Dialog>
114 </SelectItem>
gio880de162025-05-11 07:26:00 +0000115 </SelectContent>
116 </Select>
giod0026612025-05-08 13:00:36 +0000117 );
118}