From c0962de3bc52a4cab6ec09b3dd332abb86b3991f Mon Sep 17 00:00:00 2001 From: Grace Chia Date: Thu, 26 Mar 2026 17:26:33 -0700 Subject: [PATCH 1/5] feat(gui): add computing unit types --- .../src/app/dashboard/type/dashboard-entry.ts | 39 ++++++++++++++++++- .../src/app/dashboard/type/search-result.ts | 2 +- .../src/app/dashboard/type/type-predicates.ts | 5 +++ frontend/src/app/hub/service/hub.service.ts | 1 + 4 files changed, 44 insertions(+), 3 deletions(-) diff --git a/frontend/src/app/dashboard/type/dashboard-entry.ts b/frontend/src/app/dashboard/type/dashboard-entry.ts index 6dfb46cc1cd..735249d64c8 100644 --- a/frontend/src/app/dashboard/type/dashboard-entry.ts +++ b/frontend/src/app/dashboard/type/dashboard-entry.ts @@ -21,7 +21,14 @@ import { DashboardFile } from "./dashboard-file.interface"; import { DashboardWorkflow } from "./dashboard-workflow.interface"; import { DashboardProject } from "./dashboard-project.interface"; import { DashboardDataset } from "./dashboard-dataset.interface"; -import { isDashboardDataset, isDashboardFile, isDashboardProject, isDashboardWorkflow } from "./type-predicates"; +import { DashboardWorkflowComputingUnit } from "../../workspace/types/workflow-computing-unit"; +import { + isDashboardDataset, + isDashboardFile, + isDashboardProject, + isDashboardWorkflow, + isDashboardWorkflowComputingUnit, +} from "./type-predicates"; import { EntityType } from "../../hub/service/hub.service"; export interface UserInfo { @@ -50,7 +57,14 @@ export class DashboardEntry { accessibleUserIds: number[]; coverImageUrl?: string; - constructor(public value: DashboardWorkflow | DashboardProject | DashboardFile | DashboardDataset) { + constructor( + public value: + | DashboardWorkflow + | DashboardProject + | DashboardFile + | DashboardDataset + | DashboardWorkflowComputingUnit + ) { if (isDashboardWorkflow(value)) { this.type = EntityType.Workflow; this.id = value.workflow.wid; @@ -124,6 +138,20 @@ export class DashboardEntry { this.isLiked = false; this.accessibleUserIds = []; this.coverImageUrl = value.dataset.coverImage; + } else if (isDashboardWorkflowComputingUnit(value)) { + this.type = EntityType.ComputingUnit; + this.id = value.computingUnit.cuid; + this.name = value.computingUnit.name; + this.creationTime = value.computingUnit.creationTime; + this.accessLevel = value.accessPrivilege; + this.ownerName = ""; + this.ownerGoogleAvatar = ""; + this.ownerId = value.computingUnit.uid; + this.viewCount = 0; + this.cloneCount = 0; + this.likeCount = 0; + this.isLiked = false; + this.accessibleUserIds = []; } else { throw new Error("Unexpected type in DashboardEntry."); } @@ -182,4 +210,11 @@ export class DashboardEntry { } return this.value; } + + get computingUnit(): DashboardWorkflowComputingUnit { + if (!isDashboardWorkflowComputingUnit(this.value)) { + throw new Error("Value is not of type DashboardWorkflowComputingUnit"); + } + return this.value; + } } diff --git a/frontend/src/app/dashboard/type/search-result.ts b/frontend/src/app/dashboard/type/search-result.ts index 268381b0a5d..8fc14361b17 100644 --- a/frontend/src/app/dashboard/type/search-result.ts +++ b/frontend/src/app/dashboard/type/search-result.ts @@ -24,7 +24,7 @@ import { DashboardDataset } from "./dashboard-dataset.interface"; import { DashboardEntry } from "./dashboard-entry"; export interface SearchResultItem { - resourceType: "workflow" | "project" | "file" | "dataset"; + resourceType: "workflow" | "project" | "file" | "dataset" | "computing-unit"; workflow?: DashboardWorkflow; project?: DashboardProject; file?: DashboardFile; diff --git a/frontend/src/app/dashboard/type/type-predicates.ts b/frontend/src/app/dashboard/type/type-predicates.ts index 6c9cf96263d..0ed825feb49 100644 --- a/frontend/src/app/dashboard/type/type-predicates.ts +++ b/frontend/src/app/dashboard/type/type-predicates.ts @@ -21,6 +21,7 @@ import { DashboardWorkflow } from "./dashboard-workflow.interface"; import { DashboardProject } from "./dashboard-project.interface"; import { DashboardFile } from "./dashboard-file.interface"; import { DashboardDataset } from "./dashboard-dataset.interface"; +import { DashboardWorkflowComputingUnit } from "../../workspace/types/workflow-computing-unit"; export function isDashboardWorkflow(value: any): value is DashboardWorkflow { return value && typeof value.workflow === "object"; @@ -37,3 +38,7 @@ export function isDashboardFile(value: any): value is DashboardFile { export function isDashboardDataset(value: any): value is DashboardDataset { return value && typeof value.dataset === "object"; } + +export function isDashboardWorkflowComputingUnit(value: any): value is DashboardWorkflowComputingUnit { + return value && typeof value.computingUnit === "object"; +} diff --git a/frontend/src/app/hub/service/hub.service.ts b/frontend/src/app/hub/service/hub.service.ts index 231fb5e3b8c..66cd08a9149 100644 --- a/frontend/src/app/hub/service/hub.service.ts +++ b/frontend/src/app/hub/service/hub.service.ts @@ -30,6 +30,7 @@ export enum EntityType { Dataset = "dataset", Project = "project", File = "file", + ComputingUnit = "computing-unit", } export enum ActionType { From 70860c4ffa4fda7686708fc25721b2e25b1f566b Mon Sep 17 00:00:00 2001 From: Grace Chia Date: Thu, 26 Mar 2026 18:07:31 -0700 Subject: [PATCH 2/5] feat(gui): add computing unit shell --- frontend/src/app/app-routing.constant.ts | 1 + frontend/src/app/app-routing.module.ts | 5 ++++ frontend/src/app/app.module.ts | 2 ++ .../component/dashboard.component.html | 11 +++++++ .../component/dashboard.component.ts | 2 ++ .../user-computing-unit.component.html | 29 +++++++++++++++++++ .../user-computing-unit.component.scss | 29 +++++++++++++++++++ .../user-computing-unit.component.ts | 29 +++++++++++++++++++ 8 files changed, 108 insertions(+) create mode 100644 frontend/src/app/dashboard/component/user/user-computing-unit/user-computing-unit.component.html create mode 100644 frontend/src/app/dashboard/component/user/user-computing-unit/user-computing-unit.component.scss create mode 100644 frontend/src/app/dashboard/component/user/user-computing-unit/user-computing-unit.component.ts diff --git a/frontend/src/app/app-routing.constant.ts b/frontend/src/app/app-routing.constant.ts index a22f6439e26..582f6d865b1 100644 --- a/frontend/src/app/app-routing.constant.ts +++ b/frontend/src/app/app-routing.constant.ts @@ -35,6 +35,7 @@ export const DASHBOARD_USER_WORKSPACE = `${DASHBOARD_USER}/workflow`; export const DASHBOARD_USER_WORKFLOW = `${DASHBOARD_USER}/workflow`; export const DASHBOARD_USER_DATASET = `${DASHBOARD_USER}/dataset`; export const DASHBOARD_USER_DATASET_CREATE = `${DASHBOARD_USER_DATASET}/create`; +export const DASHBOARD_USER_COMPUTING_UNIT = `${DASHBOARD_USER}/unit`; export const DASHBOARD_USER_QUOTA = `${DASHBOARD_USER}/quota`; export const DASHBOARD_USER_DISCUSSION = `${DASHBOARD_USER}/discussion`; diff --git a/frontend/src/app/app-routing.module.ts b/frontend/src/app/app-routing.module.ts index a44c55393f3..a6263a2c023 100644 --- a/frontend/src/app/app-routing.module.ts +++ b/frontend/src/app/app-routing.module.ts @@ -24,6 +24,7 @@ import { UserWorkflowComponent } from "./dashboard/component/user/user-workflow/ import { UserQuotaComponent } from "./dashboard/component/user/user-quota/user-quota.component"; import { UserProjectSectionComponent } from "./dashboard/component/user/user-project/user-project-section/user-project-section.component"; import { UserProjectComponent } from "./dashboard/component/user/user-project/user-project.component"; +import { UserComputingUnitComponent } from "./dashboard/component/user/user-computing-unit/user-computing-unit.component"; import { WorkspaceComponent } from "./workspace/component/workspace.component"; import { AboutComponent } from "./hub/component/about/about.component"; import { AuthGuardService } from "./common/service/user/auth-guard.service"; @@ -130,6 +131,10 @@ routes.push({ path: "dataset/create", component: DatasetDetailComponent, }, + { + path: "unit", + component: UserComputingUnitComponent, + }, { path: "quota", component: UserQuotaComponent, diff --git a/frontend/src/app/app.module.ts b/frontend/src/app/app.module.ts index b41b1f80b73..6a92d4be2e9 100644 --- a/frontend/src/app/app.module.ts +++ b/frontend/src/app/app.module.ts @@ -185,6 +185,7 @@ import { NzCheckboxModule } from "ng-zorro-antd/checkbox"; import { NzRadioModule } from "ng-zorro-antd/radio"; import { RegistrationRequestModalComponent } from "./common/service/user/registration-request-modal/registration-request-modal.component"; import { MarkdownDescriptionComponent } from "./dashboard/component/user/markdown-description/markdown-description.component"; +import { UserComputingUnitComponent } from "./dashboard/component/user/user-computing-unit/user-computing-unit.component"; registerLocaleData(en); @@ -283,6 +284,7 @@ registerLocaleData(en); AdminSettingsComponent, RegistrationRequestModalComponent, MarkdownDescriptionComponent, + UserComputingUnitComponent, ], imports: [ BrowserModule, diff --git a/frontend/src/app/dashboard/component/dashboard.component.html b/frontend/src/app/dashboard/component/dashboard.component.html index b238f56b938..1b1bc910b43 100644 --- a/frontend/src/app/dashboard/component/dashboard.component.html +++ b/frontend/src/app/dashboard/component/dashboard.component.html @@ -97,6 +97,17 @@ nzType="database"> Datasets +
  • + + Computing Units +
  • + +
    + +

    Computing Units

    +
    + + + +
    diff --git a/frontend/src/app/dashboard/component/user/user-computing-unit/user-computing-unit.component.scss b/frontend/src/app/dashboard/component/user/user-computing-unit/user-computing-unit.component.scss new file mode 100644 index 00000000000..181db6355aa --- /dev/null +++ b/frontend/src/app/dashboard/component/user/user-computing-unit/user-computing-unit.component.scss @@ -0,0 +1,29 @@ +/** + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +@import "../../dashboard.component.scss"; +@import "../../section-style"; +@import "../../button-style"; + +.subsection-grid-container { + min-width: 100%; + width: 100%; + min-height: 100%; + height: 100%; +} diff --git a/frontend/src/app/dashboard/component/user/user-computing-unit/user-computing-unit.component.ts b/frontend/src/app/dashboard/component/user/user-computing-unit/user-computing-unit.component.ts new file mode 100644 index 00000000000..971a051934a --- /dev/null +++ b/frontend/src/app/dashboard/component/user/user-computing-unit/user-computing-unit.component.ts @@ -0,0 +1,29 @@ +/** + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +import { Component } from "@angular/core"; + +@Component({ + selector: "texera-computing-unit-section", + templateUrl: "user-computing-unit.component.html", + styleUrls: ["user-computing-unit.component.scss"], +}) +export class UserComputingUnitComponent { + +} From 3fd9979dd64a68b0c2eef5ab0cbed7128788bec8 Mon Sep 17 00:00:00 2001 From: Grace Chia Date: Tue, 31 Mar 2026 18:08:22 -0700 Subject: [PATCH 3/5] test(gui): add simple user computing unit dashboard component tests --- .../user-computing-unit.component.spec.ts | 25 +++++++++++++++++++ 1 file changed, 25 insertions(+) create mode 100644 frontend/src/app/dashboard/component/user/user-computing-unit/user-computing-unit.component.spec.ts diff --git a/frontend/src/app/dashboard/component/user/user-computing-unit/user-computing-unit.component.spec.ts b/frontend/src/app/dashboard/component/user/user-computing-unit/user-computing-unit.component.spec.ts new file mode 100644 index 00000000000..06925cf81fc --- /dev/null +++ b/frontend/src/app/dashboard/component/user/user-computing-unit/user-computing-unit.component.spec.ts @@ -0,0 +1,25 @@ +import { ComponentFixture, TestBed } from "@angular/core/testing"; +import { UserComputingUnitComponent } from "./user-computing-unit.component"; +import { NzCardModule } from "ng-zorro-antd/card"; + +describe("UserComputingUnitComponent", () => { + let component: UserComputingUnitComponent; + let fixture: ComponentFixture; + + beforeEach(async () => { + await TestBed.configureTestingModule({ + declarations: [UserComputingUnitComponent], + imports: [NzCardModule], + }).compileComponents(); + }); + + beforeEach(() => { + fixture = TestBed.createComponent(UserComputingUnitComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + it("should create", () => { + expect(component).toBeTruthy(); + }); +}); From fe35458ed4a0de07b5455a3303b0503ba5b26e8e Mon Sep 17 00:00:00 2001 From: Grace Chia Date: Tue, 31 Mar 2026 18:11:25 -0700 Subject: [PATCH 4/5] docs: add license to computing unit dashboard component tests --- .../user-computing-unit.component.spec.ts | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/frontend/src/app/dashboard/component/user/user-computing-unit/user-computing-unit.component.spec.ts b/frontend/src/app/dashboard/component/user/user-computing-unit/user-computing-unit.component.spec.ts index 06925cf81fc..a171078e9a2 100644 --- a/frontend/src/app/dashboard/component/user/user-computing-unit/user-computing-unit.component.spec.ts +++ b/frontend/src/app/dashboard/component/user/user-computing-unit/user-computing-unit.component.spec.ts @@ -1,3 +1,22 @@ +/** + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + import { ComponentFixture, TestBed } from "@angular/core/testing"; import { UserComputingUnitComponent } from "./user-computing-unit.component"; import { NzCardModule } from "ng-zorro-antd/card"; From c9e5ca7c6a347d9d19680e2f61130fdb2e3b0015 Mon Sep 17 00:00:00 2001 From: Grace Chia Date: Wed, 1 Apr 2026 21:00:22 -0700 Subject: [PATCH 5/5] style: apply prettier formatting to frontend files --- frontend/src/app/app-routing.module.ts | 6 +++--- .../user-computing-unit/user-computing-unit.component.ts | 4 +--- 2 files changed, 4 insertions(+), 6 deletions(-) diff --git a/frontend/src/app/app-routing.module.ts b/frontend/src/app/app-routing.module.ts index a6263a2c023..03744c1df9d 100644 --- a/frontend/src/app/app-routing.module.ts +++ b/frontend/src/app/app-routing.module.ts @@ -132,9 +132,9 @@ routes.push({ component: DatasetDetailComponent, }, { - path: "unit", - component: UserComputingUnitComponent, - }, + path: "unit", + component: UserComputingUnitComponent, + }, { path: "quota", component: UserQuotaComponent, diff --git a/frontend/src/app/dashboard/component/user/user-computing-unit/user-computing-unit.component.ts b/frontend/src/app/dashboard/component/user/user-computing-unit/user-computing-unit.component.ts index 971a051934a..1084a19d556 100644 --- a/frontend/src/app/dashboard/component/user/user-computing-unit/user-computing-unit.component.ts +++ b/frontend/src/app/dashboard/component/user/user-computing-unit/user-computing-unit.component.ts @@ -24,6 +24,4 @@ import { Component } from "@angular/core"; templateUrl: "user-computing-unit.component.html", styleUrls: ["user-computing-unit.component.scss"], }) -export class UserComputingUnitComponent { - -} +export class UserComputingUnitComponent {}