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 { customElement, property } from "lit/decorators.js";
|
||||||
|
|
||||||
import { TailwindElement } from "@/classes/TailwindElement";
|
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 seededCrawlSvg from "~assets/images/new-crawl-config_Seeded-Crawl.svg";
|
||||||
import urlListSvg from "~assets/images/new-crawl-config_URL-List.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
|
* @event select-job-type SelectJobTypeEvent
|
||||||
@ -33,8 +35,8 @@ export class NewWorkflowDialog extends TailwindElement {
|
|||||||
@click=${() => {
|
@click=${() => {
|
||||||
this.dispatchEvent(
|
this.dispatchEvent(
|
||||||
new CustomEvent("select-job-type", {
|
new CustomEvent("select-job-type", {
|
||||||
detail: "page-list",
|
detail: WorkflowScopeType.PageList,
|
||||||
}) as SelectJobTypeEvent,
|
}),
|
||||||
);
|
);
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
@ -63,7 +65,7 @@ export class NewWorkflowDialog extends TailwindElement {
|
|||||||
@click=${() => {
|
@click=${() => {
|
||||||
this.dispatchEvent(
|
this.dispatchEvent(
|
||||||
new CustomEvent("select-job-type", {
|
new CustomEvent("select-job-type", {
|
||||||
detail: "prefix",
|
detail: WorkflowScopeType.Prefix,
|
||||||
}) as SelectJobTypeEvent,
|
}) as SelectJobTypeEvent,
|
||||||
);
|
);
|
||||||
}}
|
}}
|
||||||
|
@ -67,6 +67,7 @@ import {
|
|||||||
import { maxLengthValidator } from "@/utils/form";
|
import { maxLengthValidator } from "@/utils/form";
|
||||||
import { getLocale } from "@/utils/localization";
|
import { getLocale } from "@/utils/localization";
|
||||||
import { isArchivingDisabled } from "@/utils/orgs";
|
import { isArchivingDisabled } from "@/utils/orgs";
|
||||||
|
import { AppStateService } from "@/utils/state";
|
||||||
import { regexEscape } from "@/utils/string";
|
import { regexEscape } from "@/utils/string";
|
||||||
import { tw } from "@/utils/tailwind";
|
import { tw } from "@/utils/tailwind";
|
||||||
import {
|
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);
|
this.updateFormState(formState);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -539,6 +539,13 @@ export class Org extends LiteElement {
|
|||||||
@select-new-dialog=${this.onSelectNewDialog}
|
@select-new-dialog=${this.onSelectNewDialog}
|
||||||
@select-job-type=${(e: SelectJobTypeEvent) => {
|
@select-job-type=${(e: SelectJobTypeEvent) => {
|
||||||
this.openDialogName = undefined;
|
this.openDialogName = undefined;
|
||||||
|
|
||||||
|
if (e.detail !== this.appState.userPreferences?.newWorkflowScopeType) {
|
||||||
|
AppStateService.partialUpdateUserPreferences({
|
||||||
|
newWorkflowScopeType: e.detail,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
this.navTo(`${this.orgBasePath}/workflows/new`, {
|
this.navTo(`${this.orgBasePath}/workflows/new`, {
|
||||||
scopeType: e.detail,
|
scopeType: e.detail,
|
||||||
});
|
});
|
||||||
|
@ -219,9 +219,12 @@ export class WorkflowsList extends LiteElement {
|
|||||||
<sl-button
|
<sl-button
|
||||||
variant="primary"
|
variant="primary"
|
||||||
size="small"
|
size="small"
|
||||||
href="${this.orgBasePath}/workflows/new"
|
|
||||||
?disabled=${this.org?.readOnly}
|
?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>
|
<sl-icon slot="prefix" name="plus-lg"></sl-icon>
|
||||||
${msg("New Workflow")}</sl-button
|
${msg("New Workflow")}</sl-button
|
||||||
|
@ -8,36 +8,10 @@ import { ScopeType, type Seed, type WorkflowParams } from "./types";
|
|||||||
|
|
||||||
import type { UserGuideEventMap } from "@/index";
|
import type { UserGuideEventMap } from "@/index";
|
||||||
import { pageNav, type Breadcrumb } from "@/layouts/pageHeader";
|
import { pageNav, type Breadcrumb } from "@/layouts/pageHeader";
|
||||||
|
import { WorkflowScopeType } from "@/types/workflow";
|
||||||
import LiteElement, { html } from "@/utils/LiteElement";
|
import LiteElement, { html } from "@/utils/LiteElement";
|
||||||
import type { FormState as WorkflowFormState } from "@/utils/workflow";
|
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:
|
* Usage:
|
||||||
* ```ts
|
* ```ts
|
||||||
@ -59,6 +33,35 @@ export class WorkflowsNew extends LiteElement {
|
|||||||
@property({ type: Object })
|
@property({ type: Object })
|
||||||
initialWorkflow?: WorkflowParams;
|
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() {
|
private renderBreadcrumbs() {
|
||||||
const breadcrumbs: Breadcrumb[] = [
|
const breadcrumbs: Breadcrumb[] = [
|
||||||
{
|
{
|
||||||
@ -103,7 +106,7 @@ export class WorkflowsNew extends LiteElement {
|
|||||||
</header>
|
</header>
|
||||||
${when(this.org, (org) => {
|
${when(this.org, (org) => {
|
||||||
const initialWorkflow = mergeDeep(
|
const initialWorkflow = mergeDeep(
|
||||||
defaultValue,
|
this.defaultNewWorkflow,
|
||||||
{
|
{
|
||||||
profileid: org.crawlingDefaults?.profileid,
|
profileid: org.crawlingDefaults?.profileid,
|
||||||
config: {
|
config: {
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
import { z } from "zod";
|
import { z } from "zod";
|
||||||
|
|
||||||
import { accessCodeSchema } from "./org";
|
import { accessCodeSchema } from "./org";
|
||||||
|
import { WorkflowScopeType } from "./workflow";
|
||||||
|
|
||||||
export const userOrgSchema = z.object({
|
export const userOrgSchema = z.object({
|
||||||
default: z.boolean().optional(),
|
default: z.boolean().optional(),
|
||||||
@ -44,3 +45,8 @@ export const userInfoSchema = z.object({
|
|||||||
orgs: z.array(userOrgSchema),
|
orgs: z.array(userOrgSchema),
|
||||||
});
|
});
|
||||||
export type UserInfo = z.infer<typeof userInfoSchema>;
|
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
|
* Store and access application-wide state
|
||||||
*/
|
*/
|
||||||
|
import { mergeDeep } from "immutable";
|
||||||
import { locked, options, transaction, use } from "lit-shared-state";
|
import { locked, options, transaction, use } from "lit-shared-state";
|
||||||
|
|
||||||
import { persist } from "./persist";
|
import { persist } from "./persist";
|
||||||
|
|
||||||
import { authSchema, type Auth } from "@/types/auth";
|
import { authSchema, type Auth } from "@/types/auth";
|
||||||
import type { OrgData } from "@/types/org";
|
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 type { AppSettings } from "@/utils/app";
|
||||||
import { isAdmin, isCrawler } from "@/utils/orgs";
|
import { isAdmin, isCrawler } from "@/utils/orgs";
|
||||||
|
|
||||||
@ -28,11 +35,15 @@ export function makeAppStateService() {
|
|||||||
@options(persist(window.sessionStorage))
|
@options(persist(window.sessionStorage))
|
||||||
userInfo: UserInfo | null = null;
|
userInfo: UserInfo | null = null;
|
||||||
|
|
||||||
|
@options(persist(window.localStorage))
|
||||||
|
userPreferences: UserPreferences | null = null;
|
||||||
|
|
||||||
// TODO persist here
|
// TODO persist here
|
||||||
auth: Auth | null = null;
|
auth: Auth | null = null;
|
||||||
|
|
||||||
// Store org slug in local storage in order to redirect
|
// Store org slug in local storage in order to redirect
|
||||||
// to the most recently visited org on next log in
|
// to the most recently visited org on next log in
|
||||||
|
// TODO move to `userPreferences`
|
||||||
@options(persist(window.localStorage))
|
@options(persist(window.localStorage))
|
||||||
orgSlug: string | null = null;
|
orgSlug: string | null = null;
|
||||||
|
|
||||||
@ -52,7 +63,7 @@ export function makeAppStateService() {
|
|||||||
)) ||
|
)) ||
|
||||||
null;
|
null;
|
||||||
|
|
||||||
if (!userOrg) {
|
if (appState.userInfo && !userOrg) {
|
||||||
console.debug("no user org matching slug in state");
|
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()
|
@transaction()
|
||||||
@unlock()
|
@unlock()
|
||||||
updateOrgSlug(orgSlug: AppState["orgSlug"]) {
|
updateOrgSlug(orgSlug: AppState["orgSlug"]) {
|
||||||
@ -152,6 +182,7 @@ export function makeAppStateService() {
|
|||||||
private _resetUser() {
|
private _resetUser() {
|
||||||
appState.auth = null;
|
appState.auth = null;
|
||||||
appState.userInfo = null;
|
appState.userInfo = null;
|
||||||
|
appState.userPreferences = null;
|
||||||
appState.orgSlug = null;
|
appState.orgSlug = null;
|
||||||
appState.org = undefined;
|
appState.org = undefined;
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user