fix: Allow deleting workflows without any crawls (#2285)
- Uses crawl count to determine whether workflow can be deleted instead of last crawl ID - Display delete confirmation dialog when trying to delete a workflow
This commit is contained in:
		
							parent
							
								
									1260aec976
								
							
						
					
					
						commit
						6a5e070ffc
					
				| @ -18,6 +18,7 @@ import type { CrawlLog } from "@/features/archived-items/crawl-logs"; | ||||
| import { CrawlStatus } from "@/features/archived-items/crawl-status"; | ||||
| import { ExclusionEditor } from "@/features/crawl-workflows/exclusion-editor"; | ||||
| import { pageNav, type Breadcrumb } from "@/layouts/pageHeader"; | ||||
| import { deleteConfirmation } from "@/strings/ui"; | ||||
| import type { APIPaginatedList } from "@/types/api"; | ||||
| import { type CrawlState } from "@/types/crawlState"; | ||||
| import { isApiError } from "@/utils/api"; | ||||
| @ -54,7 +55,13 @@ export class WorkflowDetail extends BtrixElement { | ||||
|   isCrawler!: boolean; | ||||
| 
 | ||||
|   @property({ type: String }) | ||||
|   openDialogName?: "scale" | "exclusions" | "cancel" | "stop" | "delete"; | ||||
|   openDialogName?: | ||||
|     | "scale" | ||||
|     | "exclusions" | ||||
|     | "cancel" | ||||
|     | "stop" | ||||
|     | "delete" | ||||
|     | "deleteCrawl"; | ||||
| 
 | ||||
|   @property({ type: String }) | ||||
|   initialActivePanel?: Tab; | ||||
| @ -255,14 +262,14 @@ export class WorkflowDetail extends BtrixElement { | ||||
|   render() { | ||||
|     if (this.isEditing && this.isCrawler) { | ||||
|       return html` | ||||
|         <div class="grid grid-cols-1 gap-7"> | ||||
|         <div class="grid grid-cols-1 gap-7 pb-7"> | ||||
|           ${when(this.workflow, this.renderEditor)} | ||||
|         </div> | ||||
|       `;
 | ||||
|     } | ||||
| 
 | ||||
|     return html` | ||||
|       <div class="grid grid-cols-1 gap-7"> | ||||
|       <div class="grid grid-cols-1 gap-7 pb-7"> | ||||
|         <div class="col-span-1">${this.renderBreadcrumbs()}</div> | ||||
| 
 | ||||
|         <div> | ||||
| @ -355,7 +362,7 @@ export class WorkflowDetail extends BtrixElement { | ||||
|       </btrix-dialog> | ||||
|       <btrix-dialog | ||||
|         .label=${msg("Delete Crawl?")} | ||||
|         .open=${this.openDialogName === "delete"} | ||||
|         .open=${this.openDialogName === "deleteCrawl"} | ||||
|         @sl-request-close=${() => (this.openDialogName = undefined)} | ||||
|         @sl-show=${this.showDialog} | ||||
|         @sl-after-hide=${() => (this.isDialogVisible = false)} | ||||
| @ -392,6 +399,32 @@ export class WorkflowDetail extends BtrixElement { | ||||
|       > | ||||
|         ${this.isDialogVisible ? this.renderEditScale() : ""} | ||||
|       </btrix-dialog> | ||||
|       <btrix-dialog | ||||
|         .label=${msg("Delete Workflow?")} | ||||
|         .open=${this.openDialogName === "delete"} | ||||
|         @sl-request-close=${() => (this.openDialogName = undefined)} | ||||
|         @sl-show=${this.showDialog} | ||||
|         @sl-after-hide=${() => (this.isDialogVisible = false)} | ||||
|       > | ||||
|         ${deleteConfirmation(this.renderName())} | ||||
|         <div slot="footer" class="flex justify-between"> | ||||
|           <sl-button | ||||
|             size="small" | ||||
|             .autofocus=${true} | ||||
|             @click=${() => (this.openDialogName = undefined)} | ||||
|             >${msg("Cancel")}</sl-button | ||||
|           > | ||||
|           <sl-button | ||||
|             size="small" | ||||
|             variant="danger" | ||||
|             @click=${async () => { | ||||
|               void this.delete(); | ||||
|               this.openDialogName = undefined; | ||||
|             }} | ||||
|             >${msg("Delete Workflow")}</sl-button | ||||
|           > | ||||
|         </div> | ||||
|       </btrix-dialog> | ||||
|     `;
 | ||||
|   } | ||||
| 
 | ||||
| @ -736,12 +769,12 @@ export class WorkflowDetail extends BtrixElement { | ||||
|             ${msg("Duplicate Workflow")} | ||||
|           </sl-menu-item> | ||||
|           ${when( | ||||
|             !this.lastCrawlId, | ||||
|             !workflow.crawlCount, | ||||
|             () => html` | ||||
|               <sl-divider></sl-divider> | ||||
|               <sl-menu-item | ||||
|                 style="--sl-color-neutral-700: var(--danger)" | ||||
|                 @click=${() => void this.delete()} | ||||
|                 @click=${() => (this.openDialogName = "delete")} | ||||
|               > | ||||
|                 <sl-icon name="trash3" slot="prefix"></sl-icon> | ||||
|                 ${msg("Delete Workflow")} | ||||
| @ -842,7 +875,7 @@ export class WorkflowDetail extends BtrixElement { | ||||
| 
 | ||||
|   private renderCrawls() { | ||||
|     return html` | ||||
|       <section> | ||||
|       <section class="h-56 min-h-max"> | ||||
|         <div | ||||
|           class="mb-3 flex items-center justify-end rounded-lg border bg-neutral-50 p-4" | ||||
|         > | ||||
| @ -1643,7 +1676,7 @@ export class WorkflowDetail extends BtrixElement { | ||||
| 
 | ||||
|   private readonly confirmDeleteCrawl = (crawl: Crawl) => { | ||||
|     this.crawlToDelete = crawl; | ||||
|     this.openDialogName = "delete"; | ||||
|     this.openDialogName = "deleteCrawl"; | ||||
|   }; | ||||
| 
 | ||||
|   private async deleteCrawl(crawl: Crawl) { | ||||
| @ -1666,6 +1699,9 @@ export class WorkflowDetail extends BtrixElement { | ||||
|         id: "archived-item-delete-status", | ||||
|       }); | ||||
|       void this.fetchCrawls(); | ||||
| 
 | ||||
|       // Update crawl count
 | ||||
|       void this.fetchWorkflow(); | ||||
|     } catch (e) { | ||||
|       if (this.crawlToDelete) { | ||||
|         this.confirmDeleteCrawl(this.crawlToDelete); | ||||
|  | ||||
| @ -1,7 +1,11 @@ | ||||
| import { localized, msg, str } from "@lit/localize"; | ||||
| import type { SlCheckbox, SlSelectEvent } from "@shoelace-style/shoelace"; | ||||
| import type { | ||||
|   SlCheckbox, | ||||
|   SlDialog, | ||||
|   SlSelectEvent, | ||||
| } from "@shoelace-style/shoelace"; | ||||
| import { html, type PropertyValues } from "lit"; | ||||
| import { customElement, state } from "lit/decorators.js"; | ||||
| import { customElement, query, state } from "lit/decorators.js"; | ||||
| import { ifDefined } from "lit/directives/if-defined.js"; | ||||
| import { when } from "lit/directives/when.js"; | ||||
| import queryString from "query-string"; | ||||
| @ -21,6 +25,7 @@ import { type SelectEvent } from "@/components/ui/search-combobox"; | ||||
| import type { SelectJobTypeEvent } from "@/features/crawl-workflows/new-workflow-dialog"; | ||||
| import { pageHeader } from "@/layouts/pageHeader"; | ||||
| import scopeTypeLabels from "@/strings/crawl-workflows/scopeType"; | ||||
| import { deleteConfirmation } from "@/strings/ui"; | ||||
| import type { APIPaginatedList, APIPaginationQuery } from "@/types/api"; | ||||
| import { NewWorkflowOnlyScopeType } from "@/types/workflow"; | ||||
| import { isApiError } from "@/utils/api"; | ||||
| @ -91,6 +96,9 @@ export class WorkflowsList extends BtrixElement { | ||||
|   @state() | ||||
|   private fetchErrorStatusCode?: number; | ||||
| 
 | ||||
|   @state() | ||||
|   private workflowToDelete?: ListWorkflow; | ||||
| 
 | ||||
|   @state() | ||||
|   private orderBy: { | ||||
|     field: SortField; | ||||
| @ -106,6 +114,9 @@ export class WorkflowsList extends BtrixElement { | ||||
|   @state() | ||||
|   private filterByCurrentUser = false; | ||||
| 
 | ||||
|   @query("#deleteDialog") | ||||
|   private readonly deleteDialog?: SlDialog | null; | ||||
| 
 | ||||
|   // For fuzzy search:
 | ||||
|   private readonly searchKeys = ["name", "firstSeed"]; | ||||
| 
 | ||||
| @ -311,12 +322,52 @@ export class WorkflowsList extends BtrixElement { | ||||
|             </btrix-alert> | ||||
|           </div> | ||||
|         `,
 | ||||
|         () => | ||||
|           this.workflows | ||||
|             ? this.workflows.total | ||||
|               ? this.renderWorkflowList() | ||||
|               : this.renderEmptyState() | ||||
|             : this.renderLoading(), | ||||
|         () => html` | ||||
|           <div class="pb-10"> | ||||
|             ${this.workflows | ||||
|               ? this.workflows.total | ||||
|                 ? this.renderWorkflowList() | ||||
|                 : this.renderEmptyState() | ||||
|               : this.renderLoading()} | ||||
|           </div> | ||||
|         `,
 | ||||
|       )} | ||||
|       ${this.renderDialogs()} | ||||
|     `;
 | ||||
|   } | ||||
| 
 | ||||
|   private renderDialogs() { | ||||
|     return html` | ||||
|       ${when( | ||||
|         this.workflowToDelete, | ||||
|         (workflow) => html` | ||||
|           <btrix-dialog id="deleteDialog" .label=${msg("Delete Workflow?")}> | ||||
|             ${deleteConfirmation(this.renderName(workflow))} | ||||
|             <div slot="footer" class="flex justify-between"> | ||||
|               <sl-button | ||||
|                 size="small" | ||||
|                 .autofocus=${true} | ||||
|                 @click=${() => void this.deleteDialog?.hide()} | ||||
|                 >${msg("Cancel")}</sl-button | ||||
|               > | ||||
|               <sl-button | ||||
|                 size="small" | ||||
|                 variant="danger" | ||||
|                 @click=${async () => { | ||||
|                   void this.deleteDialog?.hide(); | ||||
| 
 | ||||
|                   try { | ||||
|                     await this.delete(workflow); | ||||
|                     this.workflowToDelete = undefined; | ||||
|                   } catch { | ||||
|                     void this.deleteDialog?.show(); | ||||
|                   } | ||||
|                 }} | ||||
|                 >${msg("Delete Workflow")}</sl-button | ||||
|               > | ||||
|             </div> | ||||
|           </btrix-dialog> | ||||
|         `,
 | ||||
|       )} | ||||
|     `;
 | ||||
|   } | ||||
| @ -593,12 +644,16 @@ export class WorkflowsList extends BtrixElement { | ||||
|             ${msg("Duplicate Workflow")} | ||||
|           </sl-menu-item> | ||||
|           ${when( | ||||
|             !workflow.lastCrawlId, | ||||
|             !workflow.crawlCount, | ||||
|             () => html` | ||||
|               <sl-divider></sl-divider> | ||||
|               <sl-menu-item | ||||
|                 style="--sl-color-neutral-700: var(--danger)" | ||||
|                 @click=${() => void this.delete(workflow)} | ||||
|                 @click=${async () => { | ||||
|                   this.workflowToDelete = workflow; | ||||
|                   await this.updateComplete; | ||||
|                   void this.deleteDialog?.show(); | ||||
|                 }} | ||||
|               > | ||||
|                 <sl-icon name="trash3" slot="prefix"></sl-icon> | ||||
|                 ${msg("Delete Workflow")} | ||||
|  | ||||
							
								
								
									
										9
									
								
								frontend/src/strings/ui.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										9
									
								
								frontend/src/strings/ui.ts
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,9 @@ | ||||
| import { msg } from "@lit/localize"; | ||||
| import { html, type TemplateResult } from "lit"; | ||||
| 
 | ||||
| // TODO Refactor all generic confirmation messages to use utility
 | ||||
| export const deleteConfirmation = (name: string | TemplateResult) => | ||||
|   msg(html` | ||||
|     Are you sure you want to delete | ||||
|     <strong class="font-semibold">${name}</strong>? | ||||
|   `);
 | ||||
		Loading…
	
		Reference in New Issue
	
	Block a user