blob: dc3503f5360e9cbf0b8ce3d603c8cfe0ffb3205d [file] [log] [blame]
import { AppNode, nodeLabel, useEnv, useMessages, useProjectId, useStateStore } from "@/lib/state";
import { Button } from "./ui/button";
import { useCallback, useEffect, useState } from "react";
import { generateDodoConfig } from "@/lib/config";
import { useNodes, useReactFlow } from "@xyflow/react";
import { useToast } from "@/hooks/use-toast";
export function Actions() {
const { toast } = useToast();
const store = useStateStore();
const projectId = useProjectId();
const nodes = useNodes<AppNode>();
const env = useEnv();
const messages = useMessages();
const instance = useReactFlow();
const [ok, setOk] = useState(false);
const [loading, setLoading] = useState(false);
useEffect(() => {
setOk(!messages.some((m) => m.type === "FATAL"));
}, [messages, setOk]);
const monitor = useCallback(async () => {
const m = async function () {
const resp = await fetch(`/api/project/${projectId}/status`, {
method: "GET",
headers: {
"Content-Type": "application/json",
},
})
if (resp.status !== 200) {
return;
}
const data: { type: string, name: string, status: string }[] = await resp.json();
console.log(data);
for (const n of nodes) {
if (n.type === "network") {
continue;
}
const d = data.find((d) => n.type === d.type && nodeLabel(n) === d.name);
if (d !== undefined) {
store.updateNodeData(n.id, {
state: d?.status,
});
}
}
if (data.find((d) => d.status !== "success" && d.status != "failure") !== undefined) {
setTimeout(m, 1000);
}
};
setTimeout(m, 100);
}, [projectId, nodes]);
const deploy = useCallback(async () => {
if (projectId == null) {
return;
}
setLoading(true);
try {
const config = generateDodoConfig(nodes, env);
if (config == null) {
throw new Error("MUST NOT REACH!");
}
const resp = await fetch(`/api/project/${projectId}/deploy`, {
method: "POST",
headers: {
"Content-Type": "application/json",
},
body: JSON.stringify({
state: JSON.stringify(instance.toObject()),
config,
}),
});
if (resp.ok) {
toast({
title: "Deployment succeeded",
});
monitor();
} else {
toast({
variant: "destructive",
title: "Deployment failed",
description: await resp.text(),
});
}
} catch (e) {
console.log(e);
toast({
variant: "destructive",
title: "Deployment failed",
});
} finally {
setLoading(false);
}
}, [projectId, instance, nodes, env, setLoading]);
const [st, setSt] = useState<string>();
const save = useCallback(async () => {
if (projectId == null) {
return;
}
const resp = await fetch(`/api/project/${projectId}/saved`, {
method: "POST",
headers: {
"Content-Type": "application/json",
},
body: JSON.stringify(instance.toObject()),
});
if (resp.ok) {
toast({
title: "Save succeeded",
});
} else {
toast({
variant: "destructive",
title: "Save failed",
description: await resp.text(),
});
}
}, [projectId, instance, setSt]);
const restoreSaved = useCallback(async () => {
if (projectId == null) {
return;
}
const resp = await fetch(`/api/project/${projectId}/saved`, {
method: "GET",
});
const inst = await resp.json();
const { x = 0, y = 0, zoom = 1 } = inst.viewport;
store.setNodes(inst.nodes || []);
store.setEdges(inst.edges || []);
instance.setViewport({ x, y, zoom });
}, [projectId, instance, st]);
const clear = useCallback(() => {
store.setEdges([]);
store.setNodes([]);
instance.setViewport({ x: 0, y: 0, zoom: 1 });
}, [store]);
// TODO(gio): Update store
const deleteProject = useCallback(async () => {
if (projectId == null) {
return;
}
const resp = await fetch(`/api/project/${projectId}`, {
method: "DELETE",
});
if (resp.ok) {
clear();
store.setProject(undefined);
toast({
title: "Save succeeded",
});
} else {
toast({
variant: "destructive",
title: "Save failed",
description: await resp.text(),
});
}
}, [store, clear]);
const [props, setProps] = useState({});
useEffect(() => {
if (loading) {
setProps({ loading: true });
} else if (ok) {
setProps({ disabled: false });
} else {
setProps({ disabled: true });
}
}, [ok, loading, setProps]);
return (
<>
<Button onClick={deploy} {...props}>Deploy</Button>
<Button onClick={save}>Save</Button>
<Button onClick={restoreSaved}>Restore</Button>
<Button onClick={clear} variant="destructive">Clear</Button>
<Button onClick={deleteProject} variant="destructive" disabled={projectId === undefined}>Delete</Button>
</>
)
}