web: sort components into folders, implement pagination for table
This commit is contained in:
		
							
								
								
									
										22
									
								
								web/dist/main.js
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										22
									
								
								web/dist/main.js
									
									
									
									
										vendored
									
									
								
							
										
											
												File diff suppressed because one or more lines are too long
											
										
									
								
							
							
								
								
									
										2
									
								
								web/dist/main.js.map
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										2
									
								
								web/dist/main.js.map
									
									
									
									
										vendored
									
									
								
							
										
											
												File diff suppressed because one or more lines are too long
											
										
									
								
							| @ -14,7 +14,7 @@ export class Client { | ||||
|         return builtUrl; | ||||
|     } | ||||
|  | ||||
|     fetch<T>(url: string[], query?: { [key: string]: string }): Promise<T> { | ||||
|     fetch<T>(url: string[], query?: { [key: string]: any }): Promise<T> { | ||||
|         const finalUrl = this.makeUrl(url, query); | ||||
|         return fetch(finalUrl) | ||||
|             .then((r) => { | ||||
| @ -35,9 +35,20 @@ export class Client { | ||||
|  | ||||
| export const DefaultClient = new Client(); | ||||
|  | ||||
| export interface PBResponse { | ||||
| export interface PBPagination { | ||||
|     next?: number; | ||||
|     previous?: number; | ||||
|  | ||||
|     count: number; | ||||
|     next: string; | ||||
|     previous: string; | ||||
|     current: number; | ||||
|     total_pages: number; | ||||
|  | ||||
|     start_index: number; | ||||
|     end_index: number; | ||||
| } | ||||
|  | ||||
| export interface PBResponse { | ||||
|     pagination: PBPagination; | ||||
|  | ||||
|     results: Array<any>; | ||||
| } | ||||
|  | ||||
| @ -12,19 +12,18 @@ export class Config { | ||||
|     error_reporting_send_pii?: boolean; | ||||
|  | ||||
|     static get(): Promise<Config> { | ||||
|         return DefaultClient.fetch<Config>(["root", "config"]) | ||||
|             .then((config) => { | ||||
|                 if (config.error_reporting_enabled) { | ||||
|                     Sentry.init({ | ||||
|                         dsn: "https://33cdbcb23f8b436dbe0ee06847410b67@sentry.beryju.org/3", | ||||
|                         release: `passbook@${VERSION}`, | ||||
|                         integrations: [new Integrations.BrowserTracing()], | ||||
|                         tracesSampleRate: 1.0, | ||||
|                         environment: config.error_reporting_environment, | ||||
|                     }); | ||||
|                     console.debug(`passbook/config: Sentry enabled.`); | ||||
|                 } | ||||
|                 return config; | ||||
|             }); | ||||
|         return DefaultClient.fetch<Config>(["root", "config"]).then((config) => { | ||||
|             if (config.error_reporting_enabled) { | ||||
|                 Sentry.init({ | ||||
|                     dsn: "https://33cdbcb23f8b436dbe0ee06847410b67@sentry.beryju.org/3", | ||||
|                     release: `passbook@${VERSION}`, | ||||
|                     integrations: [new Integrations.BrowserTracing()], | ||||
|                     tracesSampleRate: 1.0, | ||||
|                     environment: config.error_reporting_environment, | ||||
|                 }); | ||||
|                 console.debug(`passbook/config: Sentry enabled.`); | ||||
|             } | ||||
|             return config; | ||||
|         }); | ||||
|     } | ||||
| } | ||||
|  | ||||
| @ -1,47 +0,0 @@ | ||||
| import { html, LitElement } from "lit-element"; | ||||
| import { until } from "lit-html/directives/until.js"; | ||||
| import { PBResponse } from "../api/client"; | ||||
| import { COMMON_STYLES } from "../common/styles"; | ||||
|  | ||||
| export abstract class Table extends LitElement { | ||||
|     abstract apiEndpoint(): Promise<PBResponse>; | ||||
|     abstract columns(): Array<string>; | ||||
|     abstract row(item: any): Array<string>; | ||||
|  | ||||
|     private data: PBResponse = <PBResponse>{}; | ||||
|  | ||||
|     static get styles() { | ||||
|         return [COMMON_STYLES]; | ||||
|     } | ||||
|  | ||||
|     private renderRows() { | ||||
|         return this.apiEndpoint() | ||||
|             .then((r) => (this.data = r)) | ||||
|             .then(() => { | ||||
|                 return this.data.results.map((item) => { | ||||
|                     const fullRow = [`<tr role="row">`].concat( | ||||
|                         this.row(item).map((col) => { | ||||
|                             return `<td role="cell">${col}</td>`; | ||||
|                         }) | ||||
|                     ); | ||||
|                     fullRow.push(`</tr>`); | ||||
|                     return html(<any>fullRow); | ||||
|                 }); | ||||
|             }); | ||||
|     } | ||||
|  | ||||
|     render() { | ||||
|         return html`<table class="pf-c-table pf-m-compact pf-m-grid-md"> | ||||
|             <thead> | ||||
|                 <tr role="row"> | ||||
|                     ${this.columns().map( | ||||
|                         (col) => html`<th role="columnheader" scope="col">${col}</th>` | ||||
|                     )} | ||||
|                 </tr> | ||||
|             </thead> | ||||
|             <tbody role="rowgroup"> | ||||
|                 ${until(this.renderRows(), html`<tr role="row"><td>loading...</tr></td>`)} | ||||
|             </tbody> | ||||
|         </table>`; | ||||
|     } | ||||
| } | ||||
| @ -1,6 +1,6 @@ | ||||
| import { getCookie } from "../utils"; | ||||
| import { getCookie } from "../../utils"; | ||||
| import { customElement, html, property } from "lit-element"; | ||||
| import { ERROR_CLASS, SUCCESS_CLASS } from "../constants"; | ||||
| import { ERROR_CLASS, SUCCESS_CLASS } from "../../constants"; | ||||
| import { SpinnerButton } from "./SpinnerButton"; | ||||
| 
 | ||||
| @customElement("pb-action-button") | ||||
| @ -10,9 +10,9 @@ import ButtonStyle from "@patternfly/patternfly/components/Button/button.css"; | ||||
| // @ts-ignore
 | ||||
| import fa from "@fortawesome/fontawesome-free/css/solid.css"; | ||||
| 
 | ||||
| import { convertToSlug } from "../utils"; | ||||
| import { convertToSlug } from "../../utils"; | ||||
| import { SpinnerButton } from "./SpinnerButton"; | ||||
| import { PRIMARY_CLASS } from "../constants"; | ||||
| import { PRIMARY_CLASS } from "../../constants"; | ||||
| 
 | ||||
| @customElement("pb-modal-button") | ||||
| export class ModalButton extends LitElement { | ||||
| @ -124,7 +124,7 @@ export class ModalButton extends LitElement { | ||||
|                     this.querySelector("[slot=modal]")!.innerHTML = t; | ||||
|                     this.updateHandlers(); | ||||
|                     this.open = true; | ||||
|                     this.querySelectorAll<SpinnerButton>("pb-spinner-button").forEach(sb => { | ||||
|                     this.querySelectorAll<SpinnerButton>("pb-spinner-button").forEach((sb) => { | ||||
|                         sb.setDone(PRIMARY_CLASS); | ||||
|                     }); | ||||
|                 }) | ||||
| @ -5,11 +5,7 @@ import GlobalsStyle from "@patternfly/patternfly/base/patternfly-globals.css"; | ||||
| import ButtonStyle from "@patternfly/patternfly/components/Button/button.css"; | ||||
| // @ts-ignore
 | ||||
| import SpinnerStyle from "@patternfly/patternfly/components/Spinner/spinner.css"; | ||||
| import { | ||||
|     ColorStyles, | ||||
|     PRIMARY_CLASS, | ||||
|     PROGRESS_CLASS, | ||||
| } from "../constants"; | ||||
| import { ColorStyles, PRIMARY_CLASS, PROGRESS_CLASS } from "../../constants"; | ||||
| 
 | ||||
| @customElement("pb-spinner-button") | ||||
| export class SpinnerButton extends LitElement { | ||||
| @ -3,8 +3,8 @@ import { css, customElement, html, LitElement, property } from "lit-element"; | ||||
| import GlobalsStyle from "@patternfly/patternfly/base/patternfly-globals.css"; | ||||
| // @ts-ignore
 | ||||
| import ButtonStyle from "@patternfly/patternfly/components/Button/button.css"; | ||||
| import { tokenByIdentifier } from "../api/token"; | ||||
| import { ColorStyles, ERROR_CLASS, PRIMARY_CLASS, SUCCESS_CLASS } from "../constants"; | ||||
| import { tokenByIdentifier } from "../../api/token"; | ||||
| import { ColorStyles, ERROR_CLASS, PRIMARY_CLASS, SUCCESS_CLASS } from "../../constants"; | ||||
| 
 | ||||
| @customElement("pb-token-copy-button") | ||||
| export class TokenCopyButton extends LitElement { | ||||
| @ -6,7 +6,7 @@ import NavStyle from "@patternfly/patternfly/components/Nav/nav.css"; | ||||
| // @ts-ignore
 | ||||
| import GlobalsStyle from "@patternfly/patternfly/base/patternfly-globals.css"; | ||||
| 
 | ||||
| import { User } from "../api/user"; | ||||
| import { User } from "../../api/user"; | ||||
| 
 | ||||
| export interface SidebarItem { | ||||
|     name: string; | ||||
| @ -3,7 +3,7 @@ import { css, customElement, html, LitElement, property } from "lit-element"; | ||||
| import PageStyle from "@patternfly/patternfly/components/Page/page.css"; | ||||
| // @ts-ignore
 | ||||
| import GlobalsStyle from "@patternfly/patternfly/base/patternfly-globals.css"; | ||||
| import { Config } from "../api/config"; | ||||
| import { Config } from "../../api/config"; | ||||
| 
 | ||||
| @customElement("pb-sidebar-brand") | ||||
| export class SidebarBrand extends LitElement { | ||||
| @ -5,7 +5,7 @@ import NavStyle from "@patternfly/patternfly/components/Nav/nav.css"; | ||||
| import fa from "@fortawesome/fontawesome-free/css/all.css"; | ||||
| // @ts-ignore
 | ||||
| import AvatarStyle from "@patternfly/patternfly/components/Avatar/avatar.css"; | ||||
| import { User } from "../api/user"; | ||||
| import { User } from "../../api/user"; | ||||
| 
 | ||||
| @customElement("pb-sidebar-user") | ||||
| export class SidebarUser extends LitElement { | ||||
							
								
								
									
										86
									
								
								web/src/elements/table/Table.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										86
									
								
								web/src/elements/table/Table.ts
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,86 @@ | ||||
| import { html, LitElement, property, TemplateResult } from "lit-element"; | ||||
| import { until } from "lit-html/directives/until.js"; | ||||
| import { PBResponse } from "../../api/client"; | ||||
| import { COMMON_STYLES } from "../../common/styles"; | ||||
|  | ||||
| export abstract class Table extends LitElement { | ||||
|     abstract apiEndpoint(page: number): Promise<PBResponse>; | ||||
|     abstract columns(): Array<string>; | ||||
|     abstract row(item: any): Array<string>; | ||||
|  | ||||
|     @property() | ||||
|     data?: PBResponse; | ||||
|  | ||||
|     @property() | ||||
|     page: number = 1; | ||||
|  | ||||
|     static get styles() { | ||||
|         return [COMMON_STYLES]; | ||||
|     } | ||||
|  | ||||
|     public fetch() { | ||||
|         this.apiEndpoint(this.page).then((r) => { | ||||
|             this.data = r; | ||||
|             this.page = r.pagination.current; | ||||
|         }); | ||||
|     } | ||||
|  | ||||
|     private renderRows(): TemplateResult[] | undefined { | ||||
|         if (!this.data) { | ||||
|             return; | ||||
|         } | ||||
|         return this.data.results.map((item) => { | ||||
|             const fullRow = [`<tr role="row">`].concat( | ||||
|                 this.row(item).map((col) => { | ||||
|                     return `<td role="cell">${col}</td>`; | ||||
|                 }) | ||||
|             ); | ||||
|             fullRow.push(`</tr>`); | ||||
|             return html(<any>fullRow); | ||||
|         }); | ||||
|     } | ||||
|  | ||||
|     render() { | ||||
|         if (!this.data) { | ||||
|             this.fetch(); | ||||
|             return; | ||||
|         } | ||||
|         return html`<div class="pf-c-toolbar"> | ||||
|                 <div class="pf-c-toolbar__content"> | ||||
|                     <div class="pf-c-toolbar__bulk-select"> | ||||
|                         <slot name="create-button"></slot> | ||||
|                         <button | ||||
|                             @click=${() => { | ||||
|                                 this.fetch(); | ||||
|                             }} | ||||
|                             class="pf-c-button pf-m-primary" | ||||
|                         > | ||||
|                             Refresh | ||||
|                         </button> | ||||
|                     </div> | ||||
|                     <pb-table-pagination | ||||
|                         class="pf-c-toolbar__item pf-m-pagination" | ||||
|                         .table=${this} | ||||
|                     ></pb-table-pagination> | ||||
|                 </div> | ||||
|             </div> | ||||
|             <table class="pf-c-table pf-m-compact pf-m-grid-md"> | ||||
|                 <thead> | ||||
|                     <tr role="row"> | ||||
|                         ${this.columns().map( | ||||
|                             (col) => html`<th role="columnheader" scope="col">${col}</th>` | ||||
|                         )} | ||||
|                     </tr> | ||||
|                 </thead> | ||||
|                 <tbody role="rowgroup"> | ||||
|                     ${this.renderRows()} | ||||
|                 </tbody> | ||||
|             </table> | ||||
|             <div class="pf-c-pagination pf-m-bottom"> | ||||
|                 <pb-table-pagination | ||||
|                     class="pf-c-toolbar__item pf-m-pagination" | ||||
|                     .table=${this} | ||||
|                 ></pb-table-pagination> | ||||
|             </div>`; | ||||
|     } | ||||
| } | ||||
							
								
								
									
										71
									
								
								web/src/elements/table/TablePagination.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										71
									
								
								web/src/elements/table/TablePagination.ts
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,71 @@ | ||||
| import { customElement, html, LitElement, property } from "lit-element"; | ||||
| import { Table } from "./Table"; | ||||
| import { COMMON_STYLES } from "../../common/styles"; | ||||
|  | ||||
| @customElement("pb-table-pagination") | ||||
| export class TablePagination extends LitElement { | ||||
|     @property() | ||||
|     table?: Table; | ||||
|  | ||||
|     static get styles() { | ||||
|         return [COMMON_STYLES]; | ||||
|     } | ||||
|  | ||||
|     previousHandler() { | ||||
|         if (!this.table?.data?.pagination.previous) { | ||||
|             console.debug(`passbook/tables: no previous`); | ||||
|             return; | ||||
|         } | ||||
|         this.table.page = this.table?.data?.pagination.previous; | ||||
|     } | ||||
|  | ||||
|     nextHandler() { | ||||
|         if (!this.table?.data?.pagination.next) { | ||||
|             console.debug(`passbook/tables: no next`); | ||||
|             return; | ||||
|         } | ||||
|         this.table.page = this.table?.data?.pagination.next; | ||||
|     } | ||||
|  | ||||
|     render() { | ||||
|         return html` <div class="pf-c-pagination pf-m-compact pf-m-hidden pf-m-visible-on-md"> | ||||
|             <div class="pf-c-pagination pf-m-compact pf-m-compact pf-m-hidden pf-m-visible-on-md"> | ||||
|                 <div class="pf-c-options-menu"> | ||||
|                     <div class="pf-c-options-menu__toggle pf-m-text pf-m-plain"> | ||||
|                         <span class="pf-c-options-menu__toggle-text"> | ||||
|                             ${this.table?.data?.pagination.start_index} - | ||||
|                             ${this.table?.data?.pagination.end_index} of | ||||
|                             ${this.table?.data?.pagination.count} | ||||
|                         </span> | ||||
|                     </div> | ||||
|                 </div> | ||||
|                 <nav class="pf-c-pagination__nav" aria-label="Pagination"> | ||||
|                     <div class="pf-c-pagination__nav-control pf-m-prev"> | ||||
|                         <button | ||||
|                             class="pf-c-button pf-m-plain" | ||||
|                             @click=${() => { | ||||
|                                 this.previousHandler(); | ||||
|                             }} | ||||
|                             disabled="${this.table?.data?.pagination.previous ? "true" : "false"}" | ||||
|                             aria-label="{% trans 'Go to previous page' %}" | ||||
|                         > | ||||
|                             <i class="fas fa-angle-left" aria-hidden="true"></i> | ||||
|                         </button> | ||||
|                     </div> | ||||
|                     <div class="pf-c-pagination__nav-control pf-m-next"> | ||||
|                         <button | ||||
|                             class="pf-c-button pf-m-plain" | ||||
|                             @click=${() => { | ||||
|                                 this.nextHandler(); | ||||
|                             }} | ||||
|                             disabled="${this.table?.data?.pagination.next ? "true" : "false"}" | ||||
|                             aria-label="{% trans 'Go to next page' %}" | ||||
|                         > | ||||
|                             <i class="fas fa-angle-right" aria-hidden="true"></i> | ||||
|                         </button> | ||||
|                     </div> | ||||
|                 </nav> | ||||
|             </div> | ||||
|         </div>`; | ||||
|     } | ||||
| } | ||||
| @ -1,18 +1,19 @@ | ||||
| import "construct-style-sheets-polyfill"; | ||||
|  | ||||
| import "./elements/ActionButton"; | ||||
| import "./elements/SpinnerButton"; | ||||
| import "./elements/AdminLoginsChart"; | ||||
| import "./elements/buttons/ActionButton"; | ||||
| import "./elements/buttons/Dropdown"; | ||||
| import "./elements/buttons/ModalButton"; | ||||
| import "./elements/buttons/SpinnerButton"; | ||||
| import "./elements/buttons/TokenCopyButton"; | ||||
| import "./elements/CodeMirror"; | ||||
| import "./elements/Dropdown"; | ||||
| import "./elements/FetchFillSlot"; | ||||
| import "./elements/Messages"; | ||||
| import "./elements/ModalButton"; | ||||
| import "./elements/Sidebar"; | ||||
| import "./elements/SidebarBrand"; | ||||
| import "./elements/SidebarUser"; | ||||
| import "./elements/sidebar/Sidebar"; | ||||
| import "./elements/sidebar/SidebarBrand"; | ||||
| import "./elements/sidebar/SidebarUser"; | ||||
| import "./elements/Tabs"; | ||||
| import "./elements/TokenCopyButton"; | ||||
| import "./elements/table/TablePagination"; | ||||
| import "./pages/applications/ApplicationViewPage"; | ||||
| import "./pages/FlowShellCard"; | ||||
| import "./pages/RouterOutlet"; | ||||
|  | ||||
| @ -2,17 +2,18 @@ import { css, customElement, html, LitElement, property, TemplateResult } from " | ||||
| import { Application } from "../../api/application"; | ||||
| import { DefaultClient, PBResponse } from "../../api/client"; | ||||
| import { COMMON_STYLES } from "../../common/styles"; | ||||
| import { Table } from "../../elements/Table"; | ||||
| import { Table } from "../../elements/table/Table"; | ||||
|  | ||||
| @customElement("pb-bound-policies-list") | ||||
| export class BoundPoliciesList extends Table { | ||||
|     @property() | ||||
|     target?: string; | ||||
|  | ||||
|     apiEndpoint(): Promise<PBResponse> { | ||||
|     apiEndpoint(page: number): Promise<PBResponse> { | ||||
|         return DefaultClient.fetch<PBResponse>(["policies", "bindings"], { | ||||
|             target: this.target!, | ||||
|             ordering: "order", | ||||
|             page: page, | ||||
|         }); | ||||
|     } | ||||
|  | ||||
|  | ||||
		Reference in New Issue
	
	Block a user
	 Jens Langhammer
					Jens Langhammer