AppManager: App installation status monitoring

Change-Id: I64f4ae0d27892b74f8827a275907cb75da09a758
diff --git a/apps/canvas/front/src/components/actions.tsx b/apps/canvas/front/src/components/actions.tsx
index 66235b3..dc3503f 100644
--- a/apps/canvas/front/src/components/actions.tsx
+++ b/apps/canvas/front/src/components/actions.tsx
@@ -19,7 +19,7 @@
         setOk(!messages.some((m) => m.type === "FATAL"));
     }, [messages, setOk]);
     const monitor = useCallback(async () => {
-        const m = async function() {
+        const m = async function () {
             const resp = await fetch(`/api/project/${projectId}/status`, {
                 method: "GET",
                 headers: {
@@ -32,14 +32,19 @@
             const data: { type: string, name: string, status: string }[] = await resp.json();
             console.log(data);
             for (const n of nodes) {
-                console.log(nodeLabel(n));
-                for (const d of data) {
-                    if (nodeLabel(n) === d.name) {
-                        store.updateNodeData(n.id, { state: d.status });
-                    }
+                if (n.type === "network") {
+                    continue;
+                }
+                const d = data.find((d) => n.type === d.type && nodeLabel(n) === d.name);
+                if (d !== undefined) {
+                    store.updateNodeData(n.id, {
+                        state: d?.status,
+                    });
                 }
             }
-            setTimeout(m, 1000);
+            if (data.find((d) => d.status !== "success" && d.status != "failure") !== undefined) {
+                setTimeout(m, 1000);
+            }
         };
         setTimeout(m, 100);
     }, [projectId, nodes]);
@@ -74,7 +79,7 @@
                     title: "Deployment failed",
                     description: await resp.text(),
                 });
-            }            
+            }
         } catch (e) {
             console.log(e);
             toast({
@@ -107,7 +112,7 @@
                 title: "Save failed",
                 description: await resp.text(),
             });
-        }            
+        }
     }, [projectId, instance, setSt]);
     const restoreSaved = useCallback(async () => {
         if (projectId == null) {
@@ -147,7 +152,7 @@
                 title: "Save failed",
                 description: await resp.text(),
             });
-        }            
+        }
     }, [store, clear]);
     const [props, setProps] = useState({});
     useEffect(() => {
@@ -168,4 +173,4 @@
             <Button onClick={deleteProject} variant="destructive" disabled={projectId === undefined}>Delete</Button>
         </>
     )
-}
\ No newline at end of file
+}
diff --git a/apps/canvas/front/src/components/node-rect.tsx b/apps/canvas/front/src/components/node-rect.tsx
index ad0bee7..a568bc0 100644
--- a/apps/canvas/front/src/components/node-rect.tsx
+++ b/apps/canvas/front/src/components/node-rect.tsx
@@ -17,7 +17,7 @@
     const hasWarning = messages.some((m) => m.type === "WARNING");
     const [classes, setClasses] = useState<string[]>([]);
     useEffect(() => {
-        const classes = ["px-4", "py-2", "rounded-md", "bg-white"];
+        const classes = ["px-4", "py-2", "rounded-md"];
         if (hasFatal) {
             classes.push("border-red-500");
         } else if (hasWarning) {
@@ -30,8 +30,14 @@
         } else {
             classes.push("border");
         }
-        if (state === "running") {
+        if (state === "processing") {
             classes.push("animate-pulse");
+        } else if (state === "success") {
+            classes.push("bg-green-500");
+        } else if (state === "failure") {
+            classes.push("bg-red-500");
+        } else {
+            classes.push("bg-white");
         }
         setClasses(classes);
     }, [selected, hasFatal, hasWarning, state, setClasses]);
@@ -44,4 +50,4 @@
         </div>
     )
 
-}
\ No newline at end of file
+}