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:
sua yoo 2023-09-14 15:21:01 -07:00 committed by GitHub
parent 52207c175e
commit 6ddba105f4
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
3 changed files with 242 additions and 144 deletions

View File

@ -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;

View File

@ -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,31 +411,46 @@ 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" size="small"
variant="primary" class="mr-auto"
?disabled=${this.isSubmitting || @click=${() => this.goToTab("metadata")}
Object.values(this.workflowIsLoading).some(
(isLoading) => isLoading === true
)}
?loading=${this.isSubmitting}
@click=${this.submitCrawlSelectionChanges}
> >
${msg("Save Crawl Selection")} <sl-icon slot="prefix" name="chevron-left"></sl-icon>
${msg("Previous Step")}
</sl-button> </sl-button>
`, <sl-button
() => html` type="button"
<sl-button size="small" @click=${() => this.goToTab("uploads")}> size="small"
@click=${() => this.goToTab("uploads")}
>
<sl-icon slot="suffix" name="chevron-right"></sl-icon> <sl-icon slot="suffix" name="chevron-right"></sl-icon>
${msg("Select Uploads")} ${msg("Select Uploads")}
</sl-button> </sl-button>
` `
)} )}
<sl-button
type="submit"
size="small"
variant="primary"
?disabled=${this.isSubmitting ||
Object.values(this.workflowIsLoading).some(
(isLoading) => isLoading === true
)}
?loading=${this.isSubmitting}
>
${this.collectionId
? msg("Save Crawls")
: this.hasItemSelection
? msg("Create Collection")
: msg("Create Collection without Items")}
</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,41 +500,38 @@ 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`
<sl-button size="small" @click=${() => this.goToTab("crawls")}>
<sl-icon slot="prefix" name="chevron-left"></sl-icon>
${msg("Select Crawls")}
</sl-button>
`
)}
${when(
this.collectionId,
() => html` () => html`
<sl-button <sl-button
class="ml-auto" type="button"
class="mr-auto"
size="small" size="small"
variant="primary" @click=${() => this.goToTab("crawls")}
?disabled=${this.isSubmitting ||
Object.values(this.workflowIsLoading).some(
(isLoading) => isLoading === true
)}
?loading=${this.isSubmitting}
@click=${this.submitCrawlSelectionChanges}
> >
${msg("Save Upload Selection")} <sl-icon slot="prefix" name="chevron-left"></sl-icon>
</sl-button> ${msg("Previous Step")}
`,
() => html`
<sl-button size="small" @click=${() => this.goToTab("metadata")}>
<sl-icon slot="suffix" name="chevron-right"></sl-icon>
${msg("Enter Metadata")}
</sl-button> </sl-button>
` `
)} )}
<sl-button
type="submit"
size="small"
variant="primary"
?disabled=${this.isSubmitting ||
Object.values(this.workflowIsLoading).some(
(isLoading) => isLoading === true
)}
?loading=${this.isSubmitting}
>
${this.collectionId
? msg("Save Uploads")
: this.hasItemSelection
? msg("Create Collection")
: msg("Create Collection without Items")}
</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(

View File

@ -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;