You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@texera.apache.org by gi...@apache.org on 2026/09/24 04:50:18 UTC

(texera) branch gh-readonly-queue/main/pr-8463-2e261e47dea226aaec304fb6edbd815d14cec93a created (now f24dab0030)

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

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


      at f24dab0030 chore(frontend): remove the unused UserWorkflowListItemComponent (#8463)

This branch includes the following new commits:

     new f24dab0030 chore(frontend): remove the unused UserWorkflowListItemComponent (#8463)

The 1 revisions listed above as "new" are entirely new to this
repository and will be described in separate emails.  The revisions
listed as "add" were already present in the repository and have only
been added to this reference.



(texera) 01/01: chore(frontend): remove the unused UserWorkflowListItemComponent (#8463)

Posted by gi...@apache.org.
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-8463-2e261e47dea226aaec304fb6edbd815d14cec93a
in repository https://gitbox.apache.org/repos/asf/texera.git

commit f24dab0030bf4ee0620562ff0d186b3b04de7d51
Author: Xinyuan Lin <xi...@uci.edu>
AuthorDate: Thu Sep 24 04:00:11 2026 +0000

    chore(frontend): remove the unused UserWorkflowListItemComponent (#8463)
    
    ### What changes were proposed in this PR?
    
    Deletes `UserWorkflowListItemComponent`, which is declared in
    `AppModule` but whose selector `texera-user-workflow-list-item` appears
    in no template — so it is never rendered. `HighlightSearchTermsPipe`
    goes with it, because the component's own template is the pipe's last
    remaining consumer. So does the CSS that only that template ever
    matched. Pure deletion, no behaviour change: **−1,201 lines**.
    
    ### History
    
    | | |
    | --- | --- |
    | **Introduced by** | #1937 (2023-05-30) — "Refactored each workflow
    list item into its own component" |
    | **Usage removed by** | #2793 (2024-08-20) — "Generalize List Item"
    replaced it with the generic `ListItemComponent`, deleting the last two
    `texera-user-workflow-list-item` uses from
    `search-results.component.html` |
    
    Dead for about two years.
    
    > Reviewer note — why the pipe is in the same PR.
    `HighlightSearchTermsPipe` arrived in #2386 and kept a second consumer
    until #7463 (2026-08-29, "remove the deprecated project feature")
    dropped `entry.name | highlightSearchTerms` from that template. Since
    then its only use is this dead component's template (L43/L106). Removing
    the component alone would leave the pipe dead in the same commit, so
    they ship together; splitting them would just create a second PR for
    code this one already orphans.
    
    > Reviewer note — the CSS. The component's stylesheet `@use`d
    `section-style.scss`, and its template was the only one producing
    `.workflow-list-item` (plus every class nested under it) and
    `.metadata-container`. Those rules go, along with a second
    `.metadata-container` copy in `search-results.component.scss` that no
    template ever matched. The pipe was the only producer of the global
    `.highlight-search-terms` rule in `styles.scss`, so that goes too. The
    `itemSize` comment in `search-results.component.html` cited
    `.workflow-list-item`, and now cites `.list-item-card`, the rule that
    sizes each row today.
    
    > Nothing else needs cleaning: `WorkflowExecutionHistoryComponent`,
    `ShareAccessComponent`, `DownloadService`, `GuiConfigService` and
    `WorkflowPersistService` all keep other consumers.
    
    ### Any related issues, documentation, discussions?
    
    Closes #8460
    
    ### How was this PR tested?
    
    Existing tests only — this PR removes a component, a pipe, their two
    specs, and CSS that no remaining template matches.
    
    From `frontend/`:
    
    - `npx ng build` — **success**. This is the gate that matters here:
    removing an `AppModule` declaration can only be proven safe by an AOT
    build, since template diagnostics are AOT-only. A leftover use of either
    selector, or an `@extend` of a removed CSS rule, would fail it.
    - `npx ng test --watch=false
    --include='**/search-results.component.spec.ts'
    --include='**/user-workflow.component.spec.ts'` — 107 tests pass (2
    files).
    - `yarn --cwd frontend format:ci` — clean.
    
    Verification, re-runnable by a reviewer (all three return nothing):
    
    ```
    git grep -n "texera-user-workflow-list-item\|UserWorkflowListItemComponent"
    git grep -n "highlightSearchTerms\|HighlightSearchTermsPipe"
    git grep -n "workflow-list-item\|metadata-container\|highlight-search-terms"
    ```
    
    ### Was this PR authored or co-authored using generative AI tooling?
    
    Generated-by: Claude Code (Claude Opus 5)
---
 frontend/src/app/app.module.ts                     |   4 -
 .../src/app/dashboard/component/section-style.scss |  84 ----
 .../search-results/search-results.component.html   |   4 +-
 .../search-results/search-results.component.scss   |   6 -
 .../highlight-search-terms.pipe.spec.ts            |  67 ---
 .../highlight-search-terms.pipe.ts                 |  40 --
 .../user-workflow-list-item.component.html         | 210 --------
 .../user-workflow-list-item.component.scss         |  21 -
 .../user-workflow-list-item.component.spec.ts      | 559 ---------------------
 .../user-workflow-list-item.component.ts           | 201 --------
 .../user-workflow/user-workflow.component.spec.ts  |   2 -
 frontend/src/styles.scss                           |   5 -
 12 files changed, 2 insertions(+), 1201 deletions(-)

diff --git a/frontend/src/app/app.module.ts b/frontend/src/app/app.module.ts
index 6c96ab6ade..ca45b08289 100644
--- a/frontend/src/app/app.module.ts
+++ b/frontend/src/app/app.module.ts
@@ -120,7 +120,6 @@ import { MarkdownModule } from "ngx-markdown";
 import { FileSaverService } from "./dashboard/service/user/file/file-saver.service";
 import { DragDropModule } from "@angular/cdk/drag-drop";
 import { ScrollingModule } from "@angular/cdk/scrolling";
-import { UserWorkflowListItemComponent } from "./dashboard/component/user/user-workflow/user-workflow-list-item/user-workflow-list-item.component";
 import { SortButtonComponent } from "./dashboard/component/user/sort-button/sort-button.component";
 import { FiltersComponent } from "./dashboard/component/user/filters/filters.component";
 import { FiltersInstructionsComponent } from "./dashboard/component/user/filters-instructions/filters-instructions.component";
@@ -139,7 +138,6 @@ import { TimeTravelComponent } from "./workspace/component/left-panel/time-trave
 import { NzModalModule } from "ng-zorro-antd/modal";
 import { NzDescriptionsModule } from "ng-zorro-antd/descriptions";
 import { OverlayModule } from "@angular/cdk/overlay";
-import { HighlightSearchTermsPipe } from "./dashboard/component/user/user-workflow/user-workflow-list-item/highlight-search-terms.pipe";
 import { en_US, provideNzI18n } from "ng-zorro-antd/i18n";
 import { FilesUploaderComponent } from "./dashboard/component/user/files-uploader/files-uploader.component";
 import { ConflictingFileModalContentComponent } from "./dashboard/component/user/files-uploader/conflicting-file-modal-content/conflicting-file-modal-content.component";
@@ -331,7 +329,6 @@ registerLocaleData(en);
     CollabWrapperComponent,
     AboutComponent,
     TexeraLoginComponent,
-    UserWorkflowListItemComponent,
     SortButtonComponent,
     FiltersComponent,
     FiltersInstructionsComponent,
@@ -339,7 +336,6 @@ registerLocaleData(en);
     PortPropertyEditFrameComponent,
     WorkflowRuntimeStatisticsComponent,
     FlarumComponent,
-    HighlightSearchTermsPipe,
     SearchBarComponent,
     ListItemComponent,
     CardItemComponent,
diff --git a/frontend/src/app/dashboard/component/section-style.scss b/frontend/src/app/dashboard/component/section-style.scss
index 784815fb4a..446187f502 100644
--- a/frontend/src/app/dashboard/component/section-style.scss
+++ b/frontend/src/app/dashboard/component/section-style.scss
@@ -94,7 +94,6 @@ $dashboard-navigation-height: 76px;
 
 /**
   * the third grid of the workflow/files list.
-  * including css shared by workflow/file item
   **/
 .section-list-container {
   grid-row-start: 3;
@@ -119,83 +118,6 @@ $dashboard-navigation-height: 76px;
     border: 1px solid #e8e8e8;
     border-radius: 4px;
   }
-
-  .metadata-container {
-    span {
-      margin: 0 1rem 0 0;
-    }
-  }
-}
-
-/**
-  * css specifically for each workflow item in the list (not shared by file items)
-  **/
-.workflow-list-item {
-  margin-bottom: 10px;
-  min-height: 70px;
-  padding: 5px 0 5px 0;
-
-  .workflow-item-checkbox {
-    margin: 8px;
-  }
-
-  .workflow-item-meta-title {
-    display: flex;
-    align-items: center;
-
-    .workflow-name {
-      font-size: 20px;
-      font-family: -apple-system, BlinkMacSystemFont, sans-serif;
-      text-align: center;
-      margin-bottom: 0;
-      color: inherit;
-      text-decoration: none;
-    }
-
-    .workflow-name:hover {
-      cursor: pointer;
-    }
-
-    i {
-      position: relative;
-      font-size: 17px;
-    }
-
-    i.workflow-is-owner-icon {
-      margin-left: 7px;
-    }
-  }
-
-  .workflow-item-meta-description {
-    display: flex;
-    align-items: center;
-    padding: 2px 8px 2px 10px;
-    margin-bottom: 5px;
-
-    .workflow-description {
-      font-size: 13px;
-      font-family: -apple-system, BlinkMacSystemFont, sans-serif;
-
-      display: inline-block;
-      min-width: 17ch;
-      border: 0 none;
-      outline: none;
-
-      &:hover {
-        cursor: pointer;
-        box-shadow: 0 0 0 1px rgb(202, 202, 202);
-      }
-    }
-
-    .workflow-editable-description {
-      margin-bottom: 5px;
-      display: inline-block;
-      min-width: 17ch;
-      border: 0 none;
-      outline: none;
-      box-shadow: 0 0 0 2px #007bff;
-    }
-  }
 }
 
 // Shared look of one resource row (the computing-unit and warehouse list
@@ -241,9 +163,3 @@ $dashboard-navigation-height: 76px;
   margin-left: 5px;
   margin-right: 5px;
 }
-
-.metadata-container {
-  span {
-    margin: 0 1rem 0 0; // add space to the right
-  }
-}
diff --git a/frontend/src/app/dashboard/component/user/search-results/search-results.component.html b/frontend/src/app/dashboard/component/user/search-results/search-results.component.html
index 084ccb0d9f..13ff93847e 100644
--- a/frontend/src/app/dashboard/component/user/search-results/search-results.component.html
+++ b/frontend/src/app/dashboard/component/user/search-results/search-results.component.html
@@ -23,8 +23,8 @@
   <!-- LIST VIEW -->
   <ng-container *ngIf="viewMode === 'list'">
     <!-- itemSize: the height (px) of each list item,
-        this MUST be approximately the same as list item size set in CSS,
-        .workflow-list-item sets the item size to be 70px, with additional paddings/margins it's approximately 80px
+        this MUST be approximately the same as the list item height set in CSS
+        (.list-item-card in list-item.component.scss)
       -->
     <cdk-virtual-scroll-viewport
       itemSize="70"
diff --git a/frontend/src/app/dashboard/component/user/search-results/search-results.component.scss b/frontend/src/app/dashboard/component/user/search-results/search-results.component.scss
index 32f4de9513..a2c66114a9 100644
--- a/frontend/src/app/dashboard/component/user/search-results/search-results.component.scss
+++ b/frontend/src/app/dashboard/component/user/search-results/search-results.component.scss
@@ -125,12 +125,6 @@ nz-content {
   margin-right: 5px;
 }
 
-.metadata-container {
-  span {
-    margin: 0 1rem 0 0; // add space to the right
-  }
-}
-
 .card-scroll-container {
   height: 100%;
   overflow-y: auto;
diff --git a/frontend/src/app/dashboard/component/user/user-workflow/user-workflow-list-item/highlight-search-terms.pipe.spec.ts b/frontend/src/app/dashboard/component/user/user-workflow/user-workflow-list-item/highlight-search-terms.pipe.spec.ts
deleted file mode 100644
index 653cab0b54..0000000000
--- a/frontend/src/app/dashboard/component/user/user-workflow/user-workflow-list-item/highlight-search-terms.pipe.spec.ts
+++ /dev/null
@@ -1,67 +0,0 @@
-/**
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements.  See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership.  The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License.  You may obtain a copy of the License at
- *
- *   http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied.  See the License for the
- * specific language governing permissions and limitations
- * under the License.
- */
-
-import { TestBed } from "@angular/core/testing";
-import { SafeHtml } from "@angular/platform-browser";
-
-import { HighlightSearchTermsPipe } from "./highlight-search-terms.pipe";
-
-describe("HighlightSearchTermsPipe", () => {
-  let pipe: HighlightSearchTermsPipe;
-
-  // Recover the raw HTML string that was wrapped by bypassSecurityTrustHtml.
-  const raw = (safe: SafeHtml): string => (safe as any).changingThisBreaksApplicationSecurity;
-
-  beforeEach(() => {
-    TestBed.configureTestingModule({ providers: [HighlightSearchTermsPipe] });
-    pipe = TestBed.inject(HighlightSearchTermsPipe);
-  });
-
-  it("returns an empty string when the value is undefined", () => {
-    expect(raw(pipe.transform(undefined, ["x"]))).toBe("");
-  });
-
-  it("returns the value unchanged when there are no terms to highlight", () => {
-    expect(raw(pipe.transform("hello", []))).toBe("hello");
-  });
-
-  it("wraps a single matching token in a highlight span", () => {
-    expect(raw(pipe.transform("hello world", ["world"]))).toBe(
-      'hello <span class="highlight-search-terms">world</span>'
-    );
-  });
-
-  it("matches case-insensitively and preserves the original casing inside the span", () => {
-    expect(raw(pipe.transform("Hello", ["hello"]))).toBe('<span class="highlight-search-terms">Hello</span>');
-  });
-
-  it("wraps every occurrence of a token (global flag)", () => {
-    expect(raw(pipe.transform("a a a", ["a"]))).toBe(
-      '<span class="highlight-search-terms">a</span> ' +
-        '<span class="highlight-search-terms">a</span> ' +
-        '<span class="highlight-search-terms">a</span>'
-    );
-  });
-
-  it("wraps each token independently when multiple terms are given", () => {
-    expect(raw(pipe.transform("hello world", ["hello", "world"]))).toBe(
-      '<span class="highlight-search-terms">hello</span> ' + '<span class="highlight-search-terms">world</span>'
-    );
-  });
-});
diff --git a/frontend/src/app/dashboard/component/user/user-workflow/user-workflow-list-item/highlight-search-terms.pipe.ts b/frontend/src/app/dashboard/component/user/user-workflow/user-workflow-list-item/highlight-search-terms.pipe.ts
deleted file mode 100644
index 44953b1f8a..0000000000
--- a/frontend/src/app/dashboard/component/user/user-workflow/user-workflow-list-item/highlight-search-terms.pipe.ts
+++ /dev/null
@@ -1,40 +0,0 @@
-/**
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements.  See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership.  The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License.  You may obtain a copy of the License at
- *
- *   http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied.  See the License for the
- * specific language governing permissions and limitations
- * under the License.
- */
-
-import { Pipe, PipeTransform } from "@angular/core";
-import { DomSanitizer, SafeHtml } from "@angular/platform-browser";
-
-@Pipe({ name: "highlightSearchTerms" })
-export class HighlightSearchTermsPipe implements PipeTransform {
-  constructor(private sanitizer: DomSanitizer) {}
-
-  transform(value: string | undefined, terms: string[]): SafeHtml {
-    if (!terms || !terms.length || !value) {
-      // Return the original value if there's nothing to highlight or if the value is undefined
-      return this.sanitizer.bypassSecurityTrustHtml(value || "");
-    }
-
-    // Escape the terms to be used in a RegExp
-    const regex = new RegExp(`(${terms.join("|")})`, "gi");
-
-    const highlightedString = value.replace(regex, '<span class="highlight-search-terms">$1</span>');
-    // Use the sanitizer to avoid security risks
-    return this.sanitizer.bypassSecurityTrustHtml(highlightedString);
-  }
-}
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
deleted file mode 100644
index 273cb55d3a..0000000000
--- a/frontend/src/app/dashboard/component/user/user-workflow/user-workflow-list-item/user-workflow-list-item.component.html
+++ /dev/null
@@ -1,210 +0,0 @@
-<!--
- Licensed to the Apache Software Foundation (ASF) under one
- or more contributor license agreements.  See the NOTICE file
- distributed with this work for additional information
- regarding copyright ownership.  The ASF licenses this file
- to you under the Apache License, Version 2.0 (the
- "License"); you may not use this file except in compliance
- with the License.  You may obtain a copy of the License at
-
-   http://www.apache.org/licenses/LICENSE-2.0
-
- Unless required by applicable law or agreed to in writing,
- software distributed under the License is distributed on an
- "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- KIND, either express or implied.  See the License for the
- specific language governing permissions and limitations
- under the License.
--->
-
-<nz-list-item class="workflow-list-item">
-  <nz-list-item-meta>
-    <!-- Margin need only when the checkbox is not visible -->
-    <nz-list-item-meta-avatar [ngStyle]="{ 'margin-left': editable ? 'none' : '16px' }">
-      <label
-        *ngIf="editable"
-        class="workflow-item-checkbox"
-        id="{{workflow.wid}}"
-        ngDefaultControl
-        [(ngModel)]="entry.checked"></label>
-      <nz-avatar
-        [ngStyle]="{ 'background-color': 'grey', 'vertical-align': 'middle' }"
-        [nzGap]="4"
-        [nzText]="'' + workflow.wid"
-        nzSize="default"></nz-avatar>
-    </nz-list-item-meta-avatar>
-
-    <!-- editable name of saved workflow -->
-    <nz-list-item-meta-title class="meta-title-container">
-      <div class="workflow-item-meta-title">
-        <div
-          *ngIf="!editingName; else customWorkflowTitle "
-          [routerLink]="USER_WORKSPACE + '/' + workflow.wid"
-          [innerHTML]="workflow.name | highlightSearchTerms : keywords"
-          class="workflow-name"></div>
-        <ng-template #customWorkflowTitle>
-          <input
-            #customName
-            (focusout)="confirmUpdateWorkflowCustomName(customName.value)"
-            (keyup.enter)="confirmUpdateWorkflowCustomName(customName.value)"
-            placeholder="{{ workflow.name }}"
-            value="{{ workflow.name }}" />
-        </ng-template>
-        <button
-          *ngIf="editable"
-          (click)="editingName = true"
-          nz-button
-          nz-tooltip="Customize Workflow Name"
-          nzSize="small"
-          nzTooltipPlacement="bottom"
-          nzType="text">
-          <i
-            nz-icon
-            nzTheme="outline"
-            nzType="edit"></i>
-        </button>
-        <button
-          *ngIf="editable"
-          (click)="editingDescription = true"
-          nz-button
-          nz-tooltip="Add Description"
-          nzSize="small"
-          nzTooltipPlacement="bottom"
-          nzType="text"
-          class="add-description-btn">
-          <i
-            nz-icon
-            nzTheme="outline"
-            nzType="plus-square"></i>
-        </button>
-        <i
-          class="workflow-is-owner-icon"
-          *ngIf="entry.workflow.isOwner"
-          nz-tooltip="You are the owner"
-          nzTooltipPlacement="bottom"
-          nz-icon
-          nzTheme="outline"
-          nzType="user"></i>
-        <i
-          *ngIf="!entry.workflow.isOwner"
-          nz-tooltip="{{
-                          entry.workflow.accessLevel
-                      }} access shared by {{ entry.workflow.ownerName }}"
-          nzTooltipPlacement="bottom"
-          nz-icon
-          nzTheme="outline"
-          nzType="team"></i>
-      </div>
-    </nz-list-item-meta-title>
-
-    <!-- editable description of saved workflow -->
-    <nz-list-item-meta-description>
-      <div class="workflow-item-meta-description">
-        <label
-          *ngIf="!editingDescription; else customWorkflowDescription "
-          (click)="editingDescription = editable"
-          [innerHTML]="workflow.description | highlightSearchTerms : keywords"
-          class="workflow-description">
-        </label>
-        <ng-template #customWorkflowDescription>
-          <input
-            title="Description"
-            #customDescription
-            (focusout)="confirmUpdateWorkflowCustomDescription(customDescription.value)"
-            (keyup.enter)="confirmUpdateWorkflowCustomDescription(customDescription.value)"
-            class="workflow-editable-description"
-            value="{{ workflow.description }}"
-            maxlength="500" />
-        </ng-template>
-      </div>
-    </nz-list-item-meta-description>
-
-    <!-- last access and created date of saved workflow -->
-    <nz-list-item-meta-description class="metadata-container">
-      <span>Last Modified: {{ workflow.lastModifiedTime | date: "yyyy-MM-dd HH:mm" }}</span>
-      <span>Created: {{ workflow.creationTime | date: "yyyy-MM-dd HH:mm" }}</span>
-    </nz-list-item-meta-description>
-  </nz-list-item-meta>
-
-  <ul nz-list-item-actions>
-    <nz-list-item-action>
-      <button
-        (click)="onClickOpenShareAccess()"
-        nz-button
-        nz-tooltip="Share the workflow {{
-                      workflow.name
-                  }} to others"
-        nzTooltipPlacement="bottom"
-        type="button">
-        <i
-          nz-icon
-          nzTheme="outline"
-          nzType="share-alt"></i>
-      </button>
-    </nz-list-item-action>
-    <nz-list-item-action *ngIf="editable">
-      <button
-        (click)="duplicated.emit()"
-        class="duplicate-workflow-btn"
-        nz-button
-        nz-tooltip="Duplicate the workflow {{
-                      workflow.name
-                  }}"
-        nzTooltipPlacement="bottom"
-        type="button">
-        <i
-          nz-icon
-          nzTheme="outline"
-          nzType="copy"></i>
-      </button>
-    </nz-list-item-action>
-    <nz-list-item-action>
-      <button
-        (click)="onClickDownloadWorkfllow()"
-        nz-button
-        nz-tooltip="Download the workflow {{
-                      workflow.name
-                  }}"
-        nzTooltipPlacement="bottom"
-        type="button">
-        <i
-          nz-icon
-          nzType="cloud-download"
-          nzTheme="outline"></i>
-      </button>
-    </nz-list-item-action>
-    <nz-list-item-action *ngIf="editable">
-      <button
-        (nzOnConfirm)="deleted.emit()"
-        nz-popconfirm
-        nzPopconfirmTitle="Confirm to delete this workflow."
-        [disabled]="!entry.workflow.isOwner"
-        class="delete-workflow-btn"
-        nz-button
-        nz-tooltip="Delete the workflow {{
-                      workflow.name
-                  }}"
-        nzTooltipPlacement="bottom">
-        <i
-          nz-icon
-          nzTheme="outline"
-          nzType="delete"></i>
-      </button>
-    </nz-list-item-action>
-    <nz-list-item-action *ngIf="this.config.env.workflowExecutionsTrackingEnabled">
-      <button
-        (click)="onClickGetWorkflowExecutions()"
-        nz-button
-        nz-tooltip="Executions of the workflow {{
-                      workflow.name
-                  }}"
-        nzTooltipPlacement="bottom"
-        type="button">
-        <i
-          nz-icon
-          nzTheme="outline"
-          nzType="history"></i>
-      </button>
-    </nz-list-item-action>
-  </ul>
-</nz-list-item>
diff --git a/frontend/src/app/dashboard/component/user/user-workflow/user-workflow-list-item/user-workflow-list-item.component.scss b/frontend/src/app/dashboard/component/user/user-workflow/user-workflow-list-item/user-workflow-list-item.component.scss
deleted file mode 100644
index 630cd34d91..0000000000
--- a/frontend/src/app/dashboard/component/user/user-workflow/user-workflow-list-item/user-workflow-list-item.component.scss
+++ /dev/null
@@ -1,21 +0,0 @@
-/**
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements.  See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership.  The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License.  You may obtain a copy of the License at
- *
- *   http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied.  See the License for the
- * specific language governing permissions and limitations
- * under the License.
- */
-
-@use "../../../section-style" as *;
-@use "../../../dashboard.component.scss" as *;
diff --git a/frontend/src/app/dashboard/component/user/user-workflow/user-workflow-list-item/user-workflow-list-item.component.spec.ts b/frontend/src/app/dashboard/component/user/user-workflow/user-workflow-list-item/user-workflow-list-item.component.spec.ts
deleted file mode 100644
index ad3d296445..0000000000
--- a/frontend/src/app/dashboard/component/user/user-workflow/user-workflow-list-item/user-workflow-list-item.component.spec.ts
+++ /dev/null
@@ -1,559 +0,0 @@
-/**
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements.  See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership.  The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License.  You may obtain a copy of the License at
- *
- *   http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied.  See the License for the
- * specific language governing permissions and limitations
- * under the License.
- */
-
-import { Component, ViewChild } from "@angular/core";
-import { ComponentFixture, TestBed, waitForAsync } from "@angular/core/testing";
-import { UserWorkflowListItemComponent } from "./user-workflow-list-item.component";
-import { FileSaverService } from "../../../../service/user/file/file-saver.service";
-import { testWorkflow1, testWorkflowEntries } from "../../../user-dashboard-test-fixtures";
-import { By } from "@angular/platform-browser";
-import { StubWorkflowPersistService } from "../../../../../common/service/workflow-persist/stub-workflow-persist.service";
-import {
-  DEFAULT_WORKFLOW_NAME,
-  WorkflowPersistService,
-} from "../../../../../common/service/workflow-persist/workflow-persist.service";
-import { DownloadService } from "../../../../service/user/download/download.service";
-import { WorkflowExecutionHistoryComponent } from "../ngbd-modal-workflow-executions/workflow-execution-history.component";
-import { ShareAccessComponent } from "../../share-access/share-access.component";
-import { Workflow } from "../../../../../common/type/workflow";
-import { of } from "rxjs";
-import { NzListComponent } from "ng-zorro-antd/list";
-import { NzModalModule, NzModalService } from "ng-zorro-antd/modal";
-import { HttpClientTestingModule } from "@angular/common/http/testing";
-import { provideRouter } from "@angular/router";
-import { DashboardEntry } from "../../../../type/dashboard-entry";
-import { NzTooltipDirective, NzTooltipModule } from "ng-zorro-antd/tooltip";
-import { commonTestProviders } from "../../../../../common/testing/test-utils";
-import type { Mocked } from "vitest";
-import { GuiConfigService } from "../../../../../common/service/gui-config.service";
-import { MockGuiConfigService } from "../../../../../common/service/gui-config.service.mock";
-
-// UserWorkflowListItemComponent is rooted at <nz-list-item>; instantiating it
-// outside an <nz-list> host throws "No provider found for NzListComponent".
-@Component({
-  standalone: true,
-  imports: [NzListComponent, UserWorkflowListItemComponent],
-  template: `
-    <nz-list>
-      <texera-user-workflow-list-item
-        [entry]="entry"
-        [editable]="editable"></texera-user-workflow-list-item>
-    </nz-list>
-  `,
-})
-class TestHostComponent {
-  entry!: DashboardEntry;
-  editable = true;
-  @ViewChild(UserWorkflowListItemComponent, { static: true }) inner!: UserWorkflowListItemComponent;
-}
-
-// A fresh DashboardEntry per call so methods that mutate the workflow (rename)
-// cannot leak into the shared testWorkflowEntries fixture.
-function makeWorkflowEntry(workflowOverrides: Partial<Workflow> = {}): DashboardEntry {
-  return new DashboardEntry({
-    workflow: { ...testWorkflow1, ...workflowOverrides },
-    isOwner: true,
-    ownerName: "Texera",
-    accessLevel: "Write",
-    ownerId: 1,
-    coverImage: null,
-  });
-}
-
-describe("UserWorkflowListItemComponent", () => {
-  let component: UserWorkflowListItemComponent;
-  let fixture: ComponentFixture<TestHostComponent>;
-  const fileSaverServiceSpy = { saveAs: vi.fn() } as unknown as Mocked<FileSaverService>;
-  beforeEach(async () => {
-    await TestBed.configureTestingModule({
-      imports: [TestHostComponent, NzModalModule, HttpClientTestingModule, NzTooltipModule],
-      providers: [
-        { provide: WorkflowPersistService, useValue: new StubWorkflowPersistService(testWorkflowEntries) },
-        { provide: FileSaverService, useValue: fileSaverServiceSpy },
-        provideRouter([]),
-        ...commonTestProviders,
-      ],
-    }).compileComponents();
-  });
-
-  beforeEach(() => {
-    fixture = TestBed.createComponent(TestHostComponent);
-    fixture.componentInstance.entry = testWorkflowEntries[0];
-    fixture.componentInstance.editable = true;
-    fixture.detectChanges();
-    component = fixture.componentInstance.inner;
-  });
-
-  it("should create", () => {
-    expect(component).toBeTruthy();
-  });
-
-  it("sends http request to backend to retrieve export json", () => {
-    // Test the workflow download button.
-    component.onClickDownloadWorkfllow();
-    expect(fileSaverServiceSpy.saveAs).toHaveBeenCalledExactlyOnceWith(
-      new Blob([JSON.stringify(testWorkflowEntries[0].workflow.workflow.content)], {
-        type: "text/plain;charset=utf-8",
-      }),
-      "workflow 1.json"
-    );
-  });
-
-  it("adding a workflow description adds a description to the workflow", waitForAsync(() => {
-    fixture.whenStable().then(() => {
-      let addWorkflowDescriptionBtn = fixture.debugElement.query(By.css(".add-description-btn"));
-      expect(addWorkflowDescriptionBtn).toBeTruthy();
-      addWorkflowDescriptionBtn.triggerEventHandler("click", null);
-      fixture.detectChanges();
-      let editableDescriptionInput = fixture.debugElement.nativeElement.querySelector(".workflow-editable-description");
-      expect(editableDescriptionInput).toBeTruthy();
-      vi.spyOn(component, "confirmUpdateWorkflowCustomDescription");
-      sendInput(editableDescriptionInput, "dummy description added by focusing out the input element.").then(() => {
-        fixture.detectChanges();
-        editableDescriptionInput.dispatchEvent(new Event("focusout"));
-        fixture.detectChanges();
-        expect(component.confirmUpdateWorkflowCustomDescription).toHaveBeenCalledTimes(1);
-      });
-    });
-  }));
-
-  it("Editing a workflow description edits a description to the workflow", waitForAsync(() => {
-    fixture.whenStable().then(() => {
-      const workflowDescriptionLabel = fixture.debugElement.query(By.css(".workflow-description"));
-      expect(workflowDescriptionLabel).toBeTruthy();
-      workflowDescriptionLabel.triggerEventHandler("click", null);
-      fixture.detectChanges();
-      let editableDescriptionInput1 = fixture.debugElement.nativeElement.querySelector(
-        ".workflow-editable-description"
-      );
-      expect(editableDescriptionInput1).toBeTruthy();
-      vi.spyOn(component, "confirmUpdateWorkflowCustomDescription");
-      sendInput(editableDescriptionInput1, "dummy description added by focusing out the input element.").then(() => {
-        fixture.detectChanges();
-        editableDescriptionInput1.dispatchEvent(new Event("focusout"));
-        fixture.detectChanges();
-        expect(component.confirmUpdateWorkflowCustomDescription).toHaveBeenCalledTimes(1);
-      });
-    });
-  }));
-
-  describe("method coverage", () => {
-    afterEach(() => {
-      vi.restoreAllMocks();
-    });
-
-    describe("confirmUpdateWorkflowCustomName", () => {
-      it("persists the new name, updates the workflow, and stops editing", () => {
-        const persist = TestBed.inject(WorkflowPersistService);
-        // The stub lacks updateWorkflowName; give this fresh-per-test instance a spy.
-        const spy = ((persist as any).updateWorkflowName = vi.fn().mockReturnValue(of(undefined)));
-        component.entry = makeWorkflowEntry({ wid: 5, name: "old" });
-        component.editingName = true;
-
-        component.confirmUpdateWorkflowCustomName("new name");
-
-        expect(spy).toHaveBeenCalledWith(5, "new name");
-        expect(component.workflow.name).toBe("new name");
-        expect(component.editingName).toBe(false);
-      });
-
-      it("falls back to the default name when the input is empty", () => {
-        const persist = TestBed.inject(WorkflowPersistService);
-        const spy = ((persist as any).updateWorkflowName = vi.fn().mockReturnValue(of(undefined)));
-        component.entry = makeWorkflowEntry({ wid: 5 });
-
-        component.confirmUpdateWorkflowCustomName("");
-
-        expect(spy).toHaveBeenCalledWith(5, DEFAULT_WORKFLOW_NAME);
-        expect(component.workflow.name).toBe(DEFAULT_WORKFLOW_NAME);
-      });
-
-      it("is a no-op when the workflow has no id", () => {
-        const persist = TestBed.inject(WorkflowPersistService);
-        const spy = ((persist as any).updateWorkflowName = vi.fn());
-        component.entry = makeWorkflowEntry({ wid: undefined });
-
-        component.confirmUpdateWorkflowCustomName("x");
-
-        expect(spy).not.toHaveBeenCalled();
-      });
-    });
-
-    describe("confirmUpdateWorkflowCustomDescription", () => {
-      it("persists the new description and stops editing", () => {
-        const persist = TestBed.inject(WorkflowPersistService);
-        const spy = ((persist as any).updateWorkflowDescription = vi.fn().mockReturnValue(of(undefined)));
-        component.entry = makeWorkflowEntry({ wid: 5 });
-        component.editingDescription = true;
-
-        component.confirmUpdateWorkflowCustomDescription("new desc");
-
-        expect(spy).toHaveBeenCalledWith(5, "new desc");
-        expect(component.workflow.description).toBe("new desc");
-        expect(component.editingDescription).toBe(false);
-      });
-
-      it("is a no-op when the workflow has no id", () => {
-        const persist = TestBed.inject(WorkflowPersistService);
-        const spy = ((persist as any).updateWorkflowDescription = vi.fn());
-        component.entry = makeWorkflowEntry({ wid: undefined });
-
-        component.confirmUpdateWorkflowCustomDescription("x");
-
-        expect(spy).not.toHaveBeenCalled();
-      });
-    });
-
-    it("onClickGetWorkflowExecutions opens the execution-history modal for the workflow", () => {
-      const modal = TestBed.inject(NzModalService);
-      const spy = vi.spyOn(modal, "create").mockReturnValue({} as any);
-      component.entry = makeWorkflowEntry({ wid: 9, name: "wf" });
-
-      component.onClickGetWorkflowExecutions();
-
-      expect(spy).toHaveBeenCalledWith(
-        expect.objectContaining({
-          nzContent: WorkflowExecutionHistoryComponent,
-          nzData: { wid: 9 },
-          nzTitle: "Execution results of Workflow: wf",
-        })
-      );
-    });
-
-    describe("onClickDownloadWorkfllow", () => {
-      it("delegates to the download service with the workflow id and name", () => {
-        const download = TestBed.inject(DownloadService);
-        const spy = vi.spyOn(download, "downloadWorkflow").mockReturnValue(of(undefined) as any);
-        component.entry = makeWorkflowEntry({ wid: 9, name: "wf" });
-
-        component.onClickDownloadWorkfllow();
-
-        expect(spy).toHaveBeenCalledWith(9, "wf");
-      });
-
-      it("does nothing when the workflow has no id", () => {
-        const download = TestBed.inject(DownloadService);
-        const spy = vi.spyOn(download, "downloadWorkflow");
-        component.entry = makeWorkflowEntry({ wid: undefined });
-
-        component.onClickDownloadWorkfllow();
-
-        expect(spy).not.toHaveBeenCalled();
-      });
-    });
-
-    it("opens the share modal for this row's workflow, carrying its write access and the owner list", async () => {
-      const entry = makeWorkflowEntry({ wid: 21, name: "wf" });
-      entry.workflow.accessLevel = "WRITE";
-      component.entry = entry;
-      const persist = TestBed.inject(WorkflowPersistService);
-      // The owner list is the autocomplete the dialog exists to offer; it is the one awaited
-      // value in the method, so nothing else proves it is resolved before the modal opens.
-      vi.spyOn(persist, "retrieveOwners").mockReturnValue(of(["a@example.com", "b@example.com"]));
-      const modal = TestBed.inject(NzModalService);
-      const spy = vi.spyOn(modal, "create").mockReturnValue({} as any);
-
-      await component.onClickOpenShareAccess();
-
-      expect(spy).toHaveBeenCalledWith(
-        expect.objectContaining({
-          nzContent: ShareAccessComponent,
-          nzData: expect.objectContaining({
-            writeAccess: true,
-            type: "workflow",
-            id: 21,
-            allOwners: ["a@example.com", "b@example.com"],
-          }),
-        })
-      );
-    });
-
-    it("marks the share modal read-only for a row the viewer can only read", async () => {
-      const entry = makeWorkflowEntry({ wid: 21 });
-      entry.workflow.accessLevel = "READ";
-      component.entry = entry;
-      const modal = TestBed.inject(NzModalService);
-      const spy = vi.spyOn(modal, "create").mockReturnValue({} as any);
-
-      await component.onClickOpenShareAccess();
-
-      expect(spy).toHaveBeenCalledWith(
-        expect.objectContaining({ nzData: expect.objectContaining({ writeAccess: false }) })
-      );
-    });
-
-    describe("mis-wired inputs", () => {
-      // Both accessors are read from the template on every change-detection pass, so a silent
-      // undefined would surface as an unrelated crash deep in ng-zorro instead of here.
-      it("refuses to read the entry before one has been provided", () => {
-        component.entry = undefined as any;
-
-        expect(() => component.entry).toThrowError("entry property must be provided to UserWorkflowListItemComponent.");
-      });
-
-      it("refuses to read a workflow off an entry that carries no workflow payload", () => {
-        // The guard tests for the payload, not for entry.type: an entry of any kind that does
-        // carry a workflow passes it, so this must not claim to be a kind check.
-        component.entry = { name: "ds" } as unknown as DashboardEntry;
-
-        expect(() => component.workflow).toThrowError(/Entry must be workflow/);
-      });
-    });
-  });
-
-  function sendInput(editableDescriptionInput: HTMLInputElement, text: string) {
-    // Helper function to change the workflow description textbox.
-    editableDescriptionInput.value = text;
-    editableDescriptionInput.dispatchEvent(new Event("input"));
-    fixture.detectChanges();
-    return fixture.whenStable();
-  }
-});
-/**
- * The list item's template carries decisions the class does not: which control a click reaches, what
- * value an edit forwards, whether a read-only row may still be edited inline, and which actions the
- * row offers. The suite above calls the component's methods directly, so none of that was rendered.
- */
-describe("UserWorkflowListItemComponent rendering", () => {
-  let fixture: ComponentFixture<TestHostComponent>;
-  let component: UserWorkflowListItemComponent;
-  let persistService: { updateWorkflowName: ReturnType<typeof vi.fn> };
-
-  async function setup(opts: { executionsTracking?: boolean } = {}) {
-    // StubWorkflowPersistService does not declare updateWorkflowName, so it cannot be spied on.
-    persistService = { updateWorkflowName: vi.fn(() => of({} as Response)) };
-    TestBed.resetTestingModule();
-    await TestBed.configureTestingModule({
-      imports: [TestHostComponent, NzModalModule, HttpClientTestingModule, NzTooltipModule],
-      providers: [
-        { provide: WorkflowPersistService, useValue: persistService },
-        { provide: FileSaverService, useValue: { saveAs: vi.fn() } },
-        provideRouter([]),
-        ...commonTestProviders,
-      ],
-    }).compileComponents();
-    if (opts.executionsTracking) {
-      (TestBed.inject(GuiConfigService) as unknown as MockGuiConfigService).setConfig({
-        workflowExecutionsTrackingEnabled: true,
-      });
-    }
-  }
-
-  /** Renders one row. */
-  function render(entry: DashboardEntry, editable = true): HTMLElement {
-    fixture = TestBed.createComponent(TestHostComponent);
-    fixture.componentInstance.entry = entry;
-    fixture.componentInstance.editable = editable;
-    fixture.detectChanges();
-    component = fixture.componentInstance.inner;
-    return fixture.nativeElement as HTMLElement;
-  }
-
-  /**
-   * Tooltip titles on the row, read off the directive: ng-zorro consumes the nz-tooltip attribute,
-   * and an interpolated title is a property binding that never appears in the DOM at all.
-   */
-  function tooltipTitles(): unknown[] {
-    return fixture.debugElement
-      .queryAll(By.directive(NzTooltipDirective))
-      .map(d => (d.injector.get(NzTooltipDirective) as NzTooltipDirective).directiveTitle);
-  }
-
-  /** The elements whose tooltip title satisfies the predicate, in document order. */
-  function byTooltip(pred: (title: string) => boolean): HTMLElement[] {
-    return fixture.debugElement
-      .queryAll(By.directive(NzTooltipDirective))
-      .filter(d => {
-        const t = (d.injector.get(NzTooltipDirective) as NzTooltipDirective).directiveTitle;
-        return typeof t === "string" && pred(t);
-      })
-      .map(d => d.nativeElement as HTMLElement);
-  }
-
-  /**
-   * The one element whose tooltip title satisfies the predicate. Indexing byTooltip() directly
-   * reports a missing or duplicated action as a TypeError on `undefined.click()` — or, worse,
-   * silently clicks the first of several; asserting the match is unique names the real problem.
-   */
-  function onlyByTooltip(pred: (title: string) => boolean): HTMLElement {
-    const matches = byTooltip(pred);
-    expect(matches).toHaveLength(1);
-    return matches[0];
-  }
-
-  beforeEach(async () => {
-    await setup();
-  });
-
-  describe("renaming", () => {
-    it("forwards the text that was typed, not the name it started with", () => {
-      // The input is seeded with the current name, so binding workflow.name instead of the input's
-      // value would still look right on screen while silently discarding every rename.
-      render(makeWorkflowEntry({ wid: 7, name: "before" }));
-
-      component.editingName = true;
-      fixture.detectChanges();
-
-      const input = (fixture.nativeElement as HTMLElement).querySelector<HTMLInputElement>("input")!;
-      expect(input.value).toBe("before");
-      input.value = "after";
-      input.dispatchEvent(new Event("focusout"));
-
-      expect(persistService.updateWorkflowName).toHaveBeenCalledWith(7, "after");
-    });
-  });
-
-  describe("inline description editing", () => {
-    it("opens for a viewer who may edit", () => {
-      const el = render(makeWorkflowEntry(), true);
-
-      el.querySelector<HTMLElement>(".workflow-description")?.click();
-      fixture.detectChanges();
-
-      expect(component.editingDescription).toBe(true);
-    });
-
-    it("stays shut for a read-only viewer", () => {
-      // The gate is the template expression editingDescription = editable; without it a shared
-      // read-only row opens an editor whose save the backend then rejects.
-      const el = render(makeWorkflowEntry(), false);
-
-      el.querySelector<HTMLElement>(".workflow-description")?.click();
-      fixture.detectChanges();
-
-      expect(component.editingDescription).toBe(false);
-    });
-  });
-
-  describe("ownership", () => {
-    it("tells a non-owner what access they have and who shared it", () => {
-      const entry = makeWorkflowEntry();
-      entry.workflow.isOwner = false;
-      entry.workflow.accessLevel = "READ";
-      entry.workflow.ownerName = "Bob";
-      render(entry);
-
-      // Pins the order of the two interpolations.
-      expect(tooltipTitles()).toContain("READ access shared by Bob");
-    });
-
-    it("shows no shared-access marker to the owner", () => {
-      const entry = makeWorkflowEntry();
-      entry.workflow.isOwner = true;
-      render(entry);
-
-      expect(tooltipTitles().some(t => typeof t === "string" && t.includes("access shared by"))).toBe(false);
-    });
-
-    it("disables deleting a workflow the viewer does not own", () => {
-      const entry = makeWorkflowEntry();
-      entry.workflow.isOwner = false;
-      const el = render(entry);
-
-      expect(el.querySelector<HTMLButtonElement>("button[nz-popconfirm]")?.disabled).toBe(true);
-    });
-  });
-
-  describe("row actions", () => {
-    it("withholds the executions action while execution tracking is off", () => {
-      render(makeWorkflowEntry());
-
-      expect(tooltipTitles().some(t => typeof t === "string" && t.startsWith("Executions of the workflow"))).toBe(
-        false
-      );
-    });
-
-    it("offers the executions action once execution tracking is on", async () => {
-      await setup({ executionsTracking: true });
-      render(makeWorkflowEntry());
-
-      expect(tooltipTitles().some(t => typeof t === "string" && t.startsWith("Executions of the workflow"))).toBe(true);
-    });
-
-    it("keeps duplicate and delete on their own outputs", () => {
-      // Adjacent icon buttons; emitting the wrong one of these would be destructive.
-      render(makeWorkflowEntry());
-      const duplicated = vi.fn();
-      const deleted = vi.fn();
-      component.duplicated.subscribe(duplicated);
-      component.deleted.subscribe(deleted);
-
-      const dup = byTooltip(t => t.startsWith("Duplicate"));
-      expect(dup.length).toBe(1);
-      dup[0].click();
-      expect(duplicated).toHaveBeenCalledTimes(1);
-      expect(deleted).not.toHaveBeenCalled();
-
-      fixture.debugElement.query(By.css("button[nz-popconfirm]")).triggerEventHandler("nzOnConfirm", null);
-      expect(deleted).toHaveBeenCalledTimes(1);
-      expect(duplicated).toHaveBeenCalledTimes(1);
-    });
-
-    it("opens the executions modal from the history action", async () => {
-      await setup({ executionsTracking: true });
-      render(makeWorkflowEntry({ wid: 11, name: "wf" }));
-      const modal = TestBed.inject(NzModalService);
-      const create = vi.spyOn(modal, "create").mockReturnValue({} as any);
-
-      onlyByTooltip(t => t.startsWith("Executions of the workflow")).click();
-
-      expect(create).toHaveBeenCalledWith(
-        expect.objectContaining({ nzContent: WorkflowExecutionHistoryComponent, nzData: { wid: 11 } })
-      );
-    });
-  });
-
-  describe("inline name and description editors", () => {
-    it("puts name editing behind the pencil and description editing behind the plus", () => {
-      // Two adjacent icon buttons on the same toolbar; swapping them would open the wrong editor.
-      render(makeWorkflowEntry());
-      expect(component.editingName).toBe(false);
-      expect(component.editingDescription).toBe(false);
-
-      onlyByTooltip(t => t === "Customize Workflow Name").click();
-
-      expect(component.editingName).toBe(true);
-      expect(component.editingDescription).toBe(false);
-
-      onlyByTooltip(t => t === "Add Description").click();
-
-      expect(component.editingDescription).toBe(true);
-    });
-  });
-
-  describe("selection checkbox", () => {
-    it("indents the avatar only when the checkbox is absent", () => {
-      const withBox = render(makeWorkflowEntry(), true).querySelector<HTMLElement>("nz-list-item-meta-avatar")!;
-      expect(withBox.style.marginLeft).not.toBe("16px");
-
-      const withoutBox = render(makeWorkflowEntry(), false).querySelector<HTMLElement>("nz-list-item-meta-avatar")!;
-      expect(withoutBox.style.marginLeft).toBe("16px");
-      expect(withoutBox.querySelector(".workflow-item-checkbox")).toBeNull();
-    });
-
-    it("records the selection on the entry", () => {
-      const entry = makeWorkflowEntry();
-      render(entry, true);
-
-      fixture.debugElement.query(By.css(".workflow-item-checkbox")).triggerEventHandler("ngModelChange", true);
-
-      expect(entry.checked).toBe(true);
-    });
-  });
-});
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
deleted file mode 100644
index f16ae54da9..0000000000
--- a/frontend/src/app/dashboard/component/user/user-workflow/user-workflow-list-item/user-workflow-list-item.component.ts
+++ /dev/null
@@ -1,201 +0,0 @@
-/**
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements.  See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership.  The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License.  You may obtain a copy of the License at
- *
- *   http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied.  See the License for the
- * specific language governing permissions and limitations
- * under the License.
- */
-
-import { Component, EventEmitter, Input, Output } from "@angular/core";
-import { NzModalService } from "ng-zorro-antd/modal";
-import { WorkflowExecutionHistoryComponent } from "../ngbd-modal-workflow-executions/workflow-execution-history.component";
-import {
-  DEFAULT_WORKFLOW_NAME,
-  WorkflowPersistService,
-} from "../../../../../common/service/workflow-persist/workflow-persist.service";
-import { UntilDestroy, untilDestroyed } from "@ngneat/until-destroy";
-import { ShareAccessComponent } from "../../share-access/share-access.component";
-import { Workflow } from "../../../../../common/type/workflow";
-import { DashboardEntry } from "../../../../type/dashboard-entry";
-import { firstValueFrom } from "rxjs";
-import { DownloadService } from "src/app/dashboard/service/user/download/download.service";
-import { USER_WORKSPACE } from "../../../../../app-routing.constant";
-import { GuiConfigService } from "../../../../../common/service/gui-config.service";
-import {
-  NzListItemComponent,
-  NzListItemMetaComponent,
-  NzListItemMetaAvatarComponent,
-  NzListItemMetaTitleComponent,
-  NzListItemMetaDescriptionComponent,
-  NzListItemActionsComponent,
-  NzListItemActionComponent,
-} from "ng-zorro-antd/list";
-import { NgStyle, NgIf, DatePipe } from "@angular/common";
-import { FormsModule } from "@angular/forms";
-import { NzAvatarComponent } from "ng-zorro-antd/avatar";
-import { RouterLink } from "@angular/router";
-import { NzSpaceCompactItemDirective } from "ng-zorro-antd/space";
-import { NzButtonComponent } from "ng-zorro-antd/button";
-import { ɵNzTransitionPatchDirective } from "ng-zorro-antd/core/transition-patch";
-import { NzTooltipDirective } from "ng-zorro-antd/tooltip";
-import { NzIconDirective } from "ng-zorro-antd/icon";
-import { NzWaveDirective } from "ng-zorro-antd/core/wave";
-import { NzPopconfirmDirective } from "ng-zorro-antd/popconfirm";
-import { HighlightSearchTermsPipe } from "./highlight-search-terms.pipe";
-
-@UntilDestroy()
-@Component({
-  selector: "texera-user-workflow-list-item",
-  templateUrl: "./user-workflow-list-item.component.html",
-  styleUrls: ["./user-workflow-list-item.component.scss"],
-  imports: [
-    NzListItemComponent,
-    NzListItemMetaComponent,
-    NzListItemMetaAvatarComponent,
-    NgStyle,
-    NgIf,
-    FormsModule,
-    NzAvatarComponent,
-    NzListItemMetaTitleComponent,
-    RouterLink,
-    NzSpaceCompactItemDirective,
-    NzButtonComponent,
-    ɵNzTransitionPatchDirective,
-    NzTooltipDirective,
-    NzIconDirective,
-    NzListItemMetaDescriptionComponent,
-    NzListItemActionsComponent,
-    NzListItemActionComponent,
-    NzWaveDirective,
-    NzPopconfirmDirective,
-    DatePipe,
-    HighlightSearchTermsPipe,
-  ],
-})
-export class UserWorkflowListItemComponent {
-  protected readonly USER_WORKSPACE = USER_WORKSPACE;
-  private _entry?: DashboardEntry;
-  @Input() public keywords: string[] = [];
-
-  @Input()
-  get entry(): DashboardEntry {
-    if (!this._entry) {
-      throw new Error("entry property must be provided to UserWorkflowListItemComponent.");
-    }
-    return this._entry;
-  }
-
-  set entry(value: DashboardEntry) {
-    this._entry = value;
-  }
-
-  get workflow(): Workflow {
-    if (!this.entry.workflow) {
-      throw new Error(
-        "Incorrect type of DashboardEntry provided to UserWorkflowListItemComponent. Entry must be workflow."
-      );
-    }
-    return this.entry.workflow.workflow;
-  }
-
-  @Input() editable = false;
-  @Output() deleted = new EventEmitter<void>();
-  @Output() duplicated = new EventEmitter<void>();
-
-  editingName = false;
-  editingDescription = false;
-
-  constructor(
-    private workflowPersistService: WorkflowPersistService,
-    private modalService: NzModalService,
-    protected config: GuiConfigService,
-    private downloadService: DownloadService
-  ) {}
-
-  /**
-   * open the workflow executions page
-   */
-  public onClickGetWorkflowExecutions(): void {
-    this.modalService.create({
-      nzContent: WorkflowExecutionHistoryComponent,
-      nzData: { wid: this.workflow.wid },
-      nzTitle: "Execution results of Workflow: " + this.workflow.name,
-      nzFooter: null,
-      nzWidth: "80%",
-      nzCentered: true,
-    });
-  }
-
-  public confirmUpdateWorkflowCustomName(name: string): void {
-    if (this.workflow.wid === undefined) {
-      return;
-    }
-
-    this.workflowPersistService
-      .updateWorkflowName(this.workflow.wid, name || DEFAULT_WORKFLOW_NAME)
-      .pipe(untilDestroyed(this))
-      .subscribe(() => {
-        this.workflow.name = name || DEFAULT_WORKFLOW_NAME;
-      })
-      .add(() => {
-        this.editingName = false;
-      });
-  }
-
-  public confirmUpdateWorkflowCustomDescription(description: string): void {
-    if (this.workflow.wid === undefined) {
-      return;
-    }
-
-    this.workflowPersistService
-      .updateWorkflowDescription(this.workflow.wid, description)
-      .pipe(untilDestroyed(this))
-      .subscribe(() => {
-        this.workflow.description = description;
-      })
-      .add(() => {
-        this.editingDescription = false;
-      });
-  }
-
-  /**
-   * open the Modal based on the workflow clicked on
-   */
-  public async onClickOpenShareAccess(): Promise<void> {
-    this.modalService.create({
-      nzContent: ShareAccessComponent,
-      nzData: {
-        writeAccess: this.entry.workflow.accessLevel === "WRITE",
-        type: "workflow",
-        id: this.workflow.wid,
-        allOwners: await firstValueFrom(this.workflowPersistService.retrieveOwners()),
-      },
-      nzFooter: null,
-      nzTitle: "Share this workflow with others",
-      nzCentered: true,
-    });
-  }
-
-  /**
-   * Download the workflow as a json file
-   */
-  public onClickDownloadWorkfllow(): void {
-    if (this.workflow.wid) {
-      this.downloadService
-        .downloadWorkflow(this.workflow.wid, this.workflow.name)
-        .pipe(untilDestroyed(this))
-        .subscribe();
-    }
-  }
-}
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 764b4bdc11..cd6ecc446c 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
@@ -57,7 +57,6 @@ import {
   testWorkflowFileNameConflictEntries,
 } from "../../user-dashboard-test-fixtures";
 import { FiltersComponent } from "../filters/filters.component";
-import { UserWorkflowListItemComponent } from "./user-workflow-list-item/user-workflow-list-item.component";
 import { SearchService } from "../../../service/user/search.service";
 import { StubSearchService } from "../../../service/user/stub-search.service";
 import { SearchResultsComponent } from "../search-results/search-results.component";
@@ -104,7 +103,6 @@ describe("SavedWorkflowSectionComponent", () => {
         UserWorkflowComponent,
         ShareAccessComponent,
         FiltersComponent,
-        UserWorkflowListItemComponent,
         SearchResultsComponent,
         FormsModule,
         RouterTestingModule,
diff --git a/frontend/src/styles.scss b/frontend/src/styles.scss
index 129826fa3d..3625505f95 100644
--- a/frontend/src/styles.scss
+++ b/frontend/src/styles.scss
@@ -65,11 +65,6 @@ hr {
   border: 0;
 }
 
-// due to innerHTML, this rule has to be global
-.highlight-search-terms {
-  color: blue;
-}
-
 .box {
   border-radius: 5px;
   box-shadow: