Cavnas: Service node details renders associated ingress

Change-Id: I5aa4e146fedacadd48af095e8df2f3fe35a8342a
diff --git a/apps/canvas/front/src/Gateways.tsx b/apps/canvas/front/src/Gateways.tsx
index dd4c482..d6de75b 100644
--- a/apps/canvas/front/src/Gateways.tsx
+++ b/apps/canvas/front/src/Gateways.tsx
@@ -30,7 +30,7 @@
 	);
 }
 
-function Gateway({ g }: { g: Access }) {
+export function Gateway({ g }: { g: Access }) {
 	const [hidden, content] = ((): [string, string] => {
 		switch (g.type) {
 			case "https":
diff --git a/apps/canvas/front/src/components/node-app.tsx b/apps/canvas/front/src/components/node-app.tsx
index 355e41b..9eeda45 100644
--- a/apps/canvas/front/src/components/node-app.tsx
+++ b/apps/canvas/front/src/components/node-app.tsx
@@ -21,6 +21,7 @@
 import { Accordion, AccordionItem, AccordionTrigger, AccordionContent } from "./ui/accordion";
 import { Name } from "./node-name";
 import { NodeDetailsProps } from "@/lib/types";
+import { Gateway } from "@/Gateways";
 
 export function NodeApp(node: ServiceNode) {
 	const { id, selected } = node;
@@ -388,6 +389,38 @@
 function Ports({ node, disabled }: { node: ServiceNode; disabled?: boolean }): React.ReactNode {
 	const { id, data } = node;
 	const store = useStateStore();
+	const nodes = useNodes<AppNode>();
+	const [portIngresses, setPortIngresses] = useState<Record<string, string[]>>({});
+
+	const httpsGateways = useMemo(
+		() => nodes.filter((n): n is GatewayHttpsNode => n.type === "gateway-https"),
+		[nodes],
+	);
+
+	useEffect(() => {
+		if (!data.ports) {
+			setPortIngresses({});
+			return;
+		}
+		const newIngresses: Record<string, string[]> = {};
+		for (const port of data.ports) {
+			newIngresses[port.id] = [];
+		}
+		for (const gateway of httpsGateways) {
+			const https = gateway.data.https;
+			if (https && https.serviceId === id && https.portId && gateway.data.network && gateway.data.subdomain) {
+				const url = `https://${gateway.data.subdomain}.${gateway.data.network}`;
+				if (newIngresses[https.portId]) {
+					newIngresses[https.portId].push(url);
+				} else {
+					newIngresses[https.portId] = [url];
+				}
+			}
+		}
+		setPortIngresses(newIngresses);
+		console.log(newIngresses);
+	}, [id, data.ports, httpsGateways]);
+
 	const [name, setName] = useState("");
 	const [value, setValue] = useState("");
 	const onSubmit = useCallback(() => {
@@ -513,18 +546,27 @@
 					data.ports &&
 					data.ports.map((p) => (
 						<>
-							<div className="flex items-center px-3">{p.name.toUpperCase()}</div>
-							<div className="flex items-center px-3">{p.value}</div>
-							<div className="flex items-center">
-								<Button
-									variant="destructive"
-									className="w-full"
-									onClick={() => removePort(p.id)}
-									disabled={disabled}
-								>
-									Remove
-								</Button>
+							<div className="contents">
+								<div className="flex items-center px-3">{p.name.toUpperCase()}</div>
+								<div className="flex items-center px-3">{p.value}</div>
+								<div className="flex items-center">
+									<Button
+										variant="destructive"
+										className="w-full"
+										onClick={() => removePort(p.id)}
+										disabled={disabled}
+									>
+										Remove
+									</Button>
+								</div>
 							</div>
+							{portIngresses[p.id]?.length > 0 && (
+								<div key={p.id} className="col-span-full pl-6">
+									{portIngresses[p.id].map((url) => (
+										<Gateway g={{ type: "https", address: url, name: p.name }} />
+									))}
+								</div>
+							)}
 						</>
 					))}
 				<div>