Canvas: Form to choose source repository

Change-Id: I48011d6374e036ead934815ed8e88dc0d1bb914e
diff --git a/apps/canvas/front/src/components/node-app.tsx b/apps/canvas/front/src/components/node-app.tsx
index 3eea939..dcccd82 100644
--- a/apps/canvas/front/src/components/node-app.tsx
+++ b/apps/canvas/front/src/components/node-app.tsx
@@ -8,7 +8,7 @@
 import { Form, FormControl, FormField, FormItem, FormMessage } from './ui/form';
 import { Input } from './ui/input';
 import { Button } from './ui/button';
-import { Handle, Position } from "@xyflow/react";
+import { Handle, Position, useNodes } from "@xyflow/react";
 import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from "./ui/select";
 import { PencilIcon, XIcon } from "lucide-react";
 import { Tooltip, TooltipContent, TooltipProvider, TooltipTrigger } from "./ui/tooltip";
@@ -61,8 +61,15 @@
   value: z.coerce.number().gt(0, "can not be negative"),
 });
 
+const sourceSchema = z.object({
+  id: z.string().min(1, "required"),
+  branch: z.string(),
+  rootDir: z.string(),
+});
+
 export function NodeAppDetails({ id, data }: ServiceNode) {
   const store = useStateStore();
+  const nodes = useNodes();
   const form = useForm<z.infer<typeof schema>>({
     resolver: zodResolver(schema),
     mode: "onChange",
@@ -225,7 +232,6 @@
           return true;
         }
         if (t.data.exposed.find((e) => e.serviceId === id && e.portId === portId)) {
-          console.log(11111, e);
           tcpRemoved.add(t.id);
           return false;
         }
@@ -276,6 +282,64 @@
       preBuildCommands: e.currentTarget.value,
     });
   }, [id, store]);
+
+  const sourceForm = useForm<z.infer<typeof sourceSchema>>({
+    resolver: zodResolver(sourceSchema),
+    mode: "onChange",
+    defaultValues: {
+      id: data?.repository?.id,
+      branch: data.repository && "branch" in data.repository ? data.repository.branch : undefined,
+      rootDir: data.repository && "rootDir" in data.repository ? data.repository.rootDir : undefined,
+    },
+  });
+  useEffect(() => {
+    const sub = sourceForm.watch((value: DeepPartial<z.infer<typeof sourceSchema>>, { name }: { name?: keyof z.infer<typeof sourceSchema> | undefined, type?: EventType | undefined }) => {
+      console.log(value);
+      if (name === "id") {
+        let edges = store.edges;
+        if (data?.repository?.id !== undefined) {
+          edges = edges.filter((e) => {
+            if (e.target === id && e.targetHandle === "repository" && e.source === data.repository.id) {
+              return false;
+            } else {
+              return true;
+            }
+          });
+        }
+        if (value.id !== undefined) {
+          edges = edges.concat({
+            id: uuidv4(),
+            source: value.id,
+            sourceHandle: "repository",
+            target: id,
+            targetHandle: "repository",
+          });
+        }
+        store.setEdges(edges);
+        store.updateNodeData<"app">(id, {
+          repository: {
+            id: value.id,
+          },
+        });
+      } else if (name === "branch") {
+        store.updateNodeData<"app">(id, {
+          repository: {
+            ...data?.repository,
+            branch: value.branch,
+          },
+        });
+      } else if (name === "rootDir") {
+        store.updateNodeData<"app">(id, {
+          repository: {
+            ...data?.repository,
+            rootDir: value.rootDir,
+          },
+        });
+      }
+    });
+    return () => sub.unsubscribe();
+  }, [id, data, sourceForm, store]);
+
   return (
     <>
       <Form {...form}>
@@ -315,6 +379,56 @@
           />
         </form>
       </Form>
+      Source
+      <Form {...sourceForm}>
+        <form className="space-y-2">
+          <FormField
+            control={sourceForm.control}
+            name="id"
+            render={({ field }) => (
+              <FormItem>
+                <Select onValueChange={field.onChange} defaultValue={field.value}>
+                  <FormControl>
+                    <SelectTrigger>
+                      <SelectValue placeholder="Repository" />
+                    </SelectTrigger>
+                  </FormControl>
+                  <SelectContent>
+                    {nodes.filter((n) => n.type === "github" && n.data.address).map((n) => (
+                      <SelectItem key={n.id} value={n.id}>{`${n.data.address!}`}</SelectItem>
+                    ))}
+                  </SelectContent>
+                </Select>
+                <FormMessage />
+              </FormItem>
+            )}
+          />
+          <FormField
+            control={sourceForm.control}
+            name="branch"
+            render={({ field }) => (
+              <FormItem>
+                <FormControl>
+                  <Input placeholder="master" className="border border-black" {...field} />
+                </FormControl>
+                <FormMessage />
+              </FormItem>
+            )}
+          />
+          <FormField
+            control={sourceForm.control}
+            name="rootDir"
+            render={({ field }) => (
+              <FormItem>
+                <FormControl>
+                  <Input placeholder="/" className="border border-black" {...field} />
+                </FormControl>
+                <FormMessage />
+              </FormItem>
+            )}
+          />
+        </form>
+      </Form>
       Ports
       <ul>
         {data && data.ports && data.ports.map((p) => (<li key={p.id}><Button size={"icon"} variant={"ghost"} onClick={() => removePort(p.id)}><XIcon /></Button> {p.name} - {p.value}</li>))}