Manage collection from archived item details (#1085)
- Lists collections that an archived item belongs to in item detail view - Improves performance of collection add component --------- Co-authored-by: Tessa Walsh <tessa@bitarchivist.net>
This commit is contained in:
parent
00eddd548d
commit
ff6650d481
@ -60,7 +60,7 @@ export class CollectionsAdd extends LiteElement {
|
|||||||
emptyText?: string;
|
emptyText?: string;
|
||||||
|
|
||||||
@state()
|
@state()
|
||||||
private collections: CollectionList = [];
|
private collectionsData: { [id: string]: Collection } = {};
|
||||||
|
|
||||||
@state()
|
@state()
|
||||||
private collectionIds: string[] = [];
|
private collectionIds: string[] = [];
|
||||||
@ -78,12 +78,17 @@ export class CollectionsAdd extends LiteElement {
|
|||||||
@state()
|
@state()
|
||||||
private searchResultsOpen = false;
|
private searchResultsOpen = false;
|
||||||
|
|
||||||
async connectedCallback() {
|
connectedCallback() {
|
||||||
if (this.initialCollections) {
|
if (this.initialCollections) {
|
||||||
this.collectionIds = this.initialCollections;
|
this.collectionIds = this.initialCollections;
|
||||||
}
|
}
|
||||||
await this.initializeCollectionsFromIds();
|
|
||||||
super.connectedCallback();
|
super.connectedCallback();
|
||||||
|
this.initializeCollectionsFromIds();
|
||||||
|
}
|
||||||
|
|
||||||
|
disconnectedCallback() {
|
||||||
|
this.onSearchInput.cancel();
|
||||||
|
super.disconnectedCallback();
|
||||||
}
|
}
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
@ -95,12 +100,12 @@ export class CollectionsAdd extends LiteElement {
|
|||||||
${this.renderSearch()}
|
${this.renderSearch()}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
${when(this.collections, () =>
|
${when(this.collectionIds, () =>
|
||||||
this.collections.length
|
this.collectionIds.length
|
||||||
? html`
|
? html`
|
||||||
<div class="mb-2">
|
<div class="mb-2">
|
||||||
<ul class="contents">
|
<ul class="contents">
|
||||||
${this.collections.map(this.renderCollectionItem, this)}
|
${this.collectionIds.map(this.renderCollectionItem, this)}
|
||||||
</ul>
|
</ul>
|
||||||
</div>
|
</div>
|
||||||
`
|
`
|
||||||
@ -132,12 +137,17 @@ export class CollectionsAdd extends LiteElement {
|
|||||||
(collection) => collection.id === collId
|
(collection) => collection.id === collId
|
||||||
);
|
);
|
||||||
if (coll) {
|
if (coll) {
|
||||||
this.collections.push(coll);
|
const { id } = coll;
|
||||||
this.collectionIds.push(coll.id);
|
if (!this.collectionsData[id]) {
|
||||||
await this.dispatchChange();
|
this.collectionsData = {
|
||||||
|
...this.collectionsData,
|
||||||
|
[id]: await this.getCollection(id),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
this.collectionIds = [...this.collectionIds, id];
|
||||||
|
this.dispatchChange();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
await this.updateComplete;
|
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<sl-input
|
<sl-input
|
||||||
@ -167,7 +177,12 @@ export class CollectionsAdd extends LiteElement {
|
|||||||
`;
|
`;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!this.searchResults.length) {
|
// Filter out stale search results from last debounce invocation
|
||||||
|
const searchResults = this.searchResults.filter((res) =>
|
||||||
|
new RegExp(`^${this.searchByValue}`, "i").test(res.name)
|
||||||
|
);
|
||||||
|
|
||||||
|
if (!searchResults.length) {
|
||||||
return html`
|
return html`
|
||||||
<sl-menu-item slot="menu-item" disabled
|
<sl-menu-item slot="menu-item" disabled
|
||||||
>${msg("No matching Collections found.")}</sl-menu-item
|
>${msg("No matching Collections found.")}</sl-menu-item
|
||||||
@ -176,7 +191,7 @@ export class CollectionsAdd extends LiteElement {
|
|||||||
}
|
}
|
||||||
|
|
||||||
return html`
|
return html`
|
||||||
${this.searchResults.map((item: Collection) => {
|
${searchResults.map((item: Collection) => {
|
||||||
return html`
|
return html`
|
||||||
<sl-menu-item class="w-full" slot="menu-item" data-key=${item.id}>
|
<sl-menu-item class="w-full" slot="menu-item" data-key=${item.id}>
|
||||||
<div class="flex w-full gap-2 items-center">
|
<div class="flex w-full gap-2 items-center">
|
||||||
@ -193,40 +208,44 @@ export class CollectionsAdd extends LiteElement {
|
|||||||
`;
|
`;
|
||||||
}
|
}
|
||||||
|
|
||||||
private renderCollectionItem(collection: Collection) {
|
private renderCollectionItem(id: string) {
|
||||||
return html`<li class="mt-1 p-2 pl-5 pr-5 border rounded-sm">
|
const collection = this.collectionsData[id];
|
||||||
<div class="flex flex-row gap-2 justify-between items-center">
|
return html`<li class="mt-1 p-1 pl-3 border rounded-sm">
|
||||||
<div class="justify-self-stretch grow truncate">${
|
<div
|
||||||
collection.name
|
class="flex flex-row gap-2 justify-between items-center transition-opacity delay-75 ${collection
|
||||||
}</div>
|
? "opacity-100"
|
||||||
<div class="text-neutral-500 text-xs text-right font-monostyle">
|
: "opacity-0"}"
|
||||||
${msg(str`${collection.crawlCount} Crawls`)}
|
>
|
||||||
</div>
|
<div class="justify-self-stretch grow truncate">
|
||||||
<sl-icon-button
|
${collection?.name}
|
||||||
name="x-lg"
|
</div>
|
||||||
data-key=${collection.id}
|
<div class="text-neutral-500 text-xs text-right font-monostyle">
|
||||||
@click=${this.removeCollection}>
|
${msg(str`${collection?.crawlCount || 0} Crawls`)}
|
||||||
</sl-icon-button>
|
</div>
|
||||||
</dib>
|
<sl-icon-button
|
||||||
</li>`;
|
name="x-lg"
|
||||||
|
data-key=${id}
|
||||||
|
?disabled=${!collection}
|
||||||
|
@click=${this.removeCollection}
|
||||||
|
>
|
||||||
|
</sl-icon-button>
|
||||||
|
</div>
|
||||||
|
</li>`;
|
||||||
}
|
}
|
||||||
|
|
||||||
private async removeCollection(event: Event) {
|
private removeCollection(event: Event) {
|
||||||
const target = event.currentTarget as HTMLElement;
|
const target = event.currentTarget as HTMLElement;
|
||||||
const collectionId = target.getAttribute("data-key");
|
const collectionId = target.getAttribute("data-key");
|
||||||
if (collectionId) {
|
if (collectionId) {
|
||||||
const collIdIndex = this.collectionIds.indexOf(collectionId);
|
const collIdIndex = this.collectionIds.indexOf(collectionId);
|
||||||
if (collIdIndex > -1) {
|
if (collIdIndex > -1) {
|
||||||
this.collectionIds.splice(collIdIndex, 1);
|
this.collectionIds = [
|
||||||
}
|
...this.collectionIds.slice(0, collIdIndex),
|
||||||
const collIndex = this.collections.findIndex(
|
...this.collectionIds.slice(collIdIndex + 1),
|
||||||
(collection) => collection.id === collectionId
|
];
|
||||||
);
|
this.dispatchChange();
|
||||||
if (collIndex > -1) {
|
|
||||||
this.collections.splice(collIndex, 1);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
await this.requestUpdate();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private onSearchInput = debounce(200)(async (e: any) => {
|
private onSearchInput = debounce(200)(async (e: any) => {
|
||||||
@ -247,9 +266,7 @@ export class CollectionsAdd extends LiteElement {
|
|||||||
|
|
||||||
private filterOutSelectedCollections(results: CollectionList) {
|
private filterOutSelectedCollections(results: CollectionList) {
|
||||||
return results.filter((result) => {
|
return results.filter((result) => {
|
||||||
return this.collections.every((coll) => {
|
return !this.collectionIds.some((id) => id === result.id);
|
||||||
return coll.id !== result.id;
|
|
||||||
});
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -291,18 +308,25 @@ export class CollectionsAdd extends LiteElement {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private async initializeCollectionsFromIds() {
|
private async initializeCollectionsFromIds() {
|
||||||
for (let i = 0; i < this.collectionIds?.length; i++) {
|
if (!this.collectionIds) return;
|
||||||
const collId = this.collectionIds[i];
|
this.collectionIds.forEach(async (id) => {
|
||||||
const data: Collection = await this.apiFetch(
|
const data = await this.getCollection(id);
|
||||||
`/orgs/${this.orgId}/collections/${collId}`,
|
|
||||||
this.authState!
|
|
||||||
);
|
|
||||||
if (data) {
|
if (data) {
|
||||||
this.collections.push(data);
|
this.collectionsData = {
|
||||||
|
...this.collectionsData,
|
||||||
|
[id]: data,
|
||||||
|
};
|
||||||
}
|
}
|
||||||
}
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private getCollection = (collId: string): Promise<Collection> => {
|
||||||
|
return this.apiFetch(
|
||||||
|
`/orgs/${this.orgId}/collections/${collId}`,
|
||||||
|
this.authState!
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
private async dispatchChange() {
|
private async dispatchChange() {
|
||||||
await this.updateComplete;
|
await this.updateComplete;
|
||||||
this.dispatchEvent(
|
this.dispatchEvent(
|
||||||
|
@ -53,6 +53,9 @@ export class CrawlMetadataEditor extends LiteElement {
|
|||||||
@state()
|
@state()
|
||||||
private tagsToSave: Tags = [];
|
private tagsToSave: Tags = [];
|
||||||
|
|
||||||
|
@state()
|
||||||
|
private collectionsToSave: string[] = [];
|
||||||
|
|
||||||
// For fuzzy search:
|
// For fuzzy search:
|
||||||
private fuse = new Fuse([], {
|
private fuse = new Fuse([], {
|
||||||
shouldSort: false,
|
shouldSort: false,
|
||||||
@ -68,6 +71,7 @@ export class CrawlMetadataEditor extends LiteElement {
|
|||||||
if (changedProperties.has("crawl") && this.crawl) {
|
if (changedProperties.has("crawl") && this.crawl) {
|
||||||
this.includeName = this.crawl.type === "upload";
|
this.includeName = this.crawl.type === "upload";
|
||||||
this.tagsToSave = this.crawl.tags || [];
|
this.tagsToSave = this.crawl.tags || [];
|
||||||
|
this.collectionsToSave = this.crawl.collectionIds || [];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -121,6 +125,18 @@ export class CrawlMetadataEditor extends LiteElement {
|
|||||||
@tags-change=${(e: TagsChangeEvent) =>
|
@tags-change=${(e: TagsChangeEvent) =>
|
||||||
(this.tagsToSave = e.detail.tags)}
|
(this.tagsToSave = e.detail.tags)}
|
||||||
></btrix-tag-input>
|
></btrix-tag-input>
|
||||||
|
<div class="mt-4">
|
||||||
|
<btrix-collections-add
|
||||||
|
.authState=${this.authState}
|
||||||
|
.initialCollections=${this.crawl.collectionIds}
|
||||||
|
.orgId=${this.crawl.oid}
|
||||||
|
.configId=${"temp"}
|
||||||
|
label=${msg("Add to Collection")}
|
||||||
|
@collections-change=${(e: CustomEvent) =>
|
||||||
|
(this.collectionsToSave = e.detail.collections)}
|
||||||
|
>
|
||||||
|
</btrix-collections-add>
|
||||||
|
</div>
|
||||||
</form>
|
</form>
|
||||||
<div slot="footer" class="flex justify-between">
|
<div slot="footer" class="flex justify-between">
|
||||||
<sl-button form="crawlDetailsForm" type="reset" size="small"
|
<sl-button form="crawlDetailsForm" type="reset" size="small"
|
||||||
@ -173,29 +189,42 @@ export class CrawlMetadataEditor extends LiteElement {
|
|||||||
if (!(await this.checkFormValidity(formEl))) return;
|
if (!(await this.checkFormValidity(formEl))) return;
|
||||||
const { crawlDescription, name } = serialize(formEl);
|
const { crawlDescription, name } = serialize(formEl);
|
||||||
|
|
||||||
|
const params: {
|
||||||
|
collectionIds?: string[];
|
||||||
|
tags?: string[];
|
||||||
|
description?: string;
|
||||||
|
name?: string;
|
||||||
|
} = {};
|
||||||
|
if (this.includeName && name && name !== this.crawl.name) {
|
||||||
|
params.name = name as string;
|
||||||
|
}
|
||||||
if (
|
if (
|
||||||
(!this.includeName || name === this.crawl.name) &&
|
crawlDescription &&
|
||||||
crawlDescription === (this.crawl!.description ?? "") &&
|
crawlDescription !== (this.crawl.description ?? "")
|
||||||
JSON.stringify(this.tagsToSave) === JSON.stringify(this.crawl!.tags)
|
|
||||||
) {
|
) {
|
||||||
|
params.description = crawlDescription as string;
|
||||||
|
}
|
||||||
|
if (JSON.stringify(this.tagsToSave) !== JSON.stringify(this.crawl.tags)) {
|
||||||
|
params.tags = this.tagsToSave;
|
||||||
|
}
|
||||||
|
if (
|
||||||
|
JSON.stringify(this.collectionsToSave) !==
|
||||||
|
JSON.stringify(this.crawl.collectionIds)
|
||||||
|
) {
|
||||||
|
params.collectionIds = this.collectionsToSave;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!Object.keys(params).length) {
|
||||||
// No changes have been made
|
// No changes have been made
|
||||||
this.requestClose();
|
this.requestClose();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const params = {
|
|
||||||
tags: this.tagsToSave,
|
|
||||||
description: crawlDescription,
|
|
||||||
name,
|
|
||||||
};
|
|
||||||
|
|
||||||
this.isSubmittingUpdate = true;
|
this.isSubmittingUpdate = true;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const data = await this.apiFetch(
|
const data = await this.apiFetch(
|
||||||
`/orgs/${this.crawl!.oid}/${
|
`/orgs/${this.crawl!.oid}/all-crawls/${this.crawl.id}`,
|
||||||
this.crawl!.type === "crawl" ? "crawls" : "uploads"
|
|
||||||
}/${this.crawl.id}`,
|
|
||||||
this.authState!,
|
this.authState!,
|
||||||
{
|
{
|
||||||
method: "PATCH",
|
method: "PATCH",
|
||||||
|
@ -750,6 +750,32 @@ ${this.crawl?.description}
|
|||||||
() => html`<sl-skeleton></sl-skeleton>`
|
() => html`<sl-skeleton></sl-skeleton>`
|
||||||
)}
|
)}
|
||||||
</btrix-desc-list-item>
|
</btrix-desc-list-item>
|
||||||
|
<btrix-desc-list-item label=${msg("In Collections")}>
|
||||||
|
${when(
|
||||||
|
this.crawl,
|
||||||
|
() =>
|
||||||
|
when(
|
||||||
|
this.crawl!.collections.length,
|
||||||
|
() => html`
|
||||||
|
<ul>
|
||||||
|
${this.crawl!.collections.map(
|
||||||
|
({ id, name }) =>
|
||||||
|
html`<li class="mt-1">
|
||||||
|
<a
|
||||||
|
class="text-primary hover:text-indigo-400"
|
||||||
|
href=${`/orgs/${this.orgId}/collections/view/${id}`}
|
||||||
|
@click=${this.navLink}
|
||||||
|
>${name}</a
|
||||||
|
>
|
||||||
|
</li>`
|
||||||
|
)}
|
||||||
|
</ul>
|
||||||
|
`,
|
||||||
|
() => noneText
|
||||||
|
),
|
||||||
|
() => html`<sl-skeleton></sl-skeleton>`
|
||||||
|
)}
|
||||||
|
</btrix-desc-list-item>
|
||||||
</btrix-desc-list>
|
</btrix-desc-list>
|
||||||
`;
|
`;
|
||||||
}
|
}
|
||||||
|
@ -129,6 +129,7 @@ export type Crawl = CrawlConfig & {
|
|||||||
seedCount: number;
|
seedCount: number;
|
||||||
stopping: boolean;
|
stopping: boolean;
|
||||||
collectionIds: string[];
|
collectionIds: string[];
|
||||||
|
collections: { id: string; name: string }[];
|
||||||
type?: "crawl" | "upload" | null;
|
type?: "crawl" | "upload" | null;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user