diff --git a/frontend/src/pages/org/workflow-detail.ts b/frontend/src/pages/org/workflow-detail.ts index 504fde3b..03da1bbb 100644 --- a/frontend/src/pages/org/workflow-detail.ts +++ b/frontend/src/pages/org/workflow-detail.ts @@ -1449,7 +1449,6 @@ export class WorkflowDetail extends LiteElement { message: msg("Starting crawl."), variant: "success", icon: "check2-circle", - duration: 8000, }); } catch (e: any) { let message = msg("Sorry, couldn't run crawl at this time."); diff --git a/frontend/src/pages/org/workflow-editor.ts b/frontend/src/pages/org/workflow-editor.ts index 0e171805..22ad0c26 100644 --- a/frontend/src/pages/org/workflow-editor.ts +++ b/frontend/src/pages/org/workflow-editor.ts @@ -1,11 +1,13 @@ import type { LitElement, TemplateResult } from "lit"; import { html as staticHtml, unsafeStatic } from "lit/static-html.js"; import type { + SlChangeEvent, SlCheckbox, SlInput, SlRadio, SlRadioGroup, SlSelect, + SlSwitch, SlTextarea, } from "@shoelace-style/shoelace"; import { state, property, query, queryAsync } from "lit/decorators.js"; @@ -90,7 +92,7 @@ type FormState = { scale: WorkflowParams["scale"]; blockAds: WorkflowParams["config"]["blockAds"]; lang: WorkflowParams["config"]["lang"]; - scheduleType: "now" | "date" | "cron" | "none"; + scheduleType: "date" | "cron" | "none"; scheduleFrequency: "daily" | "weekly" | "monthly" | ""; scheduleDayOfMonth?: number; scheduleDayOfWeek?: number; @@ -165,7 +167,7 @@ const getDefaultFormState = (): FormState => ({ scale: 1, blockAds: true, lang: undefined, - scheduleType: "now", + scheduleType: "none", scheduleFrequency: "weekly", scheduleDayOfMonth: new Date().getDate(), scheduleDayOfWeek: new Date().getDay(), @@ -174,7 +176,7 @@ const getDefaultFormState = (): FormState => ({ minute: 0, period: "AM", }, - runNow: false, + runNow: true, jobName: "", browserProfile: null, tags: [], @@ -183,9 +185,6 @@ const getDefaultFormState = (): FormState => ({ autoscrollBehavior: true, }); const defaultProgressState = getDefaultProgressState(); -const orderedTabNames = STEPS.filter( - (stepName) => defaultProgressState.tabs[stepName as StepName] -) as StepName[]; function getLocalizedWeekDays() { const now = new Date(); @@ -300,7 +299,6 @@ export class CrawlConfigEditor extends LiteElement { FormState["scheduleType"], string > = { - now: msg("Run Immediately on Save"), date: msg("Run on a Specific Date & Time"), cron: msg("Run on a Recurring Basis"), none: msg("No Schedule"), @@ -473,11 +471,7 @@ export class CrawlConfigEditor extends LiteElement { period: hours > 11 ? "PM" : "AM", }; } else { - if (this.configId) { - formState.scheduleType = "none"; - } else { - formState.scheduleType = "now"; - } + formState.scheduleType = "none"; } if (this.initialWorkflow.tags?.length) { @@ -554,6 +548,14 @@ export class CrawlConfigEditor extends LiteElement { crawlMetadata: msg("Metadata"), confirmSettings: msg("Review Settings"), }; + let orderedTabNames = STEPS.filter( + (stepName) => defaultProgressState.tabs[stepName as StepName] + ) as StepName[]; + + if (this.configId) { + // Remove review tab + orderedTabNames = orderedTabNames.slice(0, -1); + } return html`

@@ -637,34 +643,31 @@ export class CrawlConfigEditor extends LiteElement { const isActive = tabName === this.progressState.activeTab; const isConfirmSettings = tabName === "confirmSettings"; const { error: isInvalid, completed } = this.progressState.tabs[tabName]; - const iconProps = { - name: "circle", - library: "default", - class: "text-neutral-400", - }; - if (isConfirmSettings) { - iconProps.name = "info-circle"; - iconProps.class = "text-base"; - } else { - if (isInvalid) { - iconProps.name = "exclamation-circle"; - iconProps.class = "text-danger"; - } else if (isActive) { - iconProps.name = "pencil-circle-dashed"; - iconProps.library = "app"; - iconProps.class = "text-base"; - } else if (completed) { - iconProps.name = "check-circle"; - } - } + let icon: TemplateResult = html``; - return html` - + if (!this.configId) { + const iconProps = { + name: "circle", + library: "default", + class: "text-neutral-400", + }; + if (isConfirmSettings) { + iconProps.name = "info-circle"; + iconProps.class = "text-base"; + } else { + if (isInvalid) { + iconProps.name = "exclamation-circle"; + iconProps.class = "text-danger"; + } else if (isActive) { + iconProps.name = "pencil-circle-dashed"; + iconProps.library = "app"; + iconProps.class = "text-base"; + } else if (completed) { + iconProps.name = "check-circle"; + } + } + + icon = html` - + `; + } + + return html` + + ${icon} + ${content} @@ -688,8 +706,13 @@ export class CrawlConfigEditor extends LiteElement { { isFirst = false, isLast = false } = {} ) { return html` -
-
+
+
${content} ${when(this.serverError, () => this.renderErrorAlert(this.serverError!) @@ -702,98 +725,144 @@ export class CrawlConfigEditor extends LiteElement { } private renderFooter({ isFirst = false, isLast = false }) { + if (this.configId) { + return html` +
+
${this.renderRunNowToggle()}
+ + + ${msg("Save Workflow")} + +
+ `; + } + + if (!this.configId) { + return html` +
+ ${this.renderSteppedFooterButtons({ isFirst, isLast })} +
+ `; + } + return html` -
- ${isFirst - ? html` - - - ${this.configId ? msg("Cancel") : msg("Start Over")} - - ` - : html` - - - ${msg("Previous Step")} - - `} +
${when( this.configId, () => html` -
- ${when( - !isLast, - () => html` - - - ${msg("Next")} - - ` - )} - - - ${msg("Save Changes")} - -
+
${this.renderRunNowToggle()}
+ + ${msg("Save Changes")} + `, - () => - isLast - ? html` - ${this.formState.scheduleType === "now" || - this.formState.runNow - ? msg("Save & Run Crawl") - : this.formState.scheduleType === "none" - ? msg("Save Workflow") - : msg("Save & Schedule Crawl")} - ` - : html` -
- - - ${msg("Next Step")} - - { - if (this.hasRequiredFields()) { - this.updateProgressState({ - activeTab: "confirmSettings", - }); - } else { - this.nextStep(); - } - }} - > - - ${msg("Review & Save")} - -
- ` + () => this.renderSteppedFooterButtons({ isFirst, isLast }) )}
`; } + private renderSteppedFooterButtons({ + isFirst, + isLast, + }: { + isFirst: boolean; + isLast: boolean; + }) { + if (isLast) { + return html` + + ${msg("Previous Step")} + + ${this.renderRunNowToggle()} + + ${msg("Save Workflow")} + `; + } + return html` + ${isFirst + ? html` + + + ${msg("Start Over")} + + ` + : html` + + + ${msg("Previous Step")} + + `} + + + ${msg("Next Step")} + + { + if (this.hasRequiredFields()) { + this.updateProgressState({ + activeTab: "confirmSettings", + }); + } else { + this.nextStep(); + } + }} + > + + ${msg("Review & Save")} + + `; + } + + private renderRunNowToggle() { + return html` + { + this.updateFormState( + { + runNow: (e.target as SlSwitch).checked, + }, + true + ); + }} + > + ${msg("Run on Save")} + + `; + } + private renderSectionHeading(content: TemplateResult | string) { return html` @@ -1505,24 +1574,23 @@ https://archiveweb.page/images/${"logo.svg"}`} return html` ${this.renderFormCol(html` this.updateFormState({ scheduleType: (e.target as SlRadio) .value as FormState["scheduleType"], - runNow: (e.target as SlRadio).value === "now", })} > - ${this.scheduleTypeLabels["now"]} - ${this.scheduleTypeLabels["cron"]} ${this.scheduleTypeLabels["none"]} + ${this.scheduleTypeLabels["cron"]} `)} ${this.renderHelpTextCol( - msg(`Should a crawl run immediately when setup is complete, on a set - day, or on a recurring schedule?`) + msg( + `Configure crawls to run every day, week, or month at a specified time.` + ) )} ${when(this.formState.scheduleType === "cron", this.renderScheduleCron)} `; @@ -1641,17 +1709,6 @@ https://archiveweb.page/images/${"logo.svg"}`} ${this.renderHelpTextCol( msg(`A crawl will run at this time in your current timezone.`) )} - ${this.renderFormCol(html` - ${msg("Also run a crawl immediately on save")} - `)} - ${this.renderHelpTextCol( - msg(`If checked, a crawl will run at the time specified above and also - once when setup is complete.`), - false - )} `; }; @@ -1908,6 +1965,9 @@ https://archiveweb.page/images/${"logo.svg"}`} private updateFormStateOnChange(e: Event) { const elem = e.target as SlTextarea | SlInput | SlCheckbox; const name = elem.name; + if (!this.formState.hasOwnProperty(name)) { + return; + } const tagName = elem.tagName.toLowerCase(); let value: any; switch (tagName) { @@ -1932,11 +1992,9 @@ https://archiveweb.page/images/${"logo.svg"}`} default: return; } - if (name in this.formState) { - this.updateFormState({ - [name]: value, - }); - } + this.updateFormState({ + [name]: value, + }); } private tabClickHandler = (step: StepName) => (e: MouseEvent) => { @@ -1967,6 +2025,11 @@ https://archiveweb.page/images/${"logo.svg"}`} const nextTab = STEPS[STEPS.indexOf(activeTab!) + 1] as StepName; this.updateProgressState({ activeTab: nextTab, + tabs: { + [activeTab]: { + completed: true, + }, + }, }); } } @@ -2048,13 +2111,13 @@ https://archiveweb.page/images/${"logo.svg"}`} if (crawlId && storageQuotaReached) { this.notify({ - title: msg("Workflow saved."), + title: msg("Workflow saved without starting crawl."), message: msg( - "Could not start crawl with new workflow settings due to storage quota." + "Could not run crawl with new workflow settings due to storage quota." ), variant: "warning", - icon: "exclamation-triangle", - duration: 8000, + icon: "exclamation-circle", + duration: 12000, }); } else { let message = msg("Workflow created."); @@ -2078,12 +2141,24 @@ https://archiveweb.page/images/${"logo.svg"}`} ); } catch (e: any) { if (e?.isApiError) { - const isConfigError = ({ loc }: any) => - loc.some((v: string) => v === "config"); - if (e.details && e.details.some(isConfigError)) { - this.serverError = this.formatConfigServerError(e.details); + if (e.details === "crawl_already_running") { + this.notify({ + title: msg("Workflow saved without starting crawl."), + message: msg( + "Could not run crawl with new workflow settings due to already running crawl." + ), + variant: "warning", + icon: "exclamation-circle", + duration: 12000, + }); } else { - this.serverError = e.message; + const isConfigError = ({ loc }: any) => + loc.some((v: string) => v === "config"); + if (Array.isArray(e.details) && e.details.some(isConfigError)) { + this.serverError = this.formatConfigServerError(e.details); + } else { + this.serverError = e.message; + } } } else { this.serverError = msg("Something unexpected went wrong"); @@ -2154,7 +2229,7 @@ https://archiveweb.page/images/${"logo.svg"}`} description: this.formState.description, scale: this.formState.scale, profileid: this.formState.browserProfile?.id || "", - runNow: this.formState.runNow || this.formState.scheduleType === "now", + runNow: this.formState.runNow, schedule: this.formState.scheduleType === "cron" ? this.utcSchedule : "", crawlTimeout: this.formState.crawlTimeoutMinutes * 60, maxCrawlSize: this.formState.maxCrawlSizeGB * BYTES_PER_GB,