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:
		
							parent
							
								
									dde1175bd0
								
							
						
					
					
						commit
						cb9012a6df
					
				| @ -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> | ||||||
|     `;
 |     `;
 | ||||||
|   } |   } | ||||||
|  | |||||||
| @ -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 | ||||||
|  | |||||||
| @ -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", | ||||||
|       }); |       }); | ||||||
|  | |||||||
		Loading…
	
		Reference in New Issue
	
	Block a user