This is an automated email from the ASF dual-hosted git repository.

github-merge-queue[bot] pushed a commit to branch 
gh-readonly-queue/main/pr-5151-eb287f3fc2cbba5416d851078affc10537123e68
in repository https://gitbox.apache.org/repos/asf/texera.git

commit d26bff669e0e253da451be3e64567a353036e00d
Author: Matthew B. <[email protected]>
AuthorDate: Mon Jun 1 16:31:36 2026 -0700

    refactor: Drop /dashboard prefix from user-facing URLs (#5151)
    
    ### What changes were proposed in this PR?
    - Shortens user-facing URLs by dropping the `/dashboard` segment:
    `/dashboard/user/workflow/2358` becomes `/user/workflow/2358`.
    - Mounts `DashboardComponent` at `path: ""` (with a child `redirectTo:
    "about"`) instead of `"dashboard"`, and renames every route constant in
    `app-routing.constant.ts` to drop the `DASHBOARD`/`DASHBOARD_*` prefix
    (e.g. `DASHBOARD_USER_WORKFLOW` → `USER_WORKFLOW`). The standalone
    `DASHBOARD` prefix constant is removed; each constant is now an absolute
    path that already excludes the segment.
    - Removes a dead top-level `WorkspaceComponent` root route whose guard
    always redirected to `/about`; `WorkspaceComponent` stays reachable via
    `/user/workflow/:id`.
    - Updates the hardcoded `/dashboard/...` references (admin execution
    link, share-access message, navbar-hiding regex) and the Scala
    email-link path. Backend API endpoints under `@Path("/dashboard")`
    (`DashboardResource.scala`) and their corresponding `dashboard/...`
    service URLs are intentionally left unchanged — they are REST endpoints,
    not user-facing routes.
    
    > [!NOTE]
    > This only changes front-end routes. Pre-existing `/dashboard/...` deep
    links (bookmarks, the example links in
    `docs/tutorials/migrate-jupyter-notebook.md`) no longer resolve and fall
    through the `**` wildcard to `/user/workflow`.
    
    ### Any related issues, documentation, or discussions?
      Closes: #4407
    ### How was this PR tested?
    - Ran `yarn format:fix` (clean) and `sbt scalafmtAll` (clean).
    - Ran `tsc --noEmit` against the branch merged with `main` (no type
    errors).
    - Added/updated unit tests covering the new paths:
    `app-routing.constant` values, the navbar-hiding regex, sidebar
    `routerLink`s (`dashboard.component`), admin execution link,
    `list-item`/`browse-section` entry routing, logout redirect
    (`user-icon`), new-workflow navigation (`user-workflow`), share-access
    message paths, owner-revoked-access redirect (`menu`), and the
    `location.go` URL update (`workspace`).
    - Ran the affected suites locally via `ng test` — all pass.
    
    #### Before
    <img width="2560" height="245" alt="Screenshot From 2026-05-23 04-11-22"
    
src="https://github.com/user-attachments/assets/937c92bf-45e8-43de-ba17-482f63ee7c9d";
    />
    
    #### After
    <img width="2560" height="245" alt="Screenshot From 2026-05-23 04-11-04"
    
src="https://github.com/user-attachments/assets/ca53ba20-e794-472e-a8cf-fd1e24ae544a";
    />
    
    ### Was this PR authored or co-authored using generative AI tooling?
    Co-authored with Claude Opus 4.7 and Claude Opus 4.8 in compliance with
    ASF
    
    ---------
    
    Signed-off-by: Matthew B. <[email protected]>
    Co-authored-by: Claude Opus 4.8 (1M context) <[email protected]>
---
 .../texera/web/service/WorkflowEmailNotifier.scala |  2 +-
 docs/tutorials/migrate-jupyter-notebook.md         |  2 +-
 frontend/src/app/app-routing.constant.ts           | 49 ++++++++-------
 frontend/src/app/app-routing.module.ts             | 35 ++++-------
 .../app/common/service/user/auth-guard.service.ts  |  4 +-
 .../admin/execution/admin-execution.component.html |  2 +-
 .../execution/admin-execution.component.spec.ts    | 21 +++++++
 .../dashboard/component/dashboard.component.html   | 22 +++----
 .../component/dashboard.component.spec.ts          | 63 ++++++++++++++++++-
 .../app/dashboard/component/dashboard.component.ts | 51 ++++++++--------
 .../user/list-item/list-item.component.spec.ts     | 70 ++++++++++++++++++++++
 .../user/list-item/list-item.component.ts          | 20 +++----
 .../user/search-bar/search-bar.component.spec.ts   |  6 +-
 .../user/search-bar/search-bar.component.ts        |  4 +-
 .../share-access/share-access.component.spec.ts    |  8 +--
 .../user/share-access/share-access.component.ts    | 12 ++--
 .../user-dataset-list-item.component.html          |  2 +-
 .../user-dataset-list-item.component.ts            |  4 +-
 .../user-dataset/user-dataset.component.spec.ts    |  4 +-
 .../user/user-dataset/user-dataset.component.ts    |  4 +-
 .../user/user-icon/user-icon.component.spec.ts     | 32 ++++++++++
 .../user/user-icon/user-icon.component.ts          |  4 +-
 .../user-project-list-item.component.ts            |  4 +-
 .../user-workflow-list-item.component.html         |  4 +-
 .../user-workflow-list-item.component.ts           |  6 +-
 .../user-workflow/user-workflow.component.spec.ts  | 19 ++++++
 .../user/user-workflow/user-workflow.component.ts  |  4 +-
 .../service/admin/guard/admin-guard.service.ts     |  4 +-
 .../about/local-login/local-login.component.ts     |  6 +-
 .../browse-section.component.spec.ts               | 37 ++++++++++++
 .../browse-section/browse-section.component.ts     | 24 ++++----
 frontend/src/app/hub/component/hub.component.html  |  6 +-
 frontend/src/app/hub/component/hub.component.ts    | 12 ++--
 .../landing-page/landing-page.component.spec.ts    | 12 ++--
 .../landing-page/landing-page.component.ts         | 12 ++--
 .../detail/hub-workflow-detail.component.ts        |  6 +-
 .../workspace/component/menu/menu.component.html   |  2 +-
 .../component/menu/menu.component.spec.ts          | 27 +++++++++
 .../app/workspace/component/menu/menu.component.ts |  6 +-
 .../component/workspace.component.spec.ts          | 46 ++++++++++++++
 .../app/workspace/component/workspace.component.ts |  4 +-
 41 files changed, 469 insertions(+), 193 deletions(-)

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 af9a26286d..c49f63e939 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/docs/tutorials/migrate-jupyter-notebook.md 
b/docs/tutorials/migrate-jupyter-notebook.md
index 5fc875d78b..eb1b3cea74 100644
--- a/docs/tutorials/migrate-jupyter-notebook.md
+++ b/docs/tutorials/migrate-jupyter-notebook.md
@@ -16,7 +16,7 @@ Migrating notebook code into Texera operators, then wiring 
those operators with
 
 ## 2. Example: convert a "tweet analysis" notebook into a workflow
 
-> The [notebook](https://hub.texera.io/dashboard/user/dataset/124), 
[dataset](https://hub.texera.io/dashboard/user/dataset/124) and 
[workflow](https://hub.texera.io/dashboard/user/workspace/1162) in this example 
are available on [TexeraHub](https://hub.texera.io/dashboard/about).
+> The [notebook](https://hub.texera.io/user/dataset/124), 
[dataset](https://hub.texera.io/user/dataset/124) and 
[workflow](https://hub.texera.io/user/workflow/1162) in this example are 
available on [TexeraHub](https://hub.texera.io/about).
 
 ### Notebook Overview
 We will use a Tweet-Analysis notebook to demonstrate the migration process. 
The notebook has three cells:
diff --git a/frontend/src/app/app-routing.constant.ts 
b/frontend/src/app/app-routing.constant.ts
index 4181df8a95..6e06f72520 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 179caf5c08..8e5a44903e 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,
@@ -174,18 +167,10 @@ routes.push({
   ],
 });
 
-// default route renders the workspace editor directly; if userSystem is 
enabled at runtime,
-// AppComponent will navigate to DASHBOARD_ABOUT instead.
-routes.push({
-  path: "",
-  component: WorkspaceComponent,
-  canActivate: [rootRedirectGuard],
-});
-
 // 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 3e6e315871..1a69ad0289 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 41a2ceb965..907ffa8d72 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 @@
     <tr *ngFor="let execution of basicTable.data">
       <td>
         <div *ngIf="execution.access; else normalWorkflowName">
-          <a href="/dashboard/user/workflow/{{execution.workflowId}}">
+          <a href="/user/workflow/{{execution.workflowId}}">
             {{ maxStringLength(execution.workflowName, 16) }} ({{ 
execution.workflowId }})
           </a>
         </div>
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 466d0a3db0..caaa71a220 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/<id> 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 9edea62915..ba3f74fa3a 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">
             <span
               nz-icon
               nzType="container"></span>
@@ -78,7 +78,7 @@
             nz-tooltip="Open the saved workflows"
             nzMatchRouter="true"
             nzTooltipPlacement="right"
-            [routerLink]="DASHBOARD_USER_WORKFLOW">
+            [routerLink]="USER_WORKFLOW">
             <span
               nz-icon
               nzType="project"></span>
@@ -91,7 +91,7 @@
             nz-tooltip="Look up for datasets"
             nzMatchRouter="true"
             nzTooltipPlacement="right"
-            [routerLink]="DASHBOARD_USER_DATASET">
+            [routerLink]="USER_DATASET">
             <span
               nz-icon
               nzType="database"></span>
@@ -103,7 +103,7 @@
             nz-tooltip="Manage computing units"
             nzMatchRouter="true"
             nzTooltipPlacement="right"
-            [routerLink]="DASHBOARD_USER_COMPUTING_UNIT">
+            [routerLink]="USER_COMPUTING_UNIT">
             <span
               nz-icon
               nzType="deployment-unit"></span>
@@ -115,7 +115,7 @@
             nz-tooltip="Quota information"
             nzMatchRouter="true"
             nzTooltipPlacement="right"
-            [routerLink]="DASHBOARD_USER_QUOTA">
+            [routerLink]="USER_QUOTA">
             <span
               nz-icon
               nzType="dashboard"></span>
@@ -127,7 +127,7 @@
             nz-tooltip="Open the discussion forum"
             nzMatchRouter="true"
             nzTooltipPlacement="right"
-            [routerLink]="DASHBOARD_USER_DISCUSSION">
+            [routerLink]="USER_DISCUSSION">
             <span
               nz-icon
               nzType="comment"></span>
@@ -147,7 +147,7 @@
             nz-tooltip="Look up the users"
             nzMatchRouter="true"
             nzTooltipPlacement="right"
-            [routerLink]="DASHBOARD_ADMIN_USER">
+            [routerLink]="ADMIN_USER">
             <span
               nz-icon
               nzType="user"></span>
@@ -158,7 +158,7 @@
             nz-tooltip="View statistics"
             nzMatchRouter="true"
             nzTooltipPlacement="right"
-            [routerLink]="DASHBOARD_ADMIN_EXECUTION">
+            [routerLink]="ADMIN_EXECUTION">
             <span
               nz-icon
               nzType="setting"></span>
@@ -169,7 +169,7 @@
             nz-tooltip="Setup gmail"
             nzMatchRouter="true"
             nzTooltipPlacement="right"
-            [routerLink]="DASHBOARD_ADMIN_GMAIL">
+            [routerLink]="ADMIN_GMAIL">
             <span
               nz-icon
               nzType="mail"></span>
@@ -180,7 +180,7 @@
             nz-tooltip="Settings"
             nzMatchRouter="true"
             nzTooltipPlacement="right"
-            [routerLink]="DASHBOARD_ADMIN_SETTINGS">
+            [routerLink]="ADMIN_SETTINGS">
             <span
               nz-icon
               nzType="edit"></span>
@@ -194,7 +194,7 @@
         nz-menu-item
         nz-tooltip
         nzTooltipPlacement="right"
-        [routerLink]="DASHBOARD_ABOUT">
+        [routerLink]="ABOUT">
         <span
           nz-icon
           nzType="info-circle"></span>
diff --git a/frontend/src/app/dashboard/component/dashboard.component.spec.ts 
b/frontend/src/app/dashboard/component/dashboard.component.spec.ts
index 6f508ca136..29ed8426fe 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 57e6e8e284..aadf07d54b 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;
@@ -248,7 +250,4 @@ export class DashboardComponent implements OnInit {
       }, 175);
     }
   }
-
-  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 c8840a135b..44bec7e07f 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
@@ -30,6 +30,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<ListItemComponent>;
@@ -119,4 +127,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 e57cca2013..5cca45ced4 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
@@ -46,11 +46,11 @@ import { formatCount, formatRelativeTime } from 
"src/app/common/util/format.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";
@@ -145,24 +145,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.spec.ts
 
b/frontend/src/app/dashboard/component/user/search-bar/search-bar.component.spec.ts
index 922a7c4969..2540e89d2b 100644
--- 
a/frontend/src/app/dashboard/component/user/search-bar/search-bar.component.spec.ts
+++ 
b/frontend/src/app/dashboard/component/user/search-bar/search-bar.component.spec.ts
@@ -28,7 +28,7 @@ import { SearchService } from 
"../../../service/user/search.service";
 import { UserService } from "../../../../common/service/user/user.service";
 import { SortMethod } from "../../../type/sort-method";
 import { SearchResult, SearchResultItem } from "../../../type/search-result";
-import { DASHBOARD_SEARCH } from "../../../../app-routing.constant";
+import { SEARCH } from "../../../../app-routing.constant";
 import { commonTestProviders } from "../../../../common/testing/test-utils";
 
 function makeWorkflowItem(name: string, wid: number = 1): SearchResultItem {
@@ -237,11 +237,11 @@ describe("SearchBarComponent", () => {
     });
   });
 
-  it("performSearch navigates to DASHBOARD_SEARCH with the keyword as the q 
query param", () => {
+  it("performSearch navigates to SEARCH with the keyword as the q query 
param", () => {
     const nav = vi.spyOn(router, "navigate").mockResolvedValue(true);
 
     component.performSearch("hello world");
 
-    expect(nav).toHaveBeenCalledWith([DASHBOARD_SEARCH], { queryParams: { q: 
"hello world" } });
+    expect(nav).toHaveBeenCalledWith([SEARCH], { queryParams: { q: "hello 
world" } });
   });
 });
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 ae6a6c1df5..638a2f7e7b 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.spec.ts
 
b/frontend/src/app/dashboard/component/user/share-access/share-access.component.spec.ts
index 2a40a06e20..d25ed19622 100644
--- 
a/frontend/src/app/dashboard/component/user/share-access/share-access.component.spec.ts
+++ 
b/frontend/src/app/dashboard/component/user/share-access/share-access.component.spec.ts
@@ -243,22 +243,22 @@ describe("ShareAccessComponent", () => {
 
     it("uses the workflow dashboard path when sharing a workflow", () => {
       const message = grantAndCaptureMessage(setupComponent({ type: 
"workflow", id: 11 }));
-      expect(message).toContain("/dashboard/user/workflow/11");
+      expect(message).toContain("/user/workflow/11");
     });
 
     it("uses the dataset dashboard path when sharing a dataset", () => {
       const message = grantAndCaptureMessage(setupComponent({ type: "dataset", 
id: 22 }));
-      expect(message).toContain("/dashboard/user/dataset/22");
+      expect(message).toContain("/user/dataset/22");
     });
 
     it("uses the project dashboard path when sharing a project", () => {
       const message = grantAndCaptureMessage(setupComponent({ type: "project", 
id: 33 }));
-      expect(message).toContain("/dashboard/user/project/33");
+      expect(message).toContain("/user/project/33");
     });
 
     it("omits the access URL when sharing a computing-unit", () => {
       const message = grantAndCaptureMessage(setupComponent({ type: 
"computing-unit", id: 44 }));
-      expect(message).not.toContain("/dashboard/user/");
+      expect(message).not.toContain("/user/");
     });
 
     it("calls ShareAccessService.grantAccess with the selected access level 
for each tag", () => {
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 b6b51b9709..81e3739d88 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
@@ -27,11 +27,7 @@ import { GmailService } from 
"../../../../common/service/gmail/gmail.service";
 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,
-} from "../../../../app-routing.constant";
+import { 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";
 import { WorkflowPersistService } from 
"src/app/common/service/workflow-persist/workflow-persist.service";
@@ -196,9 +192,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 6fa0d48a78..1383680f8f 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 @@
       <div class="dataset-item-meta-title">
         <a
           *ngIf="!editingName; else customDatasetTitle "
-          [routerLink]="DASHBOARD_USER_DATASET + '/' + dataset.did"
+          [routerLink]="USER_DATASET + '/' + dataset.did"
           class="dataset-name"
           >{{ dataset.name }}</a
         >
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 3e51374c9b..6846911823 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.spec.ts
 
b/frontend/src/app/dashboard/component/user/user-dataset/user-dataset.component.spec.ts
index 1556c2db63..f665b3ea81 100644
--- 
a/frontend/src/app/dashboard/component/user/user-dataset/user-dataset.component.spec.ts
+++ 
b/frontend/src/app/dashboard/component/user/user-dataset/user-dataset.component.spec.ts
@@ -19,7 +19,7 @@
 
 import { of, Subject } from "rxjs";
 import { UserDatasetComponent } from "./user-dataset.component";
-import { DASHBOARD_USER_DATASET } from "../../../../app-routing.constant";
+import { USER_DATASET } from "../../../../app-routing.constant";
 import { UserDatasetVersionCreatorComponent } from 
"./user-dataset-explorer/user-dataset-version-creator/user-dataset-version-creator.component";
 import { SortMethod } from "../../../type/sort-method";
 import { User } from "../../../../common/type/user";
@@ -278,7 +278,7 @@ describe("UserDatasetComponent", () => {
 
       component.onClickOpenDatasetAddComponent();
 
-      
expect(routerMock.navigate).toHaveBeenCalledWith([`${DASHBOARD_USER_DATASET}/123`]);
+      
expect(routerMock.navigate).toHaveBeenCalledWith([`${USER_DATASET}/123`]);
     });
 
     it("on close with null result: does not navigate", () => {
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 165270c053..2deedba104 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 1681e2d1c0..2addd12014 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
@@ -18,6 +18,7 @@
  */
 
 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";
@@ -26,6 +27,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;
@@ -52,4 +54,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 4b1fd6d4de..94bb7d9d4e 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 9bb6750630..562b465e8f 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 4affee3b44..6524c4dbc6 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 @@
       <div class="workflow-item-meta-title">
         <div
           *ngIf="!editingName; else customWorkflowTitle "
-          [routerLink]="DASHBOARD_USER_WORKSPACE + '/' + workflow.wid"
+          [routerLink]="USER_WORKSPACE + '/' + workflow.wid"
           [innerHTML]="workflow.name | highlightSearchTerms : keywords"
           class="workflow-name"></div>
         <ng-template #customWorkflowTitle>
@@ -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}}
       </a>
       <div
diff --git 
a/frontend/src/app/dashboard/component/user/user-workflow/user-workflow-list-item/user-workflow-list-item.component.ts
 
b/frontend/src/app/dashboard/component/user/user-workflow/user-workflow-list-item/user-workflow-list-item.component.ts
index 4e849e6448..e26a4a7e54 100644
--- 
a/frontend/src/app/dashboard/component/user/user-workflow/user-workflow-list-item/user-workflow-list-item.component.ts
+++ 
b/frontend/src/app/dashboard/component/user/user-workflow/user-workflow-list-item/user-workflow-list-item.component.ts
@@ -32,7 +32,7 @@ import { UserProjectService } from 
"../../../../service/user/project/user-projec
 import { DashboardEntry } from "../../../../type/dashboard-entry";
 import { firstValueFrom } from "rxjs";
 import { DownloadService } from 
"src/app/dashboard/service/user/download/download.service";
-import { DASHBOARD_USER_PROJECT, DASHBOARD_USER_WORKSPACE } from 
"../../../../../app-routing.constant";
+import { USER_PROJECT, USER_WORKSPACE } from 
"../../../../../app-routing.constant";
 import { GuiConfigService } from 
"../../../../../common/service/gui-config.service";
 import {
   NzListItemComponent,
@@ -88,8 +88,8 @@ import { HighlightSearchTermsPipe } from 
"./highlight-search-terms.pipe";
   ],
 })
 export class UserWorkflowListItemComponent {
-  protected readonly DASHBOARD_USER_WORKSPACE = DASHBOARD_USER_WORKSPACE;
-  protected readonly DASHBOARD_USER_PROJECT = DASHBOARD_USER_PROJECT;
+  protected readonly USER_WORKSPACE = USER_WORKSPACE;
+  protected readonly USER_PROJECT = USER_PROJECT;
   private _entry?: DashboardEntry;
   @Input() public keywords: string[] = [];
 
diff --git 
a/frontend/src/app/dashboard/component/user/user-workflow/user-workflow.component.spec.ts
 
b/frontend/src/app/dashboard/component/user/user-workflow/user-workflow.component.spec.ts
index 959977cd15..f082ed9dc2 100644
--- 
a/frontend/src/app/dashboard/component/user/user-workflow/user-workflow.component.spec.ts
+++ 
b/frontend/src/app/dashboard/component/user/user-workflow/user-workflow.component.spec.ts
@@ -60,6 +60,8 @@ import { NzModalService } from "ng-zorro-antd/modal";
 import { NzButtonModule } from "ng-zorro-antd/button";
 import { DownloadService } from 
"../../../service/user/download/download.service";
 import { commonTestProviders } from "../../../../common/testing/test-utils";
+import { Router } from "@angular/router";
+import { USER_WORKSPACE } from "../../../../app-routing.constant";
 import type { Mocked } from "vitest";
 describe("SavedWorkflowSectionComponent", () => {
   let component: UserWorkflowComponent;
@@ -310,6 +312,23 @@ describe("SavedWorkflowSectionComponent", () => {
     );
   });
 
+  describe("onClickCreateNewWorkflowFromDashboard", () => {
+    it("navigates to /user/workflow/<wid> (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 f3129f1a9c..584191f8bf 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 f95d701194..d1e1318cc5 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 215b17e342..0096807439 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 a4c91b4abe..504101e32e 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 7629a8906d..659e018e1e 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<number, string>();
@@ -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 5ba23062a3..7343cd7d71 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">
     <span
       nz-icon
       nzType="home"></span>
@@ -36,7 +36,7 @@
     nz-tooltip="Search public workflows"
     nzMatchRouter="true"
     nzTooltipPlacement="right"
-    [routerLink]="DASHBOARD_HUB_WORKFLOW_RESULT">
+    [routerLink]="HUB_WORKFLOW_RESULT">
     <span
       nz-icon
       nzType="project"></span>
@@ -48,7 +48,7 @@
     nz-tooltip="Search public dataset"
     nzMatchRouter="true"
     nzTooltipPlacement="right"
-    [routerLink]="DASHBOARD_HUB_DATASET_RESULT">
+    [routerLink]="HUB_DATASET_RESULT">
     <span
       nz-icon
       nzType="database"></span>
diff --git a/frontend/src/app/hub/component/hub.component.ts 
b/frontend/src/app/hub/component/hub.component.ts
index ad9bc079e0..ee006fa255 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.spec.ts 
b/frontend/src/app/hub/component/landing-page/landing-page.component.spec.ts
index 8585ee1c54..d81b085037 100644
--- a/frontend/src/app/hub/component/landing-page/landing-page.component.spec.ts
+++ b/frontend/src/app/hub/component/landing-page/landing-page.component.spec.ts
@@ -30,11 +30,7 @@ import { UserService } from 
"../../../common/service/user/user.service";
 import { StubUserService } from 
"../../../common/service/user/stub-user.service";
 import { WorkflowPersistService } from 
"../../../common/service/workflow-persist/workflow-persist.service";
 import { DatasetService } from 
"../../../dashboard/service/user/dataset/dataset.service";
-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 { commonTestProviders } from "../../../common/testing/test-utils";
 
 describe("LandingPageComponent", () => {
@@ -196,18 +192,18 @@ describe("LandingPageComponent", () => {
   it("navigateToSearch routes to the workflow hub result for 'workflow'", () 
=> {
     build();
     component.navigateToSearch("workflow");
-    
expect(routerNavigateSpy).toHaveBeenCalledWith([DASHBOARD_HUB_WORKFLOW_RESULT]);
+    expect(routerNavigateSpy).toHaveBeenCalledWith([HUB_WORKFLOW_RESULT]);
   });
 
   it("navigateToSearch routes to the dataset hub result for 'dataset'", () => {
     build();
     component.navigateToSearch("dataset");
-    
expect(routerNavigateSpy).toHaveBeenCalledWith([DASHBOARD_HUB_DATASET_RESULT]);
+    expect(routerNavigateSpy).toHaveBeenCalledWith([HUB_DATASET_RESULT]);
   });
 
   it("navigateToSearch routes to the dashboard home for an unknown type", () 
=> {
     build();
     component.navigateToSearch("something-else");
-    expect(routerNavigateSpy).toHaveBeenCalledWith([DASHBOARD_HOME]);
+    expect(routerNavigateSpy).toHaveBeenCalledWith([HOME]);
   });
 });
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 b8a7e02de3..2f8a2fff7d 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 5d76382f76..810672c230 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";
@@ -203,7 +203,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.");
     });
   }
@@ -216,7 +216,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 a21e4d5642..85a856eaba 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 @@
       <nz-space-compact
         id="user-buttons"
         *ngIf="!displayParticularWorkflowVersion">
-        <a [routerLink]="DASHBOARD_USER_WORKFLOW">
+        <a [routerLink]="USER_WORKFLOW">
           <button
             nz-button
             title="dashboard">
diff --git a/frontend/src/app/workspace/component/menu/menu.component.spec.ts 
b/frontend/src/app/workspace/component/menu/menu.component.spec.ts
index 1449174215..a1865a2628 100644
--- a/frontend/src/app/workspace/component/menu/menu.component.spec.ts
+++ b/frontend/src/app/workspace/component/menu/menu.component.spec.ts
@@ -45,6 +45,8 @@ import { saveAs } from "file-saver";
 import type { ModalOptions } from "ng-zorro-antd/modal";
 import type { ComputingUnitSelectionComponent } from 
"../power-button/computing-unit-selection.component";
 import { WorkflowContent } from "../../../common/type/workflow";
+import { Router } from "@angular/router";
+import { USER_WORKFLOW } from "../../../app-routing.constant";
 import type { Mocked } from "vitest";
 
 vi.mock("file-saver", () => ({ saveAs: vi.fn() }));
@@ -466,6 +468,31 @@ describe("MenuComponent", () => {
         })
       );
     });
+
+    it("navigates to /user/workflow (no /dashboard prefix) when the modal 
reports the owner revoked their own access", async () => {
+      vi.spyOn(workflowPersistService, 
"retrieveOwners").mockReturnValue(of([]));
+      const fakeModalRef = { afterClose: of({ userRevokedOwnAccess: true }) } 
as unknown as NzModalRef;
+      vi.spyOn(modalService, "create").mockReturnValue(fakeModalRef);
+      const router = TestBed.inject(Router);
+      const navigateSpy = vi.spyOn(router, "navigate").mockResolvedValue(true);
+
+      await component.onClickOpenShareAccess();
+
+      expect(navigateSpy).toHaveBeenCalledWith([USER_WORKFLOW]);
+      expect(USER_WORKFLOW).toBe("/user/workflow");
+    });
+
+    it("does not navigate when the share-access modal closes without revoking 
own access", async () => {
+      vi.spyOn(workflowPersistService, 
"retrieveOwners").mockReturnValue(of([]));
+      const fakeModalRef = { afterClose: of(undefined) } as unknown as 
NzModalRef;
+      vi.spyOn(modalService, "create").mockReturnValue(fakeModalRef);
+      const router = TestBed.inject(Router);
+      const navigateSpy = vi.spyOn(router, "navigate").mockResolvedValue(true);
+
+      await component.onClickOpenShareAccess();
+
+      expect(navigateSpy).not.toHaveBeenCalled();
+    });
   });
 
   it("onClickCreateNewWorkflow resets the graph and navigates back to root", 
() => {
diff --git a/frontend/src/app/workspace/component/menu/menu.component.ts 
b/frontend/src/app/workspace/component/menu/menu.component.ts
index b5621d06d3..69f08e4fef 100644
--- a/frontend/src/app/workspace/component/menu/menu.component.ts
+++ b/frontend/src/app/workspace/component/menu/menu.component.ts
@@ -50,7 +50,7 @@ import { ResultExportationComponent } from 
"../result-exportation/result-exporta
 import { ReportGenerationService } from 
"../../service/report-generation/report-generation.service";
 import { ShareAccessComponent } from 
"src/app/dashboard/component/user/share-access/share-access.component";
 import { PanelService } from "../../service/panel/panel.service";
-import { DASHBOARD_USER_WORKFLOW } from "../../../app-routing.constant";
+import { USER_WORKFLOW } from "../../../app-routing.constant";
 import { ComputingUnitStatusService } from 
"../../../common/service/computing-unit/computing-unit-status/computing-unit-status.service";
 import { ComputingUnitState } from 
"../../../common/type/computing-unit-connection.interface";
 import { ComputingUnitSelectionComponent } from 
"../power-button/computing-unit-selection.component";
@@ -138,7 +138,7 @@ export class MenuComponent implements OnInit, OnDestroy {
   public showGrid: boolean = false;
   public showNumWorkers: boolean = false;
   public showStatus: boolean = false;
-  protected readonly DASHBOARD_USER_WORKFLOW = DASHBOARD_USER_WORKFLOW;
+  protected readonly USER_WORKFLOW = USER_WORKFLOW;
 
   @Input() public writeAccess: boolean = false;
   @Input() public pid?: number = undefined;
@@ -344,7 +344,7 @@ export class MenuComponent implements OnInit, OnDestroy {
 
     modalRef.afterClose.pipe(untilDestroyed(this)).subscribe(result => {
       if (result?.userRevokedOwnAccess) {
-        this.router.navigate([DASHBOARD_USER_WORKFLOW]);
+        this.router.navigate([USER_WORKFLOW]);
       }
     });
   }
diff --git a/frontend/src/app/workspace/component/workspace.component.spec.ts 
b/frontend/src/app/workspace/component/workspace.component.spec.ts
index f151ec3e4f..7659f346f3 100644
--- a/frontend/src/app/workspace/component/workspace.component.spec.ts
+++ b/frontend/src/app/workspace/component/workspace.component.spec.ts
@@ -45,6 +45,7 @@ import { OperatorReuseCacheStatusService } from 
"../service/workflow-status/oper
 import { EntityType, HubService } from "../../hub/service/hub.service";
 import { commonTestProviders } from "../../common/testing/test-utils";
 import { WorkspaceComponent } from "./workspace.component";
+import { USER_WORKSPACE } from "../../app-routing.constant";
 
 describe("WorkspaceComponent", () => {
   let component: WorkspaceComponent;
@@ -305,6 +306,51 @@ describe("WorkspaceComponent", () => {
       component.registerAutoPersistWorkflow();
       expect(workflowActionService.workflowChanged).toHaveBeenCalledTimes(1);
     });
+
+    it("updates the URL via location.go to /user/workflow/<wid> (no /dashboard 
prefix) when the persisted wid differs", async () => {
+      vi.useFakeTimers();
+      try {
+        const workflowChanged$ = new Subject<void>();
+        await createFixture();
+        
workflowActionService.workflowChanged.mockReturnValue(workflowChanged$.asObservable());
+        // Persist returns a workflow with a different wid than what's 
currently
+        // on the metadata (wid: 42 in the stub). That mismatch is the trigger
+        // for the URL update.
+        const persistedWorkflow = { ...stubWorkflow, wid: 99 } as Workflow;
+        
workflowPersistService.persistWorkflow.mockReturnValue(of(persistedWorkflow));
+
+        component.registerAutoPersistWorkflow();
+        workflowChanged$.next();
+        // Flush the debounceTime(SAVE_DEBOUNCE_TIME_IN_MS).
+        vi.advanceTimersByTime(5000);
+
+        expect(locationMock.go).toHaveBeenCalledWith(`${USER_WORKSPACE}/99`);
+        expect(USER_WORKSPACE).toBe("/user/workflow");
+      } finally {
+        vi.useRealTimers();
+      }
+    });
+
+    it("skips the URL update when the persisted wid matches the current 
metadata", async () => {
+      vi.useFakeTimers();
+      try {
+        const workflowChanged$ = new Subject<void>();
+        await createFixture();
+        
workflowActionService.workflowChanged.mockReturnValue(workflowChanged$.asObservable());
+        // Metadata wid is 42, persisted wid is also 42 → no URL update.
+        
workflowPersistService.persistWorkflow.mockReturnValue(of(stubWorkflow));
+
+        component.registerAutoPersistWorkflow();
+        workflowChanged$.next();
+        vi.advanceTimersByTime(5000);
+
+        expect(locationMock.go).not.toHaveBeenCalled();
+        // Metadata is still synced even when the URL doesn't change.
+        
expect(workflowActionService.setWorkflowMetadata).toHaveBeenCalledWith(stubWorkflow);
+      } finally {
+        vi.useRealTimers();
+      }
+    });
   });
 
   describe("updateViewCount", () => {
diff --git a/frontend/src/app/workspace/component/workspace.component.ts 
b/frontend/src/app/workspace/component/workspace.component.ts
index 9968c26f64..da220feaab 100644
--- a/frontend/src/app/workspace/component/workspace.component.ts
+++ b/frontend/src/app/workspace/component/workspace.component.ts
@@ -49,7 +49,7 @@ import { WorkflowMetadata } from 
"src/app/dashboard/type/workflow-metadata.inter
 import { EntityType, HubService } from "../../hub/service/hub.service";
 import { THROTTLE_TIME_MS } from 
"../../hub/component/workflow/detail/hub-workflow-detail.component";
 import { WorkflowCompilingService } from 
"../service/compile-workflow/workflow-compiling.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 { checkIfWorkflowBroken } from "../../common/util/workflow-check";
 import { NzSpinComponent } from "ng-zorro-antd/spin";
@@ -204,7 +204,7 @@ export class WorkspaceComponent implements AfterViewInit, 
OnInit, OnDestroy {
             .pipe(untilDestroyed(this))
             .subscribe((updatedWorkflow: Workflow) => {
               if (this.workflowActionService.getWorkflowMetadata().wid !== 
updatedWorkflow.wid) {
-                
this.location.go(`${DASHBOARD_USER_WORKSPACE}/${updatedWorkflow.wid}`);
+                this.location.go(`${USER_WORKSPACE}/${updatedWorkflow.wid}`);
               }
               this.workflowActionService.setWorkflowMetadata(updatedWorkflow);
             });

Reply via email to