blob: 1c86aae9c8e042216d9d788d55b24e32b6740f63 [file] [log] [blame]
gio3d0bf032025-06-05 06:57:26 +00001import React, { useMemo } from "react";
gio74c6f752025-07-05 04:10:58 +00002import { useStateStore, useLeadAgent } from "@/lib/state";
gio3d0bf032025-06-05 06:57:26 +00003import { NodeDetails } from "./components/node-details";
4import { Actions } from "./components/actions";
5import { Canvas } from "./components/canvas";
gio5fa66962025-06-13 09:30:40 +00006import { Separator } from "./components/ui/separator";
gio74c6f752025-07-05 04:10:58 +00007import { ResizableHandle, ResizablePanel, ResizablePanelGroup } from "./components/ui/resizable";
8import { AppNode, NodeType } from "config";
gio5fa66962025-06-13 09:30:40 +00009
10const sections: { title: string; nodes: NodeType[] }[] = [
11 {
12 title: "Services",
13 nodes: ["app"],
14 },
15 {
16 title: "Databases",
17 nodes: ["postgresql", "mongodb"],
18 },
19 {
20 title: "File systems",
21 nodes: ["volume"],
22 },
23];
gioda120432025-06-02 09:42:26 +000024
25export function Overview(): React.ReactNode {
gio74c6f752025-07-05 04:10:58 +000026 const leadAgent = useLeadAgent();
27 if (leadAgent) {
28 return (
29 <ResizablePanelGroup direction="horizontal" className="w-full h-full">
30 <ResizablePanel defaultSize={25}>
31 <iframe
32 key={leadAgent.name}
33 src={`${leadAgent.address}?m`}
34 title={leadAgent.agentName}
35 className="w-full h-full"
36 />
37 </ResizablePanel>
38 <ResizableHandle withHandle />
39 <ResizablePanel defaultSize={75} className="!overflow-y-auto !overflow-x-hidden">
40 <OverviewImpl />
41 </ResizablePanel>
42 </ResizablePanelGroup>
43 );
44 } else {
45 return <OverviewImpl />;
46 }
47}
48
49function OverviewImpl(): React.ReactNode {
gio3d0bf032025-06-05 06:57:26 +000050 const store = useStateStore();
gio3fb133d2025-06-13 07:20:24 +000051 const nodes = useMemo(() => {
gio5fa66962025-06-13 09:30:40 +000052 return store.nodes.filter((n) => n.type !== "network" && n.type !== "github" && n.type !== undefined);
gio3fb133d2025-06-13 07:20:24 +000053 }, [store.nodes]);
gio5fa66962025-06-13 09:30:40 +000054 const groupedNodes = useMemo(() => {
55 return sections
56 .map((s) => ({
57 title: s.title,
58 nodes: nodes.filter((n) => s.nodes.includes(n.type)),
59 }))
60 .filter((s) => s.nodes.length > 0);
61 }, [nodes]);
gio3d0bf032025-06-05 06:57:26 +000062 const isDeployMode = useMemo(() => store.mode === "deploy", [store.mode]);
gioda120432025-06-02 09:42:26 +000063 return (
gio3d0bf032025-06-05 06:57:26 +000064 <div className="h-full w-full overflow-auto bg-white p-2">
65 <div className="w-full flex flex-row justify-end">
gio1037ee22025-06-26 09:25:43 +000066 <Actions isOverview={true} />
gio3d0bf032025-06-05 06:57:26 +000067 <Canvas className="hidden" />
gioda120432025-06-02 09:42:26 +000068 </div>
gio5fa66962025-06-13 09:30:40 +000069 <div className="flex flex-col gap-4 pt-2">
70 {groupedNodes.map((s, index) => (
71 <>
72 {index > 0 && <Separator />}
73 <Section key={s.title} title={s.title} nodes={s.nodes} isDeployMode={isDeployMode} />
74 </>
75 ))}
76 </div>
77 </div>
78 );
79}
80
81function Section({
82 title,
83 nodes,
84 isDeployMode,
85}: {
86 title: string;
87 nodes: AppNode[];
88 isDeployMode: boolean;
89}): React.ReactNode {
90 if (nodes.length === 0) return null;
91 return (
92 <div className="w-full">
93 <h2 className="text-lg font-semibold mb-2">{title}</h2>
94 <div className="flex flex-wrap gap-4 pl-4">
95 {nodes.map((n) => (
96 <NodeDetails
97 key={n.id}
98 node={n}
99 disabled={isDeployMode}
100 showName={true}
gioe7734b22025-06-13 10:12:04 +0000101 isOverview={true}
gio5fa66962025-06-13 09:30:40 +0000102 className="min-w-[500px] rounded-lg border-gray-200 border-2 p-2"
103 />
104 ))}
gioda120432025-06-02 09:42:26 +0000105 </div>
gio3d0bf032025-06-05 06:57:26 +0000106 </div>
gioda120432025-06-02 09:42:26 +0000107 );
108}