SarahAsad23 commented on code in PR #4359:
URL: https://github.com/apache/texera/pull/4359#discussion_r3114341320


##########
frontend/src/app/workspace/component/power-button/computing-unit-selection.component.ts:
##########
@@ -637,4 +670,281 @@ export class ComputingUnitSelectionComponent implements 
OnInit {
       this.computingUnitStatusService.refreshComputingUnitList();
     }
   }
+
+  private makeEmptyPve(expanded: boolean): PveDraft {
+    return {
+      id: this.nextPveId++,
+      name: "",
+      userPackages: [],
+      newPackages: [{ name: "", operator: "==", version: "" }],
+      deletingPackages: [],
+      pipOutput: "",
+      prettyPipOutput: "",
+      expanded,
+      isInstalling: false,
+    };
+  }
+
+  addEnvironment(): void {
+    this.pves.push(this.makeEmptyPve(true));
+  }
+
+  trackByPveId(_index: number, pve: PveDraft): number {
+    return pve.id;
+  }
+
+  showPVEmodalVisible(): void {
+    this.pveModalVisible = true;
+    this.getPVEs();
+  }
+
+  getPVEs(): void {
+    const cuId: number | undefined = 
this.selectedComputingUnit?.computingUnit.cuid;
+
+    if (cuId == null) {
+      this.notificationService.error("No computing unit selected. Please 
select a CU first.");
+      return;
+    }
+
+    this.workflowPveService.setCuid(cuId);
+
+    this.workflowPveService
+      .fetchPVEs(cuId)
+      .pipe(untilDestroyed(this))
+      .subscribe({
+      next: (resp: PvePackageResponse[]) => {
+        this.pves = resp.map((pve, index) => ({
+          id: index,
+          name: pve.pveName,
+          expanded: false,
+          isInstalling: false,
+          pipOutput: "",
+          prettyPipOutput: "",
+          userPackages: (pve.userPackages ?? []).map(pkg => {
+            const [name, version] = pkg.split("==");
+            return {
+              name: name.trim(),
+              version: (version ?? "").trim(),
+              isHighlighted: false,
+            };
+          }),
+          newPackages: [],
+          deletingPackages: [],
+        }));
+      },
+      error: (err: unknown) => {
+        console.error("Failed to fetch PVEs:", err);
+        this.pves = [];
+      },
+    });
+  }
+
+  addPackage(index: number): void {
+    const env = this.pves[index];
+    env.newPackages.push({ name: "", version: "", operator: undefined, 
isHighlighted: false });
+  }
+
+  togglePackageDelete(index: number, pkg: PackageRow): void {
+    const env = this.pves[index];
+
+    pkg.isHighlighted = !pkg.isHighlighted;
+
+    const version = pkg.version ?? "";
+
+    if (pkg.isHighlighted) {
+      const exists = env.deletingPackages.some(p => p.name === pkg.name && 
(p.version ?? "") === version);
+      if (!exists) {
+        env.deletingPackages.push({ name: pkg.name, version });
+      }
+    } else {
+      env.deletingPackages = env.deletingPackages.filter(p => !(p.name === 
pkg.name && (p.version ?? "") === version));
+    }
+  }
+
+  scrollToBottomOfPipModal(index: number) {
+    setTimeout(() => {
+      const pre = document.getElementById(`pip-log-${index}`) as HTMLElement | 
null;
+      if (pre) {
+        pre.scrollTop = pre.scrollHeight;
+      }
+    }, 50);
+  }
+
+  updatePrettyPipOutput(index: number) {
+    const env = this.pves[index];
+
+    const escapeHtml = (s: string) =>
+      s
+        .replace(/&/g, "&")
+        .replace(/</g, "&lt;")
+        .replace(/>/g, "&gt;")
+        .replace(/"/g, "&quot;")
+        .replace(/'/g, "&#39;");
+
+    const raw = env.pipOutput ?? "";
+    const safe = escapeHtml(raw);
+
+    env.prettyPipOutput = safe
+      .replace(
+        /^(\[pip\].*finished with exit code\s+0.*)$/gm,
+        "<span class=\"pip-exit ok\"><strong>$1</strong></span>"
+      )
+      .replace(
+        /^(\[pip\].*finished with exit code\s+1.*)$/gm,
+        "<span class=\"pip-exit err\"><strong>$1</strong></span>"
+      )
+      .replace(
+        /^(\[pip\].*finished with exit code\s+([2-9]\d*).*)$/gm,
+        "<span class=\"pip-exit err\"><strong>$1</strong></span>"
+      )
+      .replace(/ERROR/g, "<span class=\"error\">ERROR</span>")
+      .replace(/WARNING/g, "<span class=\"warning\">WARNING</span>")
+      .replace(/already satisfied/g, "<span class=\"success\">already 
satisfied</span>")
+      .replace(/\n/g, "<br/>");
+  }
+
+  createVirtualEnvironment(index: number): void {
+    const cuId = this.selectedComputingUnit?.computingUnit.cuid;
+    const env = this.pves[index];
+
+    if (cuId == null) {
+      this.notificationService.error("No computing unit selected. Please 
select a CU first.");
+      return;
+    }
+
+    if (!env.name?.trim()) {

Review Comment:
   This check ensures the name is not empty or whitespace only. 



-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: [email protected]

For queries about this service, please contact Infrastructure at:
[email protected]

Reply via email to