Fix QA navigation (#1731)

- Resolves https://github.com/webrecorder/browsertrix/issues/1705
- Resolves https://github.com/webrecorder/browsertrix/issues/1477

Changes:

- Takes user back to archived item QA tab after submitting review.
- Prevents clicking on page in QA tab when there's no analysis runs.
- Disables analysis-related sort options in QA tab when there's no
analysis runs.
- Handles pages without a title in QA tab.
- Adds initial loading state to QA review page list.
This commit is contained in:
sua yoo 2024-04-23 12:45:06 -07:00 committed by GitHub
parent dde1175bd0
commit cb9012a6df
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
3 changed files with 106 additions and 86 deletions

View File

@ -3,9 +3,11 @@ import type { SlChangeEvent, SlSelect } from "@shoelace-style/shoelace";
import { html, type PropertyValues } from "lit"; import { html, type PropertyValues } from "lit";
import { customElement, property, query } from "lit/decorators.js"; import { customElement, property, query } from "lit/decorators.js";
import { repeat } from "lit/directives/repeat.js"; import { repeat } from "lit/directives/repeat.js";
import { when } from "lit/directives/when.js";
import { TailwindElement } from "@/classes/TailwindElement"; import { TailwindElement } from "@/classes/TailwindElement";
import { type PageChangeEvent } from "@/components/ui/pagination"; import { type PageChangeEvent } from "@/components/ui/pagination";
import { renderSpinner } from "@/pages/org/archived-item-qa/ui/spinner";
import type { APIPaginatedList, APISortQuery } from "@/types/api"; import type { APIPaginatedList, APISortQuery } from "@/types/api";
import type { ArchivedItemQAPage } from "@/types/qa"; import type { ArchivedItemQAPage } from "@/types/qa";
@ -124,67 +126,74 @@ export class PageList extends TailwindElement {
<div <div
class="scrollContainer relative -mx-2 overflow-y-auto overscroll-contain px-2" class="scrollContainer relative -mx-2 overflow-y-auto overscroll-contain px-2"
> >
${this.pages?.total ${when(
? html` this.pages,
<div ({ total, items, page, pageSize }) =>
class="sticky top-0 z-30 bg-gradient-to-b from-white to-white/85 backdrop-blur-sm" total
> ? html`
<div class="mb-0.5 ml-2 border-b py-1 text-xs text-neutral-500"> <div
${this.pages.total === this.totalPages class="sticky top-0 z-30 bg-gradient-to-b from-white to-white/85 backdrop-blur-sm"
? msg(
str`Showing all ${this.totalPages.toLocaleString()} pages`,
)
: msg(
str`Showing ${this.pages.total.toLocaleString()} of ${this.totalPages.toLocaleString()} pages`,
)}
</div>
</div>
${repeat(
this.pages.items,
({ id }) => id,
(page: ArchivedItemQAPage) => html`
<btrix-qa-page
class="is-leaf -my-4 scroll-my-8 py-4 first-of-type:mt-0 last-of-type:mb-0"
.page=${page}
statusField=${this.orderBy.field === "notes"
? "approved"
: this.orderBy.field}
?selected=${page.id === this.itemPageId}
> >
</btrix-qa-page> <div
`, class="mb-0.5 ml-2 border-b py-1 text-xs text-neutral-500"
)} >
<div class="my-2 flex justify-center"> ${total === this.totalPages
<btrix-pagination ? msg(
page=${this.pages.page} str`Showing all ${this.totalPages.toLocaleString()} pages`,
totalCount=${this.pages.total} )
size=${this.pages.pageSize} : msg(
compact str`Showing ${total.toLocaleString()} of ${this.totalPages.toLocaleString()} pages`,
@page-change=${(e: PageChangeEvent) => { )}
e.stopPropagation(); </div>
this.dispatchEvent( </div>
new CustomEvent<QaPaginationChangeDetail>( ${repeat(
"btrix-qa-pagination-change", items,
{ ({ id }) => id,
detail: { page: e.detail.page }, (page: ArchivedItemQAPage) => html`
}, <btrix-qa-page
), class="is-leaf -my-4 scroll-my-8 py-4 first-of-type:mt-0 last-of-type:mb-0"
); .page=${page}
}} statusField=${this.orderBy.field === "notes"
> ? "approved"
</btrix-pagination> : this.orderBy.field}
</div> ?selected=${page.id === this.itemPageId}
>
</btrix-qa-page>
`,
)}
<div class="my-2 flex justify-center">
<btrix-pagination
page=${page}
totalCount=${total}
size=${pageSize}
compact
@page-change=${(e: PageChangeEvent) => {
e.stopPropagation();
this.dispatchEvent(
new CustomEvent<QaPaginationChangeDetail>(
"btrix-qa-pagination-change",
{
detail: { page: e.detail.page },
},
),
);
}}
>
</btrix-pagination>
</div>
<div <div
class="sticky bottom-0 z-30 h-4 bg-gradient-to-t from-white to-white/0" class="sticky bottom-0 z-30 h-4 bg-gradient-to-t from-white to-white/0"
></div> ></div>
` `
: html`<div : html`<div
class="flex flex-col items-center justify-center gap-4 py-8 text-xs text-gray-600" class="flex flex-col items-center justify-center gap-4 py-8 text-xs text-gray-600"
> >
<sl-icon name="slash-circle"></sl-icon> <sl-icon name="slash-circle"></sl-icon>
${msg("No matching pages found")} ${msg("No matching pages found")}
</div>`} </div>`,
renderSpinner,
)}
</div> </div>
`; `;
} }

View File

@ -13,6 +13,7 @@ import {
type TemplateResult, type TemplateResult,
} from "lit"; } from "lit";
import { customElement, property, query, state } from "lit/decorators.js"; import { customElement, property, query, state } from "lit/decorators.js";
import { ifDefined } from "lit/directives/if-defined.js";
import { when } from "lit/directives/when.js"; import { when } from "lit/directives/when.js";
import queryString from "query-string"; import queryString from "query-string";
@ -681,7 +682,7 @@ export class ArchivedItemDetailQA extends TailwindElement {
class="label-same-line" class="label-same-line"
label=${msg("Sort by:")} label=${msg("Sort by:")}
size="small" size="small"
value="approved.-1" value=${this.qaRunId ? "approved.-1" : "url.1"}
pill pill
@sl-change=${(e: SlChangeEvent) => { @sl-change=${(e: SlChangeEvent) => {
const { value } = e.target as SlSelect; const { value } = e.target as SlSelect;
@ -697,11 +698,15 @@ export class ArchivedItemDetailQA extends TailwindElement {
> >
<sl-option value="title.1">${msg("Title")}</sl-option> <sl-option value="title.1">${msg("Title")}</sl-option>
<sl-option value="url.1">${msg("URL")}</sl-option> <sl-option value="url.1">${msg("URL")}</sl-option>
<sl-option value="notes.-1">${msg("Most Comments")}</sl-option> <sl-option value="notes.-1" ?disabled=${!this.qaRunId}
<sl-option value="approved.-1" >${msg("Most Comments")}</sl-option
>${msg("Recently Approved")}</sl-option
> >
<sl-option value="approved.1">${msg("Not Approved")}</sl-option> <sl-option value="approved.-1" ?disabled=${!this.qaRunId}>
${msg("Recently Approved")}
</sl-option>
<sl-option value="approved.1" ?disabled=${!this.qaRunId}>
${msg("Not Approved")}
</sl-option>
</sl-select> </sl-select>
</div> </div>
</div> </div>
@ -709,6 +714,13 @@ export class ArchivedItemDetailQA extends TailwindElement {
} }
private renderPageList() { private renderPageList() {
const pageTitle = (page: ArchivedItemPage) => html`
<div class="truncate font-medium">
${page.title ||
html`<span class="opacity-50">${msg("No page title")}</span>`}
</div>
<div class="truncate text-xs leading-4 text-neutral-600">${page.url}</div>
`;
return html` return html`
<btrix-table <btrix-table
class="-mx-3 overflow-x-auto px-5" class="-mx-3 overflow-x-auto px-5"
@ -727,28 +739,25 @@ export class ArchivedItemDetailQA extends TailwindElement {
${this.pages?.items.map( ${this.pages?.items.map(
(page, idx) => html` (page, idx) => html`
<btrix-table-row <btrix-table-row
class="${idx > 0 class="${idx > 0 ? "border-t" : ""} ${this.qaRunId
? "border-t" ? "cursor-pointer transition-colors focus-within:bg-neutral-50 hover:bg-neutral-50"
: ""} cursor-pointer select-none transition-colors focus-within:bg-neutral-50 hover:bg-neutral-50" : ""} select-none"
> >
<btrix-table-cell <btrix-table-cell
class="block overflow-hidden" class="block overflow-hidden"
rowClickTarget="a" rowClickTarget=${ifDefined(this.qaRunId ? "a" : undefined)}
> >
<a ${this.qaRunId
class="truncate text-sm font-semibold" ? html`
href=${`${ <a
this.navigate.orgBasePath href=${`${this.navigate.orgBasePath}/items/${this.itemType}/${this.crawlId}/review/screenshots?qaRunId=${this.qaRunId}&itemPageId=${page.id}`}
}/items/${this.itemType}/${this.crawlId}/review/screenshots?qaRunId=${ title=${msg(str`Review "${page.title ?? page.url}"`)}
this.qaRunId || "" @click=${this.navigate.link}
}&itemPageId=${page.id}`} >
title="${page.title ?? page.url}" ${pageTitle(page)}
@click=${this.navigate.link} </a>
>${page.title}</a `
> : pageTitle(page)}
<div class="truncate text-xs leading-4 text-neutral-600">
${page.url}
</div>
</btrix-table-cell> </btrix-table-cell>
<btrix-table-cell <btrix-table-cell
>${this.renderApprovalStatus(page)}</btrix-table-cell >${this.renderApprovalStatus(page)}</btrix-table-cell

View File

@ -376,9 +376,7 @@ export class ArchivedItemQA extends TailwindElement {
?disabled=${disableReview} ?disabled=${disableReview}
> >
<sl-icon slot="prefix" name="patch-check"> </sl-icon> <sl-icon slot="prefix" name="patch-check"> </sl-icon>
${this.item?.reviewStatus ${msg("Finish Review")}
? msg("Update Review")
: msg("Finish Review")}
</sl-button> </sl-button>
</sl-tooltip> </sl-tooltip>
</div> </div>
@ -1151,8 +1149,12 @@ export class ArchivedItemQA extends TailwindElement {
} }
void this.reviewDialog?.hide(); void this.reviewDialog?.hide();
this.navigate.to(
`${this.navigate.orgBasePath}/items/crawl/${this.itemId}#qa`,
);
this.notify.toast({ this.notify.toast({
message: msg("Submitted QA review."), message: msg("Saved QA review."),
variant: "success", variant: "success",
icon: "check2-circle", icon: "check2-circle",
}); });