+
${template}
`;
@@ -367,6 +367,7 @@ export class App extends LiteElement {
case "archive":
case "archiveAddMember":
+ case "archiveNewResourceTab":
return appLayout(html`
`);
case "accountSettings":
@@ -462,6 +464,8 @@ export class App extends LiteElement {
}
onNavigateTo(event: NavigateEvent) {
+ event.stopPropagation();
+
this.navigate(event.detail);
}
diff --git a/frontend/src/pages/archive/crawl-templates.ts b/frontend/src/pages/archive/crawl-templates.ts
new file mode 100644
index 00000000..7efd1c2d
--- /dev/null
+++ b/frontend/src/pages/archive/crawl-templates.ts
@@ -0,0 +1,240 @@
+import { state, property } from "lit/decorators.js";
+import { msg, localized } from "@lit/localize";
+
+import type { AuthState } from "../../utils/AuthService";
+import LiteElement, { html } from "../../utils/LiteElement";
+
+type CrawlTemplate = any; // TODO
+
+const initialValues = {
+ name: `Example crawl ${Date.now()}`, // TODO remove placeholder
+ runNow: true,
+ schedule: "@weekly",
+ // crawlTimeoutMinutes: 0,
+ seedUrls: "",
+ scopeType: "prefix",
+ // limit: 0,
+};
+
+@localized()
+export class CrawlTemplates extends LiteElement {
+ @property({ type: Object })
+ authState!: AuthState;
+
+ @property({ type: String })
+ archiveId!: string;
+
+ @property({ type: Boolean })
+ isNew!: boolean;
+
+ @property({ type: Array })
+ crawlTemplates?: CrawlTemplate[];
+
+ @state()
+ isRunNow: boolean = initialValues.runNow;
+
+ render() {
+ if (this.isNew) {
+ return this.renderNew();
+ }
+
+ return this.renderList();
+ }
+
+ private renderNew() {
+ return html`
+
${msg("New Crawl Template")}
+
+ ${msg(
+ "Configure a new crawl template. You can choose to run a crawl immediately upon saving this template."
+ )}
+
+
+
+
+
+
+
${msg("Basic settings")}
+
+
+
+
+
+
+
+
+
+ None
+ Daily
+ Weekly
+ Monthly
+
+
+
+
+
+ (this.isRunNow = e.target.checked)}
+ >${msg("Run immediately")}
+
+
+
+
+ ${msg("minutes")}
+
+
+
+
+
+
${msg("Pages")}
+
+
+
+
+
+
+
+ Page
+ Page SPA
+ Prefix
+ Host
+ Any
+
+
+
+
+ ${msg("pages")}
+
+
+
+
+
+ ${this.isRunNow
+ ? html`
+
+ ${msg("A crawl will start immediately on save.")}
+
+ `
+ : ""}
+
+
${msg("Save Crawl Template")}
+
+
+
+
+ `;
+ }
+
+ private renderList() {
+ return html`
+
+
+ this.navTo(`/archives/${this.archiveId}/crawl-templates/new`)}
+ >
+
+ ${msg("Create new crawl template")}
+
+
+
+
+ ${this.crawlTemplates?.map(
+ (template) => html`
${template.id}
`
+ )}
+
+ `;
+ }
+
+ private async onSubmit(event: { detail: { formData: FormData } }) {
+ if (!this.authState) return;
+
+ const { formData } = event.detail;
+
+ const crawlTimeoutMinutes = formData.get("crawlTimeoutMinutes");
+ const pageLimit = formData.get("limit");
+ const seedUrlsStr = formData.get("seedUrls");
+ const params = {
+ name: formData.get("name"),
+ schedule: formData.get("schedule"),
+ runNow: this.isRunNow,
+ crawlTimeout: crawlTimeoutMinutes ? +crawlTimeoutMinutes * 60 : 0,
+ config: {
+ seeds: (seedUrlsStr as string).trim().replace(/,/g, " ").split(/\s+/g),
+ scopeType: formData.get("scopeType"),
+ limit: pageLimit ? +pageLimit : 0,
+ },
+ };
+
+ console.log(params);
+
+ try {
+ await this.apiFetch(
+ `/archives/${this.archiveId}/crawlconfigs/`,
+ this.authState,
+ {
+ method: "POST",
+ body: JSON.stringify(params),
+ }
+ );
+
+ console.debug("success");
+
+ this.navTo(`/archives/${this.archiveId}/crawl-templates`);
+ } catch (e) {
+ console.error(e);
+ }
+ }
+}
diff --git a/frontend/src/pages/archive.ts b/frontend/src/pages/archive/index.ts
similarity index 71%
rename from frontend/src/pages/archive.ts
rename to frontend/src/pages/archive/index.ts
index 9a865ea0..6d8b4720 100644
--- a/frontend/src/pages/archive.ts
+++ b/frontend/src/pages/archive/index.ts
@@ -1,14 +1,18 @@
import { state, property } from "lit/decorators.js";
import { msg, localized, str } from "@lit/localize";
-import type { AuthState } from "../utils/AuthService";
-import type { CurrentUser } from "../types/user";
-import type { ArchiveData } from "../utils/archives";
-import LiteElement, { html } from "../utils/LiteElement";
-import { needLogin } from "../utils/auth";
-import { isOwner } from "../utils/archives";
+import type { AuthState } from "../../utils/AuthService";
+import type { CurrentUser } from "../../types/user";
+import type { ArchiveData } from "../../utils/archives";
+import LiteElement, { html } from "../../utils/LiteElement";
+import { needLogin } from "../../utils/auth";
+import { isOwner } from "../../utils/archives";
+import { CrawlTemplates } from "./crawl-templates";
-export type ArchiveTab = "settings" | "members";
+customElements.define("btrix-crawl-templates", CrawlTemplates);
+
+export type ArchiveTab = "crawl-templates" | "settings" | "members";
+type CrawlTemplate = any; // TODO
const defaultTab = "settings";
@@ -30,9 +34,16 @@ export class Archive extends LiteElement {
@property({ type: Boolean })
isAddingMember: boolean = false;
+ /** Whether new resource is being added in tab */
+ @property({ type: Boolean })
+ isNewResourceTab: boolean = false;
+
@state()
private archive?: ArchiveData;
+ @state()
+ private crawlTemplates?: CrawlTemplate[];
+
@state()
private successfullyInvitedEmail?: string;
@@ -50,8 +61,18 @@ export class Archive extends LiteElement {
}
}
- updated(changedProperties: any) {
- if (changedProperties.has("isAddingMember") && this.isAddingMember) {
+ async updated(changedProperties: any) {
+ if (
+ changedProperties.has("archiveTab") &&
+ this.archiveTab === "crawl-templates" &&
+ !this.isNewResourceTab
+ ) {
+ this.crawlTemplates = await this.getCrawlTemplates();
+
+ if (!this.crawlTemplates.length) {
+ this.navTo(`/archives/${this.archiveId}/crawl-templates/new`);
+ }
+ } else if (changedProperties.has("isAddingMember") && this.isAddingMember) {
this.successfullyInvitedEmail = undefined;
}
}
@@ -65,7 +86,7 @@ export class Archive extends LiteElement {
`;
}
- const showMembers = Boolean(this.archive.users);
+ const showMembersTab = Boolean(this.archive.users);
return html`