import { property, state } from "lit/decorators.js"; import { msg, localized, str } from "@lit/localize"; import type { ExclusionRemoveEvent } from "./queue-exclusion-table"; import type { ExclusionAddEvent, ExclusionChangeEvent, } from "./queue-exclusion-form"; import type { SeedConfig } from "../pages/org/types"; import LiteElement, { html } from "../utils/LiteElement"; import type { AuthState } from "../utils/AuthService"; type URLs = string[]; type ResponseData = { total: number; matched: URLs; }; /** * Crawl queue exclusion editor * * Usage example: * ```ts * * * ``` * * @event on-success On successful edit */ @localized() export class ExclusionEditor extends LiteElement { @property({ type: Object }) authState?: AuthState; @property({ type: String }) orgId?: string; @property({ type: String }) crawlId?: string; @property({ type: Array }) config?: SeedConfig; @property({ type: Boolean }) isActiveCrawl = false; @state() private isSubmitting = false; @state() private exclusionFieldErrorMessage = ""; @state() /** `new RegExp` constructor string */ private regex: string = ""; @state() matchedURLs: URLs | null = null; @state() private isLoading = false; willUpdate(changedProperties: Map) { if ( changedProperties.has("authState") || changedProperties.has("orgId") || changedProperties.has("crawlId") || changedProperties.has("regex") ) { this.fetchQueueMatches(); } } render() { return html`
${this.renderTable()}
${this.isActiveCrawl && this.regex ? html`
${this.renderPending()}
` : ""} ${this.isActiveCrawl ? html`
${this.renderQueue()}
` : ""}
`; } private renderTable() { return html` ${this.config ? html` ` : html`
`} ${this.isActiveCrawl ? html`
` : ""} `; } private renderPending() { return html` `; } private renderQueue() { return html``; } private handleRegexChange(e: ExclusionChangeEvent) { const { value, valid } = e.detail; if (valid) { this.regex = value; } else { this.regex = ""; } } private async deleteExclusion(e: ExclusionRemoveEvent) { const { regex } = e.detail; try { const data = await this.apiFetch( `/orgs/${this.orgId}/crawls/${this.crawlId}/exclusions?regex=${regex}`, this.authState!, { method: "DELETE", } ); if (data.success) { this.notify({ message: msg(html`Removed exclusion: ${regex}`), variant: "success", icon: "check2-circle", }); this.dispatchEvent(new CustomEvent("on-success")); } else { throw data; } } catch (e: any) { this.notify({ message: e.message === "crawl_running_cant_deactivate" ? msg("Cannot remove exclusion when crawl is no longer running.") : msg("Sorry, couldn't remove exclusion at this time."), variant: "danger", icon: "exclamation-octagon", }); } } private async fetchQueueMatches() { if (!this.regex) { this.matchedURLs = null; return; } this.isLoading = true; try { const { matched } = await this.getQueueMatches(); this.matchedURLs = matched; } catch (e) { this.notify({ message: msg("Sorry, couldn't fetch pending exclusions at this time."), variant: "danger", icon: "exclamation-octagon", }); } this.isLoading = false; } private async getQueueMatches(): Promise { const data: ResponseData = await this.apiFetch( `/orgs/${this.orgId}/crawls/${this.crawlId}/queueMatchAll?regex=${this.regex}`, this.authState! ); return data; } private async handleAddRegex(e: ExclusionAddEvent) { this.isSubmitting = true; const { regex, onSuccess } = e.detail; try { const data = await this.apiFetch( `/orgs/${this.orgId}/crawls/${this.crawlId}/exclusions?regex=${regex}`, this.authState!, { method: "POST", } ); if (data.success) { this.notify({ message: msg("Exclusion added."), variant: "success", icon: "check2-circle", }); this.regex = ""; this.matchedURLs = null; await this.updateComplete; onSuccess(); this.dispatchEvent(new CustomEvent("on-success")); } else { throw data; } } catch (e: any) { if (e.message === "exclusion_already_exists") { this.exclusionFieldErrorMessage = msg("Exclusion already exists"); } else { this.notify({ message: msg("Sorry, couldn't add exclusion at this time."), variant: "danger", icon: "exclamation-octagon", }); } } this.isSubmitting = false; } }