Canvas: Improve overview tab styling
Change-Id: I1bb656e5d277c0e8c46da5d6be0748f964551049
diff --git a/apps/canvas/front/src/Overview.tsx b/apps/canvas/front/src/Overview.tsx
index 43ba776..aca5bd2 100644
--- a/apps/canvas/front/src/Overview.tsx
+++ b/apps/canvas/front/src/Overview.tsx
@@ -1,14 +1,38 @@
import React, { useMemo } from "react";
-import { useStateStore } from "@/lib/state";
+import { useStateStore, AppNode, NodeType } from "@/lib/state";
import { NodeDetails } from "./components/node-details";
import { Actions } from "./components/actions";
import { Canvas } from "./components/canvas";
+import { Separator } from "./components/ui/separator";
+
+const sections: { title: string; nodes: NodeType[] }[] = [
+ {
+ title: "Services",
+ nodes: ["app"],
+ },
+ {
+ title: "Databases",
+ nodes: ["postgresql", "mongodb"],
+ },
+ {
+ title: "File systems",
+ nodes: ["volume"],
+ },
+];
export function Overview(): React.ReactNode {
const store = useStateStore();
const nodes = useMemo(() => {
- return store.nodes.filter((n) => n.type !== "network" && n.type !== "github");
+ return store.nodes.filter((n) => n.type !== "network" && n.type !== "github" && n.type !== undefined);
}, [store.nodes]);
+ const groupedNodes = useMemo(() => {
+ return sections
+ .map((s) => ({
+ title: s.title,
+ nodes: nodes.filter((n) => s.nodes.includes(n.type)),
+ }))
+ .filter((s) => s.nodes.length > 0);
+ }, [nodes]);
const isDeployMode = useMemo(() => store.mode === "deploy", [store.mode]);
return (
<div className="h-full w-full overflow-auto bg-white p-2">
@@ -16,14 +40,41 @@
<Actions />
<Canvas className="hidden" />
</div>
- <div className="flex flex-wrap gap-4 pt-2">
- {nodes.map((n) => {
- return (
- <div key={n.id} className="h-fit w-fit rounded-lg border-gray-200 border-2 p-2">
- <NodeDetails node={n} disabled={isDeployMode} showName={true} />
- </div>
- );
- })}
+ <div className="flex flex-col gap-4 pt-2">
+ {groupedNodes.map((s, index) => (
+ <>
+ {index > 0 && <Separator />}
+ <Section key={s.title} title={s.title} nodes={s.nodes} isDeployMode={isDeployMode} />
+ </>
+ ))}
+ </div>
+ </div>
+ );
+}
+
+function Section({
+ title,
+ nodes,
+ isDeployMode,
+}: {
+ title: string;
+ nodes: AppNode[];
+ isDeployMode: boolean;
+}): React.ReactNode {
+ if (nodes.length === 0) return null;
+ return (
+ <div className="w-full">
+ <h2 className="text-lg font-semibold mb-2">{title}</h2>
+ <div className="flex flex-wrap gap-4 pl-4">
+ {nodes.map((n) => (
+ <NodeDetails
+ key={n.id}
+ node={n}
+ disabled={isDeployMode}
+ showName={true}
+ className="min-w-[500px] rounded-lg border-gray-200 border-2 p-2"
+ />
+ ))}
</div>
</div>
);
diff --git a/apps/canvas/front/src/components/node-details.tsx b/apps/canvas/front/src/components/node-details.tsx
index 6267ac3..ab1d871 100644
--- a/apps/canvas/front/src/components/node-details.tsx
+++ b/apps/canvas/front/src/components/node-details.tsx
@@ -6,11 +6,13 @@
import { NodeGithubDetails } from "./node-github";
import { NodeGatewayTCPDetails } from "./node-gateway-tcp";
import { NodeDetailsProps } from "@/lib/types";
+import { cn } from "@/lib/utils";
-export function NodeDetails(props: NodeDetailsProps) {
+export function NodeDetails(props: NodeDetailsProps & { className?: string }) {
+ const { className, ...rest } = props;
return (
- <div className="px-1 flex flex-col gap-2">
- <NodeDetailsImpl {...props} />
+ <div className={cn("px-1 flex flex-col gap-2", className)}>
+ <NodeDetailsImpl {...rest} />
</div>
);
}