blob: cc09a592ad8dda63f601b54fdadbcbbfdbcb6904 [file] [log] [blame]
gio5f2f1002025-03-20 18:38:48 +04001import { AppNode, useEnv, useMessages, useProjectId, useStateStore } from "@/lib/state";
2import { Button } from "./ui/button";
3import { useCallback, useEffect, useState } from "react";
4import { generateDodoConfig } from "@/lib/config";
5import { useNodes, useReactFlow } from "@xyflow/react";
6import { useToast } from "@/hooks/use-toast";
7
8export function Actions() {
9 const { toast } = useToast();
10 const store = useStateStore();
11 const projectId = useProjectId();
12 const nodes = useNodes<AppNode>();
13 const env = useEnv();
14 const messages = useMessages();
15 const instance = useReactFlow();
16 const [ok, setOk] = useState(false);
17 const [loading, setLoading] = useState(false);
18 useEffect(() => {
19 setOk(!messages.some((m) => m.type === "FATAL"));
20 }, [messages, setOk]);
21 const deploy = useCallback(async () => {
22 if (projectId == null) {
23 return;
24 }
25 setLoading(true);
26 try {
27 const config = generateDodoConfig(nodes, env);
28 if (config == null) {
29 throw new Error("MUST NOT REACH!");
30 }
31 const resp = await fetch(`/api/project/${projectId}/deploy`, {
32 method: "POST",
33 headers: {
34 "Content-Type": "application/json",
35 },
36 body: JSON.stringify({
37 state: JSON.stringify(instance.toObject()),
38 config,
39 }),
40 });
41 if (resp.ok) {
42 toast({
43 title: "Deployment succeeded",
44 });
45 } else {
46 toast({
47 variant: "destructive",
48 title: "Deployment failed",
49 description: await resp.text(),
50 });
51 }
52 } catch (e) {
53 console.log(e);
54 toast({
55 variant: "destructive",
56 title: "Deployment failed",
57 });
58 } finally {
59 setLoading(false);
60 }
61 }, [projectId, instance, nodes, env, setLoading]);
62 const [st, setSt] = useState<string>();
63 const save = useCallback(async () => {
64 if (projectId == null) {
65 return;
66 }
67 const resp = await fetch(`/api/project/${projectId}/saved`, {
68 method: "POST",
69 headers: {
70 "Content-Type": "application/json",
71 },
72 body: JSON.stringify(instance.toObject()),
73 });
74 if (resp.ok) {
75 toast({
76 title: "Save succeeded",
77 });
78 } else {
79 toast({
80 variant: "destructive",
81 title: "Save failed",
82 description: await resp.text(),
83 });
84 }
85 }, [projectId, instance, setSt]);
86 const restoreSaved = useCallback(async () => {
87 if (projectId == null) {
88 return;
89 }
90 const resp = await fetch(`/api/project/${projectId}/saved`, {
91 method: "GET",
92 });
93 const inst = await resp.json();
94 const { x = 0, y = 0, zoom = 1 } = inst.viewport;
95 store.setNodes(inst.nodes || []);
96 store.setEdges(inst.edges || []);
97 instance.setViewport({ x, y, zoom });
98 }, [projectId, instance, st]);
99 const [props, setProps] = useState({});
100 useEffect(() => {
101 if (loading) {
102 setProps({ loading: true });
103 } else if (ok) {
104 setProps({ disabled: false });
105 } else {
106 setProps({ disabled: true });
107 }
108 }, [ok, loading, setProps]);
109 return (
110 <>
111 <Button onClick={deploy} {...props}>Deploy</Button>
112 <Button onClick={save}>Save</Button>
113 <Button onClick={restoreSaved}>Restore</Button>
114 </>
115 )
116}