WIP Usermanagement

master
FlayInAHook 4 years ago
parent f98ca428f2
commit 226b4bff2e

@ -66,6 +66,7 @@ import { SelectObjectDialogComponent } from './components/select-object-dialog/s
import { AutocompleteSelectComponent } from './components/autocomplete-select/autocomplete-select.component';
import { LendingStationComponent } from './pages/dataPages/lending-station/lending-station.component';
import { ProfileComponent } from './pages/profile/profile.component';
import { AdminDataPageComponent} from './components/admin-data-page/admin-data-page.component';
import {
ErrorSnackbarComponent,
SnackbarDialog,
@ -86,6 +87,10 @@ import { BikeEventsComponent } from './pages/tables/bike-events/bike-events.comp
import { BikeEventTypesComponent } from './pages/tables/bike-event-types/bike-event-types.component';
import { WorkshopTypesComponent } from './pages/tables/workshop-types/workshop-types.component';
import { FilterRowComponent } from './components/tableComponents/filter-row/filter-row.component';
import {DeleteDialogComponent} from './components/dialogs/delete/delete.dialog.component';
import {AddDialogComponent} from './components/dialogs/add/add.dialog.component';
import {EditDialogComponent} from './components/dialogs/edit/edit.dialog.component';
@NgModule({
declarations: [
@ -112,6 +117,10 @@ import { FilterRowComponent } from './components/tableComponents/filter-row/filt
SelectObjectDialogComponent,
AutocompleteSelectComponent,
LendingStationComponent,
AddDialogComponent,
EditDialogComponent,
DeleteDialogComponent,
AdminDataPageComponent,
ProfileComponent,
ErrorSnackbarComponent,
SnackbarDialog,
@ -168,6 +177,11 @@ import { FilterRowComponent } from './components/tableComponents/filter-row/filt
MatNativeDateModule,
MatChipsModule,
],
entryComponents: [
AddDialogComponent,
EditDialogComponent,
DeleteDialogComponent
],
providers: [
NavService,
MatNativeDateModule,

@ -1,171 +1,216 @@
<mat-spinner
*ngIf="isLoading"
[diameter]="32"
class="page-loading-spinner"
></mat-spinner>
<div class="page-wrapper" *ngIf="!isLoading && !data">
<h1>Seite konnte nicht gefunden werden :(</h1>
<mat-toolbar color="primary">
<span>Admin Table</span>
<span class="spacer"></span>
Reload data:
<button mat-icon-button (click)="refresh()">
<mat-icon>refresh</mat-icon>
</button>
</mat-toolbar>
<div class="container mat-elevation-z8">
<div class="form">
<mat-form-field floatPlaceholder="never" color="accent">
<input matInput #filter placeholder="Filter issues">
</mat-form-field>
</div>
<mat-table #table [dataSource]="dataSource" matSort class="mat-cell">
<!-- normal shit -->
<ng-container matColumnDef="name">
<mat-header-cell *matHeaderCellDef mat-sort-header>Name</mat-header-cell>
<mat-cell *matCellDef="let row" >{{row.name}}</mat-cell>
</ng-container>
<ng-container matColumnDef="email">
<mat-header-cell *matHeaderCellDef mat-sort-header>E-Mail</mat-header-cell>
<mat-cell *matCellDef="let row"> {{row.email}}</mat-cell>
</ng-container>
<ng-container matColumnDef="password">
<mat-header-cell *matHeaderCellDef mat-sort-header>Passwort</mat-header-cell>
<mat-cell *matCellDef="let row"> {{row.password}}</mat-cell>
</ng-container>
<ng-container matColumnDef="roles">
<mat-header-cell *matHeaderCellDef mat-sort-header>Rollen</mat-header-cell>
<mat-cell *matCellDef="let row"> {{row.roles}}</mat-cell>
</ng-container>
<!-- actions -->
<ng-container matColumnDef="actions">
<mat-header-cell *matHeaderCellDef>
<button mat-icon-button color="primary" (click)="addNew()">
<mat-icon aria-label="Example icon-button with a heart icon">add</mat-icon>
</button>
</mat-header-cell>
<mat-cell *matCellDef="let row; let i=index;">
<button mat-icon-button color="accent" (click)="startEdit(row)">
<mat-icon aria-label="Edit">edit</mat-icon>
</button>
<button mat-icon-button color="accent" (click)="deleteItem(i, row.id, row.title, row.state, row.url)">
<mat-icon aria-label="Delete">delete</mat-icon>
</button>
</mat-cell>
</ng-container>
<mat-header-row *matHeaderRowDef="displayedColumns"></mat-header-row>
<mat-row *matRowDef="let row; columns: displayedColumns;"></mat-row>
</mat-table>
<mat-paginator #paginator
[length]="dataSource.filteredData.length"
[pageIndex]="0"
[pageSize]="10"
[pageSizeOptions]="[5, 10, 25, 100]">
</mat-paginator>
</div>
<div class="data-page-wrapper" *ngIf="data && !isLoading">
<h1 class="headline">
{{ getHeadline !== undefined ? getHeadline(data) : data[headlineDataPath] }}
<mat-icon>{{ headlineIconName }}</mat-icon>
</h1>
<ng-container *ngFor="let object of propertiesInfo">
<mat-card
class="inline-card"
*ngIf="
object.type === 'Group' &&
(!object.hideCondition || !object.hideCondition(data))
"
>
<mat-card-title class="card-header">
<h2>{{ object.title }}</h2>
<!--<div *ngIf="dataLoaded">
<app-editable-table
[columns]="['name','email','password', 'Actions']"
[editableColumns] = "['name', 'email', 'password']"
[dateColumns] ="[]"
[data]="tableData"
[pageSizeOptions]="[5,20,50,100]"
[searchable]="true"
(action)="action($event)"
[notification]="yourMessage"
[maxChar]="23">
</app-editable-table>
</div>-->
<!--<div style="overflow: auto;" *ngIf="dataLoaded">
<table mat-table [dataSource]="dataSource" matSort class="example-container">
Checkbox Column
<ng-container matColumnDef="select" sticky>
<th mat-header-cell *matHeaderCellDef>
<mat-checkbox
(change)="$event ? masterToggle() : null"
[checked]="selection.hasValue() && isAllSelected()"
[indeterminate]="selection.hasValue() && !isAllSelected()"
>
</mat-checkbox>
</th>
<td
mat-cell
*matCellDef="let element"
[ngClass]="{ 'highlighted-column': column.highlighted }"
>
<ng-container
*ngIf="
column.type !== 'DateRange' &&
column.type !== 'NumRange' &&
column.possibleObjects === undefined
"
>
<app-cell
[editable]="
(element.newObject && column.acceptedForCreation) ||
(column.acceptedForUpdating && element.isLockedByMe)
"
[required]="true"
(validityChange)="
validityChange(element, column.dataPath, $event)
"
[(value)]="element[column.dataPath]"
[isList]="false"
[inputType]="column.type"
[link]="column.link ? column.link(element) : null"
></app-cell>
<ng-template #stringValue>
<span *ngIf="!column.link">{{ element[column.dataPath] }}</span>
<a
mat-button
color="primary"
*ngIf="column.link"
[routerLink]="column.link(element)"
>{{ element[column.dataPath] }}</a
>
</ng-template>
</ng-container>
</td>
</ng-container>
<ng-container
*ngFor="let column of columnInfo"
[matColumnDef]="column.def"
[sticky]="isStickyColumn(column.dataPath)"
><th mat-header-cell *matHeaderCellDef>
</th>
</ng-container>
<ng-container matColumnDef="operations" stickyEnd>
<th mat-header-cell *matHeaderCellDef></th>
<td mat-cell *matCellDef="let element">
<button
mat-button
*ngIf="data?.isLockedByMe && object.possibleObjects"
(click)="openSelectObjectDialog(object)"
mat-icon-button
color="primary"
aria-label="Example icon button with a home icon"
(click)="edit(element)"
>
<mat-icon>expand_more</mat-icon>
<mat-icon>edit</mat-icon>
</button>
</mat-card-title>
<ng-container *ngFor="let prop of object.properties">
<app-cell
*ngIf="
prop.type !== 'NumRange' &&
prop.type !== 'Link' &&
prop.type !== 'DateRange'
"
[isList]="prop.list"
[editable]="data?.isLockedByMe && prop.acceptedForUpdating"
[required]="prop.requiredForUpdating && data?.isLockedByMe"
(validityChange)="validityChange(prop.dataPath, $event)"
[(value)]="data[prop.dataPath]"
[label]="prop.translation || prop.dataPath"
[inputType]="prop.type"
></app-cell>
<app-number-range-cell
*ngIf="prop.type === 'NumRange'"
[editable]="data?.isLockedByMe && prop.acceptedForUpdating"
(validityChange)="validityChange(prop.dataPath, $event)"
[(min)]="data[prop.dataPath + '.min']"
[(max)]="data[prop.dataPath + '.max']"
[label]="prop.translation || prop.dataPath"
></app-number-range-cell>
<app-date-range-cell
*ngIf="prop.type === 'DateRange'"
[editable]="data?.isLockedByMe && prop.acceptedForUpdating"
[required]="prop.requiredForUpdating && data?.isLockedByMe"
(validityChange)="validityChange(prop.dataPath, $event)"
[(from)]="data[prop.dataPath + '.from']"
[(to)]="data[prop.dataPath + '.to']"
></app-date-range-cell>
<a
mat-button
class="link-button"
color="primary"
*ngIf="prop.type === 'Link'"
[routerLink]="prop.link(data)"
>{{ prop.linkText }}
</a>
</ng-container>
</mat-card>
<mat-card
class="inline-card"
*ngIf="
object.type === 'ReferenceTable' &&
(!object.hideCondition || !object.hideCondition(data))
"
>
<mat-card-title
><h2>{{ object.title }}</h2>
<a
mat-button
*ngIf="object.linkToTable"
color="primary"
[routerLink]="object.linkToTable(data)"
[queryParams]="
object.linkToTableParams ? object.linkToTableParams(data) : {}
"
matTooltip="Zur Tabelle"
<button
mat-icon-button
color="warn"
aria-label="Example icon button with a menu icon"
(click)="delete(element)"
>
<mat-icon>subdirectory_arrow_right</mat-icon>
<mat-icon>table_chart</mat-icon>
</a>
</mat-card-title>
<app-reference-table
[dataServiceThatProvidesThePossibleData]="object.dataService"
[nameToShowInSelection]="object.nameToShowInSelection"
[columnInfo]="object.columnInfo"
[data]="data[object.dataPath]"
[editable]="data?.isLockedByMe"
[tableDataGQLType]="object.tableDataGQLType"
(referenceIds)="addReferenceIdsToObject($event, object)"
[editableReferences]="object.editableReferences"
></app-reference-table>
</mat-card>
</ng-container>
</div>
<mat-icon>delete</mat-icon>
</button>
</td></ng-container
>
<button
mat-mini-fab
(click)="reloadPageData()"
class="floating-fab-button-top"
[disabled]="isSavingOrLocking || isLoading"
color="primary"
matTooltip="Daten aktualisieren. Achtung! Alle ungespeicherten Änderungen gehen verloren."
>
<mat-icon>sync</mat-icon>
</button>
<div id="floating-fab-button-box">
<button
mat-fab
(click)="lock()"
*ngIf="!data?.isLockedByMe && !data?.isLocked"
class="floating-fab-button"
color="primary"
[disabled]="isSavingOrLocking || isLoading"
>
<mat-icon>edit</mat-icon>
</button>
<button
mat-mini-fab
(click)="cancel()"
*ngIf="data?.isLockedByMe"
class="floating-fab-button"
[disabled]="isSavingOrLocking || isLoading"
>
<mat-icon>cancel</mat-icon>
</button>
<div
*ngIf="data?.isLockedByMe"
[matTooltip]="
countUnvalidProperties() > 0
? 'Ungültige oder nicht ausgefüllte Pflichtfelder (rot): ' +
countUnvalidProperties()
: 'Abspeichern'
"
<tr mat-header-row *matHeaderRowDef="displayedColumns"></tr>
<tr mat-row *matRowDef="let row; columns: displayedColumns;"></tr>
</table>
</div>
<mat-card
*ngIf="isLoading"
style="display: flex; justify-content: center; align-items: center"
>
<mat-progress-spinner color="primary" mode="indeterminate">
</mat-progress-spinner>
</mat-card>
<div class="button-container-explorer">
<button
mat-fab
(click)="save()"
class="floating-fab-button"
[disabled]="
isSavingOrLocking || isLoading || countUnvalidProperties() > 0
"
mat-raised-button
class="button-actions"
color="primary"
(click)="export()"
>
<mat-icon>save</mat-icon>
Export
</button>
</div>
<button
mat-fab
*ngIf="data?.isLocked"
matTooltip="Dieser Eintrag wird gerade von einem anderen Bearbeiter editiert. Aktualisieren Sie die Seite, um den neuen Status abzurufen."
class="floating-fab-button"
>
<mat-icon>locked</mat-icon>
</button>
</div>
<button
mat-raised-button
class="button-actions"
color="warn"
(click)="deleteArray()"
>
Löschen
</button>
<a routerLink="/create/C_APPLICATION_COMPONENT">
<button
mat-raised-button
class="button-actions"
color="accent"
>
Neue Applikations-Komponente
</button>
</a>
using .csv only works with csv specifically, google other types if needed
<input hidden (change)="onFileSelected()" #fileInput type="file" accept=".csv" id="file">
<button class="button-actions" color="primary" type="button" mat-raised-button (click)="fileInput.click()">Importieren</button>
</div> -->

@ -1,246 +1,187 @@
import { HttpClient } from '@angular/common/http';
import {
Component,
ElementRef,
EventEmitter,
Input,
OnDestroy,
OnInit,
Output,
ViewChild,
} from '@angular/core';
import { MatDialog } from '@angular/material/dialog';
import { ActivatedRoute } from '@angular/router';
import { deepen } from '../../helperFunctions/deepenObject';
import { flatten } from '../../helperFunctions/flattenObject';
import { SchemaService } from '../../services/schema.service';
import { SelectObjectDialogComponent } from '../select-object-dialog/select-object-dialog.component';
interface PropertyTypeInfo {
dataPath: string;
translation: string;
acceptedForUpdating?: boolean;
requiredForUpdating?: boolean;
required?: boolean;
type?: string;
}
interface PropertyGroupInfo {
type: string;
title: string;
properties: PropertyTypeInfo[];
}
interface ReferenceTableInfo {
type: string;
title: string;
dataPath: string;
dataService: any;
columnInfo: PropertyTypeInfo[];
nameToShowInSelection: any;
propertyNameOfUpdateInput: string;
referenceIds: Array<string>;
}
import { MatPaginator } from '@angular/material/paginator';
import { MatSort } from '@angular/material/sort';
import { MatTableDataSource } from '@angular/material/table';
import { fromEvent } from 'rxjs';
import { first } from 'rxjs/operators';
import { User } from '../../models/user';
import {UserService} from '../../services/user.service';
import {DeleteDialogComponent} from '../../components/dialogs/delete/delete.dialog.component';
import {AddDialogComponent} from '../../components/dialogs/add/add.dialog.component';
import {EditDialogComponent} from '../../components/dialogs/edit/edit.dialog.component';
import {deepCopy} from '../../helperFunctions/deepCopy';
@Component({
selector: 'app-admin-data-page',
templateUrl: './admin-data-page.component.html',
styleUrls: ['./admindata-page.component.scss'],
styleUrls: ['./admin-data-page.component.scss'],
})
export class DataPageComponent implements OnInit, OnDestroy {
@Input()
propertiesInfo: Array<any> = [];
@Input()
dataService: any;
/** specifies which property should be shown in the headline */
@Input()
headlineDataPath: string;
/** specifies which string should be shown in the headline. If this is provided headlineDataPath is ignored*/
@Input()
getHeadline: (any) => string;
@Input()
headlineIconName: string = 'help_outline';
@Input()
pageDataGQLType: string;
@Input()
pageDataGQLUpdateInputType: string;
@Input()
propertyNameOfUpdateInput: string;
relockingInterval = null;
@Input()
relockingIntervalDuration = 1000 * 60 * 1;
@Output() lockEvent = new EventEmitter();
@Output() saveEvent = new EventEmitter();
@Output() cancelEvent = new EventEmitter();
id: string;
data: any = null;
isLoading: boolean = false;
isSavingOrLocking: boolean = false;
propertyValidity = {};
constructor(
private route: ActivatedRoute,
private schemaService: SchemaService,
public dialog: MatDialog
) {}
ngOnInit(): void {
this.addPropertiesFromGQLSchemaToObject(this.propertiesInfo);
this.id = this.route.snapshot.paramMap.get('id');
this.reloadPageData();
this.dataService.pageData.subscribe((data) => {
if (data == null) {
this.data = null;
} else if (this.data?.isLockedByMe && data?.isLockedByMe) {
// dont overwrite data when in edit mode and relock is performed
return;
} else {
this.data = flatten(data);
export class AdminDataPageComponent implements OnInit {
displayedColumns = ['name', 'email', 'password', 'roles', 'actions'];
dataSource : MatTableDataSource<User>;
index: number;
id: number;
constructor(public httpClient: HttpClient,
public dialog: MatDialog,
private userService: UserService) {}
@ViewChild(MatPaginator, {static: true}) paginator: MatPaginator;
@ViewChild(MatSort, {static: true}) sort: MatSort;
@ViewChild('filter', {static: true}) filter: ElementRef;
ngOnInit() {
this.loadData();
}
refresh() {
this.loadData();
}
addNew() {
const dialogRef = this.dialog.open(AddDialogComponent, {
data: {user: User }
});
dialogRef.afterClosed().subscribe(result => {
if (result === 1) {
this.dataSource.data.push(this.userService.getDialogData());
this.refreshTable();
}
});
this.dataService.isLoadingPageData.subscribe(
(isLoading) => (this.isLoading = isLoading)
);
this.dataService.loadingRowIds.subscribe((loadingRowIds) => {
this.isSavingOrLocking = loadingRowIds.includes(this.id);
}
startEdit(user : User) {
const dialogRef = this.dialog.open(EditDialogComponent, {
data: deepCopy(user)
});
this.relockingInterval = setInterval(() => {
if (this.data?.isLockedByMe) {
this.lock();
dialogRef.afterClosed().subscribe(result => {
if (result === 1) {
console.log("editing done");
const foundIndex = this.dataSource.data.findIndex(x => x.email === this.userService.getDialogData().email);
this.dataSource.data[foundIndex] = this.userService.getDialogData();
this.refreshTable();
}
}, this.relockingIntervalDuration);
});
}
ngOnDestroy() {
clearInterval(this.relockingInterval);
}
deleteItem(i: number, id: number, title: string, state: string, url: string) {
this.index = i;
this.id = id;
const dialogRef = this.dialog.open(DeleteDialogComponent, {
data: {id: id, title: title, state: state, url: url}
});
addPropertiesFromGQLSchemaToObject(infoObject: any) {
for (const prop of infoObject) {
if (prop.type === 'Link') {
continue;
}
if (prop.type === 'Group') {
this.addPropertiesFromGQLSchemaToObject(prop.properties);
} else if (prop.type === 'ReferenceTable') {
prop.tableDataGQLType =
prop.tableDataGQLType ||
this.schemaService.getTypeInformation(
this.pageDataGQLType,
prop.dataPath
).type;
if (!prop.type) {
console.error(
"Didn't found type for: " +
prop.dataPath +
' on ' +
this.pageDataGQLType
);
}
prop.referenceIds = [];
} else {
const typeInformation = this.schemaService.getTypeInformation(
this.pageDataGQLType,
prop.dataPath
);
prop.type = prop.type || typeInformation.type;
prop.list = typeInformation.isList;
if (!prop.type) {
console.error(
"Didn't found type for: " +
prop.dataPath +
' on ' +
this.pageDataGQLType
);
}
prop.required =
prop.required != null ? prop.required : typeInformation.isRequired;
const updateTypeInformation = this.schemaService.getTypeInformation(
this.pageDataGQLUpdateInputType,
prop.dataPath
);
prop.acceptedForUpdating =
prop.acceptedForUpdating != null
? prop.acceptedForUpdating
: updateTypeInformation.isPartOfType;
prop.requiredForUpdating =
prop.requiredForUpdating != null
? prop.requiredForUpdating
: prop.required || typeInformation.isRequired;
dialogRef.afterClosed().subscribe(result => {
if (result === 1) {
const foundIndex = this.dataSource.data.findIndex(x => x.id === this.id);
this.dataSource.data.splice(foundIndex, 1);
this.refreshTable();
}
}
});
}
lock() {
this.lockEvent.emit(deepen(this.data));
private refreshTable() {
//this.paginator._changePageSize(this.paginator.pageSize);
this.dataSource._updateChangeSubscription();
}
validityChange(propName: string, isValid: Event) {
this.propertyValidity[propName] = isValid;
public loadData() {
this.userService.getAllUsers().pipe(first()).subscribe((data: User[]) => {
this.dataSource = new MatTableDataSource(data);
});
fromEvent(this.filter.nativeElement, 'keyup')
// .debounceTime(150)
// .distinctUntilChanged()
.subscribe(() => {
if (!this.dataSource) {
return;
}
this.dataSource.filter = this.filter.nativeElement.value;
});
}
countUnvalidProperties() {
let unvalidFieldsCount = 0;
for (const prop in this.propertyValidity) {
if (!this.propertyValidity[prop]) {
unvalidFieldsCount++;
}
}
return unvalidFieldsCount;
ngAfterViewInit() {
this.dataSource.paginator = this.paginator;
this.dataSource.sort = this.sort;
}
save() {
this.saveEvent.emit(
this.schemaService.filterObject(
this.pageDataGQLUpdateInputType,
deepen(this.data)
)
);
orderData(id: string, start?: "asc" | "desc") {
const matSort = this.dataSource.sort;
const disableClear = false;
matSort.sort({ id: null, start, disableClear });
matSort.sort({ id, start, disableClear });
this.dataSource.sort = this.sort;
}
cancel() {
this.cancelEvent.emit(deepen(this.data));
private load() {
console.log("trying to load");
}
openSelectObjectDialog(object: any) {
const dialogRef = this.dialog.open(SelectObjectDialogComponent, {
width: 'auto',
autoFocus: false,
data: {
nameToShowInSelection: object.nameToShowInSelection,
currentlySelectedObjectId: object.currentlySelectedObjectId(this.data),
possibleObjects: object.possibleObjects,
delete(user: User) {
let ownPassword : string;
this.userService.deleteUser(user.email, ownPassword)
.pipe(first())
.subscribe(
data => {
//this.loadAllObjects();
},
});
dialogRef.afterClosed().subscribe((selectedObject) => {
if (selectedObject) {
this.data[object.propertyNameOfReferenceId] = selectedObject.id;
const newObjectFlattened = flatten(selectedObject);
for (const newProperty in newObjectFlattened) {
this.data[object.propertyPrefixToOverwrite + '.' + newProperty] =
newObjectFlattened[newProperty];
}
} else if (selectedObject === null) {
this.data[object.propertyNameOfReferenceId] = null;
for (const prop in this.data) {
if (prop.startsWith(object.propertyPrefixToOverwrite)) {
this.data[prop] = null;
}
}
error => {
}
});
);
}
addReferenceIdsToObject(ids: string[], object) {
this.data[object.propertyNameOfUpdateInput] = ids;
}
edit(user: User) {
let ownPassword : string;
this.userService.updateUser(user, ownPassword)
.pipe(first())
.subscribe(
data => {
//this.loadAllObjects();
},
error => {
reloadPageData() {
this.dataService.loadPageData({ id: this.id });
}
);
}
/*findActualData(_id: string) {
for (let data of this.actualData) {
if (data._id === _id) {
return data;
}
}
} */
}

@ -0,0 +1,41 @@
import { MAT_DIALOG_DATA, MatDialogRef } from '@angular/material/dialog';
import {Component, Inject} from '@angular/core';
import {UserService} from '../../../services/user.service';
import {FormControl, Validators} from '@angular/forms';
import {User} from '../../../models/user';
@Component({
selector: 'app-add.dialog',
templateUrl: '../../dialogs/add/add.dialog.html',
styleUrls: ['../../dialogs/add/add.dialog.css']
})
export class AddDialogComponent {
constructor(public dialogRef: MatDialogRef<AddDialogComponent>,
@Inject(MAT_DIALOG_DATA) public data: User,
public userService: UserService) { }
formControl = new FormControl('', [
Validators.required
// Validators.email,
]);
getErrorMessage() {
return this.formControl.hasError('required') ? 'Required field' :
this.formControl.hasError('email') ? 'Not a valid email' :
'';
}
submit() {
// emppty stuff
}
onNoClick(): void {
this.dialogRef.close();
}
public confirmAdd(): void {
this.userService.addUser(this.data);
//this.dataService.addIssue(this.data);
}
}

@ -0,0 +1,19 @@
.container {
display: flex;
flex-direction: column;
min-width: 450px;
}
.container > * {
width: 100%;
}
.form {
display: flex;
padding-top: 6px;
}
.mat-form-field {
font-size: 16px;
flex-grow: 1;
}

@ -0,0 +1,31 @@
<div class="container">
<h3 mat-dialog-title>Add new Issue</h3>
<form class="mat-dialog-content" (ngSubmit)="submit" #formControl="ngForm">
<div class="form">
<mat-form-field color="accent">
<input matInput #input class="form-control" placeholder="Name" [(ngModel)]="data.name" name="name" required >
<mat-error *ngIf="formControl.invalid">{{getErrorMessage()}}</mat-error>
</mat-form-field>
</div>
<div class="form">
<mat-form-field color="accent">
<input matInput #input class="form-control" placeholder="E-Mail" [(ngModel)]="data.email" name="email" required >
<mat-error *ngIf="formControl.invalid">{{getErrorMessage()}}</mat-error>
</mat-form-field>
</div>
<div class="form">
<mat-form-field color="accent">
<input matInput placeholder="Passwort" [(ngModel)]="data.password" name="password">
</mat-form-field>
</div>
<div mat-dialog-actions>
<button mat-button [type]="submit" [disabled]="!formControl.valid" [mat-dialog-close]="1" (click)="confirmAdd()">Save</button>
<button mat-button (click)="onNoClick()" tabindex="-1">Cancel</button>
</div>
</form>
</div>

@ -0,0 +1,24 @@
import { MAT_DIALOG_DATA, MatDialogRef } from '@angular/material/dialog';
import {Component, Inject} from '@angular/core';
import {UserService} from '../../../services/user.service';
@Component({
selector: 'app-delete.dialog',
templateUrl: '../../dialogs/delete/delete.dialog.html',
styleUrls: ['../../dialogs/delete/delete.dialog.css']
})
export class DeleteDialogComponent {
constructor(public dialogRef: MatDialogRef<DeleteDialogComponent>,
@Inject(MAT_DIALOG_DATA) public data: any, public userService: UserService) { }
onNoClick(): void {
this.dialogRef.close();
}
confirmDelete(): void {
this.userService.deleteUser(this.data.mail, this.data.ownPassword);
//this.dataService.deleteIssue(this.data.id);
}
}

@ -0,0 +1,8 @@
.container {
display: flex;
flex-direction: column;
}
.container > * {
width: 100%;
}

@ -0,0 +1,14 @@
<div class="container">
<h3 mat-dialog-title>Are you sure?</h3>
<div mat-dialog-content>
Name: {{data.name}}
<p></p>
E-Mail: {{data.email}}
<p></p>
</div>
<div mat-dialog-actions>
<button mat-button [mat-dialog-close]="1" (click)="confirmDelete()">Delete</button>
<button mat-button (click)="onNoClick()" tabindex="-1">Cancel</button>
</div>
</div>

@ -0,0 +1,53 @@
import { MAT_DIALOG_DATA, MatDialogRef } from '@angular/material/dialog';
import {Component, Inject} from '@angular/core';
import {UserService} from '../../../services/user.service';
import {FormControl, Validators} from '@angular/forms';
import { first } from 'rxjs/operators';
import { SnackBarService } from 'src/app/services/snackbar.service';
@Component({
selector: 'app-baza.dialog',
templateUrl: '../../dialogs/edit/edit.dialog.html',
styleUrls: ['../../dialogs/edit/edit.dialog.css']
})
export class EditDialogComponent {
hide = true;
constructor(public dialogRef: MatDialogRef<EditDialogComponent>,
@Inject(MAT_DIALOG_DATA) public data: any, public userService: UserService, public snackbarService : SnackBarService) { }
formControl = new FormControl('', [
Validators.required
// Validators.email,
]);
getErrorMessage() {
return this.formControl.hasError('required') ? 'Required field' :
this.formControl.hasError('email') ? 'Not a valid email' :
'';
}
submit() {
// emppty stuff
}
onNoClick(): void {
this.dialogRef.close();
}
stopEdit(): void {
this.data.roles = [];
this.userService.updateUser(this.data).pipe(first())
.subscribe(
data => {
this.snackbarService.openSnackBar("Der Nutzer wurde erfolgreich verändert!");
//this.loadAllObjects();
},
error => {
console.log("Eroor while editing: " + JSON.stringify(error));
this.snackbarService.openSnackBar(error, "Ok", true);
}
);
}
}

@ -0,0 +1,19 @@
.container {
display: flex;
flex-direction: column;
min-width: 450px;
}
.container > * {
width: 100%;
}
.form {
display: flex;
padding-top: 6px;
}
.mat-form-field {
font-size: 16px;
flex-grow: 1;
}

@ -0,0 +1,53 @@
<div class="container">
<h3 mat-dialog-title>Issue id: {{data.id}}</h3>
<form class="mat-dialog-content" (ngSubmit)="submit" #formControl="ngForm">
<div class="form">
<mat-form-field color="accent">
<input matInput #input class="form-control" placeholder="Name" [(ngModel)]="data.name" name="name" required >
<mat-error *ngIf="formControl.invalid">{{getErrorMessage()}}</mat-error>
</mat-form-field>
</div>
<div class="form">
<mat-form-field color="accent">
<input matInput #input class="form-control" placeholder="E-Mail" [(ngModel)]="data.email" name="email" required >
<mat-error *ngIf="formControl.invalid">{{getErrorMessage()}}</mat-error>
</mat-form-field>
</div>
<div class="form">
<mat-form-field color="accent">
<input matInput placeholder="Passwort" [(ngModel)]="data.password" name="password">
</mat-form-field>
</div>
<div class="form">
<mat-form-field>
<mat-label>Eigenes Passwort</mat-label>
<input
matInput
[type]="hide ? 'password' : 'text'"
name="ownPassword"
[(ngModel)]="data.own_password"
/>
<button
mat-icon-button
matSuffix
(click)="hide = !hide"
[attr.aria-label]="'Hide password'"
[attr.aria-pressed]="hide"
>
<mat-icon>{{ hide ? "visibility_off" : "visibility" }}</mat-icon>
</button>
</mat-form-field>
</div>
<div mat-dialog-actions>
<button mat-button [type]="submit" [disabled]="!formControl.valid" [mat-dialog-close]="1" (click)="stopEdit()">Save</button>
<button mat-button (click)="onNoClick()" tabindex="-1">Cancel</button>
</div>
</form>
</div>

@ -0,0 +1,9 @@
<mat-form-field>
<mat-label>
{{label}}
</mat-label>
<mat-select [ngModel]="selectedOptions" (ngModelChange)="selectedOptions" [formControl]="formCtrl" [multiple]="multiple">
<mat-select-search [formControl]="searchCtrl"></mat-select-search>
<mat-option *ngFor="let risk of filteredInterfacesBMulti | async" [value]="risk">{{risk.NAME}}</mat-option>
</mat-select>
</mat-form-field>

@ -0,0 +1,40 @@
.mat-stepper-horizontal {
margin-top: 8px;
}
.mat-form-field {
margin-top: 16px;
}
.right-align {
text-align: right;
}
input.right-align::-webkit-outer-spin-button,
input.right-align::-webkit-inner-spin-button {
display: none;
}
input.right-align {
-moz-appearance: textfield;
}
.flex {
display: flex;
}
.form-field-less-top-margin{
margin-top: 0.5em;
}
.anti-margin{
margin-left: 0;
margin-right: 0;
}
.mat-form-field{
width: 100%;
}

@ -0,0 +1,134 @@
import { Component, OnInit, OnDestroy, Input, OnChanges, SimpleChanges } from "@angular/core";
import { Subscription, ReplaySubject, Subject } from "rxjs";
import { FormGroup, FormControl, FormBuilder } from '@angular/forms';
import { takeUntil } from 'rxjs/operators';
@Component({ selector: "select-search", templateUrl: "form-select-search.component.html", styleUrls: ["./form-select-search.component.scss"] })
export class FormSelectSearchComponent implements OnInit, OnDestroy, OnChanges {
//@Input() group: FormGroup;
//@Input() controlName: string;
@Input() formCtrl: FormControl;
@Input() data: any[];
@Input() preSelectedData: any[];
@Input() label: string;
@Input() multiple: boolean;
selectedOptions: any[] = [];
public filteredInterfacesBMulti: ReplaySubject<any[]> = new ReplaySubject<any[]>(1);
public searchCtrl: FormControl = new FormControl();
constructor(private _formBuilder: FormBuilder) {
}
ngOnChanges(changes: SimpleChanges) {
// only run when property "data" changed
if (changes['data']) {
if (this.preSelectedData !== undefined){
//console.log( this.label + " is not undefined :D");
//console.log(this.label + " preselected: " + this.preSelectedData);
//console.log(JSON.stringify(this.preSelectedData))
this.riskListGeneratorB(this.preSelectedData);
console.log(this.label + " selected options: "+ JSON.stringify(this.selectedOptions) + " all options: " + JSON.stringify(this.data));
}
//this.listenInterfacesSearchable();
this.filterInterfacesBMulti();
}
if (changes['preSelectedData']) {
if (this.preSelectedData !== undefined){
/*console.log(this.label + " preselect data change");
console.log(this.label + " preselected: " + this.preSelectedData);
console.log(JSON.stringify(this.preSelectedData))*/
this.riskListGeneratorB(this.preSelectedData);
console.log(this.label + " selected options: "+ JSON.stringify(this.selectedOptions) + " all options: " + JSON.stringify(this.data));
}
//this.listenInterfacesSearchable();
this.filterInterfacesBMulti();
}
}
ngOnInit() {
//this.group.addControl('searchCtrl', this.searchCtrl);
//this.formCtrl = this.group[this.controlName];
if (this.preSelectedData !== undefined){
/*console.log("preselected is not undefined :D");
console.log("preselected: " + this.preSelectedData);*/
this.riskListGeneratorB(this.preSelectedData);
console.log("selected options: "+ JSON.stringify(this.selectedOptions) + " all options: " + JSON.stringify(this.data));
}
this.listenInterfacesSearchable();
}
private listenInterfacesSearchable(){
this.filteredInterfacesBMulti.next(this.data.slice());
this.searchCtrl.valueChanges
.pipe(takeUntil(this._onDestroy))
.subscribe(() => {
this.filterInterfacesBMulti();
});
}
private _onDestroy = new Subject<void>();
ngOnDestroy() {
this._onDestroy.next();
this._onDestroy.complete();
}
private filterInterfacesBMulti() {
if (!this.data) {
return;
}
// get the search keyword
let search = this.searchCtrl.value;
if (!search) {
this.filteredInterfacesBMulti.next(this.data.slice());
return;
} else {
search = search.toLowerCase();
}
// filter the banks
this.filteredInterfacesBMulti.next(
this.data.filter(interf => interf.NAME.toLowerCase().indexOf(search) > -1)
);
}
private riskListGeneratorB(risks: any[]) {
for (let risk of risks) {
for (let riskItem of this.data) {
if (risk._id === riskItem._id) {
console.log(this.label + " added id actual data: " + risk._id + " id background data: " + riskItem._id);
if (this.multiple === undefined){
this.selectedOptions = riskItem;
console.log("set instead of add");
} else if (this.selectedOptions.find(e => e._id === risk._id) == undefined){ // only add it to the list if its not added already
this.selectedOptions.push(riskItem);
}
}
}
}
}
}

@ -10,7 +10,8 @@ export class User {
email: string;
own_password: string;
password: string;
roles: string[];
attributes : {
profile_url: string;
profile_url?: string;
}
}

@ -87,6 +87,9 @@
</mat-error>
</div>
<!--<app-admin-data-page *ngIf="this.authService.getCurrentUserValue.user.roles.includes('admin')"></app-admin-data-page>
-->
<app-admin-data-page></app-admin-data-page>
<!--<app-table

@ -27,7 +27,7 @@ export class ProfileComponent implements OnInit {
returnUrl : string;
constructor(private authService: AuthService, private snackBar: SnackBarService,
constructor(public authService: AuthService, private snackBar: SnackBarService,
private userService: UserService, private router: Router,
private route: ActivatedRoute, private formBuilder : FormBuilder) {}

@ -13,6 +13,13 @@ import { ObserveOnSubscriber } from 'rxjs/internal/operators/observeOn';
export class UserService {
constructor(private http: HttpClient, private authService: AuthService) {}
// Temporarily stores data from dialogs, kinda shit solution I guess
dialogData: any;
getDialogData() {
return this.dialogData;
}
public getAllUsers(): Observable<User[]> {
return this.http.get<User[]>(`${environment.authUrl}/users`);
}
@ -27,16 +34,33 @@ export class UserService {
);
}
public updateUser(user: User): Observable<User> {
public updateUser(user: User, own_password?: string): Observable<User> {
this.dialogData = user;
if (own_password !== undefined){
user.own_password = own_password;
}
console.log("Users update: " + JSON.stringify(user));
return this.http.post<User>(
`${environment.authUrl}/users/${user.email}/update`,
user
);
}
public deleteUser(email: string): Observable<any> {
return this.http.delete<any>(
`${environment.authUrl}/users/` + email + '/delete'
public deleteUser(email: string, own_password? : string): Observable<any> {
return this.http.post<any>(
`${environment.authUrl}/users/` + email + '/delete', {"own_password" : own_password}
);
}
public addUser(user : User){
if (user.attributes === undefined){
user.attributes = {};
}
this.dialogData = user;
return this.http.post<User>(
`${environment.authUrl}/users/create`,
user
);
}
}

Loading…
Cancel
Save