Allow users to configure offset when viewing crawl queued URLs (#1581)
This commit is contained in:
		
							parent
							
								
									1a668fe82f
								
							
						
					
					
						commit
						0479489bd8
					
				| @ -14,6 +14,7 @@ import("./desc-list"); | |||||||
| import("./details"); | import("./details"); | ||||||
| import("./dialog"); | import("./dialog"); | ||||||
| import("./file-list"); | import("./file-list"); | ||||||
|  | import("./inline-input"); | ||||||
| import("./input"); | import("./input"); | ||||||
| import("./language-select"); | import("./language-select"); | ||||||
| import("./locale-picker"); | import("./locale-picker"); | ||||||
|  | |||||||
							
								
								
									
										25
									
								
								frontend/src/components/ui/inline-input.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										25
									
								
								frontend/src/components/ui/inline-input.ts
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,25 @@ | |||||||
|  | import SlInput from "@shoelace-style/shoelace/dist/components/input/input.js"; | ||||||
|  | import inputStyles from "@shoelace-style/shoelace/dist/components/input/input.styles.js"; | ||||||
|  | import { css } from "lit"; | ||||||
|  | import { customElement } from "lit/decorators.js"; | ||||||
|  | 
 | ||||||
|  | /** | ||||||
|  |  * Input to use inline with text. | ||||||
|  |  */ | ||||||
|  | @customElement("btrix-inline-input") | ||||||
|  | export class InlineInput extends SlInput { | ||||||
|  |   static styles = [ | ||||||
|  |     inputStyles, | ||||||
|  |     css` | ||||||
|  |       :host { | ||||||
|  |         --sl-input-height-small: var(--sl-font-size-x-large); | ||||||
|  |         --sl-input-color: var(--sl-color-neutral-500); | ||||||
|  |       } | ||||||
|  | 
 | ||||||
|  |       .input--small .input__control { | ||||||
|  |         text-align: center; | ||||||
|  |         padding: 0 0.5ch; | ||||||
|  |       } | ||||||
|  |     `,
 | ||||||
|  |   ] as typeof SlInput.styles; | ||||||
|  | } | ||||||
| @ -33,17 +33,12 @@ export class Pagination extends LitElement { | |||||||
|   static styles = [ |   static styles = [ | ||||||
|     srOnly, |     srOnly, | ||||||
|     css` |     css` | ||||||
|       :host { |  | ||||||
|         --sl-input-height-small: var(--sl-font-size-x-large); |  | ||||||
|         --sl-input-color: var(--sl-color-neutral-500); |  | ||||||
|       } |  | ||||||
| 
 |  | ||||||
|       ul { |       ul { | ||||||
|         align-items: center; |         align-items: center; | ||||||
|         list-style: none; |         list-style: none; | ||||||
|         margin: 0; |         margin: 0; | ||||||
|         padding: 0; |         padding: 0; | ||||||
|         color: var(--sl-input-color); |         color: var(--sl-color-neutral-500); | ||||||
|       } |       } | ||||||
| 
 | 
 | ||||||
|       ul.compact { |       ul.compact { | ||||||
| @ -64,11 +59,6 @@ export class Pagination extends LitElement { | |||||||
|         cursor: pointer; |         cursor: pointer; | ||||||
|       } |       } | ||||||
| 
 | 
 | ||||||
|       sl-input::part(input) { |  | ||||||
|         text-align: center; |  | ||||||
|         padding: 0 0.5ch; |  | ||||||
|       } |  | ||||||
| 
 |  | ||||||
|       .currentPage { |       .currentPage { | ||||||
|         display: flex; |         display: flex; | ||||||
|         align-items: center; |         align-items: center; | ||||||
| @ -211,7 +201,7 @@ export class Pagination extends LitElement { | |||||||
|     return html` |     return html` | ||||||
|       <div class="pageInput"> |       <div class="pageInput"> | ||||||
|         <div class="totalPages" role="none">${this.pages}</div> |         <div class="totalPages" role="none">${this.pages}</div> | ||||||
|         <sl-input |         <btrix-inline-input | ||||||
|           class="input" |           class="input" | ||||||
|           inputmode="numeric" |           inputmode="numeric" | ||||||
|           size="small" |           size="small" | ||||||
| @ -256,7 +246,7 @@ export class Pagination extends LitElement { | |||||||
|             // Select text on focus for easy typing
 |             // Select text on focus for easy typing
 | ||||||
|             (e.target as SlInput).select(); |             (e.target as SlInput).select(); | ||||||
|           }} |           }} | ||||||
|         ></sl-input> |         ></btrix-inline-input> | ||||||
|       </div> |       </div> | ||||||
|     `;
 |     `;
 | ||||||
|   } |   } | ||||||
|  | |||||||
| @ -32,6 +32,7 @@ export class Observable extends LitElement { | |||||||
| 
 | 
 | ||||||
|   disconnectedCallback(): void { |   disconnectedCallback(): void { | ||||||
|     this.observer?.disconnect(); |     this.observer?.disconnect(); | ||||||
|  |     super.disconnectedCallback(); | ||||||
|   } |   } | ||||||
| 
 | 
 | ||||||
|   firstUpdated() { |   firstUpdated() { | ||||||
|  | |||||||
| @ -1,4 +1,9 @@ | |||||||
| import { localized, msg, str } from "@lit/localize"; | import { localized, msg, str } from "@lit/localize"; | ||||||
|  | import type { | ||||||
|  |   SlChangeEvent, | ||||||
|  |   SlInput, | ||||||
|  |   SlInputEvent, | ||||||
|  | } from "@shoelace-style/shoelace"; | ||||||
| import type { PropertyValues } from "lit"; | import type { PropertyValues } from "lit"; | ||||||
| import { customElement, property, state } from "lit/decorators.js"; | import { customElement, property, state } from "lit/decorators.js"; | ||||||
| import { when } from "lit/directives/when.js"; | import { when } from "lit/directives/when.js"; | ||||||
| @ -60,6 +65,9 @@ export class CrawlQueue extends LiteElement { | |||||||
|   @state() |   @state() | ||||||
|   private isLoading = false; |   private isLoading = false; | ||||||
| 
 | 
 | ||||||
|  |   @state() | ||||||
|  |   private pageOffset = 0; | ||||||
|  | 
 | ||||||
|   @state() |   @state() | ||||||
|   private pageSize = 50; |   private pageSize = 50; | ||||||
| 
 | 
 | ||||||
| @ -82,7 +90,10 @@ export class CrawlQueue extends LiteElement { | |||||||
|       changedProperties.has("orgId") || |       changedProperties.has("orgId") || | ||||||
|       changedProperties.has("crawlId") || |       changedProperties.has("crawlId") || | ||||||
|       changedProperties.has("pageSize") || |       changedProperties.has("pageSize") || | ||||||
|       changedProperties.has("regex") |       changedProperties.has("regex") || | ||||||
|  |       (changedProperties.has("pageOffset") && | ||||||
|  |         // Prevents double-fetch when offset is programmatically changed according to queue total
 | ||||||
|  |         !changedProperties.has("queue")) | ||||||
|     ) { |     ) { | ||||||
|       void this.fetchOnUpdate(); |       void this.fetchOnUpdate(); | ||||||
|     } |     } | ||||||
| @ -90,13 +101,66 @@ export class CrawlQueue extends LiteElement { | |||||||
| 
 | 
 | ||||||
|   render() { |   render() { | ||||||
|     return html` |     return html` | ||||||
|       <btrix-section-heading style="--margin: var(--sl-spacing-small)" |       <btrix-section-heading style="--margin: var(--sl-spacing-small)"> | ||||||
|         >${msg("Queued URLs")} ${this.renderBadge()}</btrix-section-heading |         ${this.renderOffsetControl()} ${this.renderBadge()} | ||||||
|       > |       </btrix-section-heading> | ||||||
|       ${this.renderContent()} |       ${this.renderContent()} | ||||||
|     `;
 |     `;
 | ||||||
|   } |   } | ||||||
| 
 | 
 | ||||||
|  |   private renderOffsetControl() { | ||||||
|  |     if (!this.queue) { | ||||||
|  |       return msg("Queued URLs"); | ||||||
|  |     } | ||||||
|  |     if (this.pageOffset === 0 && this.queue.total <= this.pageSize) { | ||||||
|  |       return msg( | ||||||
|  |         str`Queued URLs from 1 to ${this.queue.total.toLocaleString()}`, | ||||||
|  |       ); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     const offsetValue = this.pageOffset + 1; | ||||||
|  |     const countMax = Math.min( | ||||||
|  |       this.pageOffset + this.pageSize, | ||||||
|  |       this.queue.total, | ||||||
|  |     ); | ||||||
|  |     const getInputWidth = (v: number | string) => | ||||||
|  |       `${Math.max(v.toString().length, 3) + 2}ch`; | ||||||
|  | 
 | ||||||
|  |     return html` | ||||||
|  |       <div class="flex items-center text-neutral-500"> | ||||||
|  |         ${msg(html` | ||||||
|  |           Queued URLs from | ||||||
|  |           <btrix-inline-input | ||||||
|  |             class="mx-1 inline-block" | ||||||
|  |             style="width: ${Math.max(offsetValue.toString().length, 2) + 2}ch" | ||||||
|  |             value="1" | ||||||
|  |             inputmode="numeric" | ||||||
|  |             size="small" | ||||||
|  |             autocomplete="off" | ||||||
|  |             @sl-input=${(e: SlInputEvent) => { | ||||||
|  |               const input = e.target as SlInput; | ||||||
|  | 
 | ||||||
|  |               input.style.width = getInputWidth(input.value); | ||||||
|  |             }} | ||||||
|  |             @sl-change=${async (e: SlChangeEvent) => { | ||||||
|  |               const input = e.target as SlInput; | ||||||
|  |               const int = +input.value.replace(/\D/g, ""); | ||||||
|  | 
 | ||||||
|  |               await this.updateComplete; | ||||||
|  | 
 | ||||||
|  |               const value = Math.max(1, Math.min(int, this.queue!.total - 1)); | ||||||
|  | 
 | ||||||
|  |               input.value = value.toString(); | ||||||
|  |               this.pageOffset = value - 1; | ||||||
|  |             }} | ||||||
|  |           ></btrix-inline-input> | ||||||
|  |           to ${countMax.toLocaleString()} of | ||||||
|  |           ${this.queue.total.toLocaleString()} | ||||||
|  |         `)}
 | ||||||
|  |       </div> | ||||||
|  |     `;
 | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|   private renderContent() { |   private renderContent() { | ||||||
|     if (!this.queue?.total) { |     if (!this.queue?.total) { | ||||||
|       if (this.isLoading) { |       if (this.isLoading) { | ||||||
| @ -119,9 +183,9 @@ export class CrawlQueue extends LiteElement { | |||||||
|           const isExcluded = !isMatch && this.isExcluded(url); |           const isExcluded = !isMatch && this.isExcluded(url); | ||||||
|           return html` |           return html` | ||||||
|             <btrix-numbered-list-item> |             <btrix-numbered-list-item> | ||||||
|               <span class="${isMatch ? "text-red-600" : ""}" slot="marker" |               <span class="${isMatch ? "text-red-600" : ""}" slot="marker"> | ||||||
|                 >${idx + 1}.</span |                 ${(idx + this.pageOffset + 1).toLocaleString()}. | ||||||
|               > |               </span> | ||||||
|               <a |               <a | ||||||
|                 class="${isMatch |                 class="${isMatch | ||||||
|                   ? "text-red-500 hover:text-red-400" |                   ? "text-red-500 hover:text-red-400" | ||||||
| @ -140,7 +204,7 @@ export class CrawlQueue extends LiteElement { | |||||||
| 
 | 
 | ||||||
|       <footer class="text-center"> |       <footer class="text-center"> | ||||||
|         ${when( |         ${when( | ||||||
|           this.queue.total === this.queue.results.length, |           this.queue.total <= this.pageOffset + this.pageSize, | ||||||
|           () => |           () => | ||||||
|             html`<div class="py-3 text-xs text-neutral-400">
 |             html`<div class="py-3 text-xs text-neutral-400">
 | ||||||
|               ${msg("End of queue")} |               ${msg("End of queue")} | ||||||
| @ -165,14 +229,6 @@ export class CrawlQueue extends LiteElement { | |||||||
|     if (!this.queue) return ""; |     if (!this.queue) return ""; | ||||||
| 
 | 
 | ||||||
|     return html` |     return html` | ||||||
|       <btrix-badge class="ml-1"> |  | ||||||
|         ${this.queue.total |  | ||||||
|           ? this.queue.total > 1 |  | ||||||
|             ? msg(str`${this.queue.total.toLocaleString()} URLs`) |  | ||||||
|             : msg(str`1 URL`) |  | ||||||
|           : msg("No queue")} |  | ||||||
|       </btrix-badge> |  | ||||||
| 
 |  | ||||||
|       ${this.matchedTotal |       ${this.matchedTotal | ||||||
|         ? html` |         ? html` | ||||||
|             <btrix-badge variant="danger" class="ml-1"> |             <btrix-badge variant="danger" class="ml-1"> | ||||||
| @ -230,10 +286,13 @@ export class CrawlQueue extends LiteElement { | |||||||
|   } |   } | ||||||
| 
 | 
 | ||||||
|   private async getQueue(): Promise<ResponseData> { |   private async getQueue(): Promise<ResponseData> { | ||||||
|     const offset = "0"; |  | ||||||
|     const count = this.pageSize.toString(); |     const count = this.pageSize.toString(); | ||||||
|     const regex = this.regex; |     const regex = this.regex; | ||||||
|     const params = new URLSearchParams({ offset, count, regex }); |     const params = new URLSearchParams({ | ||||||
|  |       offset: this.pageOffset.toString(), | ||||||
|  |       count, | ||||||
|  |       regex, | ||||||
|  |     }); | ||||||
|     const data: ResponseData = await this.apiFetch( |     const data: ResponseData = await this.apiFetch( | ||||||
|       `/orgs/${this.orgId}/crawls/${this.crawlId}/queue?${params.toString()}`, |       `/orgs/${this.orgId}/crawls/${this.crawlId}/queue?${params.toString()}`, | ||||||
|       this.authState!, |       this.authState!, | ||||||
|  | |||||||
| @ -1220,7 +1220,7 @@ export class WorkflowDetail extends LiteElement { | |||||||
|     return html` |     return html` | ||||||
|       <header class="flex items-center justify-between"> |       <header class="flex items-center justify-between"> | ||||||
|         <h3 class="mb-2 text-base font-semibold leading-none"> |         <h3 class="mb-2 text-base font-semibold leading-none"> | ||||||
|           ${msg("Crawl URLs")} |           ${msg("Upcoming Pages")} | ||||||
|         </h3> |         </h3> | ||||||
|         <sl-button |         <sl-button | ||||||
|           size="small" |           size="small" | ||||||
|  | |||||||
		Loading…
	
		Reference in New Issue
	
	Block a user