Fixes allowed actions for viewers and crawlers throughout the app (#1326)
Closes #1294 ### Changes - `crawl-list` component - Adds a check if there are any items in the actions menu. If not, skip rendering the actions menu. - This allows us to give the component no actions! Currently required to remove them for viewers! - Collection Details - Hides "Remove from Collection" option for viewers - Crawls List - Removes the single "View Crawl Details" option from archived items for viewers - All the other actions were already set up correctly to be used by all roles! - Dashboard - Hides org settings gear icon button unless the user is an admin - Hides "Create New" dropdown for viewers - Workflow Details - Hides workflow edit icon button for viewers - Hides the "Delete Crawl" option in archived items for viewers - Hides the "Run Crawl" option for viewers - Workflow List - Hides all edit-related options for viewers, the only option now is copying tags - Removes the deactivate / delete options (were only visible when running a crawl) in the workflow list actions --------- Co-authored-by: Ilya Kreymer <ikreymer@gmail.com> Co-authored-by: sua yoo <sua@suayoo.com>
This commit is contained in:
		
							parent
							
								
									1218d6e767
								
							
						
					
					
						commit
						f507f1d2ec
					
				| @ -833,7 +833,7 @@ def init_crawls_api(app, user_dep, *args): | ||||
|         crawl_id: str, | ||||
|         pageSize: int = DEFAULT_PAGE_SIZE, | ||||
|         page: int = 1, | ||||
|         org: Organization = Depends(org_crawl_dep), | ||||
|         org: Organization = Depends(org_viewer_dep), | ||||
|     ): | ||||
|         crawl_raw = await ops.get_crawl_raw(crawl_id, org) | ||||
|         crawl = Crawl.from_dict(crawl_raw) | ||||
|  | ||||
| @ -207,6 +207,9 @@ export class CrawlListItem extends LitElement { | ||||
|   @state() | ||||
|   private dropdownIsOpen?: boolean; | ||||
| 
 | ||||
|   @state() | ||||
|   private hasMenuItems?: boolean; | ||||
| 
 | ||||
|   // TODO localize
 | ||||
|   private numberFormatter = new Intl.NumberFormat(undefined, { | ||||
|     notation: "compact", | ||||
| @ -357,35 +360,7 @@ export class CrawlListItem extends LitElement { | ||||
|           )} | ||||
|         </div> | ||||
|       </div> | ||||
|       <div class="col action"> | ||||
|         <sl-icon-button | ||||
|           class="dropdownTrigger" | ||||
|           label=${msg("Actions")} | ||||
|           name="three-dots-vertical" | ||||
|           @click=${(e: MouseEvent) => { | ||||
|             // Prevent anchor link default behavior
 | ||||
|             e.preventDefault(); | ||||
|             // Stop prop to anchor link
 | ||||
|             e.stopPropagation(); | ||||
|             this.dropdownIsOpen = !this.dropdownIsOpen; | ||||
|           }} | ||||
|           @focusout=${(e: FocusEvent) => { | ||||
|             const relatedTarget = e.relatedTarget as HTMLElement; | ||||
|             if (relatedTarget) { | ||||
|               if (this.menuArr[0]?.contains(relatedTarget)) { | ||||
|                 // Keep dropdown open if moving to menu selection
 | ||||
|                 return; | ||||
|               } | ||||
|               if (this.row?.isEqualNode(relatedTarget)) { | ||||
|                 // Handle with click event
 | ||||
|                 return; | ||||
|               } | ||||
|             } | ||||
|             this.dropdownIsOpen = false; | ||||
|           }} | ||||
|         > | ||||
|         </sl-icon-button> | ||||
|       </div> | ||||
|       ${this.renderActions()} | ||||
|     </a>`;
 | ||||
|   } | ||||
| 
 | ||||
| @ -406,6 +381,7 @@ export class CrawlListItem extends LitElement { | ||||
|     > | ||||
|       <slot | ||||
|         name="menu" | ||||
|         @slotchange=${() => (this.hasMenuItems = this.menuArr.length > 0)} | ||||
|         @sl-select=${() => (this.dropdownIsOpen = false)} | ||||
|       ></slot> | ||||
|     </div> `;
 | ||||
| @ -440,6 +416,42 @@ export class CrawlListItem extends LitElement { | ||||
|     `;
 | ||||
|   } | ||||
| 
 | ||||
|   private renderActions() { | ||||
|     if (!this.hasMenuItems) { | ||||
|       return; | ||||
|     } | ||||
| 
 | ||||
|     return html` <div class="col action">
 | ||||
|       <sl-icon-button | ||||
|         class="dropdownTrigger" | ||||
|         label=${msg("Actions")} | ||||
|         name="three-dots-vertical" | ||||
|         @click=${(e: MouseEvent) => { | ||||
|           // Prevent anchor link default behavior
 | ||||
|           e.preventDefault(); | ||||
|           // Stop prop to anchor link
 | ||||
|           e.stopPropagation(); | ||||
|           this.dropdownIsOpen = !this.dropdownIsOpen; | ||||
|         }} | ||||
|         @focusout=${(e: FocusEvent) => { | ||||
|           const relatedTarget = e.relatedTarget as HTMLElement; | ||||
|           if (relatedTarget) { | ||||
|             if (this.menuArr[0]?.contains(relatedTarget)) { | ||||
|               // Keep dropdown open if moving to menu selection
 | ||||
|               return; | ||||
|             } | ||||
|             if (this.row?.isEqualNode(relatedTarget)) { | ||||
|               // Handle with click event
 | ||||
|               return; | ||||
|             } | ||||
|           } | ||||
|           this.dropdownIsOpen = false; | ||||
|         }} | ||||
|       > | ||||
|       </sl-icon-button> | ||||
|     </div>`;
 | ||||
|   } | ||||
| 
 | ||||
|   private repositionDropdown() { | ||||
|     const { x, y } = this.dropdownTrigger.getBoundingClientRect(); | ||||
|     this.dropdown.style.left = `${x + window.scrollX}px`; | ||||
|  | ||||
| @ -623,7 +623,10 @@ export class CollectionDetail extends LiteElement { | ||||
|         orgSlug=${this.appState.orgSlug || ""} | ||||
|         .crawl=${item} | ||||
|       > | ||||
|         <sl-menu slot="menu"> | ||||
|         ${when( | ||||
|           this.isCrawler, | ||||
|           () => | ||||
|             html` <sl-menu slot="menu">
 | ||||
|               <sl-menu-item | ||||
|                 style="--sl-color-neutral-700: var(--warning)" | ||||
|                 @click=${() => this.removeArchivedItem(item.id, idx)} | ||||
| @ -631,7 +634,8 @@ export class CollectionDetail extends LiteElement { | ||||
|                 <sl-icon name="folder-minus" slot="prefix"></sl-icon> | ||||
|                 ${msg("Remove from Collection")} | ||||
|               </sl-menu-item> | ||||
|         </sl-menu> | ||||
|             </sl-menu>` | ||||
|         )} | ||||
|       </btrix-crawl-list-item> | ||||
|     `;
 | ||||
| 
 | ||||
|  | ||||
| @ -447,28 +447,11 @@ export class CrawlsList extends LiteElement { | ||||
|         orgSlug=${this.appState.orgSlug || ""} | ||||
|         .crawl=${item} | ||||
|       > | ||||
|         <sl-menu slot="menu"> | ||||
|           ${when( | ||||
|             this.isCrawler, | ||||
|             this.crawlerMenuItemsRenderer(item), | ||||
|             () => html` | ||||
|               <sl-menu-item | ||||
|                 @click=${() => | ||||
|                   this.navTo( | ||||
|                     `${this.orgBasePath}/items/${ | ||||
|                       item.type === "upload" ? "upload" : "crawl" | ||||
|                     }/${item.id}` | ||||
|                   )} | ||||
|               > | ||||
|                 ${msg("View Crawl Details")} | ||||
|               </sl-menu-item> | ||||
|             ` | ||||
|           )} | ||||
|         </sl-menu> | ||||
|         <sl-menu slot="menu"> ${this.crawlerMenuItemsRenderer(item)} </sl-menu> | ||||
|       </btrix-crawl-list-item> | ||||
|     `;
 | ||||
| 
 | ||||
|   private crawlerMenuItemsRenderer = (item: Crawl) => () => | ||||
|   private crawlerMenuItemsRenderer = (item: Crawl) => | ||||
|     // HACK shoelace doesn't current have a way to override non-hover
 | ||||
|     // color without resetting the --sl-color-neutral-700 variable
 | ||||
|     html` | ||||
|  | ||||
| @ -36,6 +36,12 @@ export class Dashboard extends LiteElement { | ||||
|   @property({ type: Object }) | ||||
|   authState!: AuthState; | ||||
| 
 | ||||
|   @property({ type: Boolean }) | ||||
|   isCrawler?: boolean; | ||||
| 
 | ||||
|   @property({ type: Boolean }) | ||||
|   isAdmin?: boolean; | ||||
| 
 | ||||
|   @property({ type: String }) | ||||
|   orgId!: string; | ||||
| 
 | ||||
| @ -96,14 +102,20 @@ export class Dashboard extends LiteElement { | ||||
|         <h1 class="min-w-0 text-xl font-semibold leading-8 mr-auto"> | ||||
|           ${this.org?.name} | ||||
|         </h1> | ||||
|         <sl-icon-button | ||||
|         ${when( | ||||
|           this.isAdmin, | ||||
|           () => | ||||
|             html` <sl-icon-button
 | ||||
|               href=${`${this.orgBasePath}/settings`} | ||||
|               class="text-lg" | ||||
|               name="gear" | ||||
|               label="Edit org settings" | ||||
|               @click=${this.navLink} | ||||
|         ></sl-icon-button> | ||||
|         <sl-dropdown | ||||
|             ></sl-icon-button>` | ||||
|         )} | ||||
|         ${when( | ||||
|           this.isCrawler, | ||||
|           () => html` <sl-dropdown
 | ||||
|             distance="4" | ||||
|             placement="bottom-end" | ||||
|             @sl-select=${(e: SlSelectEvent) => { | ||||
| @ -137,7 +149,8 @@ export class Dashboard extends LiteElement { | ||||
|                 ${msg("Browser Profile")} | ||||
|               </sl-menu-item> | ||||
|             </sl-menu> | ||||
|         </sl-dropdown> | ||||
|           </sl-dropdown>` | ||||
|         )} | ||||
|       </header> | ||||
|       <main> | ||||
|         <div class="flex flex-col md:flex-row gap-6"> | ||||
|  | ||||
| @ -455,6 +455,8 @@ export class Org extends LiteElement { | ||||
|         .authState=${this.authState!} | ||||
|         orgId=${this.orgId} | ||||
|         .org=${this.org || null} | ||||
|         ?isCrawler=${this.isCrawler} | ||||
|         ?isAdmin=${this.isAdmin} | ||||
|         @select-new-dialog=${this.onSelectNewDialog} | ||||
|       ></btrix-dashboard> | ||||
|     `;
 | ||||
|  | ||||
| @ -448,7 +448,7 @@ export class WorkflowDetail extends LiteElement { | ||||
|         )} | ||||
|       </h3>`;
 | ||||
|     } | ||||
|     if (this.activePanel === "settings") { | ||||
|     if (this.activePanel === "settings" && this.isCrawler == true) { | ||||
|       return html` <h3>${this.tabLabels[this.activePanel]}</h3>
 | ||||
|         <sl-icon-button | ||||
|           name="gear" | ||||
| @ -460,7 +460,7 @@ export class WorkflowDetail extends LiteElement { | ||||
|         > | ||||
|         </sl-icon-button>`; | ||||
|     } | ||||
|     if (this.activePanel === "watch") { | ||||
|     if (this.activePanel === "watch" && this.isCrawler == true) { | ||||
|       return html` <h3>${this.tabLabels[this.activePanel]}</h3>
 | ||||
|         <sl-button | ||||
|           size="small" | ||||
| @ -836,8 +836,7 @@ export class WorkflowDetail extends LiteElement { | ||||
|             this.crawls, | ||||
|             () => | ||||
|               this.crawls!.items.map( | ||||
|                 (crawl: Crawl) => html` | ||||
|                   <btrix-crawl-list-item | ||||
|                 (crawl: Crawl) => html` <btrix-crawl-list-item
 | ||||
|                   orgSlug=${this.appState.orgSlug || ""} | ||||
|                   .crawl=${crawl} | ||||
|                 > | ||||
| @ -850,7 +849,9 @@ export class WorkflowDetail extends LiteElement { | ||||
|                     hour="2-digit" | ||||
|                     minute="2-digit" | ||||
|                   ></sl-format-date> | ||||
|                     <sl-menu slot="menu"> | ||||
|                   ${when( | ||||
|                     this.isCrawler, | ||||
|                     () => html` <sl-menu slot="menu">
 | ||||
|                       <sl-menu-item | ||||
|                         style="--sl-color-neutral-700: var(--danger)" | ||||
|                         @click=${() => this.deleteCrawl(crawl)} | ||||
| @ -858,9 +859,9 @@ export class WorkflowDetail extends LiteElement { | ||||
|                         <sl-icon name="trash3" slot="prefix"></sl-icon> | ||||
|                         ${msg("Delete Crawl")} | ||||
|                       </sl-menu-item> | ||||
|                     </sl-menu> | ||||
|                   </btrix-crawl-list-item> | ||||
|                 ` | ||||
|                     </sl-menu>` | ||||
|                   )}</btrix-crawl-list-item | ||||
|                 >` | ||||
|               ), | ||||
|             () => html`<div
 | ||||
|               class="w-full flex items-center justify-center my-24 text-3xl" | ||||
| @ -1037,8 +1038,9 @@ export class WorkflowDetail extends LiteElement { | ||||
|               > | ||||
|             ` | ||||
|           )} | ||||
| 
 | ||||
|           <sl-tooltip | ||||
|           ${when( | ||||
|             this.isCrawler, | ||||
|             () => html` <sl-tooltip
 | ||||
|               content=${msg( | ||||
|                 "Org Storage Full or Monthly Execution Minutes Reached" | ||||
|               )} | ||||
| @ -1054,7 +1056,8 @@ export class WorkflowDetail extends LiteElement { | ||||
|                 <sl-icon name="play" slot="prefix"></sl-icon> | ||||
|                 ${msg("Run Crawl")} | ||||
|               </sl-button> | ||||
|           </sl-tooltip> | ||||
|             </sl-tooltip>` | ||||
|           )} | ||||
|         </div> | ||||
|       </section> | ||||
|     `;
 | ||||
|  | ||||
| @ -421,7 +421,7 @@ export class WorkflowsList extends LiteElement { | ||||
|   private renderMenuItems(workflow: ListWorkflow) { | ||||
|     return html` | ||||
|       ${when( | ||||
|         workflow.isCrawlRunning, | ||||
|         workflow.isCrawlRunning && this.isCrawler, | ||||
|         // HACK shoelace doesn't current have a way to override non-hover
 | ||||
|         // color without resetting the --sl-color-neutral-700 variable
 | ||||
|         () => html` | ||||
| @ -439,7 +439,10 @@ export class WorkflowsList extends LiteElement { | ||||
|             <sl-icon name="x-octagon" slot="prefix"></sl-icon> | ||||
|             ${msg("Cancel & Discard Crawl")} | ||||
|           </sl-menu-item> | ||||
|         `,
 | ||||
|         ` | ||||
|       )} | ||||
|       ${when( | ||||
|         this.isCrawler && !workflow.isCrawlRunning, | ||||
|         () => html` | ||||
|           <sl-menu-item | ||||
|             style="--sl-color-neutral-700: var(--success)" | ||||
| @ -453,7 +456,7 @@ export class WorkflowsList extends LiteElement { | ||||
|         ` | ||||
|       )} | ||||
|       ${when( | ||||
|         workflow.isCrawlRunning, | ||||
|         workflow.isCrawlRunning && this.isCrawler, | ||||
|         // HACK shoelace doesn't current have a way to override non-hover
 | ||||
|         // color without resetting the --sl-color-neutral-700 variable
 | ||||
|         () => html` | ||||
| @ -485,14 +488,19 @@ export class WorkflowsList extends LiteElement { | ||||
|           <sl-divider></sl-divider> | ||||
|         ` | ||||
|       )} | ||||
|       <sl-divider></sl-divider> | ||||
|       ${when( | ||||
|         this.isCrawler, | ||||
|         () => html` <sl-divider></sl-divider>
 | ||||
|           <sl-menu-item | ||||
|             @click=${() => | ||||
|           this.navTo(`${this.orgBasePath}/workflows/crawl/${workflow.id}?edit`)} | ||||
|               this.navTo( | ||||
|                 `${this.orgBasePath}/workflows/crawl/${workflow.id}?edit` | ||||
|               )} | ||||
|           > | ||||
|             <sl-icon name="gear" slot="prefix"></sl-icon> | ||||
|             ${msg("Edit Workflow Settings")} | ||||
|       </sl-menu-item> | ||||
|           </sl-menu-item>` | ||||
|       )} | ||||
|       <sl-menu-item | ||||
|         @click=${() => CopyButton.copyToClipboard(workflow.tags.join(", "))} | ||||
|         ?disabled=${!workflow.tags.length} | ||||
| @ -500,28 +508,15 @@ export class WorkflowsList extends LiteElement { | ||||
|         <sl-icon name="tags" slot="prefix"></sl-icon> | ||||
|         ${msg("Copy Tags")} | ||||
|       </sl-menu-item> | ||||
|       <sl-menu-item @click=${() => this.duplicateConfig(workflow)}> | ||||
|       ${when( | ||||
|         this.isCrawler, | ||||
|         () => html` <sl-menu-item
 | ||||
|           @click=${() => this.duplicateConfig(workflow)} | ||||
|         > | ||||
|           <sl-icon name="files" slot="prefix"></sl-icon> | ||||
|           ${msg("Duplicate Workflow")} | ||||
|       </sl-menu-item> | ||||
|       ${when(workflow.isCrawlRunning, () => { | ||||
|         const shouldDeactivate = workflow.crawlCount && !workflow.inactive; | ||||
|         return html` | ||||
|           <sl-divider></sl-divider> | ||||
|           <sl-menu-item | ||||
|             style="--sl-color-neutral-700: var(--danger)" | ||||
|             @click=${() => | ||||
|               shouldDeactivate | ||||
|                 ? this.deactivate(workflow) | ||||
|                 : this.delete(workflow)} | ||||
|           > | ||||
|             <sl-icon name="trash3" slot="prefix"></sl-icon> | ||||
|             ${shouldDeactivate | ||||
|               ? msg("Deactivate Workflow") | ||||
|               : msg("Delete Workflow")} | ||||
|           </sl-menu-item> | ||||
|         `;
 | ||||
|       })} | ||||
|         </sl-menu-item>` | ||||
|       )} | ||||
|     `;
 | ||||
|   } | ||||
| 
 | ||||
|  | ||||
		Loading…
	
		Reference in New Issue
	
	Block a user