blob: 2d6d68de215af7b6ec557590bd8d9b1d1dd22e42 [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
10export function Header() {
11 const { toast } = useToast();
12 const store = useStateStore();
13 const [projects, setProjects] = useState<Project[]>([]);
14 // TODO(gio): sth fishy is here
15 useEffect(() => {
16 store.setProjects(projects);
17 }, [projects]);
18 const refreshProjects = useCallback(async () => {
19 try {
20 const resp = await fetch("/api/project");
21 setProjects(await resp.json());
22 } catch (e) {
23 console.log(e);
24 }
25 }, [setProjects]);
26 useEffect(() => {
27 refreshProjects();
28 }, [refreshProjects]);
29 const project = useProjectId();
30 const [createNewOpen, setCreateNewOpen] = useState(false);
31 const onSelect = useCallback((projectId: string) => {
32 if (projectId === "create-new") {
33 setCreateNewOpen(true);
34 } else {
35 store.setProject(projectId);
36 }
37 }, [store]);
38 const instance = useReactFlow();
39 const restoreSaved = useCallback(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 }, [instance]);
49 useEffect(() => {
50 if (project == null) {
51 return;
52 }
53 restoreSaved(project)
54 }, [project, restoreSaved]);
55 const [name, setName] = useState<string | undefined>(undefined);
56 const updateName = useCallback((e: ChangeEvent<HTMLInputElement>) => {
57 setName(e.target.value);
58 }, [setName]);
59 const createNew = useCallback(() => {
60 console.log(name);
61 if (!name) {
62 return;
63 }
64 fetch("/api/project", {
65 method: "POST",
66 headers: {
67 "Content-Type": "application/json",
68 },
69 body: JSON.stringify({
70 name: name,
71 }),
72 }).then(async (resp) => {
73 if (!resp.ok) {
74 return;
75 }
76 const { id } = await resp.json();
77 await refreshProjects();
78 store.setProject(id as string);
79 setCreateNewOpen(false);
80 toast({
81 title: `Created project: ${name}`,
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
91 return (
92 <div className="flex flex-row h-9">
93 <Select onValueChange={onSelect} value={project}>
94 <SelectTrigger>
95 <SelectValue placeholder="Choose Project" defaultValue={project} />
96 </SelectTrigger>
97 <SelectContent>
98 {projects.map((p) => (
99 <SelectItem value={p.id}>{p.name}</SelectItem>
100 ))}
101 <SelectItem value={"create-new"}>
102 <Dialog open={createNewOpen} onOpenChange={setCreateNewOpen}>
103 <DialogTrigger>Create New</DialogTrigger>
104 <DialogContent>
105 <Input type="text" placeholder="Name" onChange={updateName} />
106 <Button onClick={createNew}>Create New</Button>
107 </DialogContent>
108 </Dialog>
109 </SelectItem>
110 </SelectContent>
111 </Select>
112
113 </div>
114 );
115}