Added frontend

This commit is contained in:
2022-02-20 19:07:41 +01:00
commit 4c21fa631a
155 changed files with 31276 additions and 0 deletions

View File

@@ -0,0 +1,201 @@
<h1>
{{'admin.auth_users.header' | translate}}
</h1>
<div class="content-wrapper">
<div class="content">
<p-table #dt [value]="users" dataKey="id" editMode="row" [rowHover]="true" [rows]="10"
[rowsPerPageOptions]="[10,25,50]" [paginator]="true" [loading]="loading" [totalRecords]="totalRecords"
[lazy]="true" (onLazyLoad)="nextPage($event)">
<ng-template pTemplate="caption">
<div class="table-caption">
<div class="table-caption-text">
<ng-container *ngIf="!loading">{{users.length}} {{'admin.auth_users.of' | translate}}
{{dt.totalRecords}}
</ng-container>
{{'admin.auth_users.users' | translate}}
</div>
<div class="table-caption-btn-wrapper btn-wrapper">
<button pButton label="{{'admin.auth_users.add' | translate}}" class="icon-btn btn"
icon="pi pi-user-plus" (click)="addUser(dt)" [disabled]="isEditingNew">
</button>
<button pButton label="{{'admin.auth_users.reset_filters' | translate}}" icon="pi pi-undo"
class="icon-btn btn" (click)="resetFilters(dt)">
</button>
</div>
</div>
</ng-template>
<ng-template pTemplate="header">
<tr>
<th pSortableColumn="firstName">
<div class="table-header-label">
<div class="table-header-text">{{'admin.auth_users.headers.first_name' | translate}}</div>
<p-sortIcon icon="sort" field="firstName" class="table-header-icon"></p-sortIcon>
</div>
</th>
<th pSortableColumn="lastName">
<div class="table-header-label">
<div class="table-header-text">{{'admin.auth_users.headers.last_name' | translate}}</div>
<p-sortIcon field="lastName" class="table-header-icon"></p-sortIcon>
</div>
</th>
<th pSortableColumn="eMail">
<div class="table-header-label">
<div class="table-header-text">{{'admin.auth_users.headers.e_mail' | translate}}</div>
<p-sortIcon field="eMail" class="table-header-icon"></p-sortIcon>
</div>
</th>
<th class="table-header-actions" pSortableColumn="confirmationId">
<div class="table-header-label">
<div class="table-header-text">{{'admin.auth_users.headers.active' | translate}}</div>
<p-sortIcon field="confirmationId" class="table-header-icon"></p-sortIcon>
</div>
</th>
<th class="table-header-small-dropdown" pSortableColumn="authRole">
<div class="table-header-label">
<div class="table-header-text">{{'admin.auth_users.headers.role' | translate}}</div>
<p-sortIcon field="authRole" class="table-header-icon"></p-sortIcon>
</div>
</th>
<th>
<div class="table-header-label">
<div class="table-header-text">{{'admin.auth_users.headers.password' | translate}}</div>
</div>
</th>
<th class="table-header-actions">
<div class="table-header-label">
<div class="table-header-text">{{'admin.auth_users.headers.actions' | translate}}</div>
</div>
</th>
</tr>
<tr>
<th>
<form [formGroup]="filterForm">
<input type="text" pInputText formControlName="firstName" placeholder="{{'admin.auth_users.headers.first_name' | translate}}">
</form>
</th>
<th>
<form [formGroup]="filterForm">
<input type="text" pInputText formControlName="lastName" placeholder="{{'admin.auth_users.headers.last_name' | translate}}">
</form>
</th>
<th>
<form [formGroup]="filterForm">
<input type="email" pInputText formControlName="eMail" placeholder="{{'admin.auth_users.headers.e_mail' | translate}}">
</form>
</th>
<th></th>
<th>
<form [formGroup]="filterForm">
<p-dropdown formControlName="authRole" [options]="authRoles"></p-dropdown>
</form>
</th>
<th></th>
<th></th>
</tr>
</ng-template>
<ng-template pTemplate="body" let-user let-editing="editing" let-ri="rowIndex">
<tr [pEditableRow]="user">
<td>
<p-cellEditor>
<ng-template pTemplate="input">
<input class="table-edit-input" pInputText type="text" [(ngModel)]="user.firstName"
[ngClass]="{ 'invalid-feedback-input': isFirstNameInvalid}">
</ng-template>
<ng-template pTemplate="output">
{{user.firstName}}
</ng-template>
</p-cellEditor>
</td>
<td>
<p-cellEditor>
<ng-template pTemplate="input">
<input class="table-edit-input" pInputText type="text" [(ngModel)]="user.lastName"
[ngClass]="{ 'invalid-feedback-input': isLastNameInvalid}">
</ng-template>
<ng-template pTemplate="output">
{{user.lastName}}
</ng-template>
</p-cellEditor>
</td>
<td>
<p-cellEditor>
<ng-template pTemplate="input">
<input class="table-edit-input" pInputText type="email" [(ngModel)]="user.eMail"
[ngClass]="{ 'invalid-feedback-input': isEMailInvalid}">
</ng-template>
<ng-template pTemplate="output">
{{user.eMail}}
</ng-template>
</p-cellEditor>
</td>
<td>
<p-cellEditor>
<ng-template pTemplate="input">
<p-checkbox [binary]="true" [(ngModel)]="user.isConfirmed"
[ngClass]="{ 'invalid-feedback-input': isEMailInvalid}" [disabled]="isEditingNew">
</p-checkbox>
</ng-template>
<ng-template pTemplate="output">
<p-checkbox [binary]="true" [ngModel]="user.isConfirmed" [disabled]="true">
</p-checkbox>
</ng-template>
</p-cellEditor>
</td>
<td>
<p-cellEditor>
<ng-template pTemplate="input">
<p-dropdown [options]="authRoles" [(ngModel)]="user.authRole"
[disabled]="user.eMail == loggedInUserEMail"></p-dropdown>
</ng-template>
<ng-template pTemplate="output">
{{user.authRole | authRole}}
</ng-template>
</p-cellEditor>
</td>
<td>
<p-cellEditor>
<ng-template pTemplate="input">
<input class="table-edit-input" pInputText type="password" [(ngModel)]="user.password"
[ngClass]="{ 'invalid-feedback-input': isPasswordInvalid}">
</ng-template>
<ng-template pTemplate="output">
</ng-template>
</p-cellEditor>
</td>
<td>
<div class="btn-wrapper">
<button *ngIf="!editing" pButton pInitEditableRow class="btn icon-btn" icon="pi pi-pencil"
(click)="onRowEditInit(dt, user, ri)"></button>
<button *ngIf="!editing" pButton class="btn icon-btn danger-icon-btn" icon="pi pi-trash"
(click)="deleteUser(user)"></button>
<button *ngIf="editing" pButton pSaveEditableRow class="btn icon-btn"
icon="pi pi-check-circle" (click)="onRowEditSave(dt, user, ri)"></button>
<button *ngIf="editing" pButton pCancelEditableRow class="btn icon-btn danger-icon-btn"
icon="pi pi-times-circle" (click)="onRowEditCancel(user, ri)"></button>
</div>
</td>
</tr>
</ng-template>
<ng-template pTemplate="Invalidmessage">
<tr>
<td colspan="7">{{'admin.auth_users.no_entries_found' | translate}}</td>
</tr>
</ng-template>
<ng-template pTemplate="paginatorleft">
</ng-template>
</p-table>
</div>
</div>

View File

@@ -0,0 +1,25 @@
import { ComponentFixture, TestBed } from '@angular/core/testing';
import { AuthUserComponent } from './auth-user.component';
describe('AuthUserComponent', () => {
let component: AuthUserComponent;
let fixture: ComponentFixture<AuthUserComponent>;
beforeEach(async () => {
await TestBed.configureTestingModule({
declarations: [ AuthUserComponent ]
})
.compileComponents();
});
beforeEach(() => {
fixture = TestBed.createComponent(AuthUserComponent);
component = fixture.componentInstance;
fixture.detectChanges();
});
it('should create', () => {
expect(component).toBeTruthy();
});
});

View File

@@ -0,0 +1,325 @@
import { Component, OnInit } from '@angular/core';
import { catchError, debounceTime, last } from 'rxjs/operators';
import { AuthRoles } from 'src/app/models/auth/auth-roles.enum';
import { AuthUserDTO } from 'src/app/models/auth/auth-user.dto';
import { AuthService } from 'src/app/services/auth/auth.service';
import { ConfirmationDialogService } from 'src/app/services/confirmation-dialog/confirmation-dialog.service';
import { SpinnerService } from 'src/app/services/spinner/spinner.service';
import { ToastService } from 'src/app/services/toast/toast.service';
import { Table } from 'primeng/table';
import { ServiceErrorCode } from 'src/app/models/error/service-error-code.enum';
import { RegisterErrorMessages } from 'src/app/models/auth/register-error-messages.enum';
import { ErrorDTO } from 'src/app/models/error/error-dto';
import { FormBuilder, FormGroup } from '@angular/forms';
import { AuthUserSelectCriterion } from 'src/app/models/selection/auth-user/auth-user-select-criterion.dto';
import { LazyLoadEvent } from 'primeng/api';
import { throwError } from 'rxjs';
import { TranslateService } from '@ngx-translate/core';
@Component({
selector: 'app-auth-user',
templateUrl: './auth-user.component.html',
styleUrls: ['./auth-user.component.scss']
})
export class AuthUserComponent implements OnInit {
users: AuthUserDTO[];
statuses: any[];
loading = true;
activityValues: number[] = [0, 100];
clonedUsers: { [s: string]: AuthUserDTO; } = {};
isEditingNew: boolean = false;
authRoles = [
{ label: AuthRoles[AuthRoles.Normal].toString(), value: AuthRoles.Normal },
{ label: AuthRoles[AuthRoles.Admin].toString(), value: AuthRoles.Admin }
]
newUserTemplate: AuthUserDTO = {
id: null,
firstName: "",
lastName: "",
eMail: "",
password: "",
authRole: AuthRoles.Normal
};
isFirstNameInvalid: boolean = false;
isLastNameInvalid: boolean = false;
isEMailInvalid: boolean = false;
isPasswordInvalid: boolean = false;
loggedInUserEMail: string = "";
filterForm: FormGroup;
searchCriterions: AuthUserSelectCriterion;
totalRecords: number;
constructor(
private authService: AuthService,
private spinnerService: SpinnerService,
private toastService: ToastService,
private confirmDialog: ConfirmationDialogService,
private fb: FormBuilder,
private translate: TranslateService
) { }
ngOnInit(): void {
this.loggedInUserEMail = this.authService.getEMailFromDecodedToken(this.authService.getDecodedToken());
this.searchCriterions = {
firstName: null,
lastName: null,
eMail: null,
authRole: null,
pageIndex: 0,
pageSize: 10,
sortColumn: null,
sortDirection: null
};
this.setFilterForm();
this.loadNextPage();
}
setFilterForm() {
this.filterForm = this.fb.group({
firstName: [null],
lastName: [null],
eMail: [null],
authRole: [null]
});
this.filterForm.valueChanges.pipe(
debounceTime(600)
).subscribe(changes => {
if (changes.firstName) {
this.searchCriterions.firstName = changes.firstName;
} else {
this.searchCriterions.firstName = undefined;
}
if (changes.lastName) {
this.searchCriterions.lastName = changes.lastName;
} else {
this.searchCriterions.lastName = undefined;
}
if (changes.eMail) {
this.searchCriterions.eMail = changes.eMail;
} else {
this.searchCriterions.eMail = undefined;
}
if (changes.authRole != null) {
this.searchCriterions.authRole = changes.authRole;
} else {
this.searchCriterions.authRole = undefined;
}
if (this.searchCriterions.pageSize)
this.searchCriterions.pageSize = 10;
if (this.searchCriterions.pageSize)
this.searchCriterions.pageIndex = 0;
this.loadNextPage();
});
}
loadNextPage() {
this.authService.getFilteredUsers(this.searchCriterions).pipe(catchError(err => {
this.loading = false;
return throwError(err);
})).subscribe(list => {
this.totalRecords = list.totalCount;
this.users = list.users;
this.loading = false;
});
}
nextPage(event: LazyLoadEvent) {
this.searchCriterions.pageSize = event.rows;
if (event.first != null && event.rows != null)
this.searchCriterions.pageIndex = event.first / event.rows;
this.searchCriterions.sortColumn = event.sortField;
this.searchCriterions.sortDirection = event.sortOrder === 1 ? 'asc' : event.sortOrder === -1 ? 'desc' : 'asc';
if (event.filters) {
// + "" => convert to string
this.searchCriterions.firstName = event.filters.firstName ? event.filters.firstName + "" : undefined;
this.searchCriterions.lastName = event.filters.lastName ? event.filters.lastName + "" : undefined;
this.searchCriterions.eMail = event.filters.eMail ? event.filters.eMail + "" : undefined;
this.searchCriterions.authRole = event.filters.authRole ? +event.filters.authRole : undefined;
}
this.loadNextPage();
}
resetFilters(table: Table) {
this.filterForm.reset();
}
initUserList(): void {
this.spinnerService.showSpinner();
this.authService.getAllUsers()
.pipe(catchError(err => {
this.spinnerService.hideSpinner();
throw err;
}))
.subscribe(users => {
this.users = users;
this.spinnerService.hideSpinner();
});
}
onRowEditInit(table: Table, user: AuthUserDTO, index: number) {
this.clonedUsers[index] = { ...user };
}
onRowEditSave(table: Table, newUser: AuthUserDTO, index: number) {
const oldUser = this.clonedUsers[index];
delete this.clonedUsers[index];
if (JSON.stringify(oldUser) === JSON.stringify(newUser) && !this.isEditingNew) {
return;
}
if (this.isEditingNew && JSON.stringify(newUser) === JSON.stringify(this.newUserTemplate)) {
this.isEditingNew = false;
this.users.splice(index, 1);
return;
}
this.isFirstNameInvalid = newUser.firstName == "";
this.isLastNameInvalid = newUser.lastName == "";
this.isEMailInvalid = newUser.eMail == "";
this.isPasswordInvalid = newUser.password == "";
if (
this.isEditingNew && (
newUser.firstName == "" ||
newUser.lastName == "" ||
newUser.eMail == ""
)
) {
table.initRowEdit(newUser);
return;
}
if (this.isEditingNew) {
this.spinnerService.showSpinner();
this.authService.register(newUser).pipe(catchError(error => {
this.spinnerService.hideSpinner();
if (error.error !== null) {
const err: ErrorDTO = error.error;
if (err.errorCode === ServiceErrorCode.InvalidData && err.message === RegisterErrorMessages.InvalidEMail) {
this.isEMailInvalid = true;
this.toastService.error(this.translate.instant('admin.auth_users.message.invalid_email'), this.translate.instant('admin.auth_users.message.invalid_email_d', { eMail: newUser.eMail }));
} else if (err.errorCode === ServiceErrorCode.InvalidUser && err.message === RegisterErrorMessages.UserAlreadyExists) {
this.isEMailInvalid = true;
this.toastService.error(this.translate.instant('admin.auth_users.message.user_already_exists'), this.translate.instant('admin.auth_users.message.user_already_exists_d', { eMail: newUser.eMail }));
}
error.error = null;
table.initRowEdit(newUser);
}
this.spinnerService.hideSpinner();
throw error;
}))
.subscribe(_ => {
this.initUserList();
this.spinnerService.hideSpinner();
this.toastService.success(this.translate.instant('admin.auth_users.message.user_added'), this.translate.instant('admin.auth_users.message.user_added_d', { eMail: newUser.eMail }));
this.isEditingNew = false;
});
this.triggerUserChangeDetection();
return;
}
this.spinnerService.showSpinner();
this.authService.updateUserAsAdmin({
authUserDTO: oldUser,
newAuthUserDTO: newUser,
changePassword: newUser.password != ""
}).pipe(catchError(err => {
this.spinnerService.hideSpinner();
this.toastService.error(this.translate.instant('admin.auth_users.message.user_change_failed'), this.translate.instant('admin.auth_users.message.user_change_failed_d', { eMail: newUser.eMail }));
this.initUserList();
throw err;
}))
.subscribe(_ => {
this.initUserList();
this.spinnerService.hideSpinner();
this.toastService.success(this.translate.instant('admin.auth_users.message.user_changed'), this.translate.instant('admin.auth_users.message.user_changed_d', { eMail: newUser.eMail }));
});
this.triggerUserChangeDetection();
}
onRowEditCancel(user: AuthUserDTO, index: number) {
this.isFirstNameInvalid = false;
this.isLastNameInvalid = false;
this.isEMailInvalid = false;
this.isPasswordInvalid = false;
if (this.isEditingNew) {
this.users.splice(index, 1);
this.triggerUserChangeDetection();
delete this.clonedUsers[index];
this.isEditingNew = false;
return;
}
this.users[index] = this.clonedUsers[index];
this.triggerUserChangeDetection();
delete this.clonedUsers[index];
}
deleteUser(user: AuthUserDTO) {
if (user.eMail == this.loggedInUserEMail) {
this.toastService.error(this.translate.instant('admin.auth_users.message.cannot_delete_user'), this.translate.instant('admin.auth_users.message.logon_with_another_user'));
return;
}
this.confirmDialog.confirmDialog(
this.translate.instant('admin.auth_users.message.user_delete'), this.translate.instant('admin.auth_users.message.user_delete_q', { eMail: user.eMail }),
() => {
this.spinnerService.showSpinner();
this.authService.deleteUserByMail(user.eMail)
.pipe(catchError(err => {
this.spinnerService.hideSpinner();
throw err;
}))
.subscribe(_ => {
this.initUserList();
this.spinnerService.hideSpinner();
this.toastService.success(this.translate.instant('admin.auth_users.message.user_deleted'), this.translate.instant('admin.auth_users.message.user_deleted_d', { eMail: user.eMail }));
});
});
}
addUser(table: Table) {
const newUser = JSON.parse(JSON.stringify(this.newUserTemplate));
newUser.id = Math.max.apply(Math, this.users.map(function (u) { return u.id; })) + 1;
console.log(newUser);
this.users.push(newUser);
this.triggerUserChangeDetection();
table.initRowEdit(newUser);
const index = this.users.findIndex(u => u.eMail == newUser.eMail);
this.onRowEditInit(table, newUser, index);
this.isEditingNew = true;
}
triggerUserChangeDetection() {
// trigger change detection (https://github.com/primefaces/primeng/issues/2219)
this.users = this.users.slice();
}
}