Save new workflow scope type preference (#2099)
Resolves https://github.com/webrecorder/browsertrix/issues/2091 <!-- Fixes #issue_number --> ### Changes Saves scope type chosen from "+ New Workflow" dropdown to local storage, as well as from within workflow editor when creating a new workflow (but not editing an existing one).
This commit is contained in:
parent
bb6e703f6a
commit
22435ddaf9
@ -3,11 +3,13 @@ import { html } from "lit";
|
||||
import { customElement, property } from "lit/decorators.js";
|
||||
|
||||
import { TailwindElement } from "@/classes/TailwindElement";
|
||||
import type { FormState as WorkflowFormState } from "@/utils/workflow";
|
||||
import { WorkflowScopeType } from "@/types/workflow";
|
||||
import seededCrawlSvg from "~assets/images/new-crawl-config_Seeded-Crawl.svg";
|
||||
import urlListSvg from "~assets/images/new-crawl-config_URL-List.svg";
|
||||
|
||||
export type SelectJobTypeEvent = CustomEvent<WorkflowFormState["scopeType"]>;
|
||||
export type SelectJobTypeEvent = CustomEvent<
|
||||
(typeof WorkflowScopeType)[keyof typeof WorkflowScopeType]
|
||||
>;
|
||||
|
||||
/**
|
||||
* @event select-job-type SelectJobTypeEvent
|
||||
@ -33,8 +35,8 @@ export class NewWorkflowDialog extends TailwindElement {
|
||||
@click=${() => {
|
||||
this.dispatchEvent(
|
||||
new CustomEvent("select-job-type", {
|
||||
detail: "page-list",
|
||||
}) as SelectJobTypeEvent,
|
||||
detail: WorkflowScopeType.PageList,
|
||||
}),
|
||||
);
|
||||
}}
|
||||
>
|
||||
@ -63,7 +65,7 @@ export class NewWorkflowDialog extends TailwindElement {
|
||||
@click=${() => {
|
||||
this.dispatchEvent(
|
||||
new CustomEvent("select-job-type", {
|
||||
detail: "prefix",
|
||||
detail: WorkflowScopeType.Prefix,
|
||||
}) as SelectJobTypeEvent,
|
||||
);
|
||||
}}
|
||||
|
@ -67,6 +67,7 @@ import {
|
||||
import { maxLengthValidator } from "@/utils/form";
|
||||
import { getLocale } from "@/utils/localization";
|
||||
import { isArchivingDisabled } from "@/utils/orgs";
|
||||
import { AppStateService } from "@/utils/state";
|
||||
import { regexEscape } from "@/utils/string";
|
||||
import { tw } from "@/utils/tailwind";
|
||||
import {
|
||||
@ -1712,6 +1713,13 @@ https://archiveweb.page/images/${"logo.svg"}`}
|
||||
}
|
||||
}
|
||||
|
||||
if (!this.configId) {
|
||||
// Remember scope type for new workflows
|
||||
AppStateService.partialUpdateUserPreferences({
|
||||
newWorkflowScopeType: value,
|
||||
});
|
||||
}
|
||||
|
||||
this.updateFormState(formState);
|
||||
}
|
||||
|
||||
|
@ -539,6 +539,13 @@ export class Org extends LiteElement {
|
||||
@select-new-dialog=${this.onSelectNewDialog}
|
||||
@select-job-type=${(e: SelectJobTypeEvent) => {
|
||||
this.openDialogName = undefined;
|
||||
|
||||
if (e.detail !== this.appState.userPreferences?.newWorkflowScopeType) {
|
||||
AppStateService.partialUpdateUserPreferences({
|
||||
newWorkflowScopeType: e.detail,
|
||||
});
|
||||
}
|
||||
|
||||
this.navTo(`${this.orgBasePath}/workflows/new`, {
|
||||
scopeType: e.detail,
|
||||
});
|
||||
|
@ -219,9 +219,12 @@ export class WorkflowsList extends LiteElement {
|
||||
<sl-button
|
||||
variant="primary"
|
||||
size="small"
|
||||
href="${this.orgBasePath}/workflows/new"
|
||||
?disabled=${this.org?.readOnly}
|
||||
@click=${this.navLink}
|
||||
@click=${() =>
|
||||
this.navTo(`${this.orgBasePath}/workflows/new`, {
|
||||
scopeType:
|
||||
this.appState.userPreferences?.newWorkflowScopeType,
|
||||
})}
|
||||
>
|
||||
<sl-icon slot="prefix" name="plus-lg"></sl-icon>
|
||||
${msg("New Workflow")}</sl-button
|
||||
|
@ -8,36 +8,10 @@ import { ScopeType, type Seed, type WorkflowParams } from "./types";
|
||||
|
||||
import type { UserGuideEventMap } from "@/index";
|
||||
import { pageNav, type Breadcrumb } from "@/layouts/pageHeader";
|
||||
import { WorkflowScopeType } from "@/types/workflow";
|
||||
import LiteElement, { html } from "@/utils/LiteElement";
|
||||
import type { FormState as WorkflowFormState } from "@/utils/workflow";
|
||||
|
||||
const defaultValue = {
|
||||
name: "",
|
||||
description: null,
|
||||
profileid: null,
|
||||
schedule: "",
|
||||
config: {
|
||||
seeds: [],
|
||||
scopeType: ScopeType.Page,
|
||||
exclude: [],
|
||||
behaviorTimeout: null,
|
||||
pageLoadTimeout: null,
|
||||
pageExtraDelay: null,
|
||||
postLoadDelay: null,
|
||||
useSitemap: false,
|
||||
failOnFailedSeed: false,
|
||||
userAgent: null,
|
||||
},
|
||||
tags: [],
|
||||
crawlTimeout: null,
|
||||
maxCrawlSize: null,
|
||||
jobType: "custom",
|
||||
scale: 1,
|
||||
autoAddCollections: [],
|
||||
crawlerChannel: "default",
|
||||
proxyId: null,
|
||||
} as WorkflowParams;
|
||||
|
||||
/**
|
||||
* Usage:
|
||||
* ```ts
|
||||
@ -59,6 +33,35 @@ export class WorkflowsNew extends LiteElement {
|
||||
@property({ type: Object })
|
||||
initialWorkflow?: WorkflowParams;
|
||||
|
||||
private get defaultNewWorkflow(): WorkflowParams {
|
||||
return {
|
||||
name: "",
|
||||
description: null,
|
||||
profileid: null,
|
||||
schedule: "",
|
||||
config: {
|
||||
scopeType: (this.appState.userPreferences?.newWorkflowScopeType ||
|
||||
WorkflowScopeType.Page) as ScopeType,
|
||||
exclude: [],
|
||||
behaviorTimeout: null,
|
||||
pageLoadTimeout: null,
|
||||
pageExtraDelay: null,
|
||||
postLoadDelay: null,
|
||||
useSitemap: false,
|
||||
failOnFailedSeed: false,
|
||||
userAgent: null,
|
||||
},
|
||||
tags: [],
|
||||
crawlTimeout: null,
|
||||
maxCrawlSize: null,
|
||||
jobType: "custom",
|
||||
scale: 1,
|
||||
autoAddCollections: [],
|
||||
crawlerChannel: "default",
|
||||
proxyId: null,
|
||||
};
|
||||
}
|
||||
|
||||
private renderBreadcrumbs() {
|
||||
const breadcrumbs: Breadcrumb[] = [
|
||||
{
|
||||
@ -103,7 +106,7 @@ export class WorkflowsNew extends LiteElement {
|
||||
</header>
|
||||
${when(this.org, (org) => {
|
||||
const initialWorkflow = mergeDeep(
|
||||
defaultValue,
|
||||
this.defaultNewWorkflow,
|
||||
{
|
||||
profileid: org.crawlingDefaults?.profileid,
|
||||
config: {
|
||||
|
@ -1,6 +1,7 @@
|
||||
import { z } from "zod";
|
||||
|
||||
import { accessCodeSchema } from "./org";
|
||||
import { WorkflowScopeType } from "./workflow";
|
||||
|
||||
export const userOrgSchema = z.object({
|
||||
default: z.boolean().optional(),
|
||||
@ -44,3 +45,8 @@ export const userInfoSchema = z.object({
|
||||
orgs: z.array(userOrgSchema),
|
||||
});
|
||||
export type UserInfo = z.infer<typeof userInfoSchema>;
|
||||
|
||||
export const userPreferencesSchema = z.object({
|
||||
newWorkflowScopeType: z.nativeEnum(WorkflowScopeType).optional(),
|
||||
});
|
||||
export type UserPreferences = z.infer<typeof userPreferencesSchema>;
|
||||
|
@ -1,13 +1,20 @@
|
||||
/**
|
||||
* Store and access application-wide state
|
||||
*/
|
||||
import { mergeDeep } from "immutable";
|
||||
import { locked, options, transaction, use } from "lit-shared-state";
|
||||
|
||||
import { persist } from "./persist";
|
||||
|
||||
import { authSchema, type Auth } from "@/types/auth";
|
||||
import type { OrgData } from "@/types/org";
|
||||
import { userInfoSchema, type UserInfo, type UserOrg } from "@/types/user";
|
||||
import {
|
||||
userInfoSchema,
|
||||
userPreferencesSchema,
|
||||
type UserInfo,
|
||||
type UserOrg,
|
||||
type UserPreferences,
|
||||
} from "@/types/user";
|
||||
import type { AppSettings } from "@/utils/app";
|
||||
import { isAdmin, isCrawler } from "@/utils/orgs";
|
||||
|
||||
@ -28,11 +35,15 @@ export function makeAppStateService() {
|
||||
@options(persist(window.sessionStorage))
|
||||
userInfo: UserInfo | null = null;
|
||||
|
||||
@options(persist(window.localStorage))
|
||||
userPreferences: UserPreferences | null = null;
|
||||
|
||||
// TODO persist here
|
||||
auth: Auth | null = null;
|
||||
|
||||
// Store org slug in local storage in order to redirect
|
||||
// to the most recently visited org on next log in
|
||||
// TODO move to `userPreferences`
|
||||
@options(persist(window.localStorage))
|
||||
orgSlug: string | null = null;
|
||||
|
||||
@ -52,7 +63,7 @@ export function makeAppStateService() {
|
||||
)) ||
|
||||
null;
|
||||
|
||||
if (!userOrg) {
|
||||
if (appState.userInfo && !userOrg) {
|
||||
console.debug("no user org matching slug in state");
|
||||
}
|
||||
|
||||
@ -113,6 +124,25 @@ export function makeAppStateService() {
|
||||
}
|
||||
}
|
||||
|
||||
@transaction()
|
||||
@unlock()
|
||||
partialUpdateUserPreferences(
|
||||
userPreferences: Partial<AppState["userPreferences"]>,
|
||||
) {
|
||||
userPreferencesSchema.nullable().parse(userPreferences);
|
||||
|
||||
if (appState.userPreferences && userPreferences) {
|
||||
appState.userPreferences = mergeDeep(
|
||||
appState.userPreferences,
|
||||
userPreferences,
|
||||
);
|
||||
} else {
|
||||
appState.userPreferences = userPreferences;
|
||||
}
|
||||
|
||||
console.log("appState.userPreferences:", appState.userPreferences);
|
||||
}
|
||||
|
||||
@transaction()
|
||||
@unlock()
|
||||
updateOrgSlug(orgSlug: AppState["orgSlug"]) {
|
||||
@ -152,6 +182,7 @@ export function makeAppStateService() {
|
||||
private _resetUser() {
|
||||
appState.auth = null;
|
||||
appState.userInfo = null;
|
||||
appState.userPreferences = null;
|
||||
appState.orgSlug = null;
|
||||
appState.org = undefined;
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user