blob: 5178b5e2b1183fb5d1c3ef6ef00997fd8602b06e [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";
4import { useReactFlow } from "@xyflow/react";
5import { Input } from "./components/ui/input";
6import { Button } from "./components/ui/button";
7import { Dialog, DialogContent, DialogTrigger } from "./components/ui/dialog";
8import { useToast } from "@/hooks/use-toast";
9
gio880de162025-05-11 07:26:00 +000010export function ProjectSelect() {
giod0026612025-05-08 13:00:36 +000011 const { toast } = useToast();
12 const store = useStateStore();
13 const [projects, setProjects] = useState<Project[]>([]);
14 const refreshProjects = useCallback(async () => {
15 try {
16 const resp = await fetch("/api/project");
17 setProjects(await resp.json());
18 } catch (e) {
19 console.log(e);
20 }
21 }, [setProjects]);
22 useEffect(() => {
23 refreshProjects();
24 }, [refreshProjects]);
25 const project = useProjectId();
26 const [createNewOpen, setCreateNewOpen] = useState(false);
27 const onSelect = useCallback(
28 (projectId: string) => {
29 if (projectId === "create-new") {
30 setCreateNewOpen(true);
31 } else {
32 store.setProject(projectId);
33 }
34 },
35 [store],
36 );
37 const instance = useReactFlow();
38 const restoreSaved = useCallback(
39 async (projectId: string) => {
40 const resp = await fetch(`/api/project/${projectId}/saved`, {
41 method: "GET",
42 });
43 const inst = await resp.json();
44 const { x = 0, y = 0, zoom = 1 } = inst.viewport;
45 instance.setNodes(inst.nodes || []);
46 instance.setEdges(inst.edges || []);
47 instance.setViewport({ x, y, zoom });
48 },
49 [instance],
50 );
51 useEffect(() => {
52 if (project == null) {
53 return;
54 }
55 restoreSaved(project);
56 }, [project, restoreSaved]);
57 const [name, setName] = useState<string | undefined>(undefined);
58 const updateName = useCallback(
59 (e: ChangeEvent<HTMLInputElement>) => {
60 setName(e.target.value);
61 },
62 [setName],
63 );
64 const createNew = useCallback(() => {
65 console.log(name);
66 if (!name) {
67 return;
68 }
69 fetch("/api/project", {
70 method: "POST",
71 headers: {
72 "Content-Type": "application/json",
73 },
74 body: JSON.stringify({
75 name: name,
76 }),
77 })
78 .then(async (resp) => {
79 if (!resp.ok) {
80 return;
81 }
82 const { id } = await resp.json();
83 await refreshProjects();
84 store.setProject(id as string);
85 setCreateNewOpen(false);
86 toast({
87 title: `Created project: ${name}`,
88 });
89 })
90 .catch((e) => {
91 console.log(e);
92 toast({
93 variant: "destructive",
94 title: `Failed to create project: ${name}`,
95 });
96 });
97 }, [name, setCreateNewOpen, toast, store, refreshProjects]);
98 return (
gio880de162025-05-11 07:26:00 +000099 <Select onValueChange={onSelect} value={project}>
100 <SelectTrigger className="w-[200px]">
101 <SelectValue placeholder="Choose Project" defaultValue={project} />
102 </SelectTrigger>
103 <SelectContent>
104 <SelectItem value={"create-new"}>
105 <Dialog open={createNewOpen} onOpenChange={setCreateNewOpen}>
106 <DialogTrigger>Create New</DialogTrigger>
107 <DialogContent>
108 <Input type="text" placeholder="Name" onChange={updateName} />
109 <Button onClick={createNew}>Create New</Button>
110 </DialogContent>
111 </Dialog>
112 </SelectItem>
113 {projects.map((p) => (
114 <SelectItem key={p.id} value={p.id}>
115 {p.name}
giod0026612025-05-08 13:00:36 +0000116 </SelectItem>
gio880de162025-05-11 07:26:00 +0000117 ))}
118 </SelectContent>
119 </Select>
giod0026612025-05-08 13:00:36 +0000120 );
121}