@tangential/admin-console
Version:
A basic administration console
575 lines (568 loc) • 92.1 kB
JavaScript
import * as i1$1 from '@angular/common';
import { CommonModule } from '@angular/common';
import * as i0 from '@angular/core';
import { Component, ViewEncapsulation, ChangeDetectionStrategy, ViewChild, NgModule, EventEmitter, Input, Output } from '@angular/core';
import * as i2$2 from '@angular/forms';
import { FormsModule } from '@angular/forms';
import * as i3$1 from '@angular/material/button';
import { MatButtonModule } from '@angular/material/button';
import * as i1$2 from '@angular/material/button-toggle';
import { MatButtonToggleModule } from '@angular/material/button-toggle';
import * as i4 from '@angular/material/checkbox';
import { MatCheckboxModule } from '@angular/material/checkbox';
import { MatGridListModule } from '@angular/material/grid-list';
import * as i4$2 from '@angular/material/icon';
import { MatIconModule } from '@angular/material/icon';
import * as i6$1 from '@angular/material/input';
import { MatInputModule } from '@angular/material/input';
import { MatListModule } from '@angular/material/list';
import { MatMenuModule } from '@angular/material/menu';
import * as i7 from '@angular/material/sort';
import { MatSort, MatSortModule } from '@angular/material/sort';
import * as i6 from '@angular/material/table';
import { MatTableDataSource, MatTableModule } from '@angular/material/table';
import { MatToolbarModule } from '@angular/material/toolbar';
import * as i5 from '@tangential/components';
import { TanjComponentsModule } from '@tangential/components';
import * as i2 from '@tangential/authorization-service';
import { AuthPermission, AuthRole, AuthUser, VisitorResolver, HasRoleGuard, AdminService, FirebaseAdminService } from '@tangential/authorization-service';
import * as i1 from '@tangential/core';
import { NameGenerator, generatePushID, Page, DefaultPageAnalytics } from '@tangential/core';
import { tap, debounceTime, distinctUntilChanged, filter, map } from 'rxjs/operators';
import * as i2$1 from '@angular/router';
import { RouterModule } from '@angular/router';
import * as i3 from '@angular/cdk/a11y';
import { SelectionModel } from '@angular/cdk/collections';
import * as i4$1 from '@tangential/plugin';
import 'rxjs';
import * as i5$1 from '@angular/material/form-field';
class AdminConsoleParentPage {
constructor(bus, adminService, changeDetectorRef) {
this.bus = bus;
this.adminService = adminService;
this.changeDetectorRef = changeDetectorRef;
this.auth$ = this.adminService.auth$().pipe(tap((v) => {
this.auth = v;
this.changeDetectorRef.markForCheck();
}));
}
}
AdminConsoleParentPage.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "14.2.10", ngImport: i0, type: AdminConsoleParentPage, deps: [{ token: i1.MessageBus }, { token: i2.AdminService }, { token: i0.ChangeDetectorRef }], target: i0.ɵɵFactoryTarget.Component });
AdminConsoleParentPage.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "14.2.10", type: AdminConsoleParentPage, selector: "tanj-admin-console-parent-page", ngImport: i0, template: '<router-outlet></router-outlet>', isInline: true, dependencies: [{ kind: "directive", type: i2$1.RouterOutlet, selector: "router-outlet", outputs: ["activate", "deactivate", "attach", "detach"], exportAs: ["outlet"] }], changeDetection: i0.ChangeDetectionStrategy.Default, encapsulation: i0.ViewEncapsulation.None });
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "14.2.10", ngImport: i0, type: AdminConsoleParentPage, decorators: [{
type: Component,
args: [{
selector: 'tanj-admin-console-parent-page',
template: '<router-outlet></router-outlet>',
encapsulation: ViewEncapsulation.None,
changeDetection: ChangeDetectionStrategy.Default
}]
}], ctorParameters: function () { return [{ type: i1.MessageBus }, { type: i2.AdminService }, { type: i0.ChangeDetectorRef }]; } });
class PermissionManagerPage {
constructor(adminService, parent, _liveAnnouncer, changeDetectorRef) {
this.adminService = adminService;
this.parent = parent;
this._liveAnnouncer = _liveAnnouncer;
this.changeDetectorRef = changeDetectorRef;
this.displayedColumns = ['select', '$key', 'description', 'createdMils', 'editedMils'];
this.dataSource = new MatTableDataSource();
this.selection = new SelectionModel(true, []);
this.rows = [];
this.selected = [];
}
ngOnInit() {
this.parent.auth$.subscribe({
next: (v) => {
this.rows = v.settings.permissions;
this.dataSource = new MatTableDataSource(v.settings.permissions);
this.dataSource.sort = this.sort;
this.changeDetectorRef.markForCheck();
}
});
}
get nextItemIndex() {
let idx = 1;
if (this.rows && this.rows.length) {
idx = (this.rows[this.rows.length - 1].orderIndex + 1);
}
return idx;
}
onAddItemAction() {
const permission = AuthPermission.from({
$key: NameGenerator.generate(),
orderIndex: this.nextItemIndex
});
this.adminService.addPermission(permission).catch((reason) => {
console.error('PermissionManagerPage', 'error adding permission', reason);
throw new Error(reason);
});
}
onRemove(key) {
this.adminService.removePermission(key).catch((reason) => {
console.error('PermissionManagerPage', 'error removing permission', reason);
throw new Error(reason);
});
}
onRemoveSelectedAction(keys) {
keys.forEach((key) => {
this.adminService.removePermission(key).catch((reason) => {
console.error('PermissionManagerPage', 'error removing permission', reason);
throw new Error(reason);
});
});
}
onItemChange(permission) {
this.adminService.updatePermission(permission).catch((reason) => {
console.error('PermissionManagerPage', 'error updating permission', reason);
throw new Error(reason);
});
}
/**
* Borrowed directly from Angular Material examples: https://material.angular.io/components/table/overview
*/
/** Whether the number of selected elements matches the total number of rows. */
isAllSelected() {
const numSelected = this.selection.selected.length;
const numRows = this.dataSource.data.length;
return numSelected === numRows;
}
/** Selects all rows if they are not all selected; otherwise clear selection. */
toggleAllRows() {
if (this.isAllSelected()) {
this.selection.clear();
return;
}
this.selection.select(...this.dataSource.data);
}
/** The label for the checkbox on the passed row */
checkboxLabel(row) {
if (!row) {
return `${this.isAllSelected() ? 'deselect' : 'select'} all`;
}
return `${this.selection.isSelected(row) ? 'deselect' : 'select'} ${row.description}`;
}
/** Announce the change in sort state for assistive technology. */
announceSortChange(sortState) {
// This example uses English messages. If your application supports
// multiple language, you would internationalize these strings.
// Furthermore, you can customize the message to add additional
// details about the values being sorted.
if (sortState.direction) {
this._liveAnnouncer.announce(`Sorted ${sortState.direction}ending`);
}
else {
this._liveAnnouncer.announce('Sorting cleared');
}
}
}
PermissionManagerPage.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "14.2.10", ngImport: i0, type: PermissionManagerPage, deps: [{ token: i2.AdminService }, { token: AdminConsoleParentPage }, { token: i3.LiveAnnouncer }, { token: i0.ChangeDetectorRef }], target: i0.ɵɵFactoryTarget.Component });
PermissionManagerPage.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "14.2.10", type: PermissionManagerPage, selector: "tanj-permission-manager", viewQueries: [{ propertyName: "sort", first: true, predicate: MatSort, descendants: true }], ngImport: i0, template: "<tanj-page-body>\n <table mat-table matSort (matSortChange)=\"announceSortChange($event)\" [dataSource]=\"dataSource\" class=\"mat-elevation-z8 tng-user-mgr-table\">\n\n <!-- Checkbox Column -->\n <ng-container matColumnDef=\"select\">\n <th mat-header-cell *matHeaderCellDef>\n <mat-checkbox (change)=\"$event ? toggleAllRows() : null\"\n [checked]=\"selection.hasValue() && isAllSelected()\"\n [indeterminate]=\"selection.hasValue() && !isAllSelected()\"\n [aria-label]=\"checkboxLabel()\">\n </mat-checkbox>\n </th>\n <td mat-cell *matCellDef=\"let row\" >\n <mat-checkbox (click)=\"$event.stopPropagation()\"\n (change)=\"$event ? selection.toggle(row) : null\"\n [checked]=\"selection.isSelected(row)\"\n [aria-label]=\"checkboxLabel(row)\">\n </mat-checkbox>\n </td>\n </ng-container>\n\n <!-- UID Column -->\n <ng-container matColumnDef=\"$key\">\n <th mat-header-cell *matHeaderCellDef mat-sort-header sortActionDescription=\"Sort by Permission ID\"> Permission ID </th>\n <td mat-cell *matCellDef=\"let element\"> {{element.$key}} </td>\n </ng-container>\n\n <!-- User name Column -->\n <ng-container matColumnDef=\"description\">\n <th mat-header-cell *matHeaderCellDef mat-sort-header sortActionDescription=\"Sort by Description\"> Description </th>\n <td mat-cell *matCellDef=\"let element\"> {{element.description}} </td>\n </ng-container>\n\n <!-- User Email Column -->\n <ng-container matColumnDef=\"createdMils\" >\n <th mat-header-cell *matHeaderCellDef mat-sort-header sortActionDescription=\"Sort by Created Date\"> Created </th>\n <td mat-cell *matCellDef=\"let element\"> {{element.createdMils | date:'yyyy-MM~dd, HH:mm:ss'}} </td>\n </ng-container>\n\n <!-- Last Sign in Column -->\n <ng-container matColumnDef=\"editedMils\">\n <th mat-header-cell *matHeaderCellDef mat-sort-header sortActionDescription=\"Sort by Last Modified\"> Last Modified </th>\n <td mat-cell *matCellDef=\"let element\"> {{element.editedMils | date:'yyyy-MM~dd, HH:mm:ss' }} </td>\n </ng-container>\n\n <tr mat-header-row *matHeaderRowDef=\"displayedColumns\"></tr>\n <tr mat-row *matRowDef=\"let row; columns: displayedColumns;\" (click)=\"selection.toggle(row)\"></tr>\n </table>\n\n</tanj-page-body>\n", dependencies: [{ kind: "component", type: i4.MatCheckbox, selector: "mat-checkbox", inputs: ["disableRipple", "color", "tabIndex"], exportAs: ["matCheckbox"] }, { kind: "component", type: i5.PageBodyComponent, selector: "tanj-page-body", inputs: ["suppressHeaderShim", "flex", "layout", "layoutAlign"] }, { kind: "component", type: i6.MatTable, selector: "mat-table, table[mat-table]", exportAs: ["matTable"] }, { kind: "directive", type: i6.MatHeaderCellDef, selector: "[matHeaderCellDef]" }, { kind: "directive", type: i6.MatHeaderRowDef, selector: "[matHeaderRowDef]", inputs: ["matHeaderRowDef", "matHeaderRowDefSticky"] }, { kind: "directive", type: i6.MatColumnDef, selector: "[matColumnDef]", inputs: ["sticky", "matColumnDef"] }, { kind: "directive", type: i6.MatCellDef, selector: "[matCellDef]" }, { kind: "directive", type: i6.MatRowDef, selector: "[matRowDef]", inputs: ["matRowDefColumns", "matRowDefWhen"] }, { kind: "directive", type: i6.MatHeaderCell, selector: "mat-header-cell, th[mat-header-cell]" }, { kind: "directive", type: i6.MatCell, selector: "mat-cell, td[mat-cell]" }, { kind: "component", type: i6.MatHeaderRow, selector: "mat-header-row, tr[mat-header-row]", exportAs: ["matHeaderRow"] }, { kind: "component", type: i6.MatRow, selector: "mat-row, tr[mat-row]", exportAs: ["matRow"] }, { kind: "directive", type: i7.MatSort, selector: "[matSort]", inputs: ["matSortDisabled", "matSortActive", "matSortStart", "matSortDirection", "matSortDisableClear"], outputs: ["matSortChange"], exportAs: ["matSort"] }, { kind: "component", type: i7.MatSortHeader, selector: "[mat-sort-header]", inputs: ["disabled", "mat-sort-header", "arrowPosition", "start", "sortActionDescription", "disableClear"], exportAs: ["matSortHeader"] }, { kind: "pipe", type: i1$1.DatePipe, name: "date" }], changeDetection: i0.ChangeDetectionStrategy.OnPush, encapsulation: i0.ViewEncapsulation.None });
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "14.2.10", ngImport: i0, type: PermissionManagerPage, decorators: [{
type: Component,
args: [{ selector: 'tanj-permission-manager', changeDetection: ChangeDetectionStrategy.OnPush, encapsulation: ViewEncapsulation.None, template: "<tanj-page-body>\n <table mat-table matSort (matSortChange)=\"announceSortChange($event)\" [dataSource]=\"dataSource\" class=\"mat-elevation-z8 tng-user-mgr-table\">\n\n <!-- Checkbox Column -->\n <ng-container matColumnDef=\"select\">\n <th mat-header-cell *matHeaderCellDef>\n <mat-checkbox (change)=\"$event ? toggleAllRows() : null\"\n [checked]=\"selection.hasValue() && isAllSelected()\"\n [indeterminate]=\"selection.hasValue() && !isAllSelected()\"\n [aria-label]=\"checkboxLabel()\">\n </mat-checkbox>\n </th>\n <td mat-cell *matCellDef=\"let row\" >\n <mat-checkbox (click)=\"$event.stopPropagation()\"\n (change)=\"$event ? selection.toggle(row) : null\"\n [checked]=\"selection.isSelected(row)\"\n [aria-label]=\"checkboxLabel(row)\">\n </mat-checkbox>\n </td>\n </ng-container>\n\n <!-- UID Column -->\n <ng-container matColumnDef=\"$key\">\n <th mat-header-cell *matHeaderCellDef mat-sort-header sortActionDescription=\"Sort by Permission ID\"> Permission ID </th>\n <td mat-cell *matCellDef=\"let element\"> {{element.$key}} </td>\n </ng-container>\n\n <!-- User name Column -->\n <ng-container matColumnDef=\"description\">\n <th mat-header-cell *matHeaderCellDef mat-sort-header sortActionDescription=\"Sort by Description\"> Description </th>\n <td mat-cell *matCellDef=\"let element\"> {{element.description}} </td>\n </ng-container>\n\n <!-- User Email Column -->\n <ng-container matColumnDef=\"createdMils\" >\n <th mat-header-cell *matHeaderCellDef mat-sort-header sortActionDescription=\"Sort by Created Date\"> Created </th>\n <td mat-cell *matCellDef=\"let element\"> {{element.createdMils | date:'yyyy-MM~dd, HH:mm:ss'}} </td>\n </ng-container>\n\n <!-- Last Sign in Column -->\n <ng-container matColumnDef=\"editedMils\">\n <th mat-header-cell *matHeaderCellDef mat-sort-header sortActionDescription=\"Sort by Last Modified\"> Last Modified </th>\n <td mat-cell *matCellDef=\"let element\"> {{element.editedMils | date:'yyyy-MM~dd, HH:mm:ss' }} </td>\n </ng-container>\n\n <tr mat-header-row *matHeaderRowDef=\"displayedColumns\"></tr>\n <tr mat-row *matRowDef=\"let row; columns: displayedColumns;\" (click)=\"selection.toggle(row)\"></tr>\n </table>\n\n</tanj-page-body>\n" }]
}], ctorParameters: function () { return [{ type: i2.AdminService }, { type: AdminConsoleParentPage }, { type: i3.LiveAnnouncer }, { type: i0.ChangeDetectorRef }]; }, propDecorators: { sort: [{
type: ViewChild,
args: [MatSort]
}] } });
class RoleManagerPage {
constructor(parent, adminService, _liveAnnouncer, changeDetectorRef) {
this.parent = parent;
this.adminService = adminService;
this._liveAnnouncer = _liveAnnouncer;
this.changeDetectorRef = changeDetectorRef;
this.displayedColumns = ['select', '$key', 'description', 'createdMils', 'editedMils'];
this.dataSource = new MatTableDataSource();
this.selection = new SelectionModel(true, []);
this.rows = [];
this.selected = [];
}
ngOnInit() {
this.parent.auth$.subscribe({
next: (v) => {
this.rows = v.settings.roles;
this.dataSource = new MatTableDataSource(v.settings.roles);
this.dataSource.sort = this.sort;
this.changeDetectorRef.markForCheck();
}
});
}
get nextItemIndex() {
let idx = 1;
if (this.rows && this.rows.length) {
idx = (this.rows[this.rows.length - 1].orderIndex + 1);
}
return idx;
}
grantPermission(role, permission) {
this.adminService.grantPermissionOnRole(role.$key, permission.$key).catch((reason) => {
console.error('RoleManagerComponent', 'could not grant permission', reason);
});
}
revokePermission(role, permission) {
this.adminService.revokePermissionOnRole(role.$key, permission.$key).catch((reason) => {
console.error('RoleManagerComponent', 'could not revoke permission', reason);
});
}
onAddItemAction() {
const role = AuthRole.from({
$key: NameGenerator.generate(),
orderIndex: this.nextItemIndex
});
this.adminService.addRole(role).catch((reason) => {
console.error('RoleManagerComponent', 'error adding role', reason);
throw new Error(reason);
});
}
onRemove(key) {
this.adminService.removeRole(key).catch((reason) => {
console.error('RoleManagerComponent', 'error removing role', reason);
throw new Error(reason);
});
}
onRemoveSelectedAction(keys) {
keys.forEach((key) => {
this.adminService.removeRole(key).catch((reason) => {
console.error('RoleManagerComponent', 'error removing role', reason);
throw new Error(reason);
});
});
}
onItemChange(role) {
this.adminService.updateRole(role).catch((reason) => {
console.log('RoleManagerComponent', 'error updating role', reason);
throw new Error(reason);
});
}
/**
* Borrowed directly from Angular Material examples: https://material.angular.io/components/table/overview
*/
/** Whether the number of selected elements matches the total number of rows. */
isAllSelected() {
const numSelected = this.selection.selected.length;
const numRows = this.dataSource.data.length;
return numSelected === numRows;
}
/** Selects all rows if they are not all selected; otherwise clear selection. */
toggleAllRows() {
if (this.isAllSelected()) {
this.selection.clear();
return;
}
this.selection.select(...this.dataSource.data);
}
/** The label for the checkbox on the passed row */
checkboxLabel(row) {
if (!row) {
return `${this.isAllSelected() ? 'deselect' : 'select'} all`;
}
return `${this.selection.isSelected(row) ? 'deselect' : 'select'} ${row.description}`;
}
/** Announce the change in sort state for assistive technology. */
announceSortChange(sortState) {
// This example uses English messages. If your application supports
// multiple language, you would internationalize these strings.
// Furthermore, you can customize the message to add additional
// details about the values being sorted.
if (sortState.direction) {
this._liveAnnouncer.announce(`Sorted ${sortState.direction}ending`);
}
else {
this._liveAnnouncer.announce('Sorting cleared');
}
}
}
RoleManagerPage.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "14.2.10", ngImport: i0, type: RoleManagerPage, deps: [{ token: AdminConsoleParentPage }, { token: i2.AdminService }, { token: i3.LiveAnnouncer }, { token: i0.ChangeDetectorRef }], target: i0.ɵɵFactoryTarget.Component });
RoleManagerPage.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "14.2.10", type: RoleManagerPage, selector: "tanj-role-manager-page", viewQueries: [{ propertyName: "sort", first: true, predicate: MatSort, descendants: true }], ngImport: i0, template: "<tanj-page-body>\n <table mat-table matSort (matSortChange)=\"announceSortChange($event)\" [dataSource]=\"dataSource\" class=\"mat-elevation-z8 tng-user-mgr-table\">\n\n <!-- Checkbox Column -->\n <ng-container matColumnDef=\"select\">\n <th mat-header-cell *matHeaderCellDef>\n <mat-checkbox (change)=\"$event ? toggleAllRows() : null\"\n [checked]=\"selection.hasValue() && isAllSelected()\"\n [indeterminate]=\"selection.hasValue() && !isAllSelected()\"\n [aria-label]=\"checkboxLabel()\">\n </mat-checkbox>\n </th>\n <td mat-cell *matCellDef=\"let row\" >\n <mat-checkbox (click)=\"$event.stopPropagation()\"\n (change)=\"$event ? selection.toggle(row) : null\"\n [checked]=\"selection.isSelected(row)\"\n [aria-label]=\"checkboxLabel(row)\">\n </mat-checkbox>\n </td>\n </ng-container>\n\n <!-- UID Column -->\n <ng-container matColumnDef=\"$key\">\n <th mat-header-cell *matHeaderCellDef mat-sort-header sortActionDescription=\"Sort by Permission ID\"> Permission ID </th>\n <td mat-cell *matCellDef=\"let element\"> {{element.$key}} </td>\n </ng-container>\n\n <!-- User name Column -->\n <ng-container matColumnDef=\"description\">\n <th mat-header-cell *matHeaderCellDef mat-sort-header sortActionDescription=\"Sort by Description\"> Description </th>\n <td mat-cell *matCellDef=\"let element\"> {{element.description}} </td>\n </ng-container>\n\n <!-- User Email Column -->\n <ng-container matColumnDef=\"createdMils\" >\n <th mat-header-cell *matHeaderCellDef mat-sort-header sortActionDescription=\"Sort by Created Date\"> Created </th>\n <td mat-cell *matCellDef=\"let element\"> {{element.createdMils | date:'yyyy-MM~dd, HH:mm:ss'}} </td>\n </ng-container>\n\n <!-- Last Sign in Column -->\n <ng-container matColumnDef=\"editedMils\">\n <th mat-header-cell *matHeaderCellDef mat-sort-header sortActionDescription=\"Sort by Last Modified\"> Last Modified </th>\n <td mat-cell *matCellDef=\"let element\"> {{element.editedMils | date:'yyyy-MM~dd, HH:mm:ss' }} </td>\n </ng-container>\n\n <tr mat-header-row *matHeaderRowDef=\"displayedColumns\"></tr>\n <tr mat-row *matRowDef=\"let row; columns: displayedColumns;\" (click)=\"selection.toggle(row)\"></tr>\n </table>\n\n</tanj-page-body>\n", dependencies: [{ kind: "component", type: i4.MatCheckbox, selector: "mat-checkbox", inputs: ["disableRipple", "color", "tabIndex"], exportAs: ["matCheckbox"] }, { kind: "component", type: i5.PageBodyComponent, selector: "tanj-page-body", inputs: ["suppressHeaderShim", "flex", "layout", "layoutAlign"] }, { kind: "component", type: i6.MatTable, selector: "mat-table, table[mat-table]", exportAs: ["matTable"] }, { kind: "directive", type: i6.MatHeaderCellDef, selector: "[matHeaderCellDef]" }, { kind: "directive", type: i6.MatHeaderRowDef, selector: "[matHeaderRowDef]", inputs: ["matHeaderRowDef", "matHeaderRowDefSticky"] }, { kind: "directive", type: i6.MatColumnDef, selector: "[matColumnDef]", inputs: ["sticky", "matColumnDef"] }, { kind: "directive", type: i6.MatCellDef, selector: "[matCellDef]" }, { kind: "directive", type: i6.MatRowDef, selector: "[matRowDef]", inputs: ["matRowDefColumns", "matRowDefWhen"] }, { kind: "directive", type: i6.MatHeaderCell, selector: "mat-header-cell, th[mat-header-cell]" }, { kind: "directive", type: i6.MatCell, selector: "mat-cell, td[mat-cell]" }, { kind: "component", type: i6.MatHeaderRow, selector: "mat-header-row, tr[mat-header-row]", exportAs: ["matHeaderRow"] }, { kind: "component", type: i6.MatRow, selector: "mat-row, tr[mat-row]", exportAs: ["matRow"] }, { kind: "directive", type: i7.MatSort, selector: "[matSort]", inputs: ["matSortDisabled", "matSortActive", "matSortStart", "matSortDirection", "matSortDisableClear"], outputs: ["matSortChange"], exportAs: ["matSort"] }, { kind: "component", type: i7.MatSortHeader, selector: "[mat-sort-header]", inputs: ["disabled", "mat-sort-header", "arrowPosition", "start", "sortActionDescription", "disableClear"], exportAs: ["matSortHeader"] }, { kind: "pipe", type: i1$1.DatePipe, name: "date" }], changeDetection: i0.ChangeDetectionStrategy.OnPush, encapsulation: i0.ViewEncapsulation.None });
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "14.2.10", ngImport: i0, type: RoleManagerPage, decorators: [{
type: Component,
args: [{ selector: 'tanj-role-manager-page', changeDetection: ChangeDetectionStrategy.OnPush, encapsulation: ViewEncapsulation.None, template: "<tanj-page-body>\n <table mat-table matSort (matSortChange)=\"announceSortChange($event)\" [dataSource]=\"dataSource\" class=\"mat-elevation-z8 tng-user-mgr-table\">\n\n <!-- Checkbox Column -->\n <ng-container matColumnDef=\"select\">\n <th mat-header-cell *matHeaderCellDef>\n <mat-checkbox (change)=\"$event ? toggleAllRows() : null\"\n [checked]=\"selection.hasValue() && isAllSelected()\"\n [indeterminate]=\"selection.hasValue() && !isAllSelected()\"\n [aria-label]=\"checkboxLabel()\">\n </mat-checkbox>\n </th>\n <td mat-cell *matCellDef=\"let row\" >\n <mat-checkbox (click)=\"$event.stopPropagation()\"\n (change)=\"$event ? selection.toggle(row) : null\"\n [checked]=\"selection.isSelected(row)\"\n [aria-label]=\"checkboxLabel(row)\">\n </mat-checkbox>\n </td>\n </ng-container>\n\n <!-- UID Column -->\n <ng-container matColumnDef=\"$key\">\n <th mat-header-cell *matHeaderCellDef mat-sort-header sortActionDescription=\"Sort by Permission ID\"> Permission ID </th>\n <td mat-cell *matCellDef=\"let element\"> {{element.$key}} </td>\n </ng-container>\n\n <!-- User name Column -->\n <ng-container matColumnDef=\"description\">\n <th mat-header-cell *matHeaderCellDef mat-sort-header sortActionDescription=\"Sort by Description\"> Description </th>\n <td mat-cell *matCellDef=\"let element\"> {{element.description}} </td>\n </ng-container>\n\n <!-- User Email Column -->\n <ng-container matColumnDef=\"createdMils\" >\n <th mat-header-cell *matHeaderCellDef mat-sort-header sortActionDescription=\"Sort by Created Date\"> Created </th>\n <td mat-cell *matCellDef=\"let element\"> {{element.createdMils | date:'yyyy-MM~dd, HH:mm:ss'}} </td>\n </ng-container>\n\n <!-- Last Sign in Column -->\n <ng-container matColumnDef=\"editedMils\">\n <th mat-header-cell *matHeaderCellDef mat-sort-header sortActionDescription=\"Sort by Last Modified\"> Last Modified </th>\n <td mat-cell *matCellDef=\"let element\"> {{element.editedMils | date:'yyyy-MM~dd, HH:mm:ss' }} </td>\n </ng-container>\n\n <tr mat-header-row *matHeaderRowDef=\"displayedColumns\"></tr>\n <tr mat-row *matRowDef=\"let row; columns: displayedColumns;\" (click)=\"selection.toggle(row)\"></tr>\n </table>\n\n</tanj-page-body>\n" }]
}], ctorParameters: function () { return [{ type: AdminConsoleParentPage }, { type: i2.AdminService }, { type: i3.LiveAnnouncer }, { type: i0.ChangeDetectorRef }]; }, propDecorators: { sort: [{
type: ViewChild,
args: [MatSort]
}] } });
class UserManagerPage {
constructor(adminService, parent, _liveAnnouncer, changeDetectorRef) {
this.adminService = adminService;
this.parent = parent;
this._liveAnnouncer = _liveAnnouncer;
this.changeDetectorRef = changeDetectorRef;
this.displayedColumns = ['select', '$key', 'displayName', 'email', 'lastSignInMils'];
this.dataSource = new MatTableDataSource();
this.selection = new SelectionModel(true, []);
this.rows = [];
this.selected = [];
this.columns = [
{ prop: '$key', name: 'Key', flexGrow: 1 },
{ prop: 'displayName', name: 'Display Name', flexGrow: 2 },
{ prop: 'email', name: 'Created', flexGrow: 2 },
{ prop: 'lastSignInMils', name: 'Last Sign In', flexGrow: 1 }
];
}
ngOnInit() {
this.parent.auth$.subscribe({
next: (v) => {
this.rows = v.users;
this.dataSource = new MatTableDataSource(v.users);
this.dataSource.sort = this.sort;
this.changeDetectorRef.markForCheck();
}
});
}
grantPermission(user, permission) {
this.adminService.grantPermissionOnUser(user, permission).catch((reason) => {
console.error('UserManagerComponent', 'could not grant permission', reason);
});
}
revokePermission(user, permission) {
console.log('UserManagerComponent', 'revokePermission');
this.adminService.revokePermissionOnUser(user, permission).catch((reason) => {
console.error('UserManagerComponent', 'could not revoke permission', reason);
});
}
grantRole(user, role) {
this.adminService.grantRoleOnUser(user, role).catch((reason) => {
console.error('UserManagerComponent', 'could not grant role', reason);
});
}
revokeRole(user, role) {
this.adminService.revokeRoleOnUser(user.$key, role.$key).catch((reason) => {
console.error('UserManagerComponent', 'could not revoke role', reason);
});
}
onAddItemAction() {
const user = new AuthUser(generatePushID());
user.displayName = 'New User ';
this.adminService.addUser(user).catch((reason) => {
console.error('UserManagerComponent', 'error adding user', reason);
throw new Error(reason);
});
}
onRemove(key) {
this.adminService.removeUser(key).catch((reason) => {
console.error('UserManagerComponent', 'error removing user', reason);
throw new Error(reason);
});
}
onRemoveSelectedAction(keys) {
keys.forEach((key) => {
this.adminService.removeUser(key).catch((reason) => {
console.error('UserManagerComponent', 'error removing user', reason);
throw new Error(reason);
});
});
}
onItemChange(user) {
this.adminService.updateUser(user).catch((reason) => {
console.error('UserManagerComponent', 'error updating user', reason);
throw new Error(reason);
});
}
/**
* Borrowed directly from Angular Material examples: https://material.angular.io/components/table/overview
*/
/** Whether the number of selected elements matches the total number of rows. */
isAllSelected() {
const numSelected = this.selection.selected.length;
const numRows = this.dataSource.data.length;
return numSelected === numRows;
}
/** Selects all rows if they are not all selected; otherwise clear selection. */
toggleAllRows() {
if (this.isAllSelected()) {
this.selection.clear();
return;
}
this.selection.select(...this.dataSource.data);
}
/** The label for the checkbox on the passed row */
checkboxLabel(row) {
if (!row) {
return `${this.isAllSelected() ? 'deselect' : 'select'} all`;
}
return `${this.selection.isSelected(row) ? 'deselect' : 'select'} ${row.displayName}`;
}
/** Announce the change in sort state for assistive technology. */
announceSortChange(sortState) {
// This example uses English messages. If your application supports
// multiple language, you would internationalize these strings.
// Furthermore, you can customize the message to add additional
// details about the values being sorted.
if (sortState.direction) {
this._liveAnnouncer.announce(`Sorted ${sortState.direction}ending`);
}
else {
this._liveAnnouncer.announce('Sorting cleared');
}
}
}
UserManagerPage.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "14.2.10", ngImport: i0, type: UserManagerPage, deps: [{ token: i2.AdminService }, { token: AdminConsoleParentPage }, { token: i3.LiveAnnouncer }, { token: i0.ChangeDetectorRef }], target: i0.ɵɵFactoryTarget.Component });
UserManagerPage.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "14.2.10", type: UserManagerPage, selector: "tanj-user-manager-page", viewQueries: [{ propertyName: "sort", first: true, predicate: MatSort, descendants: true }], ngImport: i0, template: "<tanj-page-body>\n <table mat-table matSort (matSortChange)=\"announceSortChange($event)\" [dataSource]=\"dataSource\" class=\"mat-elevation-z8 tng-user-mgr-table\">\n\n <!-- Checkbox Column -->\n <ng-container matColumnDef=\"select\">\n <th mat-header-cell *matHeaderCellDef>\n <mat-checkbox (change)=\"$event ? toggleAllRows() : null\"\n [checked]=\"selection.hasValue() && isAllSelected()\"\n [indeterminate]=\"selection.hasValue() && !isAllSelected()\"\n [aria-label]=\"checkboxLabel()\">\n </mat-checkbox>\n </th>\n <td mat-cell *matCellDef=\"let row\" >\n <mat-checkbox (click)=\"$event.stopPropagation()\"\n (change)=\"$event ? selection.toggle(row) : null\"\n [checked]=\"selection.isSelected(row)\"\n [aria-label]=\"checkboxLabel(row)\">\n </mat-checkbox>\n </td>\n </ng-container>\n\n <!-- UID Column -->\n <ng-container matColumnDef=\"$key\">\n <th mat-header-cell *matHeaderCellDef mat-sort-header sortActionDescription=\"Sort by UID\"> User ID </th>\n <td mat-cell *matCellDef=\"let element\"> {{element.$key}} </td>\n </ng-container>\n\n <!-- User name Column -->\n <ng-container matColumnDef=\"displayName\">\n <th mat-header-cell *matHeaderCellDef mat-sort-header sortActionDescription=\"Sort by name\"> User Name </th>\n <td mat-cell *matCellDef=\"let element\"> {{element.displayName}} </td>\n </ng-container>\n\n <!-- User Email Column -->\n <ng-container matColumnDef=\"email\" >\n <th mat-header-cell *matHeaderCellDef mat-sort-header sortActionDescription=\"Sort by email\"> Email </th>\n <td mat-cell *matCellDef=\"let element\"> {{element.email}} </td>\n </ng-container>\n\n <!-- Last Sign in Column -->\n <ng-container matColumnDef=\"lastSignInMils\">\n <th mat-header-cell *matHeaderCellDef mat-sort-header sortActionDescription=\"Sort by Last Sign In\"> Last Sign In </th>\n <td mat-cell *matCellDef=\"let element\"> {{element.lastSignInMils | date:'yyyy-MM~dd, HH:mm:ss' }} </td>\n </ng-container>\n\n <tr mat-header-row *matHeaderRowDef=\"displayedColumns\"></tr>\n <tr mat-row *matRowDef=\"let row; columns: displayedColumns;\" (click)=\"selection.toggle(row)\"></tr>\n </table>\n</tanj-page-body>\n", dependencies: [{ kind: "component", type: i4.MatCheckbox, selector: "mat-checkbox", inputs: ["disableRipple", "color", "tabIndex"], exportAs: ["matCheckbox"] }, { kind: "component", type: i5.PageBodyComponent, selector: "tanj-page-body", inputs: ["suppressHeaderShim", "flex", "layout", "layoutAlign"] }, { kind: "component", type: i6.MatTable, selector: "mat-table, table[mat-table]", exportAs: ["matTable"] }, { kind: "directive", type: i6.MatHeaderCellDef, selector: "[matHeaderCellDef]" }, { kind: "directive", type: i6.MatHeaderRowDef, selector: "[matHeaderRowDef]", inputs: ["matHeaderRowDef", "matHeaderRowDefSticky"] }, { kind: "directive", type: i6.MatColumnDef, selector: "[matColumnDef]", inputs: ["sticky", "matColumnDef"] }, { kind: "directive", type: i6.MatCellDef, selector: "[matCellDef]" }, { kind: "directive", type: i6.MatRowDef, selector: "[matRowDef]", inputs: ["matRowDefColumns", "matRowDefWhen"] }, { kind: "directive", type: i6.MatHeaderCell, selector: "mat-header-cell, th[mat-header-cell]" }, { kind: "directive", type: i6.MatCell, selector: "mat-cell, td[mat-cell]" }, { kind: "component", type: i6.MatHeaderRow, selector: "mat-header-row, tr[mat-header-row]", exportAs: ["matHeaderRow"] }, { kind: "component", type: i6.MatRow, selector: "mat-row, tr[mat-row]", exportAs: ["matRow"] }, { kind: "directive", type: i7.MatSort, selector: "[matSort]", inputs: ["matSortDisabled", "matSortActive", "matSortStart", "matSortDirection", "matSortDisableClear"], outputs: ["matSortChange"], exportAs: ["matSort"] }, { kind: "component", type: i7.MatSortHeader, selector: "[mat-sort-header]", inputs: ["disabled", "mat-sort-header", "arrowPosition", "start", "sortActionDescription", "disableClear"], exportAs: ["matSortHeader"] }, { kind: "pipe", type: i1$1.DatePipe, name: "date" }], changeDetection: i0.ChangeDetectionStrategy.OnPush, encapsulation: i0.ViewEncapsulation.None });
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "14.2.10", ngImport: i0, type: UserManagerPage, decorators: [{
type: Component,
args: [{ selector: 'tanj-user-manager-page', changeDetection: ChangeDetectionStrategy.OnPush, encapsulation: ViewEncapsulation.None, template: "<tanj-page-body>\n <table mat-table matSort (matSortChange)=\"announceSortChange($event)\" [dataSource]=\"dataSource\" class=\"mat-elevation-z8 tng-user-mgr-table\">\n\n <!-- Checkbox Column -->\n <ng-container matColumnDef=\"select\">\n <th mat-header-cell *matHeaderCellDef>\n <mat-checkbox (change)=\"$event ? toggleAllRows() : null\"\n [checked]=\"selection.hasValue() && isAllSelected()\"\n [indeterminate]=\"selection.hasValue() && !isAllSelected()\"\n [aria-label]=\"checkboxLabel()\">\n </mat-checkbox>\n </th>\n <td mat-cell *matCellDef=\"let row\" >\n <mat-checkbox (click)=\"$event.stopPropagation()\"\n (change)=\"$event ? selection.toggle(row) : null\"\n [checked]=\"selection.isSelected(row)\"\n [aria-label]=\"checkboxLabel(row)\">\n </mat-checkbox>\n </td>\n </ng-container>\n\n <!-- UID Column -->\n <ng-container matColumnDef=\"$key\">\n <th mat-header-cell *matHeaderCellDef mat-sort-header sortActionDescription=\"Sort by UID\"> User ID </th>\n <td mat-cell *matCellDef=\"let element\"> {{element.$key}} </td>\n </ng-container>\n\n <!-- User name Column -->\n <ng-container matColumnDef=\"displayName\">\n <th mat-header-cell *matHeaderCellDef mat-sort-header sortActionDescription=\"Sort by name\"> User Name </th>\n <td mat-cell *matCellDef=\"let element\"> {{element.displayName}} </td>\n </ng-container>\n\n <!-- User Email Column -->\n <ng-container matColumnDef=\"email\" >\n <th mat-header-cell *matHeaderCellDef mat-sort-header sortActionDescription=\"Sort by email\"> Email </th>\n <td mat-cell *matCellDef=\"let element\"> {{element.email}} </td>\n </ng-container>\n\n <!-- Last Sign in Column -->\n <ng-container matColumnDef=\"lastSignInMils\">\n <th mat-header-cell *matHeaderCellDef mat-sort-header sortActionDescription=\"Sort by Last Sign In\"> Last Sign In </th>\n <td mat-cell *matCellDef=\"let element\"> {{element.lastSignInMils | date:'yyyy-MM~dd, HH:mm:ss' }} </td>\n </ng-container>\n\n <tr mat-header-row *matHeaderRowDef=\"displayedColumns\"></tr>\n <tr mat-row *matRowDef=\"let row; columns: displayedColumns;\" (click)=\"selection.toggle(row)\"></tr>\n </table>\n</tanj-page-body>\n" }]
}], ctorParameters: function () { return [{ type: i2.AdminService }, { type: AdminConsoleParentPage }, { type: i3.LiveAnnouncer }, { type: i0.ChangeDetectorRef }]; }, propDecorators: { sort: [{
type: ViewChild,
args: [MatSort]
}] } });
class PluginsPage extends Page {
constructor(bus, router, route, parent, pluginManager, changeDetectorRef) {
super(bus);
this.router = router;
this.route = route;
this.parent = parent;
this.pluginManager = pluginManager;
this.changeDetectorRef = changeDetectorRef;
this.routeInfo = {
page: {
title: 'Admin Console'
},
analytics: DefaultPageAnalytics(),
showAds: false
};
this.visitor = null;
}
ngOnInit() {
this.pluginManager.scan();
}
}
PluginsPage.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "14.2.10", ngImport: i0, type: PluginsPage, deps: [{ token: i1.MessageBus }, { token: i2$1.Router }, { token: i2$1.ActivatedRoute }, { token: AdminConsoleParentPage }, { token: i4$1.PluginManager }, { token: i0.ChangeDetectorRef }], target: i0.ɵɵFactoryTarget.Component });
PluginsPage.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "14.2.10", type: PluginsPage, selector: "tanj-plugins-page", usesInheritance: true, ngImport: i0, template: "<div class=\"tanj-header-shim\"></div>\n<div flex layout=\"column\" layout-align=\"start\">\n <div *ngFor=\"let entry of pluginManager.pluginPaths\" flex layout=\"row\" layout-align=\"start\" class=\"tanj-plugin-entry\">\n <div class=\"tanj-entry-name\">{{entry.name}}</div>\n <a class=\"tanj-entry-path\" [routerLink]=\"['/', entry.path, 'plugin']\">{{entry.path}}</a>\n </div>\n</div>\n", dependencies: [{ kind: "directive", type: i1$1.NgForOf, selector: "[ngFor][ngForOf]", inputs: ["ngForOf", "ngForTrackBy", "ngForTemplate"] }, { kind: "directive", type: i2$1.RouterLinkWithHref, selector: "a[routerLink],area[routerLink]", inputs: ["target", "queryParams", "fragment", "queryParamsHandling", "state", "relativeTo", "preserveFragment", "skipLocationChange", "replaceUrl", "routerLink"] }], changeDetection: i0.ChangeDetectionStrategy.Default, encapsulation: i0.ViewEncapsulation.None });
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "14.2.10", ngImport: i0, type: PluginsPage, decorators: [{
type: Component,
args: [{ selector: 'tanj-plugins-page', encapsulation: ViewEncapsulation.None, changeDetection: ChangeDetectionStrategy.Default, template: "<div class=\"tanj-header-shim\"></div>\n<div flex layout=\"column\" layout-align=\"start\">\n <div *ngFor=\"let entry of pluginManager.pluginPaths\" flex layout=\"row\" layout-align=\"start\" class=\"tanj-plugin-entry\">\n <div class=\"tanj-entry-name\">{{entry.name}}</div>\n <a class=\"tanj-entry-path\" [routerLink]=\"['/', entry.path, 'plugin']\">{{entry.path}}</a>\n </div>\n</div>\n" }]
}], ctorParameters: function () { return [{ type: i1.MessageBus }, { type: i2$1.Router }, { type: i2$1.ActivatedRoute }, { type: AdminConsoleParentPage }, { type: i4$1.PluginManager }, { type: i0.ChangeDetectorRef }]; } });
const AdminRoutes = {
parent: {
path: '',
component: AdminConsoleParentPage,
resolve: { visitor: VisitorResolver },
canActivate: [HasRoleGuard],
data: { roles: ['Administrator'] },
navTargets: {
absSelf: ['/', 'admin'],
up() {
return ['/'];
},
}
},
children: {
plugins: {
path: 'plugins',
component: PluginsPage,
navTargets: {
absSelf: ['/', 'admin', 'plugins']
},
},
permissions: {
path: 'permissions',
component: PermissionManagerPage,
navTargets: {
absSelf: ['/', 'admin', 'permissions']
},
},
roles: {
path: 'roles',
component: RoleManagerPage,
navTargets: {
absSelf: ['/', 'admin', 'roles']
},
},
users: {
path: 'users',
component: UserManagerPage,
navTargets: {
absSelf: ['/', 'admin', 'users']
},
}
}
};
const routes = [
{
path: AdminRoutes.parent.path,
component: AdminRoutes.parent.component,
canActivate: AdminRoutes.parent.canActivate,
data: AdminRoutes.parent.data,
resolve: AdminRoutes.parent.resolve,
children: [
{
path: '',
children: [
AdminRoutes.children.plugins,
AdminRoutes.children.permissions,
AdminRoutes.children.roles,
AdminRoutes.children.users,
]
},
]
}
];
class AdminConsoleRoutingModule {
}
AdminConsoleRoutingModule.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "14.2.10", ngImport: i0, type: AdminConsoleRoutingModule, deps: [], target: i0.ɵɵFactoryTarget.NgModule });
AdminConsoleRoutingModule.ɵmod = i0.ɵɵngDeclareNgModule({ minVersion: "14.0.0", version: "14.2.10", ngImport: i0, type: AdminConsoleRoutingModule, imports: [i2$1.RouterModule], exports: [RouterModule] });
AdminConsoleRoutingModule.ɵinj = i0.ɵɵngDeclareInjector({ minVersion: "12.0.0", version: "14.2.10", ngImport: i0, type: AdminConsoleRoutingModule, providers: [
VisitorResolver,
], imports: [RouterModule.forChild(routes), RouterModule] });
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "14.2.10", ngImport: i0, type: AdminConsoleRoutingModule, decorators: [{
type: NgModule,
args: [{
imports: [
RouterModule.forChild(routes)
],
exports: [
RouterModule
], providers: [
VisitorResolver,
]
}]
}] });
class PermissionComponent {
constructor() {
this.remove = new EventEmitter(false);
this._focusDebouncer = new EventEmitter(false);
this.submitted = false;
this._changed = false;
let distinct = this._focusDebouncer.asObservable();
distinct = distinct.pipe(debounceTime(10), distinctUntilChanged());
this.focus = distinct.pipe(filter((v) => v === true), map(() => new Event('focus')));
this.change = distinct.pipe(filter((focused) => focused === false && this._changed), map(() => {
const change = {
previous: this._previous,
current: this.permission
};
if (!this.permission) {
throw "Missing Permission";
}
this._previous = AuthPermission.from(this.permission);
this._changed = false;
return change;
}));
this.blur = distinct.pipe(filter((v) => v === false), map(() => new Event('blur')));
}
ngOnChanges(changes) {
if (changes.permission) {
this._previous = AuthPermission.from(this.permission);
this._changed = false;
}
}
fireRemove() {
this.remove.emit(this.permission);
}
onChange(event) {
event.stopPropagation();
this._changed = true;
}
onBlur(event) {
event.stopPropagation();
this._focusDebouncer.emit(false);
}
onFocus(event) {
event.stopPropagation();
this._focusDebouncer.emit(true);
}
onSubmit() {
this.submitted = true;
}
}
PermissionComponent.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "14.2.10", ngImport: i0, type: PermissionComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
PermissionComponent.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "14.2.10", type: PermissionComponent, selector: "tanj-permission", inputs: { permission: "permission" }, outputs: { change: "change", remove: "remove", focus: "focus", blur: "blur" }, usesOnChanges: true, ngImport: i0, template: "<div flex layout=\"column\" layout-align=\"start\">\n\n <div class=\"tanj-permission-form-wrapper\" flex layout=\"column\" layout-align=\"start\">\n\n\n <form *ngIf=\"permission != null\"\n (ngSubmit)=\"onSubmit($event)\"\n #permissionForm=\"ngForm\"\n flex\n layout=\"row\"\n layout-align=\"space-between\">\n\n <div class=\"tanj-permission-inputs-wrapper\" layout=\"row\" flex=\"80\" layout-align=\"space-between start\">\n <mat-form-field flex=\"40\">\n <input matInput [(ngModel)]=\"permission.$key\" name=\"name\" #fName=\"ngModel\"\n class=\"tanj-input\"\n dividerColor=\"accent\"\n [type]=\"'text'\"\n required minlength=\"2\"\n value=\"{{permission.$key}}\"\n placeholder=\"Name\"\n (change)=\"onChange($event)\"\n (focus)=\"onFocus($event)\"\n (blur)=\"onBlur($event)\"\n />\n </mat-form-field>\n <mat-form-field flex=\"60\">\n <input matInput [(ngModel)]=\"permission.description\" name=\"description\" #fDescription=\"ngModel\"\n class=\"tanj-description tanj-input\"\n dividerColor=\"accent\"\n\n [type]=\"'text'\"\n value=\"{{permission.description}