Show invite message to super admin & layout fixes (#181)
This commit is contained in:
		
							parent
							
								
									fe31f551b2
								
							
						
					
					
						commit
						c18418ff09
					
				| @ -14,12 +14,12 @@ type LocaleNames = { | ||||
| @localized() | ||||
| export class LocalePicker extends LitElement { | ||||
|   @state() | ||||
|   private localeNames?: LocaleNames; | ||||
|   private localeNames: LocaleNames = {} as LocaleNames; | ||||
| 
 | ||||
|   private setLocaleName = (locale: LocaleCode) => { | ||||
|     this.localeNames![locale] = new Intl.DisplayNames([locale], { | ||||
|     this.localeNames[locale] = new Intl.DisplayNames([locale], { | ||||
|       type: "language", | ||||
|     }).of(locale); | ||||
|     }).of(locale)!; | ||||
|   }; | ||||
| 
 | ||||
|   async firstUpdated() { | ||||
| @ -63,22 +63,33 @@ export class LocalePicker extends LitElement { | ||||
|     const selectedLocale = getLocale(); | ||||
| 
 | ||||
|     return html` | ||||
|       <sl-select value=${selectedLocale} @sl-change=${this.localeChanged}> | ||||
|         ${allLocales.map( | ||||
|           (locale) => | ||||
|             html`<sl-menu-item
 | ||||
|               value=${locale} | ||||
|               ?selected=${locale === selectedLocale} | ||||
|             > | ||||
|               ${this.localeNames![locale]} | ||||
|             </sl-menu-item>` | ||||
|         )} | ||||
|       </sl-select> | ||||
|       <sl-dropdown | ||||
|         value=${selectedLocale} | ||||
|         @sl-select=${this.localeChanged} | ||||
|         placement="top-end" | ||||
|         distance="4" | ||||
|         hoist | ||||
|       > | ||||
|         <sl-button slot="trigger" size="small" caret | ||||
|           >${this.localeNames[selectedLocale as LocaleCode]}</sl-button | ||||
|         > | ||||
|         <sl-menu> | ||||
|           ${allLocales.map( | ||||
|             (locale) => | ||||
|               html`<sl-menu-item
 | ||||
|                 value=${locale} | ||||
|                 ?checked=${locale === selectedLocale} | ||||
|               > | ||||
|                 ${this.localeNames[locale]} | ||||
|               </sl-menu-item>` | ||||
|           )} | ||||
|         </sl-menu> | ||||
|       </sl-dropdown> | ||||
|     `;
 | ||||
|   } | ||||
| 
 | ||||
|   async localeChanged(event: Event) { | ||||
|     const newLocale = (event.target as HTMLSelectElement).value as LocaleCode; | ||||
|   async localeChanged(event: CustomEvent) { | ||||
|     const newLocale = event.detail.item.value as LocaleCode; | ||||
| 
 | ||||
|     if (newLocale !== getLocale()) { | ||||
|       const url = new URL(window.location.href); | ||||
|  | ||||
| @ -194,9 +194,7 @@ export class App extends LiteElement { | ||||
|       <div class="min-w-screen min-h-screen flex flex-col"> | ||||
|         ${this.renderNavBar()} | ||||
|         <main class="relative flex-auto flex">${this.renderPage()}</main> | ||||
|         <footer class="flex justify-center p-4 border-t"> | ||||
|           <btrix-locale-picker></btrix-locale-picker> | ||||
|         </footer> | ||||
|         <div class="border-t border-neutral-100">${this.renderFooter()}</div> | ||||
|       </div> | ||||
| 
 | ||||
|       <sl-dialog | ||||
| @ -217,34 +215,61 @@ export class App extends LiteElement { | ||||
|         > | ||||
|           <div> | ||||
|             <a href="/archives" @click="${this.navLink}" | ||||
|               ><h1 class="text-base">${msg("Browsertrix Cloud")}</h1></a | ||||
|               ><h1 class="text-sm font-medium"> | ||||
|                 ${msg("Browsertrix Cloud")} | ||||
|               </h1></a | ||||
|             > | ||||
|           </div> | ||||
|           <div class="grid grid-flow-col gap-5 items-center"> | ||||
|             ${this.authService.authState | ||||
|               ? html` <sl-dropdown placement="bottom-end">
 | ||||
|                   <div class="p-2" role="button" slot="trigger"> | ||||
|                     ${this.userInfo?.name || this.userInfo?.email} | ||||
|                     <span class="text-xs" | ||||
|                       ><sl-icon name="chevron-down"></sl-icon | ||||
|                     ></span> | ||||
|                   </div> | ||||
|                   <sl-menu> | ||||
|                   <sl-icon-button | ||||
|                     slot="trigger" | ||||
|                     name="person-circle" | ||||
|                     style="font-size: 1.5rem;" | ||||
|                   ></sl-icon-button> | ||||
| 
 | ||||
|                   <sl-menu class="w-60 min-w-min max-w-full"> | ||||
|                     <div class="px-7 py-2"> | ||||
|                       ${this.userInfo?.isAdmin | ||||
|                         ? html` | ||||
|                             <div class="mb-2"> | ||||
|                               <sl-tag | ||||
|                                 class="uppercase" | ||||
|                                 type="primary" | ||||
|                                 size="small" | ||||
|                                 >${msg("admin")}</sl-tag | ||||
|                               > | ||||
|                             </div> | ||||
|                           ` | ||||
|                         : ""} | ||||
|                       <div class="font-medium text-neutral-700"> | ||||
|                         ${this.userInfo?.name} | ||||
|                       </div> | ||||
|                       <div class="text-sm text-neutral-500"> | ||||
|                         ${this.userInfo?.email} | ||||
|                       </div> | ||||
|                     </div> | ||||
|                     <sl-divider></sl-divider> | ||||
|                     <sl-menu-item | ||||
|                       @click=${() => this.navigate(ROUTES.accountSettings)} | ||||
|                     > | ||||
|                       <sl-icon slot="prefix" name="gear"></sl-icon> | ||||
|                       ${msg("Your account")} | ||||
|                     </sl-menu-item> | ||||
|                     ${this.userInfo?.isAdmin | ||||
|                       ? html` <sl-menu-item
 | ||||
|                           @click=${() => this.navigate(ROUTES.usersInvite)} | ||||
|                         > | ||||
|                           <sl-icon slot="prefix" name="person-plus"></sl-icon> | ||||
|                           ${msg("Invite Users")} | ||||
|                         </sl-menu-item>` | ||||
|                       : ""} | ||||
|                     <sl-menu-item @click="${this.onLogOut}" | ||||
|                       >${msg("Log Out")}</sl-menu-item | ||||
|                     > | ||||
|                     <sl-divider></sl-divider> | ||||
|                     <sl-menu-item @click="${this.onLogOut}"> | ||||
|                       <sl-icon slot="prefix" name="box-arrow-right"></sl-icon> | ||||
|                       ${msg("Log Out")} | ||||
|                     </sl-menu-item> | ||||
|                   </sl-menu> | ||||
|                 </sl-dropdown>` | ||||
|               : html` | ||||
| @ -266,17 +291,36 @@ export class App extends LiteElement { | ||||
|     `;
 | ||||
|   } | ||||
| 
 | ||||
|   renderFooter() { | ||||
|     return html` | ||||
|       <footer | ||||
|         class="w-full max-w-screen-lg mx-auto p-3 box-border flex justify-between" | ||||
|       > | ||||
|         <div> | ||||
|           <sl-icon-button | ||||
|             name="github" | ||||
|             href="https://github.com/webrecorder/browsertrix-cloud" | ||||
|             target="_blank" | ||||
|           ></sl-icon-button> | ||||
|         </div> | ||||
|         <div> | ||||
|           <btrix-locale-picker></btrix-locale-picker> | ||||
|         </div> | ||||
|       </footer> | ||||
|     `;
 | ||||
|   } | ||||
| 
 | ||||
|   renderPage() { | ||||
|     switch (this.viewState.route) { | ||||
|       case "signUp": { | ||||
|         if (!this.isAppSettingsLoaded) { | ||||
|           return html`<div
 | ||||
|             class="w-full md:bg-gray-50 flex items-center justify-center" | ||||
|             class="w-full md:bg-neutral-50 flex items-center justify-center" | ||||
|           ></div>`;
 | ||||
|         } | ||||
|         if (this.isRegistrationEnabled) { | ||||
|           return html`<btrix-sign-up
 | ||||
|             class="w-full md:bg-gray-50 flex items-center justify-center" | ||||
|             class="w-full md:bg-neutral-50 flex items-center justify-center" | ||||
|             @navigate="${this.onNavigateTo}" | ||||
|             @logged-in="${this.onLoggedIn}" | ||||
|             @log-out="${this.onLogOut}" | ||||
| @ -289,7 +333,7 @@ export class App extends LiteElement { | ||||
| 
 | ||||
|       case "verify": | ||||
|         return html`<btrix-verify
 | ||||
|           class="w-full md:bg-gray-50 flex items-center justify-center" | ||||
|           class="w-full md:bg-neutral-50 flex items-center justify-center" | ||||
|           token="${this.viewState.params.token}" | ||||
|           @navigate="${this.onNavigateTo}" | ||||
|           @notify="${this.onNotify}" | ||||
| @ -300,7 +344,7 @@ export class App extends LiteElement { | ||||
| 
 | ||||
|       case "join": | ||||
|         return html`<btrix-join
 | ||||
|           class="w-full md:bg-gray-50 flex items-center justify-center" | ||||
|           class="w-full md:bg-neutral-50 flex items-center justify-center" | ||||
|           @navigate="${this.onNavigateTo}" | ||||
|           @logged-in="${this.onLoggedIn}" | ||||
|           token="${this.viewState.params.token}" | ||||
| @ -309,7 +353,7 @@ export class App extends LiteElement { | ||||
| 
 | ||||
|       case "acceptInvite": | ||||
|         return html`<btrix-accept-invite
 | ||||
|           class="w-full md:bg-gray-50 flex items-center justify-center" | ||||
|           class="w-full md:bg-neutral-50 flex items-center justify-center" | ||||
|           @navigate="${this.onNavigateTo}" | ||||
|           @logged-in="${this.onLoggedIn}" | ||||
|           @notify="${this.onNotify}" | ||||
| @ -322,7 +366,7 @@ export class App extends LiteElement { | ||||
|       case "loginWithRedirect": | ||||
|       case "forgotPassword": | ||||
|         return html`<btrix-log-in
 | ||||
|           class="w-full md:bg-gray-50 flex items-center justify-center" | ||||
|           class="w-full md:bg-neutral-50 flex items-center justify-center" | ||||
|           @navigate=${this.onNavigateTo} | ||||
|           @logged-in=${this.onLoggedIn} | ||||
|           .authState=${this.authService.authState} | ||||
| @ -332,7 +376,7 @@ export class App extends LiteElement { | ||||
| 
 | ||||
|       case "resetPassword": | ||||
|         return html`<btrix-reset-password
 | ||||
|           class="w-full md:bg-gray-50 flex items-center justify-center" | ||||
|           class="w-full md:bg-neutral-50 flex items-center justify-center" | ||||
|           @navigate=${this.onNavigateTo} | ||||
|           @logged-in=${this.onLoggedIn} | ||||
|           .authState=${this.authService.authState} | ||||
| @ -352,7 +396,7 @@ export class App extends LiteElement { | ||||
| 
 | ||||
|       case "archives": | ||||
|         return html`<btrix-archives
 | ||||
|           class="w-full max-w-screen-lg mx-auto p-2 md:py-8 box-border" | ||||
|           class="w-full md:bg-neutral-50" | ||||
|           @navigate="${this.onNavigateTo}" | ||||
|           @need-login="${this.onNeedLogin}" | ||||
|           .authState="${this.authService.authState}" | ||||
| @ -412,7 +456,7 @@ export class App extends LiteElement { | ||||
| 
 | ||||
|   renderNotFoundPage() { | ||||
|     return html`<btrix-not-found
 | ||||
|       class="w-full md:bg-gray-50 flex items-center justify-center" | ||||
|       class="w-full md:bg-neutral-50 flex items-center justify-center" | ||||
|     ></btrix-not-found>`; | ||||
|   } | ||||
| 
 | ||||
|  | ||||
| @ -18,34 +18,78 @@ export class Archives extends LiteElement { | ||||
|   userInfo?: CurrentUser; | ||||
| 
 | ||||
|   @state() | ||||
|   archiveList?: ArchiveData[]; | ||||
|   private archiveList?: ArchiveData[]; | ||||
| 
 | ||||
|   @state() | ||||
|   private isInviteComplete?: boolean; | ||||
| 
 | ||||
|   async firstUpdated() { | ||||
|     this.archiveList = await this.getArchives(); | ||||
|   } | ||||
| 
 | ||||
|   render() { | ||||
|     if (!this.archiveList) { | ||||
|       return html`<div
 | ||||
|         class="w-full flex items-center justify-center my-24 text-4xl" | ||||
|       > | ||||
|         <sl-spinner></sl-spinner> | ||||
|     if (!this.archiveList || !this.userInfo) { | ||||
|       return html` | ||||
|         <div class="flex items-center justify-center my-24 text-4xl"> | ||||
|           <sl-spinner></sl-spinner> | ||||
|         </div> | ||||
|       `;
 | ||||
|     } | ||||
| 
 | ||||
|     if (this.userInfo.isAdmin && !this.archiveList.length) { | ||||
|       return html` | ||||
|         <div class="bg-white"> | ||||
|           <header | ||||
|             class="w-full max-w-screen-lg mx-auto px-3 py-4 box-border md:py-8" | ||||
|           > | ||||
|             <h1 class="text-2xl font-medium">${msg("Archives")}</h1> | ||||
|             <p class="mt-4 text-neutral-600"> | ||||
|               ${msg("Invite users to start archiving.")} | ||||
|             </p> | ||||
|           </header> | ||||
|           <hr /> | ||||
|         </div> | ||||
|         <main class="w-full max-w-screen-lg mx-auto px-3 py-4 box-border"> | ||||
|           ${this.renderAdminOnboarding()} | ||||
|         </main> | ||||
|       `;
 | ||||
|     } | ||||
| 
 | ||||
|     return html` | ||||
|       <div class="bg-white"> | ||||
|         <header | ||||
|           class="w-full max-w-screen-lg mx-auto px-3 py-4 box-border md:py-8" | ||||
|         > | ||||
|           <h1 class="text-2xl font-medium">${msg("Archives")}</h1> | ||||
|         </header> | ||||
|         <hr /> | ||||
|       </div> | ||||
|       <main class="w-full max-w-screen-lg mx-auto px-3 py-4 box-border"> | ||||
|         ${this.renderArchives()} | ||||
|       </main> | ||||
|     `;
 | ||||
|   } | ||||
| 
 | ||||
|   private renderArchives() { | ||||
|     if (!this.archiveList?.length) { | ||||
|       return html`<div class="border rounded-lg bg-white p-4 md:p-8">
 | ||||
|         <p class="text-neutral-400 text-center"> | ||||
|           ${msg("You don't have any archives.")} | ||||
|         </p> | ||||
|       </div>`;
 | ||||
|     } | ||||
| 
 | ||||
|     return html`<div class="grid gap-4">
 | ||||
|       <h1 class="text-xl font-bold">${msg("Archives")}</h1> | ||||
| 
 | ||||
|     return html` | ||||
|       <ul class="border rounded-lg overflow-hidden"> | ||||
|         ${this.archiveList.map( | ||||
|           (archive, i) => | ||||
|           (archive) => | ||||
|             html` | ||||
|               <li | ||||
|                 class="p-3 md:p-6 hover:bg-gray-50${i > 0 ? " border-t" : ""}" | ||||
|                 class="p-3 md:p-6 bg-white border-t first:border-t-0 text-primary hover:text-indigo-400" | ||||
|                 role="button" | ||||
|                 @click=${this.makeOnArchiveClick(archive)} | ||||
|               > | ||||
|                 <span class="text-primary font-medium mr-2" | ||||
|                 <span class="font-medium mr-2 transition-colors" | ||||
|                   >${archive.name}</span | ||||
|                 > | ||||
|                 ${this.userInfo && | ||||
| @ -59,7 +103,33 @@ export class Archives extends LiteElement { | ||||
|             ` | ||||
|         )} | ||||
|       </ul> | ||||
|     </div>`;
 | ||||
|     `;
 | ||||
|   } | ||||
| 
 | ||||
|   private renderAdminOnboarding() { | ||||
|     if (this.isInviteComplete) { | ||||
|       return html` | ||||
|         <div class="border rounded-lg bg-white p-4 md:p-8"> | ||||
|           <h2 class="text-2xl font-medium mb-4">${msg("Invite a User")}</h2> | ||||
|           <sl-button @click=${() => (this.isInviteComplete = false)} | ||||
|             >${msg("Send another invite")}</sl-button | ||||
|           > | ||||
|         </div> | ||||
|       `;
 | ||||
|     } | ||||
|     return html` | ||||
|       <div class="border rounded-lg bg-white p-4 md:p-8"> | ||||
|         <h2 class="text-2xl font-medium mb-4">${msg("Invite a User")}</h2> | ||||
|         <p class="mb-4 text-neutral-600 text-sm"> | ||||
|           ${msg("Each user will manage their own archive.")} | ||||
|         </p> | ||||
| 
 | ||||
|         <btrix-invite-form | ||||
|           .authState=${this.authState} | ||||
|           @success=${() => (this.isInviteComplete = true)} | ||||
|         ></btrix-invite-form> | ||||
|       </div> | ||||
|     `;
 | ||||
|   } | ||||
| 
 | ||||
|   async getArchives(): Promise<ArchiveData[]> { | ||||
|  | ||||
| @ -20,6 +20,9 @@ import( | ||||
| import( | ||||
|   /* webpackChunkName: "shoelace" */ "@shoelace-style/shoelace/dist/components/dialog/dialog" | ||||
| ); | ||||
| import( | ||||
|   /* webpackChunkName: "shoelace" */ "@shoelace-style/shoelace/dist/components/divider/divider" | ||||
| ); | ||||
| import( | ||||
|   /* webpackChunkName: "shoelace" */ "@shoelace-style/shoelace/dist/components/form/form" | ||||
| ); | ||||
|  | ||||
		Loading…
	
		Reference in New Issue
	
	Block a user