diff --git a/amber/src/main/scala/org/apache/texera/web/service/WorkflowEmailNotifier.scala b/amber/src/main/scala/org/apache/texera/web/service/WorkflowEmailNotifier.scala index af9a26286d4..c49f63e9394 100644 --- a/amber/src/main/scala/org/apache/texera/web/service/WorkflowEmailNotifier.scala +++ b/amber/src/main/scala/org/apache/texera/web/service/WorkflowEmailNotifier.scala @@ -107,7 +107,7 @@ class WorkflowEmailNotifier( private def createDashboardUrl(): String = { val host = sessionUri.getHost val port = sessionUri.getPort - val path = s"/dashboard/user/workspace/$workflowId" + val path = s"/user/workspace/$workflowId" if (port == -1 || port == 80 || port == 443) { s"http://$host$path" } else { diff --git a/frontend/src/app/app-routing.constant.ts b/frontend/src/app/app-routing.constant.ts index 4181df8a954..6e06f725201 100644 --- a/frontend/src/app/app-routing.constant.ts +++ b/frontend/src/app/app-routing.constant.ts @@ -17,32 +17,31 @@ * under the License. */ -export const DASHBOARD = "/dashboard"; -export const DASHBOARD_HOME = `${DASHBOARD}/home`; -export const DASHBOARD_ABOUT = `${DASHBOARD}/about`; +export const HOME = "/home"; +export const ABOUT = "/about"; -export const DASHBOARD_HUB = `${DASHBOARD}/hub`; -export const DASHBOARD_HUB_WORKFLOW = `${DASHBOARD_HUB}/workflow`; -export const DASHBOARD_HUB_WORKFLOW_RESULT = `${DASHBOARD_HUB_WORKFLOW}/result`; -export const DASHBOARD_HUB_WORKFLOW_RESULT_DETAIL = `${DASHBOARD_HUB_WORKFLOW_RESULT}/detail`; -export const DASHBOARD_HUB_DATASET = `${DASHBOARD_HUB}/dataset`; -export const DASHBOARD_HUB_DATASET_RESULT = `${DASHBOARD_HUB_DATASET}/result`; -export const DASHBOARD_HUB_DATASET_RESULT_DETAIL = `${DASHBOARD_HUB_DATASET_RESULT}/detail`; +export const HUB = "/hub"; +export const HUB_WORKFLOW = `${HUB}/workflow`; +export const HUB_WORKFLOW_RESULT = `${HUB_WORKFLOW}/result`; +export const HUB_WORKFLOW_RESULT_DETAIL = `${HUB_WORKFLOW_RESULT}/detail`; +export const HUB_DATASET = `${HUB}/dataset`; +export const HUB_DATASET_RESULT = `${HUB_DATASET}/result`; +export const HUB_DATASET_RESULT_DETAIL = `${HUB_DATASET_RESULT}/detail`; -export const DASHBOARD_USER = `${DASHBOARD}/user`; -export const DASHBOARD_USER_PROJECT = `${DASHBOARD_USER}/project`; -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}/compute`; -export const DASHBOARD_USER_QUOTA = `${DASHBOARD_USER}/quota`; -export const DASHBOARD_USER_DISCUSSION = `${DASHBOARD_USER}/discussion`; +export const USER = "/user"; +export const USER_PROJECT = `${USER}/project`; +export const USER_WORKSPACE = `${USER}/workflow`; +export const USER_WORKFLOW = `${USER}/workflow`; +export const USER_DATASET = `${USER}/dataset`; +export const USER_DATASET_CREATE = `${USER_DATASET}/create`; +export const USER_COMPUTING_UNIT = `${USER}/compute`; +export const USER_QUOTA = `${USER}/quota`; +export const USER_DISCUSSION = `${USER}/discussion`; -export const DASHBOARD_ADMIN = `${DASHBOARD}/admin`; -export const DASHBOARD_ADMIN_USER = `${DASHBOARD_ADMIN}/user`; -export const DASHBOARD_ADMIN_GMAIL = `${DASHBOARD_ADMIN}/gmail`; -export const DASHBOARD_ADMIN_EXECUTION = `${DASHBOARD_ADMIN}/execution`; -export const DASHBOARD_ADMIN_SETTINGS = `${DASHBOARD_ADMIN}/settings`; +export const ADMIN = "/admin"; +export const ADMIN_USER = `${ADMIN}/user`; +export const ADMIN_GMAIL = `${ADMIN}/gmail`; +export const ADMIN_EXECUTION = `${ADMIN}/execution`; +export const ADMIN_SETTINGS = `${ADMIN}/settings`; -export const DASHBOARD_SEARCH = `${DASHBOARD}/search`; +export const SEARCH = "/search"; diff --git a/frontend/src/app/app-routing.module.ts b/frontend/src/app/app-routing.module.ts index 179caf5c088..3a3e7dcdf8a 100644 --- a/frontend/src/app/app-routing.module.ts +++ b/frontend/src/app/app-routing.module.ts @@ -17,8 +17,8 @@ * under the License. */ -import { inject, NgModule } from "@angular/core"; -import { CanActivateFn, Router, RouterModule, Routes } from "@angular/router"; +import { NgModule } from "@angular/core"; +import { RouterModule, Routes } from "@angular/router"; import { DashboardComponent } from "./dashboard/component/dashboard.component"; import { UserWorkflowComponent } from "./dashboard/component/user/user-workflow/user-workflow.component"; import { UserQuotaComponent } from "./dashboard/component/user/user-quota/user-quota.component"; @@ -38,28 +38,21 @@ import { DatasetDetailComponent } from "./dashboard/component/user/user-dataset/ import { UserDatasetComponent } from "./dashboard/component/user/user-dataset/user-dataset.component"; import { HubWorkflowDetailComponent } from "./hub/component/workflow/detail/hub-workflow-detail.component"; import { LandingPageComponent } from "./hub/component/landing-page/landing-page.component"; -import { DASHBOARD_ABOUT, DASHBOARD_USER_WORKFLOW } from "./app-routing.constant"; +import { USER_WORKFLOW } from "./app-routing.constant"; import { HubSearchResultComponent } from "./hub/component/hub-search-result/hub-search-result.component"; import { AdminSettingsComponent } from "./dashboard/component/admin/settings/admin-settings.component"; -import { GuiConfigService } from "./common/service/gui-config.service"; - -const rootRedirectGuard: CanActivateFn = () => { - const config = inject(GuiConfigService); - const router = inject(Router); - try { - return router.parseUrl(DASHBOARD_ABOUT); - } catch { - // config not loaded yet, swallow the error and let the app handle it - } - return true; -}; const routes: Routes = []; routes.push({ - path: "dashboard", + path: "", component: DashboardComponent, children: [ + { + path: "", + redirectTo: "about", + pathMatch: "full", + }, { path: "home", component: LandingPageComponent, @@ -185,7 +178,7 @@ routes.push({ // redirect all other paths to index. routes.push({ path: "**", - redirectTo: DASHBOARD_USER_WORKFLOW, + redirectTo: USER_WORKFLOW, }); @NgModule({ diff --git a/frontend/src/app/common/service/user/auth-guard.service.ts b/frontend/src/app/common/service/user/auth-guard.service.ts index 3e6e3158711..1a69ad0289f 100644 --- a/frontend/src/app/common/service/user/auth-guard.service.ts +++ b/frontend/src/app/common/service/user/auth-guard.service.ts @@ -21,7 +21,7 @@ import { Injectable } from "@angular/core"; import { ActivatedRouteSnapshot, CanActivate, Router, RouterStateSnapshot } from "@angular/router"; import { GuiConfigService } from "../gui-config.service"; import { UserService } from "./user.service"; -import { DASHBOARD_ABOUT } from "../../../app-routing.constant"; +import { ABOUT } from "../../../app-routing.constant"; /** * AuthGuardService is a service can tell the router whether @@ -38,7 +38,7 @@ export class AuthGuardService implements CanActivate { if (this.userService.isLogin()) { return true; } else { - this.router.navigate([DASHBOARD_ABOUT], { queryParams: { returnUrl: state.url === "/" ? null : state.url } }); + this.router.navigate([ABOUT], { queryParams: { returnUrl: state.url === "/" ? null : state.url } }); return false; } } diff --git a/frontend/src/app/dashboard/component/admin/execution/admin-execution.component.html b/frontend/src/app/dashboard/component/admin/execution/admin-execution.component.html index 41a2ceb9656..907ffa8d727 100644 --- a/frontend/src/app/dashboard/component/admin/execution/admin-execution.component.html +++ b/frontend/src/app/dashboard/component/admin/execution/admin-execution.component.html @@ -100,7 +100,7 @@
- + {{ maxStringLength(execution.workflowName, 16) }} ({{ execution.workflowId }})
diff --git a/frontend/src/app/dashboard/component/admin/execution/admin-execution.component.spec.ts b/frontend/src/app/dashboard/component/admin/execution/admin-execution.component.spec.ts index 466d0a3db00..caaa71a2207 100644 --- a/frontend/src/app/dashboard/component/admin/execution/admin-execution.component.spec.ts +++ b/frontend/src/app/dashboard/component/admin/execution/admin-execution.component.spec.ts @@ -18,12 +18,14 @@ */ import { ComponentFixture, inject, TestBed } from "@angular/core/testing"; +import { By } from "@angular/platform-browser"; import { AdminExecutionComponent } from "./admin-execution.component"; import { AdminExecutionService } from "../../../service/admin/execution/admin-execution.service"; import { HttpClientTestingModule, HttpTestingController } from "@angular/common/http/testing"; import { NzDropDownModule } from "ng-zorro-antd/dropdown"; import { NzModalModule } from "ng-zorro-antd/modal"; import { commonTestProviders } from "../../../../common/testing/test-utils"; +import { Execution } from "../../../../common/type/execution"; describe("AdminDashboardComponent", () => { let component: AdminExecutionComponent; @@ -45,4 +47,23 @@ describe("AdminDashboardComponent", () => { it("should create", inject([HttpTestingController], () => { expect(component).toBeTruthy(); })); + + it("renders the workflow link to /user/workflow/ when the admin has access", () => { + component.listOfExecutions = [ + { + access: true, + workflowId: 42, + workflowName: "demo workflow", + executionId: 1, + executionName: "exec", + userName: "alice", + executionStatus: "COMPLETED", + } as unknown as Execution, + ]; + component.isLoading = false; + fixture.detectChanges(); + + const anchor = fixture.debugElement.query(By.css('a[href="/user/workflow/42"]')); + expect(anchor).toBeTruthy(); + }); }); diff --git a/frontend/src/app/dashboard/component/dashboard.component.html b/frontend/src/app/dashboard/component/dashboard.component.html index 9edea629159..ba3f74fa3a1 100644 --- a/frontend/src/app/dashboard/component/dashboard.component.html +++ b/frontend/src/app/dashboard/component/dashboard.component.html @@ -65,7 +65,7 @@ nz-tooltip="Look up the user projects" nzMatchRouter="true" nzTooltipPlacement="right" - [routerLink]="DASHBOARD_USER_PROJECT"> + [routerLink]="USER_PROJECT"> @@ -78,7 +78,7 @@ nz-tooltip="Open the saved workflows" nzMatchRouter="true" nzTooltipPlacement="right" - [routerLink]="DASHBOARD_USER_WORKFLOW"> + [routerLink]="USER_WORKFLOW"> @@ -91,7 +91,7 @@ nz-tooltip="Look up for datasets" nzMatchRouter="true" nzTooltipPlacement="right" - [routerLink]="DASHBOARD_USER_DATASET"> + [routerLink]="USER_DATASET"> @@ -103,7 +103,7 @@ nz-tooltip="Manage computing units" nzMatchRouter="true" nzTooltipPlacement="right" - [routerLink]="DASHBOARD_USER_COMPUTING_UNIT"> + [routerLink]="USER_COMPUTING_UNIT"> @@ -115,7 +115,7 @@ nz-tooltip="Quota information" nzMatchRouter="true" nzTooltipPlacement="right" - [routerLink]="DASHBOARD_USER_QUOTA"> + [routerLink]="USER_QUOTA"> @@ -127,7 +127,7 @@ nz-tooltip="Open the discussion forum" nzMatchRouter="true" nzTooltipPlacement="right" - [routerLink]="DASHBOARD_USER_DISCUSSION"> + [routerLink]="USER_DISCUSSION"> @@ -147,7 +147,7 @@ nz-tooltip="Look up the users" nzMatchRouter="true" nzTooltipPlacement="right" - [routerLink]="DASHBOARD_ADMIN_USER"> + [routerLink]="ADMIN_USER"> @@ -158,7 +158,7 @@ nz-tooltip="View statistics" nzMatchRouter="true" nzTooltipPlacement="right" - [routerLink]="DASHBOARD_ADMIN_EXECUTION"> + [routerLink]="ADMIN_EXECUTION"> @@ -169,7 +169,7 @@ nz-tooltip="Setup gmail" nzMatchRouter="true" nzTooltipPlacement="right" - [routerLink]="DASHBOARD_ADMIN_GMAIL"> + [routerLink]="ADMIN_GMAIL"> @@ -180,7 +180,7 @@ nz-tooltip="Settings" nzMatchRouter="true" nzTooltipPlacement="right" - [routerLink]="DASHBOARD_ADMIN_SETTINGS"> + [routerLink]="ADMIN_SETTINGS"> @@ -194,7 +194,7 @@ nz-menu-item nz-tooltip nzTooltipPlacement="right" - [routerLink]="DASHBOARD_ABOUT"> + [routerLink]="ABOUT"> diff --git a/frontend/src/app/dashboard/component/dashboard.component.spec.ts b/frontend/src/app/dashboard/component/dashboard.component.spec.ts index 6f508ca136a..29ed8426fe2 100644 --- a/frontend/src/app/dashboard/component/dashboard.component.spec.ts +++ b/frontend/src/app/dashboard/component/dashboard.component.spec.ts @@ -35,12 +35,26 @@ import { NavigationEnd, Params, Router, + RouterLink, UrlSegment, } from "@angular/router"; import type { Mock } from "vitest"; import { HttpClientTestingModule } from "@angular/common/http/testing"; import { commonTestProviders } from "../../common/testing/test-utils"; import { GuiConfigService } from "../../common/service/gui-config.service"; +import { + ABOUT, + ADMIN_EXECUTION, + ADMIN_GMAIL, + ADMIN_SETTINGS, + ADMIN_USER, + USER_COMPUTING_UNIT, + USER_DATASET, + USER_DISCUSSION, + USER_PROJECT, + USER_QUOTA, + USER_WORKFLOW, +} from "../../app-routing.constant"; describe("DashboardComponent", () => { let component: DashboardComponent; @@ -77,11 +91,12 @@ describe("DashboardComponent", () => { isAdmin: vi.fn().mockReturnValue(false), isLogin: vi.fn().mockReturnValue(false), userChanged: vi.fn().mockReturnValue(of(null)), + getCurrentUser: vi.fn().mockReturnValue(undefined), }; routerMock = { - events: of(new NavigationEnd(1, "/dashboard", "/dashboard")), - url: "/dashboard", + events: of(new NavigationEnd(1, "/", "/")), + url: "/", navigateByUrl: vi.fn(), }; @@ -162,4 +177,48 @@ describe("DashboardComponent", () => { expect(fixture.debugElement.query(By.css("#powered-by"))).toBeTruthy(); }); + + it("should hide the navbar on workflow workspace routes", () => { + expect(component.isNavbarEnabled("/user/workflow/42")).toBe(false); + expect(component.isNavbarEnabled("/user/workflow")).toBe(true); + expect(component.isNavbarEnabled("/user/project")).toBe(true); + }); + + it("exposes route constants without the legacy /dashboard prefix", () => { + expect(USER_PROJECT).toBe("/user/project"); + expect(USER_WORKFLOW).toBe("/user/workflow"); + expect(USER_DATASET).toBe("/user/dataset"); + expect(USER_COMPUTING_UNIT).toBe("/user/compute"); + expect(USER_QUOTA).toBe("/user/quota"); + expect(USER_DISCUSSION).toBe("/user/discussion"); + expect(ADMIN_USER).toBe("/admin/user"); + expect(ADMIN_EXECUTION).toBe("/admin/execution"); + expect(ADMIN_GMAIL).toBe("/admin/gmail"); + expect(ADMIN_SETTINGS).toBe("/admin/settings"); + expect(ABOUT).toBe("/about"); + }); + + it("renders every sidebar tab's routerLink when fully enabled", () => { + (userServiceMock.isLogin as Mock).mockReturnValue(true); + component.isLogin = true; + component.isAdmin = true; + component.sidebarTabs = { + hub_enabled: false, + home_enabled: true, + workflow_enabled: true, + dataset_enabled: true, + your_work_enabled: true, + projects_enabled: true, + workflows_enabled: true, + datasets_enabled: true, + compute_enabled: true, + quota_enabled: true, + forum_enabled: true, + about_enabled: true, + }; + fixture.detectChanges(); + + // 6 "Your Work" links + 4 admin links + 1 about link = 11 + expect(fixture.debugElement.queryAll(By.directive(RouterLink)).length).toBe(11); + }); }); diff --git a/frontend/src/app/dashboard/component/dashboard.component.ts b/frontend/src/app/dashboard/component/dashboard.component.ts index 57e6e8e284e..7cc12af36e9 100644 --- a/frontend/src/app/dashboard/component/dashboard.component.ts +++ b/frontend/src/app/dashboard/component/dashboard.component.ts @@ -29,17 +29,17 @@ import { AdminSettingsService } from "../service/admin/settings/admin-settings.s import { GuiConfigService } from "../../common/service/gui-config.service"; import { - DASHBOARD_ABOUT, - DASHBOARD_ADMIN_EXECUTION, - DASHBOARD_ADMIN_GMAIL, - DASHBOARD_ADMIN_SETTINGS, - DASHBOARD_ADMIN_USER, - DASHBOARD_USER_COMPUTING_UNIT, - DASHBOARD_USER_DATASET, - DASHBOARD_USER_DISCUSSION, - DASHBOARD_USER_PROJECT, - DASHBOARD_USER_QUOTA, - DASHBOARD_USER_WORKFLOW, + ABOUT, + ADMIN_EXECUTION, + ADMIN_GMAIL, + ADMIN_SETTINGS, + ADMIN_USER, + USER_COMPUTING_UNIT, + USER_DATASET, + USER_DISCUSSION, + USER_PROJECT, + USER_QUOTA, + USER_WORKFLOW, } from "../../app-routing.constant"; import { Version } from "../../../environments/version"; import { SidebarTabs } from "../../common/type/gui-config"; @@ -105,16 +105,18 @@ export class DashboardComponent implements OnInit { about_enabled: false, }; - protected readonly DASHBOARD_USER_PROJECT = DASHBOARD_USER_PROJECT; - protected readonly DASHBOARD_USER_WORKFLOW = DASHBOARD_USER_WORKFLOW; - protected readonly DASHBOARD_USER_DATASET = DASHBOARD_USER_DATASET; - protected readonly DASHBOARD_USER_COMPUTING_UNIT = DASHBOARD_USER_COMPUTING_UNIT; - protected readonly DASHBOARD_USER_QUOTA = DASHBOARD_USER_QUOTA; - protected readonly DASHBOARD_USER_DISCUSSION = DASHBOARD_USER_DISCUSSION; - protected readonly DASHBOARD_ADMIN_USER = DASHBOARD_ADMIN_USER; - protected readonly DASHBOARD_ADMIN_GMAIL = DASHBOARD_ADMIN_GMAIL; - protected readonly DASHBOARD_ADMIN_EXECUTION = DASHBOARD_ADMIN_EXECUTION; - protected readonly DASHBOARD_ADMIN_SETTINGS = DASHBOARD_ADMIN_SETTINGS; + protected readonly USER_PROJECT = USER_PROJECT; + protected readonly USER_WORKFLOW = USER_WORKFLOW; + protected readonly USER_DATASET = USER_DATASET; + protected readonly USER_COMPUTING_UNIT = USER_COMPUTING_UNIT; + protected readonly USER_QUOTA = USER_QUOTA; + protected readonly USER_DISCUSSION = USER_DISCUSSION; + protected readonly ADMIN_USER = ADMIN_USER; + protected readonly ADMIN_GMAIL = ADMIN_GMAIL; + protected readonly ADMIN_EXECUTION = ADMIN_EXECUTION; + protected readonly ADMIN_SETTINGS = ADMIN_SETTINGS; + protected readonly ABOUT = ABOUT; + protected readonly String = String; constructor( private userService: UserService, @@ -158,7 +160,7 @@ export class DashboardComponent implements OnInit { .pipe(untilDestroyed(this)) .subscribe(() => { this.ngZone.run(() => { - this.router.navigateByUrl(this.route.snapshot.queryParams["returnUrl"] || DASHBOARD_USER_WORKFLOW); + this.router.navigateByUrl(this.route.snapshot.queryParams["returnUrl"] || USER_WORKFLOW); }); }); }); @@ -232,7 +234,7 @@ export class DashboardComponent implements OnInit { isNavbarEnabled(currentRoute: string) { // Hide navbar for workflow workspace pages (with numeric ID) - if (currentRoute.match(/\/dashboard\/user\/workflow\/\d+/)) { + if (currentRoute.match(/\/user\/workflow\/\d+/)) { return false; } return true; @@ -249,6 +251,4 @@ export class DashboardComponent implements OnInit { } } - protected readonly DASHBOARD_ABOUT = DASHBOARD_ABOUT; - protected readonly String = String; } diff --git a/frontend/src/app/dashboard/component/user/list-item/list-item.component.spec.ts b/frontend/src/app/dashboard/component/user/list-item/list-item.component.spec.ts index debcc5b99eb..93401267cb7 100644 --- a/frontend/src/app/dashboard/component/user/list-item/list-item.component.spec.ts +++ b/frontend/src/app/dashboard/component/user/list-item/list-item.component.spec.ts @@ -31,6 +31,14 @@ import { UserService } from "../../../../common/service/user/user.service"; import { commonTestProviders } from "../../../../common/testing/test-utils"; import type { Mocked } from "vitest"; import { DashboardEntry } from "src/app/dashboard/type/dashboard-entry"; +import { + HUB_DATASET_RESULT_DETAIL, + HUB_WORKFLOW_RESULT_DETAIL, + USER_DATASET, + USER_PROJECT, + USER_WORKSPACE, +} from "../../../../app-routing.constant"; + describe("ListItemComponent", () => { let component: ListItemComponent; let fixture: ComponentFixture; @@ -104,4 +112,66 @@ describe("ListItemComponent", () => { expect(component.entry.description).toBe("Old Description"); expect(component.editingDescription).toBe(false); }); + + describe("initializeEntry routes", () => { + const baseStats = { likeCount: 0, viewCount: 0, isLiked: false }; + + it("routes owned workflows to the user workspace", () => { + component.currentUid = 1; + component.entry = { + id: 100, + type: "workflow", + workflow: { isOwner: true }, + accessibleUserIds: [1], + ...baseStats, + } as unknown as DashboardEntry; + component.initializeEntry(); + expect(component.entryLink).toEqual([USER_WORKSPACE, "100"]); + }); + + it("routes non-owned workflows to the hub workflow detail page", () => { + component.currentUid = 1; + component.entry = { + id: 101, + type: "workflow", + workflow: { isOwner: false }, + accessibleUserIds: [2], + ...baseStats, + } as unknown as DashboardEntry; + component.initializeEntry(); + expect(component.entryLink).toEqual([HUB_WORKFLOW_RESULT_DETAIL, "101"]); + }); + + it("routes projects to the user project page", () => { + component.entry = { id: 200, type: "project", ...baseStats } as unknown as DashboardEntry; + component.initializeEntry(); + expect(component.entryLink).toEqual([USER_PROJECT, "200"]); + }); + + it("routes owned datasets to the user dataset page", () => { + component.currentUid = 1; + component.entry = { + id: 300, + type: "dataset", + dataset: { isOwner: true }, + accessibleUserIds: [1], + ...baseStats, + } as unknown as DashboardEntry; + component.initializeEntry(); + expect(component.entryLink).toEqual([USER_DATASET, "300"]); + }); + + it("routes non-owned datasets to the hub dataset detail page", () => { + component.currentUid = 1; + component.entry = { + id: 301, + type: "dataset", + dataset: { isOwner: false }, + accessibleUserIds: [2], + ...baseStats, + } as unknown as DashboardEntry; + component.initializeEntry(); + expect(component.entryLink).toEqual([HUB_DATASET_RESULT_DETAIL, "301"]); + }); + }); }); diff --git a/frontend/src/app/dashboard/component/user/list-item/list-item.component.ts b/frontend/src/app/dashboard/component/user/list-item/list-item.component.ts index 4cb8af764c7..c635a62108f 100644 --- a/frontend/src/app/dashboard/component/user/list-item/list-item.component.ts +++ b/frontend/src/app/dashboard/component/user/list-item/list-item.component.ts @@ -45,11 +45,11 @@ import { formatSize } from "src/app/common/util/size-formatter.util"; import { DatasetService, DEFAULT_DATASET_NAME } from "../../../service/user/dataset/dataset.service"; import { NotificationService } from "../../../../common/service/notification/notification.service"; import { - DASHBOARD_HUB_DATASET_RESULT_DETAIL, - DASHBOARD_HUB_WORKFLOW_RESULT_DETAIL, - DASHBOARD_USER_DATASET, - DASHBOARD_USER_PROJECT, - DASHBOARD_USER_WORKSPACE, + HUB_DATASET_RESULT_DETAIL, + HUB_WORKFLOW_RESULT_DETAIL, + USER_DATASET, + USER_PROJECT, + USER_WORKSPACE, } from "../../../../app-routing.constant"; import { isDefined } from "../../../../common/util/predicate"; import { NzCardComponent } from "ng-zorro-antd/card"; @@ -144,24 +144,24 @@ export class ListItemComponent implements OnChanges { this.disableDelete = !this.entry.workflow.isOwner; this.owners = this.entry.accessibleUserIds; if (this.currentUid !== undefined && this.owners.includes(this.currentUid)) { - this.entryLink = [DASHBOARD_USER_WORKSPACE, String(this.entry.id)]; + this.entryLink = [USER_WORKSPACE, String(this.entry.id)]; } else { - this.entryLink = [DASHBOARD_HUB_WORKFLOW_RESULT_DETAIL, String(this.entry.id)]; + this.entryLink = [HUB_WORKFLOW_RESULT_DETAIL, String(this.entry.id)]; } this.size = this.entry.size; } this.iconType = "project"; } else if (this.entry.type === "project") { - this.entryLink = [DASHBOARD_USER_PROJECT, String(this.entry.id)]; + this.entryLink = [USER_PROJECT, String(this.entry.id)]; this.iconType = "container"; } else if (this.entry.type === "dataset") { if (typeof this.entry.id === "number") { this.disableDelete = !this.entry.dataset.isOwner; this.owners = this.entry.accessibleUserIds; if (this.currentUid !== undefined && this.owners.includes(this.currentUid)) { - this.entryLink = [DASHBOARD_USER_DATASET, String(this.entry.id)]; + this.entryLink = [USER_DATASET, String(this.entry.id)]; } else { - this.entryLink = [DASHBOARD_HUB_DATASET_RESULT_DETAIL, String(this.entry.id)]; + this.entryLink = [HUB_DATASET_RESULT_DETAIL, String(this.entry.id)]; } this.iconType = "database"; this.size = this.entry.size; diff --git a/frontend/src/app/dashboard/component/user/search-bar/search-bar.component.ts b/frontend/src/app/dashboard/component/user/search-bar/search-bar.component.ts index ae6a6c1df59..638a2f7e7b1 100644 --- a/frontend/src/app/dashboard/component/user/search-bar/search-bar.component.ts +++ b/frontend/src/app/dashboard/component/user/search-bar/search-bar.component.ts @@ -28,7 +28,7 @@ import { DashboardEntry } from "../../../type/dashboard-entry"; import { Observable, of, Subject } from "rxjs"; import { debounceTime, switchMap } from "rxjs/operators"; import { UserService } from "../../../../common/service/user/user.service"; -import { DASHBOARD_SEARCH } from "../../../../app-routing.constant"; +import { SEARCH } from "../../../../app-routing.constant"; import { ɵNzTransitionPatchDirective } from "ng-zorro-antd/core/transition-patch"; import { NzSpaceCompactItemDirective } from "ng-zorro-antd/space"; import { NzInputGroupComponent, NzInputDirective } from "ng-zorro-antd/input"; @@ -137,7 +137,7 @@ export class SearchBarComponent { } performSearch(keyword: string) { - this.router.navigate([DASHBOARD_SEARCH], { queryParams: { q: keyword } }); + this.router.navigate([SEARCH], { queryParams: { q: keyword } }); } convertToName(resultItem: SearchResultItem): string { diff --git a/frontend/src/app/dashboard/component/user/share-access/share-access.component.ts b/frontend/src/app/dashboard/component/user/share-access/share-access.component.ts index b6b51b9709a..d6a6388a871 100644 --- a/frontend/src/app/dashboard/component/user/share-access/share-access.component.ts +++ b/frontend/src/app/dashboard/component/user/share-access/share-access.component.ts @@ -28,9 +28,9 @@ import { NZ_MODAL_DATA, NzModalRef, NzModalService } from "ng-zorro-antd/modal"; import { NotificationService } from "../../../../common/service/notification/notification.service"; import { HttpErrorResponse } from "@angular/common/http"; import { - DASHBOARD_USER_DATASET, - DASHBOARD_USER_PROJECT, - DASHBOARD_USER_WORKFLOW, + USER_DATASET, + USER_PROJECT, + USER_WORKFLOW, } from "../../../../app-routing.constant"; import { NzMessageService } from "ng-zorro-antd/message"; import { DatasetService } from "../../../service/user/dataset/dataset.service"; @@ -196,9 +196,9 @@ export class ShareAccessComponent implements OnInit, OnDestroy { let message = `${this.userService.getCurrentUser()?.email} shared a ${this.type} with you`; if (this.type !== "computing-unit") { let routePath = ""; - if (this.type === "workflow") routePath = DASHBOARD_USER_WORKFLOW; - if (this.type === "dataset") routePath = DASHBOARD_USER_DATASET; - if (this.type === "project") routePath = DASHBOARD_USER_PROJECT; + if (this.type === "workflow") routePath = USER_WORKFLOW; + if (this.type === "dataset") routePath = USER_DATASET; + if (this.type === "project") routePath = USER_PROJECT; message += `, access the ${this.type} at ${location.origin}${routePath}/${this.id}`; } this.accessService diff --git a/frontend/src/app/dashboard/component/user/user-dataset/user-dataset-list-item/user-dataset-list-item.component.html b/frontend/src/app/dashboard/component/user/user-dataset/user-dataset-list-item/user-dataset-list-item.component.html index 6fa0d48a789..1383680f8fb 100644 --- a/frontend/src/app/dashboard/component/user/user-dataset/user-dataset-list-item/user-dataset-list-item.component.html +++ b/frontend/src/app/dashboard/component/user/user-dataset/user-dataset-list-item/user-dataset-list-item.component.html @@ -33,7 +33,7 @@
{{ dataset.name }} diff --git a/frontend/src/app/dashboard/component/user/user-dataset/user-dataset-list-item/user-dataset-list-item.component.ts b/frontend/src/app/dashboard/component/user/user-dataset/user-dataset-list-item/user-dataset-list-item.component.ts index 3e51374c9bd..68469118233 100644 --- a/frontend/src/app/dashboard/component/user/user-dataset/user-dataset-list-item/user-dataset-list-item.component.ts +++ b/frontend/src/app/dashboard/component/user/user-dataset/user-dataset-list-item/user-dataset-list-item.component.ts @@ -25,7 +25,7 @@ import { ShareAccessComponent } from "../../share-access/share-access.component" import { NotificationService } from "../../../../../common/service/notification/notification.service"; import { NzModalService } from "ng-zorro-antd/modal"; import { DashboardDataset } from "../../../../type/dashboard-dataset.interface"; -import { DASHBOARD_USER_DATASET } from "../../../../../app-routing.constant"; +import { USER_DATASET } from "../../../../../app-routing.constant"; import { NzListItemComponent, NzListItemMetaComponent, @@ -73,7 +73,7 @@ import { NzPopconfirmDirective } from "ng-zorro-antd/popconfirm"; ], }) export class UserDatasetListItemComponent { - protected readonly DASHBOARD_USER_DATASET = DASHBOARD_USER_DATASET; + protected readonly USER_DATASET = USER_DATASET; private _entry?: DashboardDataset; diff --git a/frontend/src/app/dashboard/component/user/user-dataset/user-dataset.component.ts b/frontend/src/app/dashboard/component/user/user-dataset/user-dataset.component.ts index 165270c0537..2deedba1048 100644 --- a/frontend/src/app/dashboard/component/user/user-dataset/user-dataset.component.ts +++ b/frontend/src/app/dashboard/component/user/user-dataset/user-dataset.component.ts @@ -28,7 +28,7 @@ import { DashboardEntry } from "../../../type/dashboard-entry"; import { SearchResultsComponent } from "../search-results/search-results.component"; import { FiltersComponent } from "../filters/filters.component"; import { firstValueFrom } from "rxjs"; -import { DASHBOARD_USER_DATASET } from "../../../../app-routing.constant"; +import { USER_DATASET } from "../../../../app-routing.constant"; import { NzModalService } from "ng-zorro-antd/modal"; import { UserDatasetVersionCreatorComponent } from "./user-dataset-explorer/user-dataset-version-creator/user-dataset-version-creator.component"; import { DashboardDataset } from "../../../type/dashboard-dataset.interface"; @@ -202,7 +202,7 @@ export class UserDatasetComponent implements AfterViewInit { modal.afterClose.pipe(untilDestroyed(this)).subscribe(result => { if (result != null) { const dashboardDataset: DashboardDataset = result as DashboardDataset; - this.router.navigate([`${DASHBOARD_USER_DATASET}/${dashboardDataset.dataset.did}`]); + this.router.navigate([`${USER_DATASET}/${dashboardDataset.dataset.did}`]); } }); } diff --git a/frontend/src/app/dashboard/component/user/user-icon/user-icon.component.spec.ts b/frontend/src/app/dashboard/component/user/user-icon/user-icon.component.spec.ts index 11731e0cf60..706a13a4300 100644 --- a/frontend/src/app/dashboard/component/user/user-icon/user-icon.component.spec.ts +++ b/frontend/src/app/dashboard/component/user/user-icon/user-icon.component.spec.ts @@ -19,6 +19,7 @@ import { NO_ERRORS_SCHEMA } from "@angular/core"; import { ComponentFixture, TestBed } from "@angular/core/testing"; +import { Router } from "@angular/router"; import { UserIconComponent } from "./user-icon.component"; import { UserService } from "../../../../common/service/user/user.service"; import { HttpClientTestingModule } from "@angular/common/http/testing"; @@ -27,6 +28,7 @@ import { NzDropDownModule } from "ng-zorro-antd/dropdown"; import { RouterTestingModule } from "@angular/router/testing"; import { AboutComponent } from "../../../../hub/component/about/about.component"; import { commonTestProviders } from "../../../../common/testing/test-utils"; +import { ABOUT } from "../../../../app-routing.constant"; describe("UserIconComponent", () => { let component: UserIconComponent; @@ -54,4 +56,34 @@ describe("UserIconComponent", () => { it("should create", () => { expect(component).toBeTruthy(); }); + + describe("onClickLogout", () => { + it("navigates to /about (no /dashboard prefix) after logout", () => { + const router = TestBed.inject(Router); + const navigateSpy = vi.spyOn(router, "navigate").mockResolvedValue(true); + const userService = TestBed.inject(UserService); + const logoutSpy = vi.spyOn(userService, "logout").mockImplementation(() => {}); + + component.onClickLogout(); + + expect(logoutSpy).toHaveBeenCalledTimes(1); + expect(navigateSpy).toHaveBeenCalledWith([ABOUT]); + expect(ABOUT).toBe("/about"); + }); + + it("clears the flarum_remember cookie on logout", () => { + const router = TestBed.inject(Router); + vi.spyOn(router, "navigate").mockResolvedValue(true); + const userService = TestBed.inject(UserService); + vi.spyOn(userService, "logout").mockImplementation(() => {}); + // Seed the cookie so we can observe it being cleared. jsdom's + // document.cookie is the test surface here; assigning a value with a + // past expiry should expire the cookie immediately. + document.cookie = "flarum_remember=token; path=/;"; + + component.onClickLogout(); + + expect(document.cookie).not.toContain("flarum_remember=token"); + }); + }); }); diff --git a/frontend/src/app/dashboard/component/user/user-icon/user-icon.component.ts b/frontend/src/app/dashboard/component/user/user-icon/user-icon.component.ts index 4b1fd6d4de1..94bb7d9d4e1 100644 --- a/frontend/src/app/dashboard/component/user/user-icon/user-icon.component.ts +++ b/frontend/src/app/dashboard/component/user/user-icon/user-icon.component.ts @@ -22,7 +22,7 @@ import { UserService } from "../../../../common/service/user/user.service"; import { User } from "../../../../common/type/user"; import { UntilDestroy } from "@ngneat/until-destroy"; import { Router } from "@angular/router"; -import { DASHBOARD_ABOUT } from "../../../../app-routing.constant"; +import { ABOUT } from "../../../../app-routing.constant"; import { UserAvatarComponent } from "../user-avatar/user-avatar.component"; import { ɵNzTransitionPatchDirective } from "ng-zorro-antd/core/transition-patch"; import { NzDropdownDirective, NzDropdownMenuComponent } from "ng-zorro-antd/dropdown"; @@ -63,6 +63,6 @@ export class UserIconComponent { public onClickLogout(): void { this.userService.logout(); document.cookie = "flarum_remember=; expires=Thu, 01 Jan 1970 00:00:00 UTC; path=/;"; - this.router.navigate([DASHBOARD_ABOUT]); + this.router.navigate([ABOUT]); } } diff --git a/frontend/src/app/dashboard/component/user/user-project/user-project-list-item/user-project-list-item.component.ts b/frontend/src/app/dashboard/component/user/user-project/user-project-list-item/user-project-list-item.component.ts index 9bb67506301..562b465e8fe 100644 --- a/frontend/src/app/dashboard/component/user/user-project/user-project-list-item/user-project-list-item.component.ts +++ b/frontend/src/app/dashboard/component/user/user-project/user-project-list-item/user-project-list-item.component.ts @@ -25,7 +25,7 @@ import { UntilDestroy, untilDestroyed } from "@ngneat/until-destroy"; import { ShareAccessComponent } from "../../share-access/share-access.component"; import { NzModalService } from "ng-zorro-antd/modal"; import { UserService } from "../../../../../common/service/user/user.service"; -import { DASHBOARD_USER_PROJECT } from "../../../../../app-routing.constant"; +import { USER_PROJECT } from "../../../../../app-routing.constant"; import { NzListItemComponent, NzListItemMetaComponent, @@ -90,7 +90,7 @@ import { HighlightSearchTermsPipe } from "../../user-workflow/user-workflow-list ], }) export class UserProjectListItemComponent implements OnInit { - public readonly ROUTER_USER_PROJECT_BASE_URL = DASHBOARD_USER_PROJECT; + public readonly ROUTER_USER_PROJECT_BASE_URL = USER_PROJECT; public readonly MAX_PROJECT_DESCRIPTION_CHAR_COUNT = 10000; private _entry?: DashboardProject; @Input() public keywords: string[] = []; diff --git a/frontend/src/app/dashboard/component/user/user-workflow/user-workflow-list-item/user-workflow-list-item.component.html b/frontend/src/app/dashboard/component/user/user-workflow/user-workflow-list-item/user-workflow-list-item.component.html index 4affee3b44c..6524c4dbc60 100644 --- a/frontend/src/app/dashboard/component/user/user-workflow/user-workflow-list-item/user-workflow-list-item.component.html +++ b/frontend/src/app/dashboard/component/user/user-workflow/user-workflow-list-item/user-workflow-list-item.component.html @@ -39,7 +39,7 @@
@@ -139,7 +139,7 @@ class="project-label-name" [ngClass]="{'color-tag' : true, 'light-color' : isLightColor(userProjectsMap.get(projectID)!.color!), 'dark-color' : !isLightColor(userProjectsMap.get(projectID)!.color!)}" [ngStyle]="{'color' : isLightColor(userProjectsMap.get(projectID)!.color!) ? 'black' : 'white', 'background-color' : '#' + userProjectsMap.get(projectID)!.color}" - [routerLink]="DASHBOARD_USER_PROJECT + '/' + userProjectsMap.get(projectID)!.pid"> + [routerLink]="USER_PROJECT + '/' + userProjectsMap.get(projectID)!.pid"> {{userProjectsMap.get(projectID)!.name}}
{ let component: UserWorkflowComponent; @@ -312,6 +314,23 @@ describe("SavedWorkflowSectionComponent", () => { ); }); + describe("onClickCreateNewWorkflowFromDashboard", () => { + it("navigates to /user/workflow/ (no /dashboard prefix) on successful creation", () => { + const router = TestBed.inject(Router); + const navigateSpy = vi.spyOn(router, "navigate").mockResolvedValue(true); + const persist = TestBed.inject(WorkflowPersistService) as any; + // StubWorkflowPersistService doesn't define createWorkflow — assign the + // method here so the component's call resolves to a controlled observable. + persist.createWorkflow = vi.fn().mockReturnValue(of({ workflow: { wid: 99 } })); + component.pid = undefined; + + component.onClickCreateNewWorkflowFromDashboard(); + + expect(navigateSpy).toHaveBeenCalledWith([USER_WORKSPACE, 99]); + expect(USER_WORKSPACE).toBe("/user/workflow"); + }); + }); + it("downloads checked files", async () => { // If multiple workflows in a single batch download have name conflicts, rename them as workflow-1, workflow-2, etc. component.searchResultsComponent.entries = component.searchResultsComponent.entries.concat( diff --git a/frontend/src/app/dashboard/component/user/user-workflow/user-workflow.component.ts b/frontend/src/app/dashboard/component/user/user-workflow/user-workflow.component.ts index f3129f1a9cf..584191f8bf5 100644 --- a/frontend/src/app/dashboard/component/user/user-workflow/user-workflow.component.ts +++ b/frontend/src/app/dashboard/component/user/user-workflow/user-workflow.component.ts @@ -43,7 +43,7 @@ import { UserProjectService } from "../../../service/user/project/user-project.s import { map, mergeMap, switchMap, tap } from "rxjs/operators"; import { DashboardWorkflow } from "../../../type/dashboard-workflow.interface"; import { DownloadService } from "../../../service/user/download/download.service"; -import { DASHBOARD_USER_WORKSPACE } from "../../../../app-routing.constant"; +import { USER_WORKSPACE } from "../../../../app-routing.constant"; import { GuiConfigService } from "../../../../common/service/gui-config.service"; import { NzCardComponent } from "ng-zorro-antd/card"; import { NzSpaceCompactItemDirective, NzSpaceCompactComponent } from "ng-zorro-antd/space"; @@ -293,7 +293,7 @@ export class UserWorkflowComponent implements AfterViewInit { .subscribe({ next: (wid: number | undefined) => { // Use the wid here for navigation - this.router.navigate([DASHBOARD_USER_WORKSPACE, wid]).then(null); + this.router.navigate([USER_WORKSPACE, wid]).then(null); }, error: (err: unknown) => this.notificationService.error("Workflow creation failed"), }); diff --git a/frontend/src/app/dashboard/service/admin/guard/admin-guard.service.ts b/frontend/src/app/dashboard/service/admin/guard/admin-guard.service.ts index f95d701194a..d1e1318cc57 100644 --- a/frontend/src/app/dashboard/service/admin/guard/admin-guard.service.ts +++ b/frontend/src/app/dashboard/service/admin/guard/admin-guard.service.ts @@ -20,7 +20,7 @@ import { Injectable } from "@angular/core"; import { CanActivate, Router } from "@angular/router"; import { UserService } from "../../../../common/service/user/user.service"; -import { DASHBOARD_USER_WORKFLOW } from "../../../../app-routing.constant"; +import { USER_WORKFLOW } from "../../../../app-routing.constant"; /** * AuthGuardService is a service can tell the router whether @@ -37,7 +37,7 @@ export class AdminGuardService implements CanActivate { if (this.userService.isAdmin()) { return true; } else { - this.router.navigate([DASHBOARD_USER_WORKFLOW]); + this.router.navigate([USER_WORKFLOW]); return false; } } diff --git a/frontend/src/app/hub/component/about/local-login/local-login.component.ts b/frontend/src/app/hub/component/about/local-login/local-login.component.ts index 215b17e3425..00968074395 100644 --- a/frontend/src/app/hub/component/about/local-login/local-login.component.ts +++ b/frontend/src/app/hub/component/about/local-login/local-login.component.ts @@ -25,7 +25,7 @@ import { UserService } from "../../../../common/service/user/user.service"; import { NotificationService } from "../../../../common/service/notification/notification.service"; import { catchError } from "rxjs/operators"; import { throwError } from "rxjs"; -import { DASHBOARD_USER_WORKFLOW } from "../../../../app-routing.constant"; +import { USER_WORKFLOW } from "../../../../app-routing.constant"; import { GuiConfigService } from "../../../../common/service/gui-config.service"; import { NzTabsComponent, NzTabComponent } from "ng-zorro-antd/tabs"; import { NgIf } from "@angular/common"; @@ -131,9 +131,7 @@ export class LocalLoginComponent implements OnInit { }), untilDestroyed(this) ) - .subscribe(() => - this.router.navigateByUrl(this.route.snapshot.queryParams["returnUrl"] || DASHBOARD_USER_WORKFLOW) - ); + .subscribe(() => this.router.navigateByUrl(this.route.snapshot.queryParams["returnUrl"] || USER_WORKFLOW)); } /** diff --git a/frontend/src/app/hub/component/browse-section/browse-section.component.spec.ts b/frontend/src/app/hub/component/browse-section/browse-section.component.spec.ts index a4c91b4abe8..504101e32e0 100644 --- a/frontend/src/app/hub/component/browse-section/browse-section.component.spec.ts +++ b/frontend/src/app/hub/component/browse-section/browse-section.component.spec.ts @@ -23,6 +23,13 @@ import { WorkflowPersistService } from "../../../common/service/workflow-persist import { DatasetService } from "../../../dashboard/service/user/dataset/dataset.service"; import { ChangeDetectorRef } from "@angular/core"; import { commonTestProviders } from "../../../common/testing/test-utils"; +import { DashboardEntry } from "../../../dashboard/type/dashboard-entry"; +import { + HUB_DATASET_RESULT_DETAIL, + HUB_WORKFLOW_RESULT_DETAIL, + USER_DATASET, + USER_WORKSPACE, +} from "../../../app-routing.constant"; describe("BrowseSectionComponent", () => { let component: BrowseSectionComponent; @@ -46,4 +53,34 @@ describe("BrowseSectionComponent", () => { it("should create", () => { expect(component).toBeTruthy(); }); + + describe("entityRoutes initialization", () => { + it("routes owned workflows to the user workspace", () => { + component.currentUid = 1; + component.entities = [{ id: 100, type: "workflow", accessibleUserIds: [1] } as unknown as DashboardEntry]; + component.ngOnInit(); + expect(component.entityRoutes[100]).toEqual([USER_WORKSPACE, "100"]); + }); + + it("routes non-owned workflows to the hub workflow detail page", () => { + component.currentUid = 1; + component.entities = [{ id: 101, type: "workflow", accessibleUserIds: [2] } as unknown as DashboardEntry]; + component.ngOnInit(); + expect(component.entityRoutes[101]).toEqual([HUB_WORKFLOW_RESULT_DETAIL, "101"]); + }); + + it("routes owned datasets to the user dataset page", () => { + component.currentUid = 1; + component.entities = [{ id: 200, type: "dataset", accessibleUserIds: [1] } as unknown as DashboardEntry]; + component.ngOnInit(); + expect(component.entityRoutes[200]).toEqual([USER_DATASET, "200"]); + }); + + it("routes non-owned datasets to the hub dataset detail page", () => { + component.currentUid = 1; + component.entities = [{ id: 201, type: "dataset", accessibleUserIds: [2] } as unknown as DashboardEntry]; + component.ngOnInit(); + expect(component.entityRoutes[201]).toEqual([HUB_DATASET_RESULT_DETAIL, "201"]); + }); + }); }); diff --git a/frontend/src/app/hub/component/browse-section/browse-section.component.ts b/frontend/src/app/hub/component/browse-section/browse-section.component.ts index 7629a8906d8..659e018e1e1 100644 --- a/frontend/src/app/hub/component/browse-section/browse-section.component.ts +++ b/frontend/src/app/hub/component/browse-section/browse-section.component.ts @@ -23,10 +23,10 @@ import { WorkflowPersistService } from "../../../common/service/workflow-persist import { DatasetService } from "../../../dashboard/service/user/dataset/dataset.service"; import { UntilDestroy } from "@ngneat/until-destroy"; import { - DASHBOARD_HUB_DATASET_RESULT_DETAIL, - DASHBOARD_HUB_WORKFLOW_RESULT_DETAIL, - DASHBOARD_USER_DATASET, - DASHBOARD_USER_WORKSPACE, + HUB_DATASET_RESULT_DETAIL, + HUB_WORKFLOW_RESULT_DETAIL, + USER_DATASET, + USER_WORKSPACE, } from "../../../app-routing.constant"; import { AppSettings } from "../../../common/app-setting"; import { NgIf, NgFor, NgStyle, DatePipe } from "@angular/common"; @@ -59,10 +59,10 @@ export class BrowseSectionComponent implements OnInit, OnChanges { @Input() currentUid: number | undefined; defaultBackground: string = "../../../../../assets/card_background.jpg"; - protected readonly DASHBOARD_HUB_WORKFLOW_RESULT_DETAIL = DASHBOARD_HUB_WORKFLOW_RESULT_DETAIL; - protected readonly DASHBOARD_USER_WORKSPACE = DASHBOARD_USER_WORKSPACE; - protected readonly DASHBOARD_HUB_DATASET_RESULT_DETAIL = DASHBOARD_HUB_DATASET_RESULT_DETAIL; - protected readonly DASHBOARD_USER_DATASET = DASHBOARD_USER_DATASET; + protected readonly HUB_WORKFLOW_RESULT_DETAIL = HUB_WORKFLOW_RESULT_DETAIL; + protected readonly USER_WORKSPACE = USER_WORKSPACE; + protected readonly HUB_DATASET_RESULT_DETAIL = HUB_DATASET_RESULT_DETAIL; + protected readonly USER_DATASET = USER_DATASET; entityRoutes: { [key: number]: string[] } = {}; private coverImageUrls = new Map(); @@ -97,15 +97,15 @@ export class BrowseSectionComponent implements OnInit, OnChanges { if (entity.type === "workflow") { if (this.currentUid !== undefined && owners.includes(this.currentUid)) { - this.entityRoutes[entityId] = [this.DASHBOARD_USER_WORKSPACE, String(entityId)]; + this.entityRoutes[entityId] = [this.USER_WORKSPACE, String(entityId)]; } else { - this.entityRoutes[entityId] = [this.DASHBOARD_HUB_WORKFLOW_RESULT_DETAIL, String(entityId)]; + this.entityRoutes[entityId] = [this.HUB_WORKFLOW_RESULT_DETAIL, String(entityId)]; } } else if (entity.type === "dataset") { if (this.currentUid !== undefined && owners.includes(this.currentUid)) { - this.entityRoutes[entityId] = [this.DASHBOARD_USER_DATASET, String(entityId)]; + this.entityRoutes[entityId] = [this.USER_DATASET, String(entityId)]; } else { - this.entityRoutes[entityId] = [this.DASHBOARD_HUB_DATASET_RESULT_DETAIL, String(entityId)]; + this.entityRoutes[entityId] = [this.HUB_DATASET_RESULT_DETAIL, String(entityId)]; } } else { throw new Error("Unexpected type in DashboardEntry."); diff --git a/frontend/src/app/hub/component/hub.component.html b/frontend/src/app/hub/component/hub.component.html index 5ba23062a3f..7343cd7d717 100644 --- a/frontend/src/app/hub/component/hub.component.html +++ b/frontend/src/app/hub/component/hub.component.html @@ -24,7 +24,7 @@ nz-tooltip="Home Page" nzMatchRouter="true" nzTooltipPlacement="right" - [routerLink]="DASHBOARD_HOME"> + [routerLink]="HOME"> @@ -36,7 +36,7 @@ nz-tooltip="Search public workflows" nzMatchRouter="true" nzTooltipPlacement="right" - [routerLink]="DASHBOARD_HUB_WORKFLOW_RESULT"> + [routerLink]="HUB_WORKFLOW_RESULT"> @@ -48,7 +48,7 @@ nz-tooltip="Search public dataset" nzMatchRouter="true" nzTooltipPlacement="right" - [routerLink]="DASHBOARD_HUB_DATASET_RESULT"> + [routerLink]="HUB_DATASET_RESULT"> diff --git a/frontend/src/app/hub/component/hub.component.ts b/frontend/src/app/hub/component/hub.component.ts index ad9bc079e0f..ee006fa2556 100644 --- a/frontend/src/app/hub/component/hub.component.ts +++ b/frontend/src/app/hub/component/hub.component.ts @@ -18,11 +18,7 @@ */ import { Component, Input } from "@angular/core"; -import { - DASHBOARD_HOME, - DASHBOARD_HUB_DATASET_RESULT, - DASHBOARD_HUB_WORKFLOW_RESULT, -} from "../../app-routing.constant"; +import { HOME, HUB_DATASET_RESULT, HUB_WORKFLOW_RESULT } from "../../app-routing.constant"; import { GuiConfigService } from "../../common/service/gui-config.service"; import { SidebarTabs } from "../../common/type/gui-config"; import { NgIf } from "@angular/common"; @@ -41,9 +37,9 @@ import { NzIconDirective } from "ng-zorro-antd/icon"; export class HubComponent { @Input() isLogin: boolean = false; @Input() sidebarTabs: SidebarTabs = {} as SidebarTabs; - protected readonly DASHBOARD_HOME = DASHBOARD_HOME; - protected readonly DASHBOARD_HUB_WORKFLOW_RESULT = DASHBOARD_HUB_WORKFLOW_RESULT; - protected readonly DASHBOARD_HUB_DATASET_RESULT = DASHBOARD_HUB_DATASET_RESULT; + protected readonly HOME = HOME; + protected readonly HUB_WORKFLOW_RESULT = HUB_WORKFLOW_RESULT; + protected readonly HUB_DATASET_RESULT = HUB_DATASET_RESULT; constructor(protected config: GuiConfigService) {} } diff --git a/frontend/src/app/hub/component/landing-page/landing-page.component.ts b/frontend/src/app/hub/component/landing-page/landing-page.component.ts index b8a7e02de3f..2f8a2fff7d9 100644 --- a/frontend/src/app/hub/component/landing-page/landing-page.component.ts +++ b/frontend/src/app/hub/component/landing-page/landing-page.component.ts @@ -24,11 +24,7 @@ import { UntilDestroy, untilDestroyed } from "@ngneat/until-destroy"; import { Router } from "@angular/router"; import { SearchService } from "../../../dashboard/service/user/search.service"; import { DashboardEntry } from "../../../dashboard/type/dashboard-entry"; -import { - DASHBOARD_HOME, - DASHBOARD_HUB_DATASET_RESULT, - DASHBOARD_HUB_WORKFLOW_RESULT, -} from "../../../app-routing.constant"; +import { HOME, HUB_DATASET_RESULT, HUB_WORKFLOW_RESULT } from "../../../app-routing.constant"; import { UserService } from "../../../common/service/user/user.service"; import { BrowseSectionComponent } from "../browse-section/browse-section.component"; @@ -119,13 +115,13 @@ export class LandingPageComponent implements OnInit { switch (type) { case "workflow": - path = DASHBOARD_HUB_WORKFLOW_RESULT; + path = HUB_WORKFLOW_RESULT; break; case "dataset": - path = DASHBOARD_HUB_DATASET_RESULT; + path = HUB_DATASET_RESULT; break; default: - path = DASHBOARD_HOME; + path = HOME; } this.router.navigate([path]); diff --git a/frontend/src/app/hub/component/workflow/detail/hub-workflow-detail.component.ts b/frontend/src/app/hub/component/workflow/detail/hub-workflow-detail.component.ts index 5eb1561b50b..e71d2c692b1 100644 --- a/frontend/src/app/hub/component/workflow/detail/hub-workflow-detail.component.ts +++ b/frontend/src/app/hub/component/workflow/detail/hub-workflow-detail.component.ts @@ -30,7 +30,7 @@ import { Role, User } from "src/app/common/type/user"; import { NotificationService } from "../../../../common/service/notification/notification.service"; import { WorkflowPersistService } from "../../../../common/service/workflow-persist/workflow-persist.service"; import { NZ_MODAL_DATA } from "ng-zorro-antd/modal"; -import { DASHBOARD_HUB_WORKFLOW_RESULT, DASHBOARD_USER_WORKSPACE } from "../../../../app-routing.constant"; +import { HUB_WORKFLOW_RESULT, USER_WORKSPACE } from "../../../../app-routing.constant"; import { NgIf, NgClass } from "@angular/common"; import { NzSpaceCompactItemDirective } from "ng-zorro-antd/space"; import { NzButtonComponent } from "ng-zorro-antd/button"; @@ -202,7 +202,7 @@ export class HubWorkflowDetailComponent implements AfterViewInit, OnDestroy, OnI } goBack(): void { - this.router.navigateByUrl(DASHBOARD_HUB_WORKFLOW_RESULT).catch(() => { + this.router.navigateByUrl(HUB_WORKFLOW_RESULT).catch(() => { this.notificationService.error("Go back failed. Please try again."); }); } @@ -215,7 +215,7 @@ export class HubWorkflowDetailComponent implements AfterViewInit, OnDestroy, OnI .cloneWorkflow(this.wid) .pipe(untilDestroyed(this)) .subscribe(newWid => { - this.router.navigate([`${DASHBOARD_USER_WORKSPACE}/${newWid}`]).then(() => { + this.router.navigate([`${USER_WORKSPACE}/${newWid}`]).then(() => { this.notificationService.success("Clone Successful"); }); }); diff --git a/frontend/src/app/workspace/component/menu/menu.component.html b/frontend/src/app/workspace/component/menu/menu.component.html index a21e4d56429..85a856eaba8 100644 --- a/frontend/src/app/workspace/component/menu/menu.component.html +++ b/frontend/src/app/workspace/component/menu/menu.component.html @@ -85,7 +85,7 @@ - +