Canvas: Get ready for trial

Change-Id: I16088fa041dd0fb35ac801ddbbedf3c1c6e8563d
diff --git a/apps/canvas/back/src/index.ts b/apps/canvas/back/src/index.ts
index cd8249d..6dede14 100644
--- a/apps/canvas/back/src/index.ts
+++ b/apps/canvas/back/src/index.ts
@@ -478,19 +478,15 @@
 		resp.write(
 			JSON.stringify({
 				// TODO(gio): get from env or command line flags
-				managerAddr: "http://10.42.0.211:8081",
+				managerAddr: "http://10.42.0.95:8081",
 				deployKey: project.deployKey,
 				integrations: {
 					github: !!project.githubToken,
 				},
 				networks: [
 					{
-						name: "Public",
-						domain: "v1.dodo.cloud",
-					},
-					{
-						name: "Private",
-						domain: "p.v1.dodo.cloud",
+						name: "Trial",
+						domain: "trial.dodoapp.xyz",
 					},
 				],
 				services,
diff --git a/apps/canvas/front/src/Integrations.tsx b/apps/canvas/front/src/Integrations.tsx
index 55acf1a..185a884 100644
--- a/apps/canvas/front/src/Integrations.tsx
+++ b/apps/canvas/front/src/Integrations.tsx
@@ -85,10 +85,26 @@
 
 				{(!githubService || isEditing) && (
 					<div className="flex flex-row items-center gap-1 text-sm">
-						<div>Follow the link to generate new PAT:</div>
-						<a href="https://github.com/settings/personal-access-tokens" target="_blank">
-							https://github.com/settings/personal-access-tokens
-						</a>
+						<div>
+							Follow the link to generate new PAT:{" "}
+							<a href="https://github.com/settings/personal-access-tokens" target="_blank">
+								https://github.com/settings/personal-access-tokens
+							</a>
+							<br />
+							Grant following <b>Repository</b> permissions:
+							<ul>
+								<li>
+									<b>Contents</b> - Read-only access so dodo can clone the repository
+								</li>
+								<li>
+									<b>Metadata</b> - Read-only access so dodo can search for repositories
+								</li>
+								<li>
+									<b>Administration</b> - Read and write access so dodo automatically add deploy keys
+									to repositories
+								</li>
+							</ul>
+						</div>
 					</div>
 				)}
 				{(!githubService || isEditing) && (
diff --git a/apps/canvas/front/src/Messages.tsx b/apps/canvas/front/src/Messages.tsx
index 8cd6652..0701afb 100644
--- a/apps/canvas/front/src/Messages.tsx
+++ b/apps/canvas/front/src/Messages.tsx
@@ -1,5 +1,5 @@
 import { Button } from "./components/ui/button";
-import { AppNode, AppState, Message, nodeLabel, useMessages } from "./lib/state";
+import { AppNode, AppState, Message, nodeLabel, useMessages, useProjectId } from "./lib/state";
 import { useCallback, useEffect, useState } from "react";
 import { useNodes } from "@xyflow/react";
 import { Accordion, AccordionContent, AccordionItem, AccordionTrigger } from "./components/ui/accordion";
@@ -8,6 +8,7 @@
 
 export function Messages() {
 	const nodes = useNodes<AppNode>();
+	const projectId = useProjectId();
 	const [nodeMap, setNodeMap] = useState<Map<string, AppNode>>(new Map());
 	useEffect(() => {
 		setNodeMap(new Map(nodes.map((n) => [n.id, n])));
@@ -24,14 +25,25 @@
 	const [grouped, setGrouped] = useState<Map<string, Message[]>>(new Map());
 	useEffect(() => {
 		const g = new Map<string, Message[]>();
-		messages.forEach((m) => {
-			const id = m.nodeId || "global";
-			const existing: Message[] = g.get(id) || [];
-			existing.push(m);
-			g.set(id, existing);
-		});
+		if (projectId == null) {
+			g.set("global", [
+				{
+					id: "global",
+					nodeId: undefined,
+					message: "Create a new project or select existing one to get started",
+					type: "FATAL",
+				},
+			]);
+		} else {
+			messages.forEach((m) => {
+				const id = m.nodeId || "global";
+				const existing: Message[] = g.get(id) || [];
+				existing.push(m);
+				g.set(id, existing);
+			});
+		}
 		setGrouped(g);
-	}, [messages, setGrouped]);
+	}, [projectId, messages, setGrouped]);
 	const [open, setOpen] = useState<string[]>([...grouped.keys()]);
 	useEffect(() => {
 		// TODO(gio): do not reopen closed ones
diff --git a/apps/canvas/front/src/components/actions.tsx b/apps/canvas/front/src/components/actions.tsx
index c7481c1..b8771b3 100644
--- a/apps/canvas/front/src/components/actions.tsx
+++ b/apps/canvas/front/src/components/actions.tsx
@@ -164,6 +164,9 @@
 		if (projectId == null) {
 			return;
 		}
+		if (!confirm("Are you sure you want to delete this project? This action cannot be undone.")) {
+			return;
+		}
 		const resp = await fetch(`/api/project/${projectId}`, {
 			method: "DELETE",
 		});
diff --git a/apps/canvas/front/src/components/resources.tsx b/apps/canvas/front/src/components/resources.tsx
index ddb68ac..1fd631a 100644
--- a/apps/canvas/front/src/components/resources.tsx
+++ b/apps/canvas/front/src/components/resources.tsx
@@ -3,7 +3,7 @@
 import { useCallback, useState } from "react";
 import { Accordion, AccordionTrigger } from "./ui/accordion";
 import { AccordionContent, AccordionItem } from "@radix-ui/react-accordion";
-import { AppState, NodeType, useCategories, useStateStore } from "@/lib/state";
+import { AppState, NodeType, useCategories, useMode, useProjectId, useStateStore } from "@/lib/state";
 import { CategoryItem } from "@/lib/categories";
 import { Icon } from "./icon";
 
@@ -25,7 +25,8 @@
 export function Resources() {
 	const store = useStateStore();
 	const categories = useCategories();
-
+	const projectId = useProjectId();
+	const mode = useMode();
 	const onResourceAdd = useCallback(
 		(item: CategoryItem<NodeType>) => {
 			return () => addResource(item, store);
@@ -48,6 +49,7 @@
 										key={item.title}
 										onClick={onResourceAdd(item)}
 										style={{ justifyContent: "flex-start" }}
+										disabled={projectId == null || mode !== "edit"}
 									>
 										<Icon type={item.type} />
 										{item.title}
diff --git a/apps/canvas/front/src/lib/config.ts b/apps/canvas/front/src/lib/config.ts
index 63f6642..572fc78 100644
--- a/apps/canvas/front/src/lib/config.ts
+++ b/apps/canvas/front/src/lib/config.ts
@@ -449,7 +449,7 @@
 					id: `${n.id}-${p.id}-no-ingress`,
 					type: "WARNING",
 					nodeId: n.id,
-					message: `Connect to ingress: ${p.name} - ${p.value}`,
+					message: `Connect to gateway: ${p.name} - ${p.value}`,
 					onHighlight: (store) => {
 						store.updateNode(n.id, { selected: true });
 						store.setHighlightCategory("gateways", true);
diff --git a/apps/canvas/front/src/lib/state.ts b/apps/canvas/front/src/lib/state.ts
index 9d74d5b..aae7600 100644
--- a/apps/canvas/front/src/lib/state.ts
+++ b/apps/canvas/front/src/lib/state.ts
@@ -85,7 +85,7 @@
 	"hugo:latest",
 	"php:8.2-apache",
 	"nextjs:deno-2.0.0",
-	"node-23.1.0",
+	"nodejs:23.1.0",
 ] as const;
 export type ServiceType = (typeof ServiceTypes)[number];