From 8e944da933aa63735da7e80901fdddfe76acf55c Mon Sep 17 00:00:00 2001 From: sua yoo Date: Mon, 22 Sep 2025 13:24:07 -0700 Subject: [PATCH 01/11] create new section --- .../docs/docs/user-guide/workflow-setup.md | 10 ++-- .../crawl-workflows/workflow-editor.ts | 54 ++++++++++++------- .../src/strings/crawl-workflows/section.ts | 1 + frontend/src/utils/workflow.ts | 3 ++ 4 files changed, 44 insertions(+), 24 deletions(-) diff --git a/frontend/docs/docs/user-guide/workflow-setup.md b/frontend/docs/docs/user-guide/workflow-setup.md index 5c698c7959..b7d020d5f7 100644 --- a/frontend/docs/docs/user-guide/workflow-setup.md +++ b/frontend/docs/docs/user-guide/workflow-setup.md @@ -392,6 +392,12 @@ You can use a tool like [crontab.guru](https://crontab.guru/) to check Cron synt Cron schedules are always in [UTC](https://en.wikipedia.org/wiki/Coordinated_Universal_Time). +## Collection + +### Auto-Add + +Search for and specify [collections](collection.md) that this crawl workflow should automatically add archived items to as soon as crawling finishes. Canceled and Failed crawls will not be added to collections. + ## Metadata Describe and organize your crawl workflow and the resulting archived items. @@ -407,7 +413,3 @@ Leave optional notes about the workflow's configuration. ### Tags Apply tags to the workflow. Tags applied to the workflow will propagate to every crawl created with it at the time of crawl creation. - -### Collection Auto-Add - -Search for and specify [collections](collection.md) that this crawl workflow should automatically add archived items to as soon as crawling finishes. Canceled and Failed crawls will not be added to collections. diff --git a/frontend/src/features/crawl-workflows/workflow-editor.ts b/frontend/src/features/crawl-workflows/workflow-editor.ts index f9229deca6..4368902ab2 100644 --- a/frontend/src/features/crawl-workflows/workflow-editor.ts +++ b/frontend/src/features/crawl-workflows/workflow-editor.ts @@ -204,6 +204,10 @@ const getDefaultProgressState = (hasConfigId = false): ProgressState => { error: false, completed: hasConfigId, }, + collection: { + error: false, + completed: hasConfigId, + }, metadata: { error: false, completed: hasConfigId, @@ -2279,6 +2283,30 @@ https://archiveweb.page/images/${"logo.svg"}`} `; }; + private renderCollection() { + return html` + ${inputCol(html` + + this.updateFormState( + { + autoAddCollections: e.detail.collections, + }, + true, + )} + > + `)} + ${this.renderHelpTextCol( + msg(`Automatically add crawls from this workflow to one or more collections + as soon as they complete. + Individual crawls can be selected from within the collection later.`), + )} + `; + } + private renderJobMetadata() { const isPageScope = isPageScopeType(this.formState.scopeType); @@ -2363,25 +2391,6 @@ https://archiveweb.page/images/${"logo.svg"}`} msg(`Create or assign this crawl (and its outputs) to one or more tags to help organize your archived items.`), )} - ${inputCol(html` - - this.updateFormState( - { - autoAddCollections: e.detail.collections, - }, - true, - )} - > - `)} - ${this.renderHelpTextCol( - msg(`Automatically add crawls from this workflow to one or more collections - as soon as they complete. - Individual crawls can be selected from within the collection later.`), - )} `; } @@ -2452,9 +2461,14 @@ https://archiveweb.page/images/${"logo.svg"}`} desc: msg("Schedule recurring crawls."), render: this.renderJobScheduling, }, + { + name: "collection", + desc: msg("Add crawls from this workflow to one or more collections."), + render: this.renderCollection, + }, { name: "metadata", - desc: msg("Describe and organize crawls from this workflow."), + desc: msg("Describe and tag this workflow and its crawls."), render: this.renderJobMetadata, }, ]; diff --git a/frontend/src/strings/crawl-workflows/section.ts b/frontend/src/strings/crawl-workflows/section.ts index 04ebcb13c5..429c2bbb82 100644 --- a/frontend/src/strings/crawl-workflows/section.ts +++ b/frontend/src/strings/crawl-workflows/section.ts @@ -8,6 +8,7 @@ const section: Record = { behaviors: msg("Page Behavior"), browserSettings: msg("Browser Settings"), scheduling: msg("Scheduling"), + collection: msg("Collection"), metadata: msg("Metadata"), }; diff --git a/frontend/src/utils/workflow.ts b/frontend/src/utils/workflow.ts index 5a317abab3..3d74596901 100644 --- a/frontend/src/utils/workflow.ts +++ b/frontend/src/utils/workflow.ts @@ -39,6 +39,7 @@ export const SECTIONS = [ "behaviors", "browserSettings", "scheduling", + "collection", "metadata", ] as const; export const sectionsEnum = z.enum(SECTIONS); @@ -50,6 +51,7 @@ export enum GuideHash { Behaviors = "page-behavior", BrowserSettings = "browser-settings", Scheduling = "scheduling", + Collection = "collection", Metadata = "metadata", } @@ -64,6 +66,7 @@ export const workflowTabToGuideHash: Record = { behaviors: GuideHash.Behaviors, browserSettings: GuideHash.BrowserSettings, scheduling: GuideHash.Scheduling, + collection: GuideHash.Metadata, metadata: GuideHash.Metadata, }; From 35d299bab11fd3b096d6e4f3f14e58065f377034 Mon Sep 17 00:00:00 2001 From: sua yoo Date: Mon, 22 Sep 2025 14:33:32 -0700 Subject: [PATCH 02/11] update label --- frontend/src/features/collections/collections-add.ts | 2 +- frontend/src/features/crawl-workflows/workflow-editor.ts | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/frontend/src/features/collections/collections-add.ts b/frontend/src/features/collections/collections-add.ts index 63f2e69782..e405c60a6b 100644 --- a/frontend/src/features/collections/collections-add.ts +++ b/frontend/src/features/collections/collections-add.ts @@ -99,7 +99,7 @@ export class CollectionsAdd extends BtrixElement { render() { return html`
${this.renderSearch()} diff --git a/frontend/src/features/crawl-workflows/workflow-editor.ts b/frontend/src/features/crawl-workflows/workflow-editor.ts index 4368902ab2..cf6def5bfb 100644 --- a/frontend/src/features/crawl-workflows/workflow-editor.ts +++ b/frontend/src/features/crawl-workflows/workflow-editor.ts @@ -2287,6 +2287,7 @@ https://archiveweb.page/images/${"logo.svg"}`} return html` ${inputCol(html` Date: Mon, 22 Sep 2025 15:07:58 -0700 Subject: [PATCH 03/11] update workflow settings --- frontend/src/components/ui/config-details.ts | 75 ++++-------------- frontend/src/features/collections/index.ts | 1 + .../collections/linked-collections-list.ts | 76 +++++++++++++++++++ 3 files changed, 93 insertions(+), 59 deletions(-) create mode 100644 frontend/src/features/collections/linked-collections-list.ts diff --git a/frontend/src/components/ui/config-details.ts b/frontend/src/components/ui/config-details.ts index b2c6a8378a..03b9155d7b 100644 --- a/frontend/src/components/ui/config-details.ts +++ b/frontend/src/components/ui/config-details.ts @@ -16,9 +16,7 @@ import { import { labelFor } from "@/strings/crawl-workflows/labels"; import scopeTypeLabel from "@/strings/crawl-workflows/scopeType"; import sectionStrings from "@/strings/crawl-workflows/section"; -import type { Collection } from "@/types/collection"; import { WorkflowScopeType, type StorageSeedFile } from "@/types/workflow"; -import { isApiError } from "@/utils/api"; import { unescapeCustomPrefix } from "@/utils/crawl-workflows/unescapeCustomPrefix"; import { DEPTH_SUPPORTED_SCOPES, isPageScopeType } from "@/utils/crawler"; import { humanizeSchedule } from "@/utils/cron"; @@ -60,13 +58,9 @@ export class ConfigDetails extends BtrixElement { maxPagesPerCrawl?: number; }; - @state() - private collections: Collection[] = []; - async connectedCallback() { super.connectedCallback(); void this.fetchOrgDefaults(); - await this.fetchCollections(); } render() { @@ -313,6 +307,22 @@ export class ConfigDetails extends BtrixElement { )} `, })} + ${when(!this.hideMetadata, () => + this.renderSection({ + id: "collection", + heading: sectionStrings.collection, + renderDescItems: () => html` + ${this.renderSetting( + html`${msg("Auto-Add")}`, + crawlConfig?.autoAddCollections.length + ? html`` + : undefined, + )} + `, + }), + )} ${when(!this.hideMetadata, () => this.renderSection({ id: "crawl-metadata", @@ -338,21 +348,6 @@ export class ConfigDetails extends BtrixElement { ) : [], )} - ${this.renderSetting( - msg("Collections"), - this.collections.length - ? this.collections.map( - (coll) => - html` - ${coll.name} - - (${this.localize.number(coll.crawlCount)} - ${pluralOf("items", coll.crawlCount)}) - - `, - ) - : undefined, - )} `, }), )} @@ -633,44 +628,6 @@ export class ConfigDetails extends BtrixElement { `; } - private async fetchCollections() { - if (this.crawlConfig?.autoAddCollections) { - try { - await this.getCollections(); - } catch (e) { - this.notify.toast({ - message: - isApiError(e) && e.statusCode === 404 - ? msg("Collections not found.") - : msg( - "Sorry, couldn't retrieve Collection details at this time.", - ), - variant: "danger", - icon: "exclamation-octagon", - id: "collection-fetch-status", - }); - } - } - } - - private async getCollections() { - const collections: Collection[] = []; - const orgId = this.crawlConfig?.oid; - - if (this.crawlConfig?.autoAddCollections && orgId) { - for (const collectionId of this.crawlConfig.autoAddCollections) { - const data = await this.api.fetch( - `/orgs/${orgId}/collections/${collectionId}`, - ); - if (data) { - collections.push(data); - } - } - } - this.collections = collections; - this.requestUpdate(); - } - // TODO Consolidate with workflow-editor private async fetchOrgDefaults() { try { diff --git a/frontend/src/features/collections/index.ts b/frontend/src/features/collections/index.ts index a4fbc66b0a..af7f36d009 100644 --- a/frontend/src/features/collections/index.ts +++ b/frontend/src/features/collections/index.ts @@ -6,6 +6,7 @@ import("./collection-edit-dialog"); import("./collection-create-dialog"); import("./collection-initial-view-dialog"); import("./collection-workflow-list"); +import("./linked-collections-list"); import("./select-collection-access"); import("./select-collection-page"); import("./share-collection"); diff --git a/frontend/src/features/collections/linked-collections-list.ts b/frontend/src/features/collections/linked-collections-list.ts new file mode 100644 index 0000000000..59deb92cb4 --- /dev/null +++ b/frontend/src/features/collections/linked-collections-list.ts @@ -0,0 +1,76 @@ +import { localized, msg } from "@lit/localize"; +import { Task } from "@lit/task"; +import { html } from "lit"; +import { customElement, property } from "lit/decorators.js"; +import isEqual from "lodash/fp/isEqual"; + +import { BtrixElement } from "@/classes/BtrixElement"; +import type { Collection } from "@/types/collection"; +import { pluralOf } from "@/utils/pluralize"; + +/** + * Display list of collections that are linked to a workflow or archived item by ID. + */ +@customElement("btrix-linked-collections-list") +@localized() +export class LinkedCollectionsList extends BtrixElement { + /** + * List of collection IDs, checked against values so collection data is not + * unnecessarily fetched if IDs have not changed + */ + @property({ type: Array, hasChanged: (a, b) => !isEqual(a, b) }) + collectionIds: string[] = []; + + private readonly collectionsTask = new Task(this, { + task: async ([ids], { signal }) => { + return Promise.all(ids.map(async (id) => this.getCollection(id, signal))); + }, + args: () => [this.collectionIds] as const, + }); + + render() { + return this.collectionsTask.render({ + complete: this.renderList, + }); + } + + private readonly renderList = (items: (Collection | undefined)[]) => { + const collections = items.filter((v): v is Collection => v !== undefined); + + if (!collections.length) { + return; + } + + return html`
    + ${collections.map( + (col) => + html`
  • +
    ${col.name}
    +
    + ${col.crawlCount} + ${pluralOf("items", col.crawlCount)} +
    +
    + + + + +
    +
  • `, + )} +
`; + }; + + private async getCollection(id: string, signal: AbortSignal) { + return this.api.fetch( + `/orgs/${this.orgId}/collections/${id}`, + { signal }, + ); + } +} From b19205e6636e1630f75297c367fd5cbb836eb9ff Mon Sep 17 00:00:00 2001 From: sua yoo Date: Mon, 22 Sep 2025 15:31:21 -0700 Subject: [PATCH 04/11] update archived item settings --- frontend/src/components/ui/config-details.ts | 4 +- frontend/src/features/collections/index.ts | 2 +- .../collections/linked-collections-list.ts | 76 ------------------- .../collections/linked-collections/index.ts | 1 + .../linked-collections-list.ts | 60 +++++++++++++++ .../linked-collections/linked-collections.ts | 58 ++++++++++++++ .../archived-item-detail.ts | 43 ++++++----- 7 files changed, 146 insertions(+), 98 deletions(-) delete mode 100644 frontend/src/features/collections/linked-collections-list.ts create mode 100644 frontend/src/features/collections/linked-collections/index.ts create mode 100644 frontend/src/features/collections/linked-collections/linked-collections-list.ts create mode 100644 frontend/src/features/collections/linked-collections/linked-collections.ts diff --git a/frontend/src/components/ui/config-details.ts b/frontend/src/components/ui/config-details.ts index 03b9155d7b..19fda6be1e 100644 --- a/frontend/src/components/ui/config-details.ts +++ b/frontend/src/components/ui/config-details.ts @@ -315,9 +315,9 @@ export class ConfigDetails extends BtrixElement { ${this.renderSetting( html`${msg("Auto-Add")}`, crawlConfig?.autoAddCollections.length - ? html`` + >` : undefined, )} `, diff --git a/frontend/src/features/collections/index.ts b/frontend/src/features/collections/index.ts index af7f36d009..7c43e87442 100644 --- a/frontend/src/features/collections/index.ts +++ b/frontend/src/features/collections/index.ts @@ -6,7 +6,7 @@ import("./collection-edit-dialog"); import("./collection-create-dialog"); import("./collection-initial-view-dialog"); import("./collection-workflow-list"); -import("./linked-collections-list"); +import("./linked-collections"); import("./select-collection-access"); import("./select-collection-page"); import("./share-collection"); diff --git a/frontend/src/features/collections/linked-collections-list.ts b/frontend/src/features/collections/linked-collections-list.ts deleted file mode 100644 index 59deb92cb4..0000000000 --- a/frontend/src/features/collections/linked-collections-list.ts +++ /dev/null @@ -1,76 +0,0 @@ -import { localized, msg } from "@lit/localize"; -import { Task } from "@lit/task"; -import { html } from "lit"; -import { customElement, property } from "lit/decorators.js"; -import isEqual from "lodash/fp/isEqual"; - -import { BtrixElement } from "@/classes/BtrixElement"; -import type { Collection } from "@/types/collection"; -import { pluralOf } from "@/utils/pluralize"; - -/** - * Display list of collections that are linked to a workflow or archived item by ID. - */ -@customElement("btrix-linked-collections-list") -@localized() -export class LinkedCollectionsList extends BtrixElement { - /** - * List of collection IDs, checked against values so collection data is not - * unnecessarily fetched if IDs have not changed - */ - @property({ type: Array, hasChanged: (a, b) => !isEqual(a, b) }) - collectionIds: string[] = []; - - private readonly collectionsTask = new Task(this, { - task: async ([ids], { signal }) => { - return Promise.all(ids.map(async (id) => this.getCollection(id, signal))); - }, - args: () => [this.collectionIds] as const, - }); - - render() { - return this.collectionsTask.render({ - complete: this.renderList, - }); - } - - private readonly renderList = (items: (Collection | undefined)[]) => { - const collections = items.filter((v): v is Collection => v !== undefined); - - if (!collections.length) { - return; - } - - return html`
    - ${collections.map( - (col) => - html`
  • -
    ${col.name}
    -
    - ${col.crawlCount} - ${pluralOf("items", col.crawlCount)} -
    -
    - - - - -
    -
  • `, - )} -
`; - }; - - private async getCollection(id: string, signal: AbortSignal) { - return this.api.fetch( - `/orgs/${this.orgId}/collections/${id}`, - { signal }, - ); - } -} diff --git a/frontend/src/features/collections/linked-collections/index.ts b/frontend/src/features/collections/linked-collections/index.ts new file mode 100644 index 0000000000..2450e9505c --- /dev/null +++ b/frontend/src/features/collections/linked-collections/index.ts @@ -0,0 +1 @@ +import "./linked-collections"; diff --git a/frontend/src/features/collections/linked-collections/linked-collections-list.ts b/frontend/src/features/collections/linked-collections/linked-collections-list.ts new file mode 100644 index 0000000000..6df7bd886f --- /dev/null +++ b/frontend/src/features/collections/linked-collections/linked-collections-list.ts @@ -0,0 +1,60 @@ +import { localized, msg } from "@lit/localize"; +import { html, nothing } from "lit"; +import { customElement, property } from "lit/decorators.js"; +import { when } from "lit/directives/when.js"; + +import { TailwindElement } from "@/classes/TailwindElement"; +import type { Collection } from "@/types/collection"; +import { pluralOf } from "@/utils/pluralize"; + +@customElement("btrix-linked-collections-list") +@localized() +export class LinkedCollectionsList extends TailwindElement { + @property({ type: Array }) + collections: Partial[] = []; + + @property({ type: String }) + baseUrl?: string; + + render() { + if (!this.collections.length) { + return; + } + + return html`
    + ${this.collections.map( + (col) => + html`
  • +
    ${col.name}
    + ${col.crawlCount !== undefined + ? html` +
    + ${col.crawlCount} + ${pluralOf("items", col.crawlCount)} +
    + ` + : nothing} + ${when( + this.baseUrl, + (baseUrl) => + html`
    + + + + +
    `, + )} +
  • `, + )} +
`; + } +} diff --git a/frontend/src/features/collections/linked-collections/linked-collections.ts b/frontend/src/features/collections/linked-collections/linked-collections.ts new file mode 100644 index 0000000000..63264dd08d --- /dev/null +++ b/frontend/src/features/collections/linked-collections/linked-collections.ts @@ -0,0 +1,58 @@ +import { localized } from "@lit/localize"; +import { Task } from "@lit/task"; +import { html } from "lit"; +import { customElement, property } from "lit/decorators.js"; +import isEqual from "lodash/fp/isEqual"; + +import { BtrixElement } from "@/classes/BtrixElement"; +import type { Collection } from "@/types/collection"; + +import "./linked-collections-list"; + +/** + * Display list of collections that are linked to a workflow or archived item by ID. + */ +@customElement("btrix-linked-collections") +@localized() +export class LinkedCollections extends BtrixElement { + /** + * List of collection IDs, checked against values so collection data is not + * unnecessarily fetched if IDs have not changed + */ + @property({ type: Array, hasChanged: (a, b) => !isEqual(a, b) }) + collectionIds: string[] = []; + + private readonly collectionsTask = new Task(this, { + task: async ([ids], { signal }) => { + // The API doesn't currently support getting collections by a list of IDs + return Promise.all(ids.map(async (id) => this.getCollection(id, signal))); + }, + args: () => [this.collectionIds] as const, + }); + + render() { + return this.collectionsTask.render({ + complete: (items) => { + const collections = items.filter( + (v): v is Collection => v !== undefined, + ); + + if (!collections.length) { + return; + } + + return html``; + }, + }); + } + + private async getCollection(id: string, signal: AbortSignal) { + return this.api.fetch( + `/orgs/${this.orgId}/collections/${id}`, + { signal }, + ); + } +} diff --git a/frontend/src/pages/org/archived-item-detail/archived-item-detail.ts b/frontend/src/pages/org/archived-item-detail/archived-item-detail.ts index 32a31fcfb1..3a525e7525 100644 --- a/frontend/src/pages/org/archived-item-detail/archived-item-detail.ts +++ b/frontend/src/pages/org/archived-item-detail/archived-item-detail.ts @@ -403,13 +403,13 @@ export class ArchivedItemDetail extends BtrixElement { break; default: sectionContent = html` -
-
+
+
${this.renderPanel(msg("Overview"), this.renderOverview(), [ tw`rounded-lg border p-4`, ])}
-
+
${this.renderPanel( html` ${this.renderTitle(msg("Metadata"))} @@ -429,6 +429,11 @@ export class ArchivedItemDetail extends BtrixElement { [tw`rounded-lg border p-4`], )}
+
+ ${this.renderPanel(msg("Collections"), this.renderCollections(), [ + tw`rounded-lg border p-4`, + ])} +
`; break; @@ -959,26 +964,26 @@ export class ArchivedItemDetail extends BtrixElement { () => html``, )} - + + `; + } + + private renderCollections() { + const noneText = html`${msg("None")}`; + return html` + + ${when( this.item, - () => + (item) => when( - this.item!.collections.length, + item.collections.length, () => html` -
    - ${this.item!.collections.map( - ({ id, name }) => - html`
  • - ${name} -
  • `, - )} -
+ `, () => noneText, ), From 7d92bd1d68db08daef015abf71f10f0676d20e6d Mon Sep 17 00:00:00 2001 From: sua yoo Date: Tue, 23 Sep 2025 11:52:03 -0700 Subject: [PATCH 05/11] replace with new list --- .../features/collections/collections-add.ts | 71 ++------- .../linked-collections-list.ts | 138 +++++++++++++----- .../linked-collections/linked-collections.ts | 81 +++++++--- .../src/layouts/collections/metadataColumn.ts | 2 +- frontend/src/pages/org/collection-detail.ts | 4 +- frontend/src/pages/public/org.ts | 10 +- frontend/src/types/collection.ts | 6 +- frontend/src/types/storage.ts | 2 +- 8 files changed, 187 insertions(+), 127 deletions(-) diff --git a/frontend/src/features/collections/collections-add.ts b/frontend/src/features/collections/collections-add.ts index e405c60a6b..18cbeb854f 100644 --- a/frontend/src/features/collections/collections-add.ts +++ b/frontend/src/features/collections/collections-add.ts @@ -9,6 +9,7 @@ import queryString from "query-string"; import { BtrixElement } from "@/classes/BtrixElement"; import type { Combobox } from "@/components/ui/combobox"; +import type { BtrixRemoveLinkedCollectionEvent } from "@/features/collections/linked-collections/linked-collections-list"; import type { APIPaginatedList, APIPaginationQuery, @@ -51,9 +52,6 @@ export class CollectionsAdd extends BtrixElement { @property({ type: String }) emptyText?: string; - @state() - private collectionsData: { [id: string]: Collection } = {}; - @state() private collectionIds: string[] = []; @@ -89,7 +87,6 @@ export class CollectionsAdd extends BtrixElement { this.collectionIds = this.initialCollections; } super.connectedCallback(); - void this.initializeCollectionsFromIds(); } disconnectedCallback() { @@ -109,9 +106,15 @@ export class CollectionsAdd extends BtrixElement { this.collectionIds.length ? html`
-
    - ${this.collectionIds.map(this.renderCollectionItem, this)} -
+ { + const { id } = e.detail.item; + + this.removeCollection(id); + }} + >
` : this.emptyText @@ -142,12 +145,6 @@ export class CollectionsAdd extends BtrixElement { ); if (coll) { const { id } = coll; - if (!(this.collectionsData[id] as Collection | undefined)) { - this.collectionsData = { - ...this.collectionsData, - [id]: (await this.getCollection(id))!, - }; - } this.collectionIds = [...this.collectionIds, id]; void this.dispatchChange(); } @@ -225,35 +222,7 @@ export class CollectionsAdd extends BtrixElement { }); } - private renderCollectionItem(id: string) { - const collection = this.collectionsData[id] as Collection | undefined; - return html`
  • -
    -
    - ${collection?.name} -
    -
    - ${msg(str`${collection?.crawlCount || 0} items`)} -
    - - -
    -
  • `; - } - - private removeCollection(event: Event) { - const target = event.currentTarget as HTMLElement; - const collectionId = target.getAttribute("data-key"); + private removeCollection(collectionId: string) { if (collectionId) { const collIdIndex = this.collectionIds.indexOf(collectionId); if (collIdIndex > -1) { @@ -325,24 +294,6 @@ export class CollectionsAdd extends BtrixElement { return data; } - private async initializeCollectionsFromIds() { - this.collectionIds.forEach(async (id) => { - const data = await this.getCollection(id); - if (data) { - this.collectionsData = { - ...this.collectionsData, - [id]: data, - }; - } - }); - } - - private readonly getCollection = async ( - collId: string, - ): Promise => { - return this.api.fetch(`/orgs/${this.orgId}/collections/${collId}`); - }; - private async dispatchChange() { await this.updateComplete; this.dispatchEvent( diff --git a/frontend/src/features/collections/linked-collections/linked-collections-list.ts b/frontend/src/features/collections/linked-collections/linked-collections-list.ts index 6df7bd886f..b5ef9c0c2b 100644 --- a/frontend/src/features/collections/linked-collections/linked-collections-list.ts +++ b/frontend/src/features/collections/linked-collections/linked-collections-list.ts @@ -1,60 +1,124 @@ import { localized, msg } from "@lit/localize"; -import { html, nothing } from "lit"; +import clsx from "clsx"; +import { html } from "lit"; import { customElement, property } from "lit/decorators.js"; -import { when } from "lit/directives/when.js"; import { TailwindElement } from "@/classes/TailwindElement"; -import type { Collection } from "@/types/collection"; +import type { BtrixRemoveEvent } from "@/events/btrix-remove"; +import { collectionSchema, type Collection } from "@/types/collection"; import { pluralOf } from "@/utils/pluralize"; +import { tw } from "@/utils/tailwind"; + +// NOTE Some API endpoints return only the ID for a collection +export type CollectionLikeItem = Collection | { id: string; name?: string }; + +export type BtrixRemoveLinkedCollectionEvent = + BtrixRemoveEvent; + +const isActualCollection = (item: CollectionLikeItem): item is Collection => { + try { + collectionSchema.parse(item); + return true; + } catch (err) { + if (item.name) { + console.debug(err); + } + } + return false; +}; @customElement("btrix-linked-collections-list") @localized() export class LinkedCollectionsList extends TailwindElement { @property({ type: Array }) - collections: Partial[] = []; + collections: CollectionLikeItem[] = []; @property({ type: String }) baseUrl?: string; + @property({ type: Boolean }) + removable?: boolean; + render() { if (!this.collections.length) { return; } return html`
      - ${this.collections.map( - (col) => - html`
    • -
      ${col.name}
      - ${col.crawlCount !== undefined - ? html` -
      - ${col.crawlCount} - ${pluralOf("items", col.crawlCount)} -
      - ` - : nothing} - ${when( - this.baseUrl, - (baseUrl) => - html`
      - - - - -
      `, - )} -
    • `, - )} + ${this.collections.map(this.renderItem)}
    `; } + + private readonly renderItem = (item: CollectionLikeItem) => { + const actual = isActualCollection(item); + + const content = [ + html`
    ${item.name}
    `, + ]; + + if (actual) { + content.push( + html`
    + ${item.crawlCount} + ${pluralOf("items", item.crawlCount)} +
    `, + ); + } + + if (this.baseUrl) { + content.push( + html`
    + + + + +
    `, + ); + } + + if (this.removable) { + content.push( + html`
    + + + this.dispatchEvent( + new CustomEvent( + "btrix-remove", + { + detail: { + item: item, + }, + bubbles: true, + composed: true, + }, + ), + )} + > + +
    `, + ); + } + + return html`
  • + ${content} +
  • `; + }; } diff --git a/frontend/src/features/collections/linked-collections/linked-collections.ts b/frontend/src/features/collections/linked-collections/linked-collections.ts index 63264dd08d..9764ebf5df 100644 --- a/frontend/src/features/collections/linked-collections/linked-collections.ts +++ b/frontend/src/features/collections/linked-collections/linked-collections.ts @@ -4,6 +4,8 @@ import { html } from "lit"; import { customElement, property } from "lit/decorators.js"; import isEqual from "lodash/fp/isEqual"; +import type { CollectionLikeItem } from "./linked-collections-list"; + import { BtrixElement } from "@/classes/BtrixElement"; import type { Collection } from "@/types/collection"; @@ -22,37 +24,72 @@ export class LinkedCollections extends BtrixElement { @property({ type: Array, hasChanged: (a, b) => !isEqual(a, b) }) collectionIds: string[] = []; + @property({ type: Boolean }) + removable?: boolean; + + // Use a custom abort controller rather than the one provided by `Task` + // to only abort on disconnect + private collectionsTaskController = new AbortController(); + + private readonly collectionsMap = new Map< + string, + Promise + >(); + + disconnectedCallback(): void { + this.collectionsTaskController.abort(); + super.disconnectedCallback(); + } + + connectedCallback(): void { + this.collectionsTaskController = new AbortController(); + super.connectedCallback(); + } + private readonly collectionsTask = new Task(this, { - task: async ([ids], { signal }) => { + task: async ([ids]) => { // The API doesn't currently support getting collections by a list of IDs - return Promise.all(ids.map(async (id) => this.getCollection(id, signal))); + const requests: Promise[] = []; + + ids.forEach(async (id) => { + let request = this.collectionsMap.get(id); + + if (!request) { + request = this.fetchCollection( + id, + this.collectionsTaskController.signal, + ); + + this.collectionsMap.set(id, request); + } + + requests.push(request); + }); + + return await Promise.all(requests); }, args: () => [this.collectionIds] as const, }); render() { - return this.collectionsTask.render({ - complete: (items) => { - const collections = items.filter( - (v): v is Collection => v !== undefined, - ); - - if (!collections.length) { - return; - } + const collections = + this.collectionsTask.value || this.collectionIds.map((id) => ({ id })); - return html``; - }, - }); + return html``; } - private async getCollection(id: string, signal: AbortSignal) { - return this.api.fetch( - `/orgs/${this.orgId}/collections/${id}`, - { signal }, - ); + private async fetchCollection(id: string, signal: AbortSignal) { + try { + return await this.api.fetch( + `/orgs/${this.orgId}/collections/${id}`, + { signal }, + ); + } catch { + return { id }; + } } } diff --git a/frontend/src/layouts/collections/metadataColumn.ts b/frontend/src/layouts/collections/metadataColumn.ts index f687cf1ee9..350602c88d 100644 --- a/frontend/src/layouts/collections/metadataColumn.ts +++ b/frontend/src/layouts/collections/metadataColumn.ts @@ -15,7 +15,7 @@ export function metadataItemWithCollection( render, }: { label: string | TemplateResult; - render: (c: PublicCollection) => TemplateResult | string; + render: (c: Collection | PublicCollection) => TemplateResult | string; }) { return html` diff --git a/frontend/src/pages/org/collection-detail.ts b/frontend/src/pages/org/collection-detail.ts index dc37d61b19..f05a7af653 100644 --- a/frontend/src/pages/org/collection-detail.ts +++ b/frontend/src/pages/org/collection-detail.ts @@ -639,7 +639,9 @@ export class CollectionDetail extends BtrixElement { private renderDetailItem( label: string | TemplateResult, - renderContent: (collection: PublicCollection) => TemplateResult | string, + renderContent: ( + collection: Collection | PublicCollection, + ) => TemplateResult | string, ) { return metadataItemWithCollection(this.collection)({ label, diff --git a/frontend/src/pages/public/org.ts b/frontend/src/pages/public/org.ts index 7d325f15d0..91a2f122e7 100644 --- a/frontend/src/pages/public/org.ts +++ b/frontend/src/pages/public/org.ts @@ -8,7 +8,11 @@ import queryString from "query-string"; import { BtrixElement } from "@/classes/BtrixElement"; import { page, pageHeading } from "@/layouts/page"; import type { APIPaginatedList, APISortQuery } from "@/types/api"; -import { CollectionAccess, type Collection } from "@/types/collection"; +import { + CollectionAccess, + type Collection, + type PublicCollection, +} from "@/types/collection"; import type { OrgData, PublicOrgCollections } from "@/types/org"; import { SortDirection } from "@/types/utils"; import { richText } from "@/utils/rich-text"; @@ -321,7 +325,7 @@ export class PublicOrg extends BtrixElement { } private async getUserPublicCollections({ orgId }: { orgId: string }) { - const params: APISortQuery & { + const params: APISortQuery & { access: CollectionAccess; } = { sortBy: "dateLatest", @@ -330,7 +334,7 @@ export class PublicOrg extends BtrixElement { }; const query = queryString.stringify(params); - const data = await this.api.fetch>( + const data = await this.api.fetch>( `/orgs/${orgId}/collections?${query}`, ); diff --git a/frontend/src/types/collection.ts b/frontend/src/types/collection.ts index f4837eae5d..5bed574004 100644 --- a/frontend/src/types/collection.ts +++ b/frontend/src/types/collection.ts @@ -25,7 +25,7 @@ export const publicCollectionSchema = z.object({ orgName: z.string(), orgPublicProfile: z.boolean(), name: z.string(), - created: z.string().datetime(), + created: z.string().datetime().nullable(), // TODO check if this just needed migration to not be null modified: z.string().datetime(), caption: z.string().nullable(), description: z.string().nullable(), @@ -47,13 +47,15 @@ export const publicCollectionSchema = z.object({ totalSize: z.number(), allowPublicDownload: z.boolean(), homeUrl: z.string().url().nullable(), - homeUrlPageId: z.string().url().nullable(), + homeUrlPageId: z.string().nullable(), homeUrlTs: z.string().datetime().nullable(), access: z.nativeEnum(CollectionAccess), }); export type PublicCollection = z.infer; export const collectionSchema = publicCollectionSchema.extend({ + orgName: z.string().optional(), + orgPublicProfile: z.boolean().optional(), tags: z.array(z.string()), access: z.nativeEnum(CollectionAccess), }); diff --git a/frontend/src/types/storage.ts b/frontend/src/types/storage.ts index ac53700148..dd8b6e0689 100644 --- a/frontend/src/types/storage.ts +++ b/frontend/src/types/storage.ts @@ -1,7 +1,7 @@ import { z } from "zod"; export const storageFileSchema = z.object({ - id: z.string(), + id: z.string().optional(), name: z.string(), path: z.string().url(), hash: z.string(), From 2764665e5fac29eebc39df867d5ee674da802ecb Mon Sep 17 00:00:00 2001 From: sua yoo Date: Tue, 23 Sep 2025 11:53:53 -0700 Subject: [PATCH 06/11] update section name --- frontend/docs/docs/user-guide/workflow-setup.md | 4 ++-- frontend/src/components/ui/config-details.ts | 4 +++- frontend/src/features/crawl-workflows/workflow-editor.ts | 2 +- frontend/src/strings/crawl-workflows/section.ts | 2 +- 4 files changed, 7 insertions(+), 5 deletions(-) diff --git a/frontend/docs/docs/user-guide/workflow-setup.md b/frontend/docs/docs/user-guide/workflow-setup.md index b7d020d5f7..3f86223c5f 100644 --- a/frontend/docs/docs/user-guide/workflow-setup.md +++ b/frontend/docs/docs/user-guide/workflow-setup.md @@ -392,9 +392,9 @@ You can use a tool like [crontab.guru](https://crontab.guru/) to check Cron synt Cron schedules are always in [UTC](https://en.wikipedia.org/wiki/Coordinated_Universal_Time). -## Collection +## Collections -### Auto-Add +### Auto-Add to Collection Search for and specify [collections](collection.md) that this crawl workflow should automatically add archived items to as soon as crawling finishes. Canceled and Failed crawls will not be added to collections. diff --git a/frontend/src/components/ui/config-details.ts b/frontend/src/components/ui/config-details.ts index 19fda6be1e..b72c518bf3 100644 --- a/frontend/src/components/ui/config-details.ts +++ b/frontend/src/components/ui/config-details.ts @@ -313,7 +313,9 @@ export class ConfigDetails extends BtrixElement { heading: sectionStrings.collection, renderDescItems: () => html` ${this.renderSetting( - html`${msg("Auto-Add")}`, + html`${msg("Auto-Add to Collection")}`, crawlConfig?.autoAddCollections.length ? html` = { behaviors: msg("Page Behavior"), browserSettings: msg("Browser Settings"), scheduling: msg("Scheduling"), - collection: msg("Collection"), + collection: msg("Collections"), metadata: msg("Metadata"), }; From b4c8affdc9b76b3ec98bf348e85c15645f78bad3 Mon Sep 17 00:00:00 2001 From: sua yoo Date: Tue, 23 Sep 2025 12:18:03 -0700 Subject: [PATCH 07/11] clean up imports --- .../features/collections/collections-add.ts | 2 +- .../linked-collections-list.ts | 26 +++++-------------- .../linked-collections/linked-collections.ts | 2 +- .../collections/linked-collections/types.ts | 8 ++++++ .../collections/linked-collections/utils.ts | 17 ++++++++++++ 5 files changed, 33 insertions(+), 22 deletions(-) create mode 100644 frontend/src/features/collections/linked-collections/types.ts create mode 100644 frontend/src/features/collections/linked-collections/utils.ts diff --git a/frontend/src/features/collections/collections-add.ts b/frontend/src/features/collections/collections-add.ts index 18cbeb854f..bf3850ec80 100644 --- a/frontend/src/features/collections/collections-add.ts +++ b/frontend/src/features/collections/collections-add.ts @@ -9,7 +9,7 @@ import queryString from "query-string"; import { BtrixElement } from "@/classes/BtrixElement"; import type { Combobox } from "@/components/ui/combobox"; -import type { BtrixRemoveLinkedCollectionEvent } from "@/features/collections/linked-collections/linked-collections-list"; +import type { BtrixRemoveLinkedCollectionEvent } from "@/features/collections/linked-collections/types"; import type { APIPaginatedList, APIPaginationQuery, diff --git a/frontend/src/features/collections/linked-collections/linked-collections-list.ts b/frontend/src/features/collections/linked-collections/linked-collections-list.ts index b5ef9c0c2b..365cd91b37 100644 --- a/frontend/src/features/collections/linked-collections/linked-collections-list.ts +++ b/frontend/src/features/collections/linked-collections/linked-collections-list.ts @@ -3,30 +3,16 @@ import clsx from "clsx"; import { html } from "lit"; import { customElement, property } from "lit/decorators.js"; +import type { + BtrixRemoveLinkedCollectionEvent, + CollectionLikeItem, +} from "./types"; +import { isActualCollection } from "./utils"; + import { TailwindElement } from "@/classes/TailwindElement"; -import type { BtrixRemoveEvent } from "@/events/btrix-remove"; -import { collectionSchema, type Collection } from "@/types/collection"; import { pluralOf } from "@/utils/pluralize"; import { tw } from "@/utils/tailwind"; -// NOTE Some API endpoints return only the ID for a collection -export type CollectionLikeItem = Collection | { id: string; name?: string }; - -export type BtrixRemoveLinkedCollectionEvent = - BtrixRemoveEvent; - -const isActualCollection = (item: CollectionLikeItem): item is Collection => { - try { - collectionSchema.parse(item); - return true; - } catch (err) { - if (item.name) { - console.debug(err); - } - } - return false; -}; - @customElement("btrix-linked-collections-list") @localized() export class LinkedCollectionsList extends TailwindElement { diff --git a/frontend/src/features/collections/linked-collections/linked-collections.ts b/frontend/src/features/collections/linked-collections/linked-collections.ts index 9764ebf5df..52426a4ec9 100644 --- a/frontend/src/features/collections/linked-collections/linked-collections.ts +++ b/frontend/src/features/collections/linked-collections/linked-collections.ts @@ -4,7 +4,7 @@ import { html } from "lit"; import { customElement, property } from "lit/decorators.js"; import isEqual from "lodash/fp/isEqual"; -import type { CollectionLikeItem } from "./linked-collections-list"; +import type { CollectionLikeItem } from "./types"; import { BtrixElement } from "@/classes/BtrixElement"; import type { Collection } from "@/types/collection"; diff --git a/frontend/src/features/collections/linked-collections/types.ts b/frontend/src/features/collections/linked-collections/types.ts new file mode 100644 index 0000000000..e9e6943e86 --- /dev/null +++ b/frontend/src/features/collections/linked-collections/types.ts @@ -0,0 +1,8 @@ +import type { BtrixRemoveEvent } from "@/events/btrix-remove"; +import type { Collection } from "@/types/collection"; + +// NOTE Some API endpoints return only the ID for a collection +export type CollectionLikeItem = Collection | { id: string; name?: string }; + +export type BtrixRemoveLinkedCollectionEvent = + BtrixRemoveEvent; diff --git a/frontend/src/features/collections/linked-collections/utils.ts b/frontend/src/features/collections/linked-collections/utils.ts new file mode 100644 index 0000000000..01d3075ebb --- /dev/null +++ b/frontend/src/features/collections/linked-collections/utils.ts @@ -0,0 +1,17 @@ +import type { CollectionLikeItem } from "./types"; + +import { collectionSchema, type Collection } from "@/types/collection"; + +export const isActualCollection = ( + item: CollectionLikeItem, +): item is Collection => { + try { + collectionSchema.parse(item); + return true; + } catch (err) { + if (item.name) { + console.debug(err); + } + } + return false; +}; From c41bd11c2959a444359be80d9553c59bcebd165f Mon Sep 17 00:00:00 2001 From: sua yoo Date: Tue, 23 Sep 2025 12:56:45 -0700 Subject: [PATCH 08/11] load async --- .../linked-collections-list.ts | 21 +++++++++++++------ .../linked-collections/linked-collections.ts | 10 ++++++--- .../collections/linked-collections/utils.ts | 12 +---------- 3 files changed, 23 insertions(+), 20 deletions(-) diff --git a/frontend/src/features/collections/linked-collections/linked-collections-list.ts b/frontend/src/features/collections/linked-collections/linked-collections-list.ts index 365cd91b37..0beadea2cf 100644 --- a/frontend/src/features/collections/linked-collections/linked-collections-list.ts +++ b/frontend/src/features/collections/linked-collections/linked-collections-list.ts @@ -2,6 +2,7 @@ import { localized, msg } from "@lit/localize"; import clsx from "clsx"; import { html } from "lit"; import { customElement, property } from "lit/decorators.js"; +import { until } from "lit/directives/until.js"; import type { BtrixRemoveLinkedCollectionEvent, @@ -17,7 +18,9 @@ import { tw } from "@/utils/tailwind"; @localized() export class LinkedCollectionsList extends TailwindElement { @property({ type: Array }) - collections: CollectionLikeItem[] = []; + collections: (CollectionLikeItem & { + request?: Promise; + })[] = []; @property({ type: String }) baseUrl?: string; @@ -31,15 +34,22 @@ export class LinkedCollectionsList extends TailwindElement { } return html`
      - ${this.collections.map(this.renderItem)} + ${this.collections.map((item) => + item.request + ? until(item.request.then(this.renderItem)) + : this.renderItem(item, { loading: true }), + )}
    `; } - private readonly renderItem = (item: CollectionLikeItem) => { + private readonly renderItem = ( + item: CollectionLikeItem, + { loading } = { loading: false }, + ) => { const actual = isActualCollection(item); const content = [ - html`
    ${item.name}
    `, + html`
    ${item.name}
    `, ]; if (actual) { @@ -97,8 +107,7 @@ export class LinkedCollectionsList extends TailwindElement { } return html`
  • { // The API doesn't currently support getting collections by a list of IDs - const requests: Promise[] = []; + const collectionsWithRequest: { + id: string; + request: Promise; + }[] = []; ids.forEach(async (id) => { let request = this.collectionsMap.get(id); @@ -63,10 +66,10 @@ export class LinkedCollections extends BtrixElement { this.collectionsMap.set(id, request); } - requests.push(request); + collectionsWithRequest.push({ id, request }); }); - return await Promise.all(requests); + return collectionsWithRequest; }, args: () => [this.collectionIds] as const, }); @@ -76,6 +79,7 @@ export class LinkedCollections extends BtrixElement { this.collectionsTask.value || this.collectionIds.map((id) => ({ id })); return html` { - try { - collectionSchema.parse(item); - return true; - } catch (err) { - if (item.name) { - console.debug(err); - } - } - return false; -}; +): item is Collection => collectionSchema.safeParse(item).success; From 620ce93b116320b2b60c295cbac181084b420014 Mon Sep 17 00:00:00 2001 From: sua yoo Date: Tue, 23 Sep 2025 13:56:16 -0700 Subject: [PATCH 09/11] rename archived item dialog --- .../archived-items/item-metadata-editor.ts | 19 +++++++------- .../archived-item-detail.ts | 25 +++++++++++++++---- frontend/src/pages/org/archived-items.ts | 2 +- 3 files changed, 31 insertions(+), 15 deletions(-) diff --git a/frontend/src/features/archived-items/item-metadata-editor.ts b/frontend/src/features/archived-items/item-metadata-editor.ts index 1825f0ae2c..d719dbee37 100644 --- a/frontend/src/features/archived-items/item-metadata-editor.ts +++ b/frontend/src/features/archived-items/item-metadata-editor.ts @@ -1,8 +1,10 @@ import { localized, msg } from "@lit/localize"; import { serialize } from "@shoelace-style/shoelace/dist/utilities/form.js"; import Fuse from "fuse.js"; +import { html } from "lit"; import { customElement, property, state } from "lit/decorators.js"; +import { BtrixElement } from "@/classes/BtrixElement"; import type { TagInputEvent, Tags, @@ -12,7 +14,6 @@ import { type CollectionsChangeEvent } from "@/features/collections/collections- import type { ArchivedItem } from "@/types/crawler"; import { type WorkflowTag, type WorkflowTags } from "@/types/workflow"; import { maxLengthValidator } from "@/utils/form"; -import LiteElement, { html } from "@/utils/LiteElement"; /** * Usage: @@ -30,7 +31,7 @@ import LiteElement, { html } from "@/utils/LiteElement"; */ @customElement("btrix-item-metadata-editor") @localized() -export class CrawlMetadataEditor extends LiteElement { +export class CrawlMetadataEditor extends BtrixElement { @property({ type: Object }) crawl?: ArchivedItem; @@ -78,7 +79,7 @@ export class CrawlMetadataEditor extends LiteElement { render() { return html` (this.isDialogVisible = true)} @sl-after-hide=${() => (this.isDialogVisible = false)} @@ -125,11 +126,11 @@ export class CrawlMetadataEditor extends LiteElement { @tags-change=${(e: TagsChangeEvent) => (this.tagsToSave = e.detail.tags)} > -
    +
    (this.collectionsToSave = e.detail.collections)} > @@ -166,7 +167,7 @@ export class CrawlMetadataEditor extends LiteElement { private async fetchTags() { if (!this.crawl) return; try { - const { tags } = await this.apiFetch( + const { tags } = await this.api.fetch( `/orgs/${this.crawl.oid}/crawlconfigs/tagCounts`, ); @@ -220,7 +221,7 @@ export class CrawlMetadataEditor extends LiteElement { this.isSubmittingUpdate = true; try { - const data = await this.apiFetch<{ updated: boolean }>( + const data = await this.api.fetch<{ updated: boolean }>( `/orgs/${this.crawl.oid}/all-crawls/${this.crawl.id}`, { method: "PATCH", @@ -233,7 +234,7 @@ export class CrawlMetadataEditor extends LiteElement { } this.dispatchEvent(new CustomEvent("updated")); - this.notify({ + this.notify.toast({ message: msg("Successfully saved crawl details."), variant: "success", icon: "check2-circle", @@ -241,7 +242,7 @@ export class CrawlMetadataEditor extends LiteElement { }); this.requestClose(); } catch (e) { - this.notify({ + this.notify.toast({ message: msg("Sorry, couldn't save crawl details at this time."), variant: "danger", icon: "exclamation-octagon", diff --git a/frontend/src/pages/org/archived-item-detail/archived-item-detail.ts b/frontend/src/pages/org/archived-item-detail/archived-item-detail.ts index 3a525e7525..9a2c9162e2 100644 --- a/frontend/src/pages/org/archived-item-detail/archived-item-detail.ts +++ b/frontend/src/pages/org/archived-item-detail/archived-item-detail.ts @@ -420,7 +420,7 @@ export class ArchivedItemDetail extends BtrixElement { class="text-base" name="pencil" @click=${this.openMetadataEditor} - label=${msg("Edit Metadata")} + label=${msg("Edit Archived Item")} > `, )} @@ -430,9 +430,24 @@ export class ArchivedItemDetail extends BtrixElement { )}
    - ${this.renderPanel(msg("Collections"), this.renderCollections(), [ - tw`rounded-lg border p-4`, - ])} + ${this.renderPanel( + html` + ${this.renderTitle(msg("Collections"))} + ${when( + this.isCrawler, + () => html` + + `, + )} + `, + this.renderCollections(), + [tw`rounded-lg border p-4`], + )}
    `; @@ -658,7 +673,7 @@ export class ArchivedItemDetail extends BtrixElement { }} > - ${msg("Edit Metadata")} + ${msg("Edit Archived Item")} `, diff --git a/frontend/src/pages/org/archived-items.ts b/frontend/src/pages/org/archived-items.ts index 2393eb5713..5096a5d33f 100644 --- a/frontend/src/pages/org/archived-items.ts +++ b/frontend/src/pages/org/archived-items.ts @@ -593,7 +593,7 @@ export class CrawlsList extends BtrixElement { }} > - ${msg("Edit Metadata")} + ${msg("Edit Archived Item")} `, From 57fc1b9b5516ac7430ad5194ac9e7d61c113f934 Mon Sep 17 00:00:00 2001 From: sua yoo Date: Tue, 23 Sep 2025 14:10:23 -0700 Subject: [PATCH 10/11] update comment --- frontend/src/types/collection.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/frontend/src/types/collection.ts b/frontend/src/types/collection.ts index 5bed574004..dd06b2c795 100644 --- a/frontend/src/types/collection.ts +++ b/frontend/src/types/collection.ts @@ -25,8 +25,8 @@ export const publicCollectionSchema = z.object({ orgName: z.string(), orgPublicProfile: z.boolean(), name: z.string(), - created: z.string().datetime().nullable(), // TODO check if this just needed migration to not be null - modified: z.string().datetime(), + created: z.string().datetime().nullable(), // NOTE dates may be null for older collections since we can't backfill + modified: z.string().datetime().nullable(), caption: z.string().nullable(), description: z.string().nullable(), resources: z.array(z.string()), From ddd2f3b727ddea99674b1027732acca9c06bc7fb Mon Sep 17 00:00:00 2001 From: sua yoo Date: Tue, 23 Sep 2025 14:12:16 -0700 Subject: [PATCH 11/11] update section key --- frontend/src/components/ui/config-details.ts | 2 +- frontend/src/features/crawl-workflows/workflow-editor.ts | 8 ++++---- frontend/src/strings/crawl-workflows/section.ts | 2 +- frontend/src/utils/workflow.ts | 6 +++--- 4 files changed, 9 insertions(+), 9 deletions(-) diff --git a/frontend/src/components/ui/config-details.ts b/frontend/src/components/ui/config-details.ts index b72c518bf3..b8c84b5832 100644 --- a/frontend/src/components/ui/config-details.ts +++ b/frontend/src/components/ui/config-details.ts @@ -310,7 +310,7 @@ export class ConfigDetails extends BtrixElement { ${when(!this.hideMetadata, () => this.renderSection({ id: "collection", - heading: sectionStrings.collection, + heading: sectionStrings.collections, renderDescItems: () => html` ${this.renderSetting( html` { error: false, completed: hasConfigId, }, - collection: { + collections: { error: false, completed: hasConfigId, }, @@ -2283,7 +2283,7 @@ https://archiveweb.page/images/${"logo.svg"}`} `; }; - private renderCollection() { + private renderCollections() { return html` ${inputCol(html` = { behaviors: msg("Page Behavior"), browserSettings: msg("Browser Settings"), scheduling: msg("Scheduling"), - collection: msg("Collections"), + collections: msg("Collections"), metadata: msg("Metadata"), }; diff --git a/frontend/src/utils/workflow.ts b/frontend/src/utils/workflow.ts index 3d74596901..d6930f2fae 100644 --- a/frontend/src/utils/workflow.ts +++ b/frontend/src/utils/workflow.ts @@ -39,7 +39,7 @@ export const SECTIONS = [ "behaviors", "browserSettings", "scheduling", - "collection", + "collections", "metadata", ] as const; export const sectionsEnum = z.enum(SECTIONS); @@ -51,7 +51,7 @@ export enum GuideHash { Behaviors = "page-behavior", BrowserSettings = "browser-settings", Scheduling = "scheduling", - Collection = "collection", + Collections = "collections", Metadata = "metadata", } @@ -66,7 +66,7 @@ export const workflowTabToGuideHash: Record = { behaviors: GuideHash.Behaviors, browserSettings: GuideHash.BrowserSettings, scheduling: GuideHash.Scheduling, - collection: GuideHash.Metadata, + collections: GuideHash.Collections, metadata: GuideHash.Metadata, };