Enable saving individual collection form sections (#1166)
- Moves metadata tab to first position - Adds save button to each section, stays in edit view on saving - Validates name exists before moving to next section or saving - Changes save button text to "Create Collection without Items" if crawl/uploads aren't selected in new collection - Fix server error not showing in UI
This commit is contained in:
parent
52207c175e
commit
6ddba105f4
@ -87,7 +87,7 @@ export class CollectionEdit extends LiteElement {
|
|||||||
e.detail.values;
|
e.detail.values;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
if (oldCrawlIds && oldCrawlIds) {
|
if (crawlIds && oldCrawlIds) {
|
||||||
await this.saveCrawlSelection({
|
await this.saveCrawlSelection({
|
||||||
crawlIds,
|
crawlIds,
|
||||||
oldCrawlIds,
|
oldCrawlIds,
|
||||||
@ -96,27 +96,29 @@ export class CollectionEdit extends LiteElement {
|
|||||||
await this.saveMetadata({
|
await this.saveMetadata({
|
||||||
name,
|
name,
|
||||||
description,
|
description,
|
||||||
isPublic: isPublic === "on",
|
isPublic,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
this.navTo(`/orgs/${this.orgId}/collections/view/${this.collectionId}`);
|
|
||||||
this.notify({
|
this.notify({
|
||||||
message: msg(
|
message: msg(
|
||||||
html`Successfully updated <strong>${name}</strong> Collection.`
|
html`Successfully updated <strong>${name}</strong> Collection.`
|
||||||
),
|
),
|
||||||
variant: "success",
|
variant: "success",
|
||||||
icon: "check2-circle",
|
icon: "check2-circle",
|
||||||
duration: 8000,
|
|
||||||
});
|
});
|
||||||
} catch (e: any) {
|
} catch (e: any) {
|
||||||
if (e?.isApiError) {
|
if (e?.isApiError) {
|
||||||
this.serverError = e?.message;
|
this.serverError = e?.message as string;
|
||||||
} else {
|
} else {
|
||||||
this.serverError = msg("Something unexpected went wrong");
|
this.serverError = msg("Something unexpected went wrong");
|
||||||
}
|
}
|
||||||
|
|
||||||
console.log(this.serverError);
|
this.notify({
|
||||||
|
message: this.serverError,
|
||||||
|
variant: "danger",
|
||||||
|
icon: "exclamation-octagon",
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
this.isSubmitting = false;
|
this.isSubmitting = false;
|
||||||
|
|||||||
@ -1,5 +1,5 @@
|
|||||||
import type { TemplateResult } from "lit";
|
import type { TemplateResult } from "lit";
|
||||||
import { state, property } from "lit/decorators.js";
|
import { state, property, query } from "lit/decorators.js";
|
||||||
import { msg, localized, str } from "@lit/localize";
|
import { msg, localized, str } from "@lit/localize";
|
||||||
import { when } from "lit/directives/when.js";
|
import { when } from "lit/directives/when.js";
|
||||||
import { guard } from "lit/directives/guard.js";
|
import { guard } from "lit/directives/guard.js";
|
||||||
@ -17,7 +17,7 @@ import uniqBy from "lodash/fp/uniqBy";
|
|||||||
import Fuse from "fuse.js";
|
import Fuse from "fuse.js";
|
||||||
import queryString from "query-string";
|
import queryString from "query-string";
|
||||||
import { serialize } from "@shoelace-style/shoelace/dist/utilities/form.js";
|
import { serialize } from "@shoelace-style/shoelace/dist/utilities/form.js";
|
||||||
import type { SlMenuItem } from "@shoelace-style/shoelace";
|
import type { SlInput, SlMenuItem } from "@shoelace-style/shoelace";
|
||||||
|
|
||||||
import type {
|
import type {
|
||||||
CheckboxChangeEvent,
|
CheckboxChangeEvent,
|
||||||
@ -36,7 +36,7 @@ import type { Collection } from "../../types/collection";
|
|||||||
import type { Crawl, CrawlState, Upload, Workflow } from "../../types/crawler";
|
import type { Crawl, CrawlState, Upload, Workflow } from "../../types/crawler";
|
||||||
import type { PageChangeEvent } from "../../components/pagination";
|
import type { PageChangeEvent } from "../../components/pagination";
|
||||||
|
|
||||||
const TABS = ["crawls", "uploads", "metadata"] as const;
|
const TABS = ["metadata", "crawls", "uploads"] as const;
|
||||||
type Tab = (typeof TABS)[number];
|
type Tab = (typeof TABS)[number];
|
||||||
type SearchFields = "name" | "firstSeed";
|
type SearchFields = "name" | "firstSeed";
|
||||||
type SearchResult = {
|
type SearchResult = {
|
||||||
@ -84,9 +84,14 @@ export type CollectionSubmitEvent = CustomEvent<{
|
|||||||
description: string | null;
|
description: string | null;
|
||||||
crawlIds: string[];
|
crawlIds: string[];
|
||||||
oldCrawlIds?: string[];
|
oldCrawlIds?: string[];
|
||||||
isPublic: string | null;
|
isPublic: boolean;
|
||||||
};
|
};
|
||||||
}>;
|
}>;
|
||||||
|
type FormValues = {
|
||||||
|
name: string;
|
||||||
|
description: string;
|
||||||
|
isPublic?: string;
|
||||||
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @event on-submit
|
* @event on-submit
|
||||||
@ -114,9 +119,15 @@ export class CollectionEditor extends LiteElement {
|
|||||||
@state()
|
@state()
|
||||||
private collectionCrawls?: Crawl[];
|
private collectionCrawls?: Crawl[];
|
||||||
|
|
||||||
|
@state()
|
||||||
|
private collectionUploads?: Upload[];
|
||||||
|
|
||||||
// Store crawl IDs to compare later
|
// Store crawl IDs to compare later
|
||||||
private savedCollectionCrawlIds: string[] = [];
|
private savedCollectionCrawlIds: string[] = [];
|
||||||
|
|
||||||
|
// Store upload IDs to compare later
|
||||||
|
private savedCollectionUploadIds: string[] = [];
|
||||||
|
|
||||||
@state()
|
@state()
|
||||||
private workflows?: APIPaginatedList & {
|
private workflows?: APIPaginatedList & {
|
||||||
items: Workflow[];
|
items: Workflow[];
|
||||||
@ -144,6 +155,10 @@ export class CollectionEditor extends LiteElement {
|
|||||||
[crawlId: string]: Crawl;
|
[crawlId: string]: Crawl;
|
||||||
} = {};
|
} = {};
|
||||||
|
|
||||||
|
@state()
|
||||||
|
private selectedUploads: {
|
||||||
|
[uploadId: string]: Upload;
|
||||||
|
} = {};
|
||||||
@state()
|
@state()
|
||||||
private activeTab: Tab = TABS[0];
|
private activeTab: Tab = TABS[0];
|
||||||
|
|
||||||
@ -165,10 +180,20 @@ export class CollectionEditor extends LiteElement {
|
|||||||
@state()
|
@state()
|
||||||
private searchResultsOpen = false;
|
private searchResultsOpen = false;
|
||||||
|
|
||||||
|
@query("#collectionForm-name-input")
|
||||||
|
private nameInput?: SlInput;
|
||||||
|
|
||||||
private get hasSearchStr() {
|
private get hasSearchStr() {
|
||||||
return this.searchByValue.length >= MIN_SEARCH_LENGTH;
|
return this.searchByValue.length >= MIN_SEARCH_LENGTH;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private get hasItemSelection() {
|
||||||
|
return Boolean(
|
||||||
|
Object.keys(this.selectedCrawls).length ||
|
||||||
|
Object.keys(this.selectedUploads).length
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
private get selectedSearchFilterKey() {
|
private get selectedSearchFilterKey() {
|
||||||
return Object.keys(this.fieldLabels).find((key) =>
|
return Object.keys(this.fieldLabels).find((key) =>
|
||||||
Boolean((this.filterWorkflowsBy as any)[key])
|
Boolean((this.filterWorkflowsBy as any)[key])
|
||||||
@ -201,6 +226,16 @@ export class CollectionEditor extends LiteElement {
|
|||||||
};
|
};
|
||||||
|
|
||||||
protected async willUpdate(changedProperties: Map<string, any>) {
|
protected async willUpdate(changedProperties: Map<string, any>) {
|
||||||
|
if (
|
||||||
|
changedProperties.has("activeTab") &&
|
||||||
|
!changedProperties.get("activeTab") &&
|
||||||
|
this.activeTab
|
||||||
|
) {
|
||||||
|
// First tab load
|
||||||
|
if (this.activeTab !== "metadata" && !this.collectionId) {
|
||||||
|
this.goToTab("metadata");
|
||||||
|
}
|
||||||
|
}
|
||||||
if (changedProperties.has("orgId") && this.orgId) {
|
if (changedProperties.has("orgId") && this.orgId) {
|
||||||
this.fetchSearchValues();
|
this.fetchSearchValues();
|
||||||
}
|
}
|
||||||
@ -313,6 +348,7 @@ export class CollectionEditor extends LiteElement {
|
|||||||
<div class="border rounded-lg py-2 flex-1">
|
<div class="border rounded-lg py-2 flex-1">
|
||||||
${guard(
|
${guard(
|
||||||
[
|
[
|
||||||
|
this.activeTab === "crawls",
|
||||||
this.isCrawler,
|
this.isCrawler,
|
||||||
this.collectionCrawls,
|
this.collectionCrawls,
|
||||||
this.selectedCrawls,
|
this.selectedCrawls,
|
||||||
@ -375,12 +411,32 @@ export class CollectionEditor extends LiteElement {
|
|||||||
</footer>
|
</footer>
|
||||||
</section>
|
</section>
|
||||||
<footer
|
<footer
|
||||||
class="col-span-full border rounded-lg px-6 py-4 flex justify-end"
|
class="col-span-full border rounded-lg px-6 py-4 flex gap-2 justify-end"
|
||||||
>
|
>
|
||||||
${when(
|
${when(
|
||||||
this.collectionId,
|
!this.collectionId,
|
||||||
() => html`
|
() => html`
|
||||||
<sl-button
|
<sl-button
|
||||||
|
type="button"
|
||||||
|
size="small"
|
||||||
|
class="mr-auto"
|
||||||
|
@click=${() => this.goToTab("metadata")}
|
||||||
|
>
|
||||||
|
<sl-icon slot="prefix" name="chevron-left"></sl-icon>
|
||||||
|
${msg("Previous Step")}
|
||||||
|
</sl-button>
|
||||||
|
<sl-button
|
||||||
|
type="button"
|
||||||
|
size="small"
|
||||||
|
@click=${() => this.goToTab("uploads")}
|
||||||
|
>
|
||||||
|
<sl-icon slot="suffix" name="chevron-right"></sl-icon>
|
||||||
|
${msg("Select Uploads")}
|
||||||
|
</sl-button>
|
||||||
|
`
|
||||||
|
)}
|
||||||
|
<sl-button
|
||||||
|
type="submit"
|
||||||
size="small"
|
size="small"
|
||||||
variant="primary"
|
variant="primary"
|
||||||
?disabled=${this.isSubmitting ||
|
?disabled=${this.isSubmitting ||
|
||||||
@ -388,18 +444,13 @@ export class CollectionEditor extends LiteElement {
|
|||||||
(isLoading) => isLoading === true
|
(isLoading) => isLoading === true
|
||||||
)}
|
)}
|
||||||
?loading=${this.isSubmitting}
|
?loading=${this.isSubmitting}
|
||||||
@click=${this.submitCrawlSelectionChanges}
|
|
||||||
>
|
>
|
||||||
${msg("Save Crawl Selection")}
|
${this.collectionId
|
||||||
|
? msg("Save Crawls")
|
||||||
|
: this.hasItemSelection
|
||||||
|
? msg("Create Collection")
|
||||||
|
: msg("Create Collection without Items")}
|
||||||
</sl-button>
|
</sl-button>
|
||||||
`,
|
|
||||||
() => html`
|
|
||||||
<sl-button size="small" @click=${() => this.goToTab("uploads")}>
|
|
||||||
<sl-icon slot="suffix" name="chevron-right"></sl-icon>
|
|
||||||
${msg("Select Uploads")}
|
|
||||||
</sl-button>
|
|
||||||
`
|
|
||||||
)}
|
|
||||||
</footer>
|
</footer>
|
||||||
</section>
|
</section>
|
||||||
`;
|
`;
|
||||||
@ -414,7 +465,7 @@ export class CollectionEditor extends LiteElement {
|
|||||||
</h4>
|
</h4>
|
||||||
<div class="border rounded-lg py-2 flex-1">
|
<div class="border rounded-lg py-2 flex-1">
|
||||||
${guard(
|
${guard(
|
||||||
[this.collectionCrawls, this.selectedCrawls],
|
[this.collectionUploads, this.selectedUploads],
|
||||||
this.renderCollectionUploadList
|
this.renderCollectionUploadList
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
@ -423,7 +474,7 @@ export class CollectionEditor extends LiteElement {
|
|||||||
<h4 class="text-base font-semibold mb-3">${msg("All Uploads")}</h4>
|
<h4 class="text-base font-semibold mb-3">${msg("All Uploads")}</h4>
|
||||||
<div class="flex-1">
|
<div class="flex-1">
|
||||||
${guard(
|
${guard(
|
||||||
[this.isCrawler, this.uploads, this.selectedCrawls],
|
[this.isCrawler, this.uploads, this.selectedUploads],
|
||||||
this.renderUploadList
|
this.renderUploadList
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
@ -449,22 +500,24 @@ export class CollectionEditor extends LiteElement {
|
|||||||
</footer>
|
</footer>
|
||||||
</section>
|
</section>
|
||||||
<footer
|
<footer
|
||||||
class="col-span-full border rounded-lg px-6 py-4 flex justify-between"
|
class="col-span-full border rounded-lg px-6 py-4 flex gap-2 justify-end"
|
||||||
>
|
>
|
||||||
${when(
|
${when(
|
||||||
!this.collectionId,
|
!this.collectionId,
|
||||||
() => html`
|
() => html`
|
||||||
<sl-button size="small" @click=${() => this.goToTab("crawls")}>
|
<sl-button
|
||||||
|
type="button"
|
||||||
|
class="mr-auto"
|
||||||
|
size="small"
|
||||||
|
@click=${() => this.goToTab("crawls")}
|
||||||
|
>
|
||||||
<sl-icon slot="prefix" name="chevron-left"></sl-icon>
|
<sl-icon slot="prefix" name="chevron-left"></sl-icon>
|
||||||
${msg("Select Crawls")}
|
${msg("Previous Step")}
|
||||||
</sl-button>
|
</sl-button>
|
||||||
`
|
`
|
||||||
)}
|
)}
|
||||||
${when(
|
|
||||||
this.collectionId,
|
|
||||||
() => html`
|
|
||||||
<sl-button
|
<sl-button
|
||||||
class="ml-auto"
|
type="submit"
|
||||||
size="small"
|
size="small"
|
||||||
variant="primary"
|
variant="primary"
|
||||||
?disabled=${this.isSubmitting ||
|
?disabled=${this.isSubmitting ||
|
||||||
@ -472,18 +525,13 @@ export class CollectionEditor extends LiteElement {
|
|||||||
(isLoading) => isLoading === true
|
(isLoading) => isLoading === true
|
||||||
)}
|
)}
|
||||||
?loading=${this.isSubmitting}
|
?loading=${this.isSubmitting}
|
||||||
@click=${this.submitCrawlSelectionChanges}
|
|
||||||
>
|
>
|
||||||
${msg("Save Upload Selection")}
|
${this.collectionId
|
||||||
|
? msg("Save Uploads")
|
||||||
|
: this.hasItemSelection
|
||||||
|
? msg("Create Collection")
|
||||||
|
: msg("Create Collection without Items")}
|
||||||
</sl-button>
|
</sl-button>
|
||||||
`,
|
|
||||||
() => html`
|
|
||||||
<sl-button size="small" @click=${() => this.goToTab("metadata")}>
|
|
||||||
<sl-icon slot="suffix" name="chevron-right"></sl-icon>
|
|
||||||
${msg("Enter Metadata")}
|
|
||||||
</sl-button>
|
|
||||||
`
|
|
||||||
)}
|
|
||||||
</footer>
|
</footer>
|
||||||
</section>
|
</section>
|
||||||
`;
|
`;
|
||||||
@ -495,6 +543,7 @@ export class CollectionEditor extends LiteElement {
|
|||||||
<div class="p-6">
|
<div class="p-6">
|
||||||
<sl-input
|
<sl-input
|
||||||
class="mb-2 with-max-help-text"
|
class="mb-2 with-max-help-text"
|
||||||
|
id="collectionForm-name-input"
|
||||||
name="name"
|
name="name"
|
||||||
label=${msg("Name")}
|
label=${msg("Name")}
|
||||||
placeholder=${msg("My Collection")}
|
placeholder=${msg("My Collection")}
|
||||||
@ -519,28 +568,42 @@ export class CollectionEditor extends LiteElement {
|
|||||||
>
|
>
|
||||||
</label>
|
</label>
|
||||||
</div>
|
</div>
|
||||||
<footer class="border-t px-6 py-4 flex justify-between">
|
<footer class="border-t px-6 py-4 flex gap-2 justify-end">
|
||||||
${when(
|
${when(
|
||||||
!this.collectionId,
|
!this.collectionId,
|
||||||
() => html`
|
() => html`
|
||||||
<sl-button size="small" @click=${() => this.goToTab("uploads")}>
|
<sl-button
|
||||||
<sl-icon slot="prefix" name="chevron-left"></sl-icon>
|
type="button"
|
||||||
${msg("Select Uploads")}
|
size="small"
|
||||||
|
variant="primary"
|
||||||
|
@click=${async () => {
|
||||||
|
await this.nameInput;
|
||||||
|
const isValid = this.nameInput!.reportValidity();
|
||||||
|
if (isValid) {
|
||||||
|
this.goToTab("crawls");
|
||||||
|
}
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<sl-icon slot="suffix" name="chevron-right"></sl-icon>
|
||||||
|
${msg("Select Items")}
|
||||||
</sl-button>
|
</sl-button>
|
||||||
`
|
`
|
||||||
)}
|
)}
|
||||||
<sl-button
|
<sl-button
|
||||||
class="ml-auto"
|
|
||||||
type="submit"
|
type="submit"
|
||||||
size="small"
|
size="small"
|
||||||
variant="primary"
|
variant=${this.collectionId ? "primary" : "default"}
|
||||||
?disabled=${this.isSubmitting ||
|
?disabled=${this.isSubmitting ||
|
||||||
Object.values(this.workflowIsLoading).some(
|
Object.values(this.workflowIsLoading).some(
|
||||||
(isLoading) => isLoading === true
|
(isLoading) => isLoading === true
|
||||||
)}
|
)}
|
||||||
?loading=${this.isSubmitting}
|
?loading=${this.isSubmitting}
|
||||||
>
|
>
|
||||||
${this.collectionId ? msg("Save Metadata") : msg("Save Collection")}
|
${this.collectionId
|
||||||
|
? msg("Save Metadata")
|
||||||
|
: this.hasItemSelection
|
||||||
|
? msg("Create Collection")
|
||||||
|
: msg("Create Collection without Items")}
|
||||||
</sl-button>
|
</sl-button>
|
||||||
</footer>
|
</footer>
|
||||||
</section>
|
</section>
|
||||||
@ -548,12 +611,17 @@ export class CollectionEditor extends LiteElement {
|
|||||||
};
|
};
|
||||||
|
|
||||||
private renderCollectionWorkflowList = () => {
|
private renderCollectionWorkflowList = () => {
|
||||||
|
if (this.activeTab !== "crawls") {
|
||||||
|
// Prevent rendering workflow list when tab isn't visible
|
||||||
|
// in order to accurately calculate visible item size
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
if (this.collectionId && !this.collectionCrawls) {
|
if (this.collectionId && !this.collectionCrawls) {
|
||||||
return this.renderLoading();
|
return this.renderLoading();
|
||||||
}
|
}
|
||||||
|
|
||||||
const crawlsInCollection =
|
const crawlsInCollection = this.collectionCrawls || [];
|
||||||
this.collectionCrawls?.filter((crawl) => crawl.type !== "upload") || [];
|
|
||||||
|
|
||||||
if (!crawlsInCollection.length) {
|
if (!crawlsInCollection.length) {
|
||||||
return html`
|
return html`
|
||||||
@ -592,13 +660,11 @@ export class CollectionEditor extends LiteElement {
|
|||||||
};
|
};
|
||||||
|
|
||||||
private renderCollectionUploadList = () => {
|
private renderCollectionUploadList = () => {
|
||||||
if (this.collectionId && !this.collectionCrawls) {
|
if (this.collectionId && !this.collectionUploads) {
|
||||||
return this.renderLoading();
|
return this.renderLoading();
|
||||||
}
|
}
|
||||||
|
|
||||||
const uploadsInCollection = (this.collectionCrawls?.filter(
|
const uploadsInCollection = this.collectionUploads || [];
|
||||||
(crawl) => crawl.type === "upload"
|
|
||||||
) || []) as Upload[];
|
|
||||||
|
|
||||||
if (!uploadsInCollection.length) {
|
if (!uploadsInCollection.length) {
|
||||||
return html`
|
return html`
|
||||||
@ -636,9 +702,9 @@ export class CollectionEditor extends LiteElement {
|
|||||||
aria-controls=${selectedCrawlIds.join(" ")}
|
aria-controls=${selectedCrawlIds.join(" ")}
|
||||||
@on-change=${(e: CheckboxChangeEvent) => {
|
@on-change=${(e: CheckboxChangeEvent) => {
|
||||||
if (e.detail.checked || !allChecked) {
|
if (e.detail.checked || !allChecked) {
|
||||||
this.selectCrawls(crawls);
|
this.selectItems(crawls, "crawl");
|
||||||
} else {
|
} else {
|
||||||
this.deselectCrawls(crawls);
|
this.deselectItems(crawls, "crawl");
|
||||||
}
|
}
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
@ -715,39 +781,39 @@ export class CollectionEditor extends LiteElement {
|
|||||||
`;
|
`;
|
||||||
};
|
};
|
||||||
|
|
||||||
private renderCrawl(crawl: Crawl, workflowId?: string) {
|
private renderCrawl(item: Crawl, workflowId?: string) {
|
||||||
return html`
|
return html`
|
||||||
<btrix-checkbox-list-item
|
<btrix-checkbox-list-item
|
||||||
id=${crawl.id}
|
id=${item.id}
|
||||||
name="crawlIds"
|
name="crawlIds"
|
||||||
value=${crawl.id}
|
value=${item.id}
|
||||||
?checked=${this.selectedCrawls[crawl.id]}
|
?checked=${this.selectedCrawls[item.id]}
|
||||||
@on-change=${(e: CheckboxChangeEvent) => {
|
@on-change=${(e: CheckboxChangeEvent) => {
|
||||||
if (e.detail.checked) {
|
if (e.detail.checked) {
|
||||||
this.selectedCrawls = mergeDeep(this.selectedCrawls, {
|
this.selectedCrawls = mergeDeep(this.selectedCrawls, {
|
||||||
[crawl.id]: crawl,
|
[item.id]: item,
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
this.selectedCrawls = omit([crawl.id])(this.selectedCrawls) as any;
|
this.selectedCrawls = omit([item.id])(this.selectedCrawls) as any;
|
||||||
}
|
}
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<div class="flex items-center">
|
<div class="flex items-center">
|
||||||
<btrix-crawl-status
|
<btrix-crawl-status
|
||||||
state=${crawl.state}
|
state=${item.state}
|
||||||
hideLabel
|
hideLabel
|
||||||
></btrix-crawl-status>
|
></btrix-crawl-status>
|
||||||
<div class="flex-1">
|
<div class="flex-1">
|
||||||
${workflowId
|
${workflowId
|
||||||
? html`<sl-format-date
|
? html`<sl-format-date
|
||||||
date=${`${crawl.finished}Z`}
|
date=${`${item.finished}Z`}
|
||||||
month="2-digit"
|
month="2-digit"
|
||||||
day="2-digit"
|
day="2-digit"
|
||||||
year="2-digit"
|
year="2-digit"
|
||||||
hour="2-digit"
|
hour="2-digit"
|
||||||
minute="2-digit"
|
minute="2-digit"
|
||||||
></sl-format-date>`
|
></sl-format-date>`
|
||||||
: this.renderSeedsLabel(crawl.firstSeed, crawl.seedCount)}
|
: this.renderSeedsLabel(item.firstSeed, item.seedCount)}
|
||||||
</div>
|
</div>
|
||||||
<div class="w-16 font-monostyle truncate">
|
<div class="w-16 font-monostyle truncate">
|
||||||
<sl-tooltip content=${msg("Pages in crawl")}>
|
<sl-tooltip content=${msg("Pages in crawl")}>
|
||||||
@ -757,7 +823,7 @@ export class CollectionEditor extends LiteElement {
|
|||||||
name="file-earmark-richtext"
|
name="file-earmark-richtext"
|
||||||
></sl-icon>
|
></sl-icon>
|
||||||
<div class="ml-1 text-xs">
|
<div class="ml-1 text-xs">
|
||||||
${this.numberFormatter.format(+(crawl.stats?.done || 0))}
|
${this.numberFormatter.format(+(item.stats?.done || 0))}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</sl-tooltip>
|
</sl-tooltip>
|
||||||
@ -765,7 +831,7 @@ export class CollectionEditor extends LiteElement {
|
|||||||
<div class="w-14">
|
<div class="w-14">
|
||||||
<sl-format-bytes
|
<sl-format-bytes
|
||||||
class="text-neutral-500 text-xs font-monostyle"
|
class="text-neutral-500 text-xs font-monostyle"
|
||||||
value=${crawl.fileSize || 0}
|
value=${item.fileSize || 0}
|
||||||
display="narrow"
|
display="narrow"
|
||||||
></sl-format-bytes>
|
></sl-format-bytes>
|
||||||
</div>
|
</div>
|
||||||
@ -774,29 +840,29 @@ export class CollectionEditor extends LiteElement {
|
|||||||
`;
|
`;
|
||||||
}
|
}
|
||||||
|
|
||||||
private renderUpload = (crawl: Upload) => {
|
private renderUpload = (item: Upload) => {
|
||||||
return html`
|
return html`
|
||||||
<btrix-checkbox-list-item
|
<btrix-checkbox-list-item
|
||||||
id=${crawl.id}
|
id=${item.id}
|
||||||
name="crawlIds"
|
name="crawlIds"
|
||||||
value=${crawl.id}
|
value=${item.id}
|
||||||
?checked=${this.selectedCrawls[crawl.id]}
|
?checked=${this.selectedUploads[item.id]}
|
||||||
@on-change=${(e: CheckboxChangeEvent) => {
|
@on-change=${(e: CheckboxChangeEvent) => {
|
||||||
if (e.detail.checked) {
|
if (e.detail.checked) {
|
||||||
this.selectedCrawls = mergeDeep(this.selectedCrawls, {
|
this.selectedUploads = mergeDeep(this.selectedUploads, {
|
||||||
[crawl.id]: crawl,
|
[item.id]: item,
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
this.selectedCrawls = omit([crawl.id])(this.selectedCrawls) as any;
|
this.selectedUploads = omit([item.id])(this.selectedUploads) as any;
|
||||||
}
|
}
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<div class="flex items-center">
|
<div class="flex items-center">
|
||||||
<div class="flex-1">${crawl.name}</div>
|
<div class="flex-1">${item.name}</div>
|
||||||
<div class="w-14">
|
<div class="w-14">
|
||||||
<sl-format-bytes
|
<sl-format-bytes
|
||||||
class="text-neutral-500 text-xs font-monostyle"
|
class="text-neutral-500 text-xs font-monostyle"
|
||||||
value=${crawl.fileSize || 0}
|
value=${item.fileSize || 0}
|
||||||
display="narrow"
|
display="narrow"
|
||||||
></sl-format-bytes>
|
></sl-format-bytes>
|
||||||
</div>
|
</div>
|
||||||
@ -994,7 +1060,7 @@ export class CollectionEditor extends LiteElement {
|
|||||||
if (e.detail.checked || !allChecked) {
|
if (e.detail.checked || !allChecked) {
|
||||||
this.selectWorkflow(workflow.id);
|
this.selectWorkflow(workflow.id);
|
||||||
} else {
|
} else {
|
||||||
this.deselectCrawls(crawls);
|
this.deselectItems(crawls, "crawl");
|
||||||
}
|
}
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
@ -1067,28 +1133,28 @@ export class CollectionEditor extends LiteElement {
|
|||||||
`;
|
`;
|
||||||
};
|
};
|
||||||
|
|
||||||
private renderUploadItem = (crawl: Upload) => {
|
private renderUploadItem = (upload: Upload) => {
|
||||||
return html`
|
return html`
|
||||||
<btrix-checkbox-list-item
|
<btrix-checkbox-list-item
|
||||||
?checked=${this.selectedCrawls[crawl.id]}
|
?checked=${this.selectedUploads[upload.id]}
|
||||||
@on-change=${(e: CheckboxChangeEvent) => {
|
@on-change=${(e: CheckboxChangeEvent) => {
|
||||||
if (e.detail.checked) {
|
if (e.detail.checked) {
|
||||||
this.collectionCrawls = uniqBy("id")([
|
this.collectionUploads = uniqBy("id")([
|
||||||
...(this.collectionCrawls || []),
|
...(this.collectionUploads || []),
|
||||||
...[crawl],
|
...[upload],
|
||||||
] as any) as any;
|
] as any) as any;
|
||||||
this.selectCrawls([crawl]);
|
this.selectItems([upload], "upload");
|
||||||
} else {
|
} else {
|
||||||
this.deselectCrawls([crawl]);
|
this.deselectItems([upload], "upload");
|
||||||
}
|
}
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<div class="flex items-center">
|
<div class="flex items-center">
|
||||||
<div class="flex-1">${crawl.name}</div>
|
<div class="flex-1">${upload.name}</div>
|
||||||
<div class="w-14">
|
<div class="w-14">
|
||||||
<sl-format-bytes
|
<sl-format-bytes
|
||||||
class="text-neutral-500 text-xs font-monostyle"
|
class="text-neutral-500 text-xs font-monostyle"
|
||||||
value=${crawl.fileSize || 0}
|
value=${upload.fileSize || 0}
|
||||||
display="narrow"
|
display="narrow"
|
||||||
></sl-format-bytes>
|
></sl-format-bytes>
|
||||||
</div>
|
</div>
|
||||||
@ -1156,26 +1222,27 @@ export class CollectionEditor extends LiteElement {
|
|||||||
</div>
|
</div>
|
||||||
`;
|
`;
|
||||||
|
|
||||||
private selectCrawls(crawls: (Crawl | Upload)[]) {
|
private selectItems(items: (Crawl | Upload)[], itemType: Crawl["type"]) {
|
||||||
const allCrawls = crawls.reduce(
|
const allItems = keyBy("id")(items);
|
||||||
(acc: any, crawl: Crawl | Upload) => ({
|
if (itemType === "upload") {
|
||||||
...acc,
|
this.selectedUploads = mergeDeep(this.selectedUploads, allItems);
|
||||||
[crawl.id]: crawl,
|
} else {
|
||||||
}),
|
this.selectedCrawls = mergeDeep(this.selectedCrawls, allItems);
|
||||||
{}
|
}
|
||||||
);
|
|
||||||
this.selectedCrawls = mergeDeep(this.selectedCrawls, allCrawls);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private deselectCrawls(crawls: (Crawl | Upload)[]) {
|
private deselectItems(items: (Crawl | Upload)[], itemType: Crawl["type"]) {
|
||||||
this.selectedCrawls = omit(crawls.map(({ id }) => id))(
|
const omitter = omit(items.map(({ id }) => id));
|
||||||
this.selectedCrawls
|
if (itemType === "upload") {
|
||||||
) as any;
|
this.selectedUploads = omitter(this.selectedUploads) as any;
|
||||||
|
} else {
|
||||||
|
this.selectedCrawls = omitter(this.selectedCrawls) as any;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private async selectWorkflow(workflowId: string) {
|
private async selectWorkflow(workflowId: string) {
|
||||||
const crawls = await this.fetchWorkflowCrawls(workflowId);
|
const crawls = await this.fetchWorkflowCrawls(workflowId);
|
||||||
this.selectCrawls(crawls);
|
this.selectItems(crawls, "crawl");
|
||||||
}
|
}
|
||||||
|
|
||||||
private checkboxGroupUpdated = async (el: any) => {
|
private checkboxGroupUpdated = async (el: any) => {
|
||||||
@ -1235,34 +1302,33 @@ export class CollectionEditor extends LiteElement {
|
|||||||
}
|
}
|
||||||
}) as any;
|
}) as any;
|
||||||
|
|
||||||
private async submitCrawlSelectionChanges() {
|
|
||||||
this.dispatchEvent(
|
|
||||||
<CollectionSubmitEvent>new CustomEvent("on-submit", {
|
|
||||||
detail: {
|
|
||||||
values: {
|
|
||||||
oldCrawlIds: this.savedCollectionCrawlIds,
|
|
||||||
crawlIds: Object.keys(this.selectedCrawls),
|
|
||||||
},
|
|
||||||
},
|
|
||||||
})
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
private async onSubmit(event: SubmitEvent) {
|
private async onSubmit(event: SubmitEvent) {
|
||||||
event.preventDefault();
|
event.preventDefault();
|
||||||
event.stopPropagation();
|
event.stopPropagation();
|
||||||
await this.updateComplete;
|
await this.updateComplete;
|
||||||
|
|
||||||
const form = event.target as HTMLFormElement;
|
const form = event.target as HTMLFormElement;
|
||||||
if (form.querySelector("[data-invalid]")) {
|
const isNameValid = this.nameInput!.checkValidity();
|
||||||
|
if (!isNameValid) {
|
||||||
|
this.goToTab("metadata");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const values = serialize(form);
|
const formValues = serialize(form) as FormValues;
|
||||||
if (!this.collectionId) {
|
let values: any = {};
|
||||||
// Crawl IDs can only be saved in new collections
|
|
||||||
values.crawlIds = Object.keys(this.selectedCrawls);
|
if (this.collectionId) {
|
||||||
|
values = this.getEditedValues(formValues);
|
||||||
|
} else {
|
||||||
|
values.name = formValues.name;
|
||||||
|
values.description = formValues.description;
|
||||||
|
values.isPublic = Boolean(formValues.isPublic);
|
||||||
|
values.crawlIds = [
|
||||||
|
...Object.keys(this.selectedCrawls),
|
||||||
|
...Object.keys(this.selectedUploads),
|
||||||
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
this.dispatchEvent(
|
this.dispatchEvent(
|
||||||
<CollectionSubmitEvent>new CustomEvent("on-submit", {
|
<CollectionSubmitEvent>new CustomEvent("on-submit", {
|
||||||
detail: { values },
|
detail: { values },
|
||||||
@ -1270,6 +1336,36 @@ export class CollectionEditor extends LiteElement {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private getEditedValues(formValues: FormValues) {
|
||||||
|
const values: any = {};
|
||||||
|
switch (this.activeTab) {
|
||||||
|
case "metadata": {
|
||||||
|
values.name = formValues.name;
|
||||||
|
values.description = formValues.description;
|
||||||
|
values.isPublic = Boolean(formValues.isPublic);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case "crawls": {
|
||||||
|
values.crawlIds = Object.keys(this.selectedCrawls);
|
||||||
|
if (this.collectionId) {
|
||||||
|
values.oldCrawlIds = this.savedCollectionCrawlIds;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case "uploads": {
|
||||||
|
values.crawlIds = Object.keys(this.selectedUploads);
|
||||||
|
if (this.collectionId) {
|
||||||
|
values.oldCrawlIds = this.savedCollectionUploadIds;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
return values;
|
||||||
|
}
|
||||||
|
|
||||||
private getActivePanelFromHash = () => {
|
private getActivePanelFromHash = () => {
|
||||||
const hashValue = window.location.hash.slice(1);
|
const hashValue = window.location.hash.slice(1);
|
||||||
if (TABS.includes(hashValue as any)) {
|
if (TABS.includes(hashValue as any)) {
|
||||||
@ -1378,23 +1474,21 @@ export class CollectionEditor extends LiteElement {
|
|||||||
crawlsRes.status === "fulfilled" ? crawlsRes.value.items : [];
|
crawlsRes.status === "fulfilled" ? crawlsRes.value.items : [];
|
||||||
const uploads =
|
const uploads =
|
||||||
uploadsRes.status === "fulfilled" ? uploadsRes.value.items : [];
|
uploadsRes.status === "fulfilled" ? uploadsRes.value.items : [];
|
||||||
const crawlsAndUploads = [...crawls, ...uploads];
|
|
||||||
|
|
||||||
this.selectedCrawls = mergeDeep(
|
this.selectedCrawls = mergeDeep(this.selectedCrawls, keyBy("id")(crawls));
|
||||||
this.selectedCrawls,
|
this.selectedUploads = mergeDeep(
|
||||||
crawlsAndUploads.reduce(
|
this.selectedUploads,
|
||||||
(acc, crawl) => ({
|
keyBy("id")(uploads)
|
||||||
...acc,
|
|
||||||
[crawl.id]: crawl,
|
|
||||||
}),
|
|
||||||
{}
|
|
||||||
)
|
|
||||||
);
|
);
|
||||||
|
|
||||||
// TODO remove omit once API removes errors
|
// TODO remove omit once API removes errors
|
||||||
this.collectionCrawls = crawlsAndUploads.map(omit("errors")) as Crawl[];
|
this.collectionCrawls = crawls.map(omit("errors")) as Crawl[];
|
||||||
|
this.collectionUploads = uploads;
|
||||||
// Store crawl IDs to compare later
|
// Store crawl IDs to compare later
|
||||||
this.savedCollectionCrawlIds = this.collectionCrawls.map(({ id }) => id);
|
this.savedCollectionCrawlIds = this.collectionCrawls.map(({ id }) => id);
|
||||||
|
this.savedCollectionUploadIds = this.collectionUploads.map(
|
||||||
|
({ id }) => id
|
||||||
|
);
|
||||||
} catch {
|
} catch {
|
||||||
this.notify({
|
this.notify({
|
||||||
message: msg(
|
message: msg(
|
||||||
|
|||||||
@ -56,7 +56,6 @@ export class CollectionsNew extends LiteElement {
|
|||||||
|
|
||||||
private async onSubmit(e: CollectionSubmitEvent) {
|
private async onSubmit(e: CollectionSubmitEvent) {
|
||||||
this.isSubmitting = true;
|
this.isSubmitting = true;
|
||||||
console.log("submit", e.detail.values);
|
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const { name, description, crawlIds, isPublic } = e.detail.values;
|
const { name, description, crawlIds, isPublic } = e.detail.values;
|
||||||
@ -69,7 +68,7 @@ export class CollectionsNew extends LiteElement {
|
|||||||
name,
|
name,
|
||||||
description,
|
description,
|
||||||
crawlIds,
|
crawlIds,
|
||||||
public: isPublic === "on",
|
public: isPublic,
|
||||||
}),
|
}),
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
@ -78,18 +77,21 @@ export class CollectionsNew extends LiteElement {
|
|||||||
message: msg(str`Successfully created "${data.name}" Collection.`),
|
message: msg(str`Successfully created "${data.name}" Collection.`),
|
||||||
variant: "success",
|
variant: "success",
|
||||||
icon: "check2-circle",
|
icon: "check2-circle",
|
||||||
duration: 8000,
|
|
||||||
});
|
});
|
||||||
|
|
||||||
this.navTo(`/orgs/${this.orgId}/collections`);
|
this.navTo(`/orgs/${this.orgId}/collections/view/${data.id}`);
|
||||||
} catch (e: any) {
|
} catch (e: any) {
|
||||||
if (e?.isApiError) {
|
if (e?.isApiError) {
|
||||||
this.serverError = e?.message;
|
this.serverError = e?.message as string;
|
||||||
} else {
|
} else {
|
||||||
this.serverError = msg("Something unexpected went wrong");
|
this.serverError = msg("Something unexpected went wrong");
|
||||||
}
|
}
|
||||||
|
|
||||||
console.log(this.serverError);
|
this.notify({
|
||||||
|
message: this.serverError,
|
||||||
|
variant: "danger",
|
||||||
|
icon: "exclamation-octagon",
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
this.isSubmitting = false;
|
this.isSubmitting = false;
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user