Crawl workflow detail page improvements (#823)
Resolves #817 - Adds relevant action buttons to each Workflow detail tab header - Adds "Delete" action menu item to crawls in Crawls tab - Prevent automatically switching to "Watch" tab after running crawl from detail page - Removes "Stop" confirmation prompt and only shows "Cancel" confirmation prompt if there are one or more pages crawled - Replaces "Cancel" confirmation prompt with web component dialog (partially addresses Switch to in-page dialogue boxes #619) - Fixes hash routing to fix going back with browser back button
This commit is contained in:
		
							parent
							
								
									aae0e6590e
								
							
						
					
					
						commit
						0d23b45dac
					
				| @ -20,8 +20,9 @@ import { | |||||||
| } from "lit/decorators.js"; | } from "lit/decorators.js"; | ||||||
| import { when } from "lit/directives/when.js"; | import { when } from "lit/directives/when.js"; | ||||||
| import { msg, localized, str } from "@lit/localize"; | import { msg, localized, str } from "@lit/localize"; | ||||||
| import type { SlIconButton, SlMenu } from "@shoelace-style/shoelace"; | import type { SlMenu } from "@shoelace-style/shoelace"; | ||||||
| 
 | 
 | ||||||
|  | import type { Button } from "./button"; | ||||||
| import { RelativeDuration } from "./relative-duration"; | import { RelativeDuration } from "./relative-duration"; | ||||||
| import type { Crawl } from "../types/crawler"; | import type { Crawl } from "../types/crawler"; | ||||||
| import { srOnly, truncate, dropdown } from "../utils/css"; | import { srOnly, truncate, dropdown } from "../utils/css"; | ||||||
| @ -190,7 +191,7 @@ export class CrawlListItem extends LitElement { | |||||||
|   dropdown!: HTMLElement; |   dropdown!: HTMLElement; | ||||||
| 
 | 
 | ||||||
|   @query(".dropdownTrigger") |   @query(".dropdownTrigger") | ||||||
|   dropdownTrigger!: SlIconButton; |   dropdownTrigger!: Button; | ||||||
| 
 | 
 | ||||||
|   @queryAssignedElements({ selector: "sl-menu", slot: "menu" }) |   @queryAssignedElements({ selector: "sl-menu", slot: "menu" }) | ||||||
|   private menuArr!: Array<SlMenu>; |   private menuArr!: Array<SlMenu>; | ||||||
| @ -336,10 +337,10 @@ export class CrawlListItem extends LitElement { | |||||||
|       </div> |       </div> | ||||||
|       <div class="col action"> |       <div class="col action"> | ||||||
|         <slot name="menuTrigger"> |         <slot name="menuTrigger"> | ||||||
|           <sl-icon-button |           <btrix-button | ||||||
|             class="dropdownTrigger" |             class="dropdownTrigger" | ||||||
|             name="three-dots-vertical" |  | ||||||
|             label=${msg("Actions")} |             label=${msg("Actions")} | ||||||
|  |             icon | ||||||
|             @click=${(e: MouseEvent) => { |             @click=${(e: MouseEvent) => { | ||||||
|               // Prevent anchor link default behavior
 |               // Prevent anchor link default behavior
 | ||||||
|               e.preventDefault(); |               e.preventDefault(); | ||||||
| @ -361,7 +362,9 @@ export class CrawlListItem extends LitElement { | |||||||
|               } |               } | ||||||
|               this.dropdownIsOpen = false; |               this.dropdownIsOpen = false; | ||||||
|             }} |             }} | ||||||
|           ></sl-icon-button> |           > | ||||||
|  |             <sl-icon name="three-dots-vertical"></sl-icon> | ||||||
|  |           </btrix-button> | ||||||
|         </slot> |         </slot> | ||||||
|       </div> |       </div> | ||||||
|     </a>`;
 |     </a>`;
 | ||||||
| @ -443,6 +446,11 @@ export class CrawlList extends LitElement { | |||||||
|     columnCss, |     columnCss, | ||||||
|     hostVars, |     hostVars, | ||||||
|     css` |     css` | ||||||
|  |       .listHeader, .list { | ||||||
|  |         margin-left: var(--row-offset); | ||||||
|  |         margin-right: var(--row-offset); | ||||||
|  |       } | ||||||
|  | 
 | ||||||
|       .listHeader { |       .listHeader { | ||||||
|         line-height: 1; |         line-height: 1; | ||||||
|       } |       } | ||||||
| @ -451,8 +459,6 @@ export class CrawlList extends LitElement { | |||||||
|         border: 1px solid var(--sl-panel-border-color); |         border: 1px solid var(--sl-panel-border-color); | ||||||
|         border-radius: var(--sl-border-radius-medium); |         border-radius: var(--sl-border-radius-medium); | ||||||
|         overflow: hidden; |         overflow: hidden; | ||||||
|         margin-left: var(--row-offset); |  | ||||||
|         margin-right: var(--row-offset); |  | ||||||
|       } |       } | ||||||
| 
 | 
 | ||||||
|       .row { |       .row { | ||||||
|  | |||||||
| @ -253,7 +253,9 @@ export class WorkflowListItem extends LitElement { | |||||||
|     return html`<a
 |     return html`<a
 | ||||||
|       class="item row" |       class="item row" | ||||||
|       role="button" |       role="button" | ||||||
|       href=${`/orgs/${this.workflow?.oid}/workflows/crawl/${this.workflow?.id}`} |       href=${`/orgs/${this.workflow?.oid}/workflows/crawl/${ | ||||||
|  |         this.workflow?.id | ||||||
|  |       }#${this.workflow?.currCrawlId ? "watch" : "artifacts"}`}
 | ||||||
|       @click=${async (e: MouseEvent) => { |       @click=${async (e: MouseEvent) => { | ||||||
|         e.preventDefault(); |         e.preventDefault(); | ||||||
|         await this.updateComplete; |         await this.updateComplete; | ||||||
|  | |||||||
| @ -25,7 +25,7 @@ import { DASHBOARD_ROUTE } from "../../routes"; | |||||||
| 
 | 
 | ||||||
| const SECTIONS = ["artifacts", "watch", "settings"] as const; | const SECTIONS = ["artifacts", "watch", "settings"] as const; | ||||||
| type Tab = (typeof SECTIONS)[number]; | type Tab = (typeof SECTIONS)[number]; | ||||||
| 
 | const DEFAULT_SECTION: Tab = "artifacts"; | ||||||
| const POLL_INTERVAL_SECONDS = 10; | const POLL_INTERVAL_SECONDS = 10; | ||||||
| 
 | 
 | ||||||
| /** | /** | ||||||
| @ -75,6 +75,12 @@ export class WorkflowDetail extends LiteElement { | |||||||
|   @state() |   @state() | ||||||
|   private isDialogVisible: boolean = false; |   private isDialogVisible: boolean = false; | ||||||
| 
 | 
 | ||||||
|  |   @state() | ||||||
|  |   private isAttemptCancel: boolean = false; | ||||||
|  | 
 | ||||||
|  |   @state() | ||||||
|  |   private isCancelingOrStoppingCrawl: boolean = false; | ||||||
|  | 
 | ||||||
|   @state() |   @state() | ||||||
|   private filterBy: Partial<Record<keyof Crawl, any>> = {}; |   private filterBy: Partial<Record<keyof Crawl, any>> = {}; | ||||||
| 
 | 
 | ||||||
| @ -106,10 +112,7 @@ export class WorkflowDetail extends LiteElement { | |||||||
| 
 | 
 | ||||||
|   connectedCallback(): void { |   connectedCallback(): void { | ||||||
|     // Set initial active section and dialog based on URL #hash value
 |     // Set initial active section and dialog based on URL #hash value
 | ||||||
|     const hash = window.location.hash.slice(1); |     this.getActivePanelFromHash(); | ||||||
|     if (SECTIONS.includes(hash as any)) { |  | ||||||
|       this.activePanel = hash as Tab; |  | ||||||
|     } |  | ||||||
| 
 | 
 | ||||||
|     if ( |     if ( | ||||||
|       this.openDialogName && |       this.openDialogName && | ||||||
| @ -118,11 +121,13 @@ export class WorkflowDetail extends LiteElement { | |||||||
|       this.isDialogVisible = true; |       this.isDialogVisible = true; | ||||||
|     } |     } | ||||||
|     super.connectedCallback(); |     super.connectedCallback(); | ||||||
|  |     window.addEventListener("hashchange", this.getActivePanelFromHash); | ||||||
|   } |   } | ||||||
| 
 | 
 | ||||||
|   disconnectedCallback(): void { |   disconnectedCallback(): void { | ||||||
|     this.stopPoll(); |     this.stopPoll(); | ||||||
|     super.disconnectedCallback(); |     super.disconnectedCallback(); | ||||||
|  |     window.removeEventListener("hashchange", this.getActivePanelFromHash); | ||||||
|   } |   } | ||||||
| 
 | 
 | ||||||
|   willUpdate(changedProperties: Map<string, any>) { |   willUpdate(changedProperties: Map<string, any>) { | ||||||
| @ -143,41 +148,51 @@ export class WorkflowDetail extends LiteElement { | |||||||
|         }); |         }); | ||||||
|       } |       } | ||||||
| 
 | 
 | ||||||
|       if (this.activePanel !== window.location.hash.slice(1)) { |  | ||||||
|         window.location.hash = `#${this.activePanel}`; |  | ||||||
|       } |  | ||||||
| 
 |  | ||||||
|       if (this.activePanel === "artifacts") { |       if (this.activePanel === "artifacts") { | ||||||
|         this.fetchCrawls(); |         this.fetchCrawls(); | ||||||
|       } |       } | ||||||
|     } |     } | ||||||
|   } |   } | ||||||
| 
 | 
 | ||||||
|  |   updated(changedProperties: Map<string, any>) { | ||||||
|  |     const prevWorkflow = changedProperties.get("workflow"); | ||||||
|  |     if ( | ||||||
|  |       prevWorkflow?.currCrawlId && | ||||||
|  |       !this.workflow?.currCrawlId && | ||||||
|  |       this.activePanel === "watch" | ||||||
|  |     ) { | ||||||
|  |       this.goToTab(DEFAULT_SECTION, { replace: true }); | ||||||
|  |     } | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   private getActivePanelFromHash = () => { | ||||||
|  |     const hashValue = window.location.hash.slice(1); | ||||||
|  |     if (SECTIONS.includes(hashValue as any)) { | ||||||
|  |       this.activePanel = hashValue as Tab; | ||||||
|  |     } else { | ||||||
|  |       this.goToTab(DEFAULT_SECTION, { replace: true }); | ||||||
|  |     } | ||||||
|  |   }; | ||||||
|  | 
 | ||||||
|  |   private goToTab(tab: Tab, { replace = false } = {}) { | ||||||
|  |     const path = `${window.location.href.split("#")[0]}#${tab}`; | ||||||
|  |     if (replace) { | ||||||
|  |       window.history.replaceState(null, "", path); | ||||||
|  |     } else { | ||||||
|  |       window.history.pushState(null, "", path); | ||||||
|  |     } | ||||||
|  |     this.activePanel = tab; | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|   private async fetchWorkflow() { |   private async fetchWorkflow() { | ||||||
|     this.stopPoll(); |     this.stopPoll(); | ||||||
|     this.isLoading = true; |     this.isLoading = true; | ||||||
| 
 | 
 | ||||||
|     try { |     try { | ||||||
|       this.workflow = await this.getWorkflow(); |       this.workflow = await this.getWorkflow(); | ||||||
|       let activePanel = this.activePanel; |       if (this.workflow.currCrawlId) { | ||||||
| 
 |         this.fetchCurrentCrawl(); | ||||||
|       if (!this.activePanel) { |  | ||||||
|         if (this.workflow.currCrawlId) { |  | ||||||
|           activePanel = "watch"; |  | ||||||
|         } else { |  | ||||||
|           activePanel = "artifacts"; |  | ||||||
|         } |  | ||||||
|       } |       } | ||||||
| 
 |  | ||||||
|       if (activePanel === "watch") { |  | ||||||
|         if (this.workflow.currCrawlId) { |  | ||||||
|           this.fetchCurrentCrawl(); |  | ||||||
|         } else { |  | ||||||
|           activePanel = "artifacts"; |  | ||||||
|         } |  | ||||||
|       } |  | ||||||
| 
 |  | ||||||
|       this.activePanel = activePanel; |  | ||||||
|     } catch (e: any) { |     } catch (e: any) { | ||||||
|       this.notify({ |       this.notify({ | ||||||
|         message: |         message: | ||||||
| @ -249,6 +264,31 @@ export class WorkflowDetail extends LiteElement { | |||||||
|           </div>` |           </div>` | ||||||
|         )} |         )} | ||||||
|       </div> |       </div> | ||||||
|  | 
 | ||||||
|  |       <btrix-dialog | ||||||
|  |         label=${msg("Cancel Crawl?")} | ||||||
|  |         ?open=${this.isAttemptCancel} | ||||||
|  |         @sl-request-close=${() => (this.isAttemptCancel = false)} | ||||||
|  |       > | ||||||
|  |         ${msg( | ||||||
|  |           "Canceling will discard all pages crawled. Are you sure you want to discard them?" | ||||||
|  |         )} | ||||||
|  |         <div slot="footer" class="flex justify-between"> | ||||||
|  |           <sl-button size="small" @click=${() => (this.isAttemptCancel = false)} | ||||||
|  |             >Keep Crawling</sl-button | ||||||
|  |           > | ||||||
|  |           <sl-button | ||||||
|  |             size="small" | ||||||
|  |             variant="primary" | ||||||
|  |             ?loading=${this.isCancelingOrStoppingCrawl} | ||||||
|  |             @click=${async () => { | ||||||
|  |               await this.cancel(); | ||||||
|  |               this.isAttemptCancel = false; | ||||||
|  |             }} | ||||||
|  |             >Cancel & Discard Crawl</sl-button | ||||||
|  |           > | ||||||
|  |         </div> | ||||||
|  |       </btrix-dialog> | ||||||
|     `;
 |     `;
 | ||||||
|   } |   } | ||||||
| 
 | 
 | ||||||
| @ -313,26 +353,71 @@ export class WorkflowDetail extends LiteElement { | |||||||
|     if (!this.activePanel) return; |     if (!this.activePanel) return; | ||||||
|     if (this.activePanel === "artifacts") { |     if (this.activePanel === "artifacts") { | ||||||
|       return html`<h3>
 |       return html`<h3>
 | ||||||
|         ${this.workflow?.crawlCount === 1 |           ${this.workflow?.crawlCount === 1 | ||||||
|           ? msg(str`${this.workflow?.crawlCount} Crawl`) |             ? msg(str`${this.workflow?.crawlCount} Crawl`) | ||||||
|           : msg(str`${this.workflow?.crawlCount} Crawls`)} |             : msg(str`${this.workflow?.crawlCount} Crawls`)} | ||||||
|       </h3>`;
 |         </h3> | ||||||
|  |         <sl-button | ||||||
|  |           size="small" | ||||||
|  |           @click=${() => this.runNow()} | ||||||
|  |           ?disabled=${this.workflow?.currCrawlId} | ||||||
|  |         > | ||||||
|  |           <sl-icon name="play" slot="prefix"></sl-icon> | ||||||
|  |           <span>${msg("Run")}</span> | ||||||
|  |         </sl-button>`; | ||||||
|     } |     } | ||||||
| 
 |     if (this.activePanel === "settings") { | ||||||
|     if (this.activePanel === "watch") { |  | ||||||
|       return html` <h3>${this.tabLabels[this.activePanel]}</h3>
 |       return html` <h3>${this.tabLabels[this.activePanel]}</h3>
 | ||||||
|         <sl-button |         <sl-button | ||||||
|           size="small" |           size="small" | ||||||
|           ?disabled=${this.workflow?.currCrawlState !== "running"} |           @click=${() => | ||||||
|           @click=${() => { |             this.navTo( | ||||||
|             this.openDialogName = "scale"; |               `/orgs/${this.workflow?.oid}/workflows/crawl/${this.workflow?.id}?edit` | ||||||
|             this.isDialogVisible = true; |             )} | ||||||
|           }} |  | ||||||
|         > |         > | ||||||
|           <sl-icon name="plus-slash-minus" slot="prefix"></sl-icon> |           <sl-icon name="gear" slot="prefix"></sl-icon> | ||||||
|           <span> ${msg("Crawler Instances")} </span> |           <span>${msg("Edit")}</span> | ||||||
|         </sl-button>`; |         </sl-button>`; | ||||||
|     } |     } | ||||||
|  |     if (this.activePanel === "watch") { | ||||||
|  |       return html` <h3>${this.tabLabels[this.activePanel]}</h3>
 | ||||||
|  |         <sl-button-group> | ||||||
|  |           <sl-button | ||||||
|  |             size="small" | ||||||
|  |             ?disabled=${this.workflow?.currCrawlState !== "running"} | ||||||
|  |             @click=${() => { | ||||||
|  |               this.openDialogName = "scale"; | ||||||
|  |               this.isDialogVisible = true; | ||||||
|  |             }} | ||||||
|  |           > | ||||||
|  |             <sl-icon name="plus-slash-minus" slot="prefix"></sl-icon> | ||||||
|  |             <span> ${msg("Edit Instances")} </span> | ||||||
|  |           </sl-button> | ||||||
|  |           <sl-button | ||||||
|  |             size="small" | ||||||
|  |             @click=${() => this.stop()} | ||||||
|  |             ?disabled=${!this.workflow?.currCrawlId || | ||||||
|  |             this.isCancelingOrStoppingCrawl || | ||||||
|  |             this.workflow?.currCrawlState === "stopping"} | ||||||
|  |           > | ||||||
|  |             <sl-icon name="dash-circle" slot="prefix"></sl-icon> | ||||||
|  |             <span>${msg("Stop")}</span> | ||||||
|  |           </sl-button> | ||||||
|  |           <sl-button | ||||||
|  |             size="small" | ||||||
|  |             @click=${() => this.requestCancelCrawl()} | ||||||
|  |             ?disabled=${!this.workflow?.currCrawlId || | ||||||
|  |             this.isCancelingOrStoppingCrawl} | ||||||
|  |           > | ||||||
|  |             <sl-icon | ||||||
|  |               name="x-octagon" | ||||||
|  |               slot="prefix" | ||||||
|  |               class="text-danger" | ||||||
|  |             ></sl-icon> | ||||||
|  |             <span class="text-danger">${msg("Cancel")}</span> | ||||||
|  |           </sl-button> | ||||||
|  |         </sl-button-group>`; | ||||||
|  |     } | ||||||
| 
 | 
 | ||||||
|     return html`<h3>${this.tabLabels[this.activePanel]}</h3>`; |     return html`<h3>${this.tabLabels[this.activePanel]}</h3>`; | ||||||
|   } |   } | ||||||
| @ -346,7 +431,6 @@ export class WorkflowDetail extends LiteElement { | |||||||
|         class="block font-medium rounded-sm mb-2 mr-2 p-2 transition-all ${isActive |         class="block font-medium rounded-sm mb-2 mr-2 p-2 transition-all ${isActive | ||||||
|           ? "text-blue-600 bg-blue-50 shadow-sm" |           ? "text-blue-600 bg-blue-50 shadow-sm" | ||||||
|           : "text-neutral-600 hover:bg-neutral-50"}" |           : "text-neutral-600 hover:bg-neutral-50"}" | ||||||
|         @click=${() => (this.activePanel = tabName)} |  | ||||||
|         aria-selected=${isActive} |         aria-selected=${isActive} | ||||||
|       > |       > | ||||||
|         ${this.tabLabels[tabName]} |         ${this.tabLabels[tabName]} | ||||||
| @ -384,7 +468,7 @@ export class WorkflowDetail extends LiteElement { | |||||||
|     const workflow = this.workflow; |     const workflow = this.workflow; | ||||||
| 
 | 
 | ||||||
|     return html` |     return html` | ||||||
|       <sl-dropdown placement="bottom-end" distance="4"> |       <sl-dropdown placement="bottom-end" distance="4" hoist> | ||||||
|         <sl-button slot="trigger" size="small" caret |         <sl-button slot="trigger" size="small" caret | ||||||
|           >${msg("Actions")}</sl-button |           >${msg("Actions")}</sl-button | ||||||
|         > |         > | ||||||
| @ -396,14 +480,16 @@ export class WorkflowDetail extends LiteElement { | |||||||
|             () => html` |             () => html` | ||||||
|               <sl-menu-item |               <sl-menu-item | ||||||
|                 @click=${() => this.stop()} |                 @click=${() => this.stop()} | ||||||
|                 ?disabled=${workflow.currCrawlState === "stopping"} |                 ?disabled=${workflow.currCrawlState === "stopping" || | ||||||
|  |                 this.isCancelingOrStoppingCrawl} | ||||||
|               > |               > | ||||||
|                 <sl-icon name="dash-circle" slot="prefix"></sl-icon> |                 <sl-icon name="dash-circle" slot="prefix"></sl-icon> | ||||||
|                 ${msg("Stop Crawl")} |                 ${msg("Stop Crawl")} | ||||||
|               </sl-menu-item> |               </sl-menu-item> | ||||||
|               <sl-menu-item |               <sl-menu-item | ||||||
|                 style="--sl-color-neutral-700: var(--danger)" |                 style="--sl-color-neutral-700: var(--danger)" | ||||||
|                 @click=${() => this.cancel()} |                 ?disabled=${this.isCancelingOrStoppingCrawl} | ||||||
|  |                 @click=${() => this.requestCancelCrawl()} | ||||||
|               > |               > | ||||||
|                 <sl-icon name="x-octagon" slot="prefix"></sl-icon> |                 <sl-icon name="x-octagon" slot="prefix"></sl-icon> | ||||||
|                 ${msg("Cancel & Discard Crawl")} |                 ${msg("Cancel & Discard Crawl")} | ||||||
| @ -624,8 +710,15 @@ export class WorkflowDetail extends LiteElement { | |||||||
|                       hour="2-digit" |                       hour="2-digit" | ||||||
|                       minute="2-digit" |                       minute="2-digit" | ||||||
|                     ></sl-format-date> |                     ></sl-format-date> | ||||||
|                     <!-- Hide menu trigger: --> |                     <sl-menu slot="menu"> | ||||||
|                     <div slot="menuTrigger" role="none"></div> |                       <sl-menu-item | ||||||
|  |                         style="--sl-color-neutral-700: var(--danger)" | ||||||
|  |                         @click=${() => this.deleteCrawl(crawl)} | ||||||
|  |                       > | ||||||
|  |                         <sl-icon name="trash" slot="prefix"></sl-icon> | ||||||
|  |                         ${msg("Delete Crawl")} | ||||||
|  |                       </sl-menu-item> | ||||||
|  |                     </sl-menu> | ||||||
|                   </btrix-crawl-list-item> |                   </btrix-crawl-list-item> | ||||||
|                 ` |                 ` | ||||||
|               ), |               ), | ||||||
| @ -887,6 +980,15 @@ export class WorkflowDetail extends LiteElement { | |||||||
|     this.fetchWorkflow(); |     this.fetchWorkflow(); | ||||||
|   } |   } | ||||||
| 
 | 
 | ||||||
|  |   private requestCancelCrawl() { | ||||||
|  |     const pagesDone = this.currentCrawl?.stats?.done; | ||||||
|  |     if (pagesDone && +pagesDone > 0) { | ||||||
|  |       this.isAttemptCancel = true; | ||||||
|  |     } else { | ||||||
|  |       this.cancel(); | ||||||
|  |     } | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|   private async scale(value: Crawl["scale"]) { |   private async scale(value: Crawl["scale"]) { | ||||||
|     if (!this.workflow?.currCrawlId) return; |     if (!this.workflow?.currCrawlId) return; | ||||||
|     this.isSubmittingUpdate = true; |     this.isSubmittingUpdate = true; | ||||||
| @ -1081,7 +1183,10 @@ export class WorkflowDetail extends LiteElement { | |||||||
| 
 | 
 | ||||||
|   private async cancel() { |   private async cancel() { | ||||||
|     if (!this.workflow?.currCrawlId) return; |     if (!this.workflow?.currCrawlId) return; | ||||||
|     if (window.confirm(msg("Are you sure you want to cancel the crawl?"))) { | 
 | ||||||
|  |     this.isCancelingOrStoppingCrawl = true; | ||||||
|  | 
 | ||||||
|  |     try { | ||||||
|       const data = await this.apiFetch( |       const data = await this.apiFetch( | ||||||
|         `/orgs/${this.orgId}/crawls/${this.workflow.currCrawlId}/cancel`, |         `/orgs/${this.orgId}/crawls/${this.workflow.currCrawlId}/cancel`, | ||||||
|         this.authState!, |         this.authState!, | ||||||
| @ -1092,18 +1197,25 @@ export class WorkflowDetail extends LiteElement { | |||||||
|       if (data.success === true) { |       if (data.success === true) { | ||||||
|         this.fetchWorkflow(); |         this.fetchWorkflow(); | ||||||
|       } else { |       } else { | ||||||
|         this.notify({ |         throw data; | ||||||
|           message: msg("Something went wrong, couldn't cancel crawl."), |  | ||||||
|           variant: "danger", |  | ||||||
|           icon: "exclamation-octagon", |  | ||||||
|         }); |  | ||||||
|       } |       } | ||||||
|  |     } catch { | ||||||
|  |       this.notify({ | ||||||
|  |         message: msg("Something went wrong, couldn't cancel crawl."), | ||||||
|  |         variant: "danger", | ||||||
|  |         icon: "exclamation-octagon", | ||||||
|  |       }); | ||||||
|     } |     } | ||||||
|  | 
 | ||||||
|  |     this.isCancelingOrStoppingCrawl = false; | ||||||
|   } |   } | ||||||
| 
 | 
 | ||||||
|   private async stop() { |   private async stop() { | ||||||
|     if (!this.workflow?.currCrawlId) return; |     if (!this.workflow?.currCrawlId) return; | ||||||
|     if (window.confirm(msg("Are you sure you want to stop the crawl?"))) { | 
 | ||||||
|  |     this.isCancelingOrStoppingCrawl = true; | ||||||
|  | 
 | ||||||
|  |     try { | ||||||
|       const data = await this.apiFetch( |       const data = await this.apiFetch( | ||||||
|         `/orgs/${this.orgId}/crawls/${this.workflow.currCrawlId}/stop`, |         `/orgs/${this.orgId}/crawls/${this.workflow.currCrawlId}/stop`, | ||||||
|         this.authState!, |         this.authState!, | ||||||
| @ -1114,13 +1226,17 @@ export class WorkflowDetail extends LiteElement { | |||||||
|       if (data.success === true) { |       if (data.success === true) { | ||||||
|         this.fetchWorkflow(); |         this.fetchWorkflow(); | ||||||
|       } else { |       } else { | ||||||
|         this.notify({ |         throw data; | ||||||
|           message: msg("Something went wrong, couldn't stop crawl."), |  | ||||||
|           variant: "danger", |  | ||||||
|           icon: "exclamation-octagon", |  | ||||||
|         }); |  | ||||||
|       } |       } | ||||||
|  |     } catch { | ||||||
|  |       this.notify({ | ||||||
|  |         message: msg("Something went wrong, couldn't stop crawl."), | ||||||
|  |         variant: "danger", | ||||||
|  |         icon: "exclamation-octagon", | ||||||
|  |       }); | ||||||
|     } |     } | ||||||
|  | 
 | ||||||
|  |     this.isCancelingOrStoppingCrawl = false; | ||||||
|   } |   } | ||||||
| 
 | 
 | ||||||
|   private async runNow(): Promise<void> { |   private async runNow(): Promise<void> { | ||||||
| @ -1132,7 +1248,6 @@ export class WorkflowDetail extends LiteElement { | |||||||
|           method: "POST", |           method: "POST", | ||||||
|         } |         } | ||||||
|       ); |       ); | ||||||
|       this.activePanel = "watch"; |  | ||||||
|       this.fetchWorkflow(); |       this.fetchWorkflow(); | ||||||
| 
 | 
 | ||||||
|       this.notify({ |       this.notify({ | ||||||
| @ -1141,8 +1256,8 @@ export class WorkflowDetail extends LiteElement { | |||||||
|             <br /> |             <br /> | ||||||
|             <a |             <a | ||||||
|               class="underline hover:no-underline" |               class="underline hover:no-underline" | ||||||
|               href="/orgs/${this.orgId}/workflows/crawl/${this.workflowId}" |               href="/orgs/${this.orgId}/workflows/crawl/${this | ||||||
|               @click="${this.navLink.bind(this)}" |                 .workflowId}#watch" | ||||||
|               >Watch crawl</a |               >Watch crawl</a | ||||||
|             >` |             >` | ||||||
|         ), |         ), | ||||||
| @ -1158,6 +1273,37 @@ export class WorkflowDetail extends LiteElement { | |||||||
|       }); |       }); | ||||||
|     } |     } | ||||||
|   } |   } | ||||||
|  | 
 | ||||||
|  |   private async deleteCrawl(crawl: Crawl) { | ||||||
|  |     try { | ||||||
|  |       const data = await this.apiFetch( | ||||||
|  |         `/orgs/${crawl.oid}/crawls/delete`, | ||||||
|  |         this.authState!, | ||||||
|  |         { | ||||||
|  |           method: "POST", | ||||||
|  |           body: JSON.stringify({ | ||||||
|  |             crawl_ids: [crawl.id], | ||||||
|  |           }), | ||||||
|  |         } | ||||||
|  |       ); | ||||||
|  | 
 | ||||||
|  |       this.crawls = this.crawls!.filter((c) => c.id !== crawl.id); | ||||||
|  |       this.notify({ | ||||||
|  |         message: msg(`Successfully deleted crawl`), | ||||||
|  |         variant: "success", | ||||||
|  |         icon: "check2-circle", | ||||||
|  |       }); | ||||||
|  |       this.fetchCrawls(); | ||||||
|  |     } catch (e: any) { | ||||||
|  |       this.notify({ | ||||||
|  |         message: | ||||||
|  |           (e.isApiError && e.message) || | ||||||
|  |           msg("Sorry, couldn't run crawl at this time."), | ||||||
|  |         variant: "danger", | ||||||
|  |         icon: "exclamation-octagon", | ||||||
|  |       }); | ||||||
|  |     } | ||||||
|  |   } | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| customElements.define("btrix-workflow-detail", WorkflowDetail); | customElements.define("btrix-workflow-detail", WorkflowDetail); | ||||||
|  | |||||||
| @ -227,40 +227,40 @@ export class WorkflowsList extends LiteElement { | |||||||
| 
 | 
 | ||||||
|         <div class="flex items-center w-full md:w-fit"> |         <div class="flex items-center w-full md:w-fit"> | ||||||
|           <div class="whitespace-nowrap text-sm text-0-500 mr-2"> |           <div class="whitespace-nowrap text-sm text-0-500 mr-2"> | ||||||
|               ${msg("Sort by:")} |             ${msg("Sort by:")} | ||||||
|             </div> |  | ||||||
|             <sl-select |  | ||||||
|               class="flex-1 md:min-w-[9.2rem]" |  | ||||||
|               size="small" |  | ||||||
|               pill |  | ||||||
|               value=${this.orderBy.field} |  | ||||||
|               @sl-select=${(e: any) => { |  | ||||||
|                 const field = e.detail.item.value as SortField; |  | ||||||
|                 this.orderBy = { |  | ||||||
|                   field: field, |  | ||||||
|                   direction: |  | ||||||
|                     sortableFields[field].defaultDirection || |  | ||||||
|                     this.orderBy.direction, |  | ||||||
|                 }; |  | ||||||
|               }} |  | ||||||
|             > |  | ||||||
|               ${Object.entries(sortableFields).map( |  | ||||||
|                 ([value, { label }]) => html` |  | ||||||
|                   <sl-menu-item value=${value}>${label}</sl-menu-item> |  | ||||||
|                 ` |  | ||||||
|               )} |  | ||||||
|             </sl-select> |  | ||||||
|             <sl-icon-button |  | ||||||
|               name="arrow-down-up" |  | ||||||
|               label=${msg("Reverse sort")} |  | ||||||
|               @click=${() => { |  | ||||||
|                 this.orderBy = { |  | ||||||
|                   ...this.orderBy, |  | ||||||
|                   direction: this.orderBy.direction === "asc" ? "desc" : "asc", |  | ||||||
|                 }; |  | ||||||
|               }} |  | ||||||
|             ></sl-icon-button> |  | ||||||
|           </div> |           </div> | ||||||
|  |           <sl-select | ||||||
|  |             class="flex-1 md:min-w-[9.2rem]" | ||||||
|  |             size="small" | ||||||
|  |             pill | ||||||
|  |             value=${this.orderBy.field} | ||||||
|  |             @sl-select=${(e: any) => { | ||||||
|  |               const field = e.detail.item.value as SortField; | ||||||
|  |               this.orderBy = { | ||||||
|  |                 field: field, | ||||||
|  |                 direction: | ||||||
|  |                   sortableFields[field].defaultDirection || | ||||||
|  |                   this.orderBy.direction, | ||||||
|  |               }; | ||||||
|  |             }} | ||||||
|  |           > | ||||||
|  |             ${Object.entries(sortableFields).map( | ||||||
|  |               ([value, { label }]) => html` | ||||||
|  |                 <sl-menu-item value=${value}>${label}</sl-menu-item> | ||||||
|  |               ` | ||||||
|  |             )} | ||||||
|  |           </sl-select> | ||||||
|  |           <sl-icon-button | ||||||
|  |             name="arrow-down-up" | ||||||
|  |             label=${msg("Reverse sort")} | ||||||
|  |             @click=${() => { | ||||||
|  |               this.orderBy = { | ||||||
|  |                 ...this.orderBy, | ||||||
|  |                 direction: this.orderBy.direction === "asc" ? "desc" : "asc", | ||||||
|  |               }; | ||||||
|  |             }} | ||||||
|  |           ></sl-icon-button> | ||||||
|  |         </div> | ||||||
|       </div> |       </div> | ||||||
| 
 | 
 | ||||||
|       <div class="flex flex-wrap items-center justify-between"> |       <div class="flex flex-wrap items-center justify-between"> | ||||||
| @ -298,7 +298,9 @@ export class WorkflowsList extends LiteElement { | |||||||
|         </div> |         </div> | ||||||
|         <div class="flex items-center justify-end"> |         <div class="flex items-center justify-end"> | ||||||
|           <label> |           <label> | ||||||
|             <span class="text-neutral-500 mr-1 text-xs">${msg("Show Only Mine")}</span> |             <span class="text-neutral-500 mr-1 text-xs" | ||||||
|  |               >${msg("Show Only Mine")}</span | ||||||
|  |             > | ||||||
|             <sl-switch |             <sl-switch | ||||||
|               @sl-change=${(e: CustomEvent) => |               @sl-change=${(e: CustomEvent) => | ||||||
|                 (this.filterByCurrentUser = (e.target as SlCheckbox).checked)} |                 (this.filterByCurrentUser = (e.target as SlCheckbox).checked)} | ||||||
| @ -655,7 +657,7 @@ export class WorkflowsList extends LiteElement { | |||||||
|             <br /> |             <br /> | ||||||
|             <a |             <a | ||||||
|               class="underline hover:no-underline" |               class="underline hover:no-underline" | ||||||
|               href="/orgs/${this.orgId}/workflows/crawl/${workflow.id}" |               href="/orgs/${this.orgId}/workflows/crawl/${workflow.id}#watch" | ||||||
|               @click=${this.navLink.bind(this)} |               @click=${this.navLink.bind(this)} | ||||||
|               >Watch crawl</a |               >Watch crawl</a | ||||||
|             >` |             >` | ||||||
|  | |||||||
		Loading…
	
		Reference in New Issue
	
	Block a user