Skip to content

Commit

Permalink
feat: Update collection sorting & metadata (#2324)
Browse files Browse the repository at this point in the history
- Fixes #2321
- Resolves #2323

Follows #2327, should be
rebased and merged afterwards

## Changes

- Refactors dashboard and org profile preview to use private API
endpoint, to fix public collections not showing the org visibility is
hidden
- Enables sorting collections by `dateLatest`, sorts public collections
by `dateLatest` by default
- Enables sorting collections by page count
- Shows collection period (i.e. `dateEarliest` to `dateLatest`) in
collections list
- Shows same collection metadata in private and public views, updates
private view info bar
- Fixes "Update Org Profile" action item showing for crawler roles





<!-- ## Follow-ups -->

---------

Co-authored-by: Tessa Walsh <[email protected]>
  • Loading branch information
SuaYoo and tw4l authored Jan 22, 2025
1 parent 3366b18 commit 273648b
Show file tree
Hide file tree
Showing 15 changed files with 329 additions and 198 deletions.
26 changes: 22 additions & 4 deletions backend/btrixcloud/colls.py
Original file line number Diff line number Diff line change
Expand Up @@ -396,7 +396,7 @@ async def list_collections(
page = page - 1
skip = page * page_size

match_query: dict[str, object] = {"oid": org.id}
match_query: Dict[str, Union[str, UUID, int, object]] = {"oid": org.id}

if name:
match_query["name"] = name
Expand All @@ -409,15 +409,33 @@ async def list_collections(
elif access:
match_query["access"] = access

aggregate = [{"$match": match_query}]
aggregate: List[Dict[str, Union[str, UUID, int, object]]] = [
{"$match": match_query}
]

if sort_by:
if sort_by not in ("modified", "name", "description", "totalSize"):
if sort_by not in (
"created",
"modified",
"dateLatest",
"name",
"crawlCount",
"pageCount",
"totalSize",
"description",
"caption",
):
raise HTTPException(status_code=400, detail="invalid_sort_by")
if sort_direction not in (1, -1):
raise HTTPException(status_code=400, detail="invalid_sort_direction")

aggregate.extend([{"$sort": {sort_by: sort_direction}}])
sort_query = {sort_by: sort_direction}

# add secondary sort keys:
if sort_by == "dateLatest":
sort_query["dateEarliest"] = sort_direction

aggregate.extend([{"$sort": sort_query}])

aggregate.extend(
[
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -135,12 +135,13 @@ export class CollectionMetadataDialog extends BtrixElement {
<sl-tooltip>
<span slot="content">
${msg(
"Write a short description that summarizes this collection. If the collection is public, this description will be visible next to the collection name.",
"Write a short description that summarizes this collection. If the collection is shareable, this will appear next to the collection name.",
)}
${this.collection
? nothing
: msg(
"You can write a longer description in the 'About' section after creating the collection.",
html`You can add a longer description in the “About”
section after creating the collection.`,
)}
</span>
<sl-icon
Expand Down
57 changes: 57 additions & 0 deletions frontend/src/layouts/collections/metadataColumn.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
import { msg } from "@lit/localize";
import { html, type TemplateResult } from "lit";
import { when } from "lit/directives/when.js";

import { monthYearDateRange } from "@/strings/utils";
import type { Collection, PublicCollection } from "@/types/collection";
import localize from "@/utils/localize";
import { pluralOf } from "@/utils/pluralize";

export function metadataItemWithCollection(
collection?: Collection | PublicCollection,
) {
return function metadataItem({
label,
render,
}: {
label: string | TemplateResult;
render: (c: PublicCollection) => TemplateResult | string;
}) {
return html`
<btrix-desc-list-item label=${label}>
${when(
collection,
render,
() => html`<sl-skeleton class="w-full"></sl-skeleton>`,
)}
</btrix-desc-list-item>
`;
};
}

export function metadataColumn(collection?: Collection | PublicCollection) {
const metadataItem = metadataItemWithCollection(collection);

return html`
<btrix-desc-list>
${metadataItem({
label: msg("Collection Period"),
render: (col) => html`
<span class="font-sans">
${monthYearDateRange(col.dateEarliest, col.dateLatest)}
</span>
`,
})}
${metadataItem({
label: msg("Pages in Collection"),
render: (col) =>
`${localize.number(col.pageCount)} ${pluralOf("pages", col.pageCount)}`,
})}
${metadataItem({
label: msg("Total Page Snapshots"),
render: (col) =>
`${localize.number(col.snapshotCount)} ${pluralOf("snapshots", col.snapshotCount)}`,
})}
</btrix-desc-list>
`;
}
36 changes: 3 additions & 33 deletions frontend/src/pages/collections/collection.ts
Original file line number Diff line number Diff line change
@@ -1,11 +1,12 @@
import { localized, msg, str } from "@lit/localize";
import { localized, msg } from "@lit/localize";
import { Task, TaskStatus } from "@lit/task";
import { html, type TemplateResult } from "lit";
import { customElement, property } from "lit/decorators.js";
import { ifDefined } from "lit/directives/if-defined.js";
import { when } from "lit/directives/when.js";

import { BtrixElement } from "@/classes/BtrixElement";
import { metadataColumn } from "@/layouts/collections/metadataColumn";
import { page } from "@/layouts/page";
import { RouteNamespace } from "@/routes";
import type { PublicCollection } from "@/types/collection";
Expand Down Expand Up @@ -211,39 +212,8 @@ export class Collection extends BtrixElement {
`;
}

// TODO Consolidate with collection-detail.ts
private renderAbout(collection: PublicCollection) {
const dateRange = () => {
if (!collection.dateEarliest || !collection.dateLatest) {
return msg("n/a");
}
const format: Intl.DateTimeFormatOptions = {
month: "long",
year: "numeric",
};
const dateEarliest = this.localize.date(collection.dateEarliest, format);
const dateLatest = this.localize.date(collection.dateLatest, format);

if (dateEarliest === dateLatest) return dateLatest;

return msg(str`${dateEarliest} to ${dateLatest}`, {
desc: "Date range formatted to show full month name and year",
});
};

const metadata = html`
<btrix-desc-list>
<btrix-desc-list-item label=${msg("Collection Period")}>
<span class="font-sans">${dateRange()}</span>
</btrix-desc-list-item>
<btrix-desc-list-item label=${msg("Total Pages")}>
${this.localize.number(collection.pageCount)}
</btrix-desc-list-item>
<btrix-desc-list-item label=${msg("Collection Size")}>
${this.localize.bytes(collection.totalSize)}
</btrix-desc-list-item>
</btrix-desc-list>
`;
const metadata = metadataColumn(collection);

if (collection.description) {
return html`
Expand Down
4 changes: 3 additions & 1 deletion frontend/src/pages/org/archived-item-qa/archived-item-qa.ts
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@ import type {
} from "@/types/api";
import type { ArchivedItem, ArchivedItemPageComment } from "@/types/crawler";
import type { ArchivedItemQAPage, QARun } from "@/types/qa";
import { SortDirection as APISortDirection } from "@/types/utils";
import {
isActive,
isSuccessfullyFinished,
Expand Down Expand Up @@ -553,7 +554,8 @@ export class ArchivedItemQA extends BtrixElement {
.pages=${this.pages}
.orderBy=${{
field: this.sortPagesBy.sortBy,
direction: (this.sortPagesBy.sortDirection === -1
direction: (this.sortPagesBy.sortDirection ===
APISortDirection.Descending
? "desc"
: "asc") as SortDirection,
}}
Expand Down
122 changes: 58 additions & 64 deletions frontend/src/pages/org/collection-detail.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,13 +14,21 @@ import type { MarkdownEditor } from "@/components/ui/markdown-editor";
import type { PageChangeEvent } from "@/components/ui/pagination";
import { SelectCollectionAccess } from "@/features/collections/select-collection-access";
import type { ShareCollection } from "@/features/collections/share-collection";
import {
metadataColumn,
metadataItemWithCollection,
} from "@/layouts/collections/metadataColumn";
import { pageHeader, pageNav, type Breadcrumb } from "@/layouts/pageHeader";
import type {
APIPaginatedList,
APIPaginationQuery,
APISortQuery,
} from "@/types/api";
import { CollectionAccess, type Collection } from "@/types/collection";
import {
CollectionAccess,
type Collection,
type PublicCollection,
} from "@/types/collection";
import type { ArchivedItem, Crawl, Upload } from "@/types/crawler";
import type { CrawlState } from "@/types/crawlState";
import { pluralOf } from "@/utils/pluralize";
Expand All @@ -43,7 +51,7 @@ export class CollectionDetail extends BtrixElement {
collectionId!: string;

@property({ type: String })
collectionTab: Tab = Tab.Replay;
collectionTab: Tab | null = Tab.Replay;

@state()
private collection?: Collection;
Expand Down Expand Up @@ -105,6 +113,9 @@ export class CollectionDetail extends BtrixElement {
void this.fetchCollection();
void this.fetchArchivedItems({ page: 1 });
}
if (changedProperties.has("collectionTab") && this.collectionTab === null) {
this.collectionTab = Tab.Replay;
}
}

protected async updated(
Expand Down Expand Up @@ -472,16 +483,6 @@ export class CollectionDetail extends BtrixElement {
(col) =>
`${this.localize.number(col.crawlCount)} ${pluralOf("items", col.crawlCount)}`,
)}
${this.renderDetailItem(msg("Total Size"), (col) =>
this.localize.bytes(col.totalSize || 0, {
unitDisplay: "narrow",
}),
)}
${this.renderDetailItem(
msg("Total Pages"),
(col) =>
`${this.localize.number(col.pageCount)} ${pluralOf("pages", col.pageCount)}`,
)}
${when(this.collection?.created, (created) =>
// Collections created before 49516bc4 is released may not have date in db
created
Expand All @@ -495,12 +496,13 @@ export class CollectionDetail extends BtrixElement {
year="numeric"
hour="numeric"
minute="numeric"
time-zone-name="short"
></btrix-format-date>`,
)
: nothing,
)}
${this.renderDetailItem(
msg("Last Updated"),
msg("Last Modified"),
(col) =>
html`<btrix-format-date
date=${col.modified}
Expand All @@ -509,6 +511,7 @@ export class CollectionDetail extends BtrixElement {
year="numeric"
hour="numeric"
minute="numeric"
time-zone-name="short"
></btrix-format-date>`,
)}
</btrix-desc-list>
Expand All @@ -517,67 +520,58 @@ export class CollectionDetail extends BtrixElement {

private renderDetailItem(
label: string | TemplateResult,
renderContent: (collection: Collection) => TemplateResult | string,
renderContent: (collection: PublicCollection) => TemplateResult | string,
) {
return html`
<btrix-desc-list-item label=${label}>
${when(
this.collection,
() => renderContent(this.collection!),
() => html`<sl-skeleton class="w-full"></sl-skeleton>`,
)}
</btrix-desc-list-item>
`;
return metadataItemWithCollection(this.collection)({
label,
render: renderContent,
});
}

// TODO Consolidate with collection.ts
private renderAbout() {
const dateRange = (collection: Collection) => {
if (!collection.dateEarliest || !collection.dateLatest) {
return msg("n/a");
}
const format: Intl.DateTimeFormatOptions = {
month: "long",
year: "numeric",
};
const dateEarliest = this.localize.date(collection.dateEarliest, format);
const dateLatest = this.localize.date(collection.dateLatest, format);

if (dateEarliest === dateLatest) return dateLatest;

return msg(str`${dateEarliest} to ${dateLatest}`, {
desc: "Date range formatted to show full month name and year",
});
};
const skeleton = html`<sl-skeleton class="w-24"></sl-skeleton>`;

const metadata = html`
<btrix-desc-list>
<btrix-desc-list-item label=${msg("Collection Period")}>
<span class="font-sans"
>${this.collection ? dateRange(this.collection) : skeleton}</span
>
</btrix-desc-list-item>
</btrix-desc-list>
`;
const metadata = metadataColumn(this.collection);

return html`
<div class="flex flex-1 flex-col gap-10 lg:flex-row">
<section class="flex w-full max-w-4xl flex-col leading-relaxed">
<header class="mb-3 flex min-h-8 items-end justify-between">
<h2 class="text-base font-semibold leading-none">
${msg("Description")}
</h2>
<header class="mb-2 flex min-h-8 items-center justify-between">
<div class="flex items-center gap-2">
<h2 class="text-base font-medium">
${msg("About This Collection")}
</h2>
<sl-tooltip>
<div slot="content">
<p class="mb-3">
${msg(
html`Describe your collection in long-form rich text (e.g.
<strong>bold</strong> and <em>italicized</em> text.)`,
)}
</p>
<p>
${msg(
html`If this collection is shareable, this will appear in
the “About This Collection” section of the shared
collection.`,
)}
</p>
</div>
<sl-icon
name="info-circle"
class="size-4 text-base text-neutral-500 [vertical-align:-.175em]"
></sl-icon>
</sl-tooltip>
</div>
${when(
this.collection?.description && !this.isEditingDescription,
() => html`
<sl-button
size="small"
@click=${() => (this.isEditingDescription = true)}
>
<sl-icon name="pencil" slot="prefix"></sl-icon>
${msg("Edit Description")}
</sl-button>
<sl-tooltip content=${msg("Edit description")}>
<sl-icon-button
class="text-base"
name="pencil"
@click=${() => (this.isEditingDescription = true)}
>
</sl-icon-button>
</sl-tooltip>
`,
)}
</header>
Expand All @@ -602,7 +596,7 @@ export class CollectionDetail extends BtrixElement {
`
: html`
<div class="text-center text-neutral-500">
<p class="mb-3">
<p class="mb-3 max-w-prose">
${msg("No description provided.")}
</p>
<sl-button
Expand Down
Loading

0 comments on commit 273648b

Please sign in to comment.