diff --git a/frontend/src/pages/org/workflow-detail.ts b/frontend/src/pages/org/workflow-detail.ts index 2181dc2b..8732ade7 100644 --- a/frontend/src/pages/org/workflow-detail.ts +++ b/frontend/src/pages/org/workflow-detail.ts @@ -27,6 +27,7 @@ const SECTIONS = ["artifacts", "watch", "settings"] as const; type Tab = (typeof SECTIONS)[number]; const DEFAULT_SECTION: Tab = "artifacts"; const POLL_INTERVAL_SECONDS = 10; +const ABORT_REASON_CANCLED = "canceled"; /** * Usage: @@ -52,7 +53,7 @@ export class WorkflowDetail extends LiteElement { isCrawler!: boolean; @property({ type: String }) - openDialogName?: "scale" | "exclusions"; + openDialogName?: "scale" | "exclusions" | "cancel" | "stop"; @state() private workflow?: Workflow; @@ -75,9 +76,6 @@ export class WorkflowDetail extends LiteElement { @state() private isDialogVisible: boolean = false; - @state() - private isAttemptCancel: boolean = false; - @state() private isCancelingOrStoppingCrawl: boolean = false; @@ -98,6 +96,10 @@ export class WorkflowDetail extends LiteElement { private isPanelHeaderVisible?: boolean; + // Use to cancel requests + private getWorkflowController: AbortController | null = null; + private getWorkflowPromise?: Promise; + private readonly jobTypeLabels: Record = { "url-list": msg("URL List"), "seed-crawl": msg("Seeded Crawl"), @@ -113,13 +115,6 @@ export class WorkflowDetail extends LiteElement { connectedCallback(): void { // Set initial active section and dialog based on URL #hash value this.getActivePanelFromHash(); - - if ( - this.openDialogName && - (this.openDialogName === "scale" || this.openDialogName === "exclusions") - ) { - this.isDialogVisible = true; - } super.connectedCallback(); window.addEventListener("hashchange", this.getActivePanelFromHash); } @@ -130,6 +125,15 @@ export class WorkflowDetail extends LiteElement { window.removeEventListener("hashchange", this.getActivePanelFromHash); } + firstUpdated() { + if ( + this.openDialogName && + (this.openDialogName === "scale" || this.openDialogName === "exclusions") + ) { + this.showDialog(); + } + } + willUpdate(changedProperties: Map) { if ( (changedProperties.has("workflowId") && this.workflowId) || @@ -189,7 +193,8 @@ export class WorkflowDetail extends LiteElement { this.isLoading = true; try { - this.workflow = await this.getWorkflow(); + this.getWorkflowPromise = this.getWorkflow(); + this.workflow = await this.getWorkflowPromise; if (this.workflow.currCrawlId) { this.fetchCurrentCrawl(); } @@ -214,6 +219,13 @@ export class WorkflowDetail extends LiteElement { } } + private cancelInProgressGetWorkflow() { + if (this.getWorkflowController) { + this.getWorkflowController.abort(ABORT_REASON_CANCLED); + this.getWorkflowController = null; + } + } + render() { if (this.isEditing && this.isCrawler) { return html` @@ -265,16 +277,48 @@ export class WorkflowDetail extends LiteElement { )} + (this.openDialogName = undefined)} + @sl-show=${this.showDialog} + @sl-after-hide=${() => (this.isDialogVisible = false)} + > + ${msg( + "Pages crawled so far will be saved and marked as incomplete. Are you sure you want to stop crawling?" + )} +
+ (this.openDialogName = undefined)} + >Keep Crawling + { + await this.stop(); + this.openDialogName = undefined; + }} + >Stop Crawling +
+
(this.isAttemptCancel = false)} + ?open=${this.openDialogName === "cancel"} + @sl-request-close=${() => (this.openDialogName = undefined)} + @sl-show=${this.showDialog} + @sl-after-hide=${() => (this.isDialogVisible = false)} > ${msg( "Canceling will discard all pages crawled. Are you sure you want to discard them?" )}
- (this.isAttemptCancel = false)} + (this.openDialogName = undefined)} >Keep Crawling { await this.cancel(); - this.isAttemptCancel = false; + this.openDialogName = undefined; }} >Cancel & Discard Crawl @@ -385,17 +429,14 @@ export class WorkflowDetail extends LiteElement { { - this.openDialogName = "scale"; - this.isDialogVisible = true; - }} + @click=${() => (this.openDialogName = "scale")} > ${msg("Edit Instances")} this.stop()} + @click=${() => (this.openDialogName = "stop")} ?disabled=${!this.workflow?.currCrawlId || this.isCancelingOrStoppingCrawl || this.workflow?.currCrawlStopping} @@ -405,7 +446,7 @@ export class WorkflowDetail extends LiteElement { this.requestCancelCrawl()} + @click=${() => (this.openDialogName = "cancel")} ?disabled=${!this.workflow?.currCrawlId || this.isCancelingOrStoppingCrawl} > @@ -479,7 +520,7 @@ export class WorkflowDetail extends LiteElement { // color without resetting the --sl-color-neutral-700 variable () => html` this.stop()} + @click=${() => (this.openDialogName = "stop")} ?disabled=${workflow.currCrawlStopping || this.isCancelingOrStoppingCrawl} > @@ -489,7 +530,7 @@ export class WorkflowDetail extends LiteElement { this.requestCancelCrawl()} + @click=${() => (this.openDialogName = "cancel")} > ${msg("Cancel & Discard Crawl")} @@ -509,20 +550,12 @@ export class WorkflowDetail extends LiteElement { workflow.currCrawlState === "running", () => html` - { - this.openDialogName = "scale"; - this.isDialogVisible = true; - }} - > + (this.openDialogName = "scale")}> ${msg("Edit Crawler Instances")} { - this.openDialogName = "exclusions"; - this.isDialogVisible = true; - }} + @click=${() => (this.openDialogName = "exclusions")} > ${msg("Edit Exclusions")} @@ -804,7 +837,11 @@ export class WorkflowDetail extends LiteElement { ${isStarting || isWaiting ? html`

- ${isStarting ? msg("Crawl starting...") : msg("Crawl waiting for available resources before it can start...")} + ${isStarting + ? msg("Crawl starting...") + : msg( + "Crawl waiting for available resources before it can start..." + )}

` : isActive(this.workflow.currCrawlState) @@ -838,11 +875,7 @@ export class WorkflowDetail extends LiteElement { label=${msg("Edit Crawler Instances")} ?open=${this.openDialogName === "scale"} @sl-request-close=${() => (this.openDialogName = undefined)} - @sl-show=${async () => { - await this.fetchCurrentCrawl(); - await this.updateComplete; - this.isDialogVisible = true; - }} + @sl-show=${this.showDialog} @sl-after-hide=${() => (this.isDialogVisible = false)} > ${this.isDialogVisible ? this.renderEditScale() : ""} @@ -869,10 +902,7 @@ export class WorkflowDetail extends LiteElement { { - this.openDialogName = "exclusions"; - this.isDialogVisible = true; - }} + @click=${() => (this.openDialogName = "exclusions")} > ${msg("Edit Exclusions")} @@ -895,7 +925,7 @@ export class WorkflowDetail extends LiteElement { ?open=${this.openDialogName === "exclusions"} style=${/* max-w-screen-lg: */ `--width: 1124px;`} @sl-request-close=${() => (this.openDialogName = undefined)} - @sl-show=${() => (this.isDialogVisible = true)} + @sl-show=${this.showDialog} @sl-after-hide=${() => (this.isDialogVisible = false)} > ${this.workflow && this.isDialogVisible @@ -950,7 +980,10 @@ export class WorkflowDetail extends LiteElement { this.scale(value)} + @click=${async () => { + await this.scale(value); + this.openDialogName = undefined; + }} ?disabled=${this.isSubmittingUpdate} >${label} @@ -978,19 +1011,15 @@ export class WorkflowDetail extends LiteElement { `; } + private showDialog = async () => { + await this.getWorkflowPromise; + this.isDialogVisible = true; + }; + private handleExclusionChange(e: CustomEvent) { 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"]) { if (!this.workflow?.currCrawlId) return; this.isSubmittingUpdate = true; @@ -1015,9 +1044,6 @@ export class WorkflowDetail extends LiteElement { } else { throw new Error("unhandled API response"); } - - this.openDialogName = undefined; - this.isDialogVisible = false; } catch { this.notify({ message: msg("Sorry, couldn't change crawl scale at this time."), @@ -1030,11 +1056,15 @@ export class WorkflowDetail extends LiteElement { } private async getWorkflow(): Promise { + this.getWorkflowController = new AbortController(); const data: Workflow = await this.apiFetch( `/orgs/${this.orgId}/crawlconfigs/${this.workflowId}`, - this.authState! + this.authState!, + { + signal: this.getWorkflowController.signal, + } ); - + this.getWorkflowController = null; return data; } @@ -1082,6 +1112,7 @@ export class WorkflowDetail extends LiteElement { private stopPoll() { window.clearTimeout(this.timerId); + this.cancelInProgressGetWorkflow(); } private async getCrawl(crawlId: Crawl["id"]): Promise {