Create Page template component

urls
Max Ehrlicher-Schmidt 4 years ago
parent 314fb3c33d
commit 14c080195e

@ -45,7 +45,8 @@ import {SidenavProfileComponent} from './components/sidenav-profile/sidenav-prof
import { NavService }from './components/menu-list-item/nav.service';
import { TokenInterceptor } from './helper/token.interceptor';
import { BikeComponent } from './pages/dataPages/bike/bike.component';
import { TableComponent, DeleteConfirmationDialog } from './components/table/table.component'
import { TableComponent, DeleteConfirmationDialog } from './components/table/table.component';
import { DataPageComponent } from './components/data-page/data-page.component'
@NgModule({
@ -61,7 +62,8 @@ import { TableComponent, DeleteConfirmationDialog } from './components/table/tab
CellComponent,
DeleteConfirmationDialog,
BikeComponent,
TableComponent
TableComponent,
DataPageComponent
],
imports: [
BrowserModule,

@ -0,0 +1,62 @@
<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>
</div>
<div class="data-page-wrapper" *ngIf="data">
<h1>
{{ data[headlineDataPath] }}
</h1>
<div *ngFor="let prop of propertiesInfo">
<app-cell
[editable]="data.isLockedByMe && !prop.readonly"
[(value)]="data[prop.name]"
[label]="prop.translation || prop.name"
[inputType]="prop.type"
></app-cell>
</div>
</div>
<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>
<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-fab
(click)="save()"
*ngIf="data.isLockedByMe"
class="floating-fab-button"
[disabled]="isSavingOrLocking || isLoading"
>
<mat-icon>save</mat-icon>
</button>
<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>

@ -0,0 +1,19 @@
.page-loading-spinner {
margin: 0 auto;
}
.data-page-wrapper {
height: 100%;
box-sizing: border-box;
overflow: auto;
padding: 2em;
}
.floating-fab-button {
position: absolute;
bottom: 2em;
right: 2em;
}
.floating-fab-button-top {
position: absolute;
top: 2em;
right: 2em;
}

@ -0,0 +1,102 @@
import {
AfterViewInit,
Component,
EventEmitter,
Input,
OnInit,
Output,
} from '@angular/core';
import { ActivatedRoute } from '@angular/router';
import { deepen } from 'src/app/helperFunctions/deepenObject';
import { flatten } from 'src/app/helperFunctions/flattenObject';
import { BikesService } from 'src/app/services/bikes.service';
import { SchemaService } from 'src/app/services/schema.service';
import { runInThisContext } from 'vm';
@Component({
selector: 'app-data-page',
templateUrl: './data-page.component.html',
styleUrls: ['./data-page.component.scss'],
})
export class DataPageComponent implements OnInit {
@Input()
propertiesInfo: {
name: string;
translation: string;
readonly?: boolean;
type?: string;
}[] = [];
@Input()
dataService: any;
@Input()
headlineDataPath: string;
@Input()
pageDataGQLType: string = 'CargoBike';
@Input()
pageDataGQLUpdateInputType: string = 'CargoBikeUpdateInput';
@Output() lockEvent = new EventEmitter();
@Output() saveEvent = new EventEmitter();
id: string;
data: any;
isLoading: boolean = false;
isSavingOrLocking: boolean = false;
constructor(
private route: ActivatedRoute,
private schemaService: SchemaService
) {}
ngOnInit(): void {
this.addPropertiesFromGQLSchemaToPropertiesInfo();
this.id = this.route.snapshot.paramMap.get('id');
this.reloadPageData();
this.dataService.pageData.subscribe((data) => {
this.data = flatten(data);
});
this.dataService.isLoadingPageData.subscribe(
(isLoading) => (this.isLoading = isLoading)
);
this.dataService.loadingRowIds.subscribe(loadingRowIds => {
console.log(loadingRowIds);
this.isSavingOrLocking = loadingRowIds.includes(this.id);
})
}
addPropertiesFromGQLSchemaToPropertiesInfo() {
for (const column of this.propertiesInfo) {
const typeInformation = this.schemaService.getTypeInformation(
this.pageDataGQLType,
column.name
);
column.type = column.type || typeInformation.type;
}
for (const column of this.propertiesInfo) {
const typeInformation = this.schemaService.getTypeInformation(
this.pageDataGQLUpdateInputType,
column.name
);
column.readonly = column.readonly || !typeInformation.isPartOfType;
}
}
lock() {
this.lockEvent.emit(deepen(this.data));
}
save() {
this.saveEvent.emit(
this.schemaService.filterObject(
this.pageDataGQLUpdateInputType,
deepen(this.data)
)
);
}
reloadPageData() {
this.dataService.loadPageData({ id: this.id });
}
}

@ -136,7 +136,7 @@
<div class="button-wrapper" *ngIf="!element.newObject">
<button
mat-icon-button
(click)="edit(element)"
(click)="lock(element)"
*ngIf="
!element.isLockedByMe &&
!element.isLocked &&

@ -7,7 +7,7 @@ import {
ViewChild,
} from '@angular/core';
import { CdkDragDrop, moveItemInArray } from '@angular/cdk/drag-drop';
import { BikesService, CargoBikeResult } from 'src/app/services/bikes.service';
import { CargoBikeResult } from 'src/app/services/bikes.service';
import { flatten } from 'src/app/helperFunctions/flattenObject';
import { deepen } from 'src/app/helperFunctions/deepenObject';
import { SchemaService } from 'src/app/services/schema.service';
@ -65,17 +65,16 @@ export class TableComponent {
relockingInterval = null;
@Input()
relockingIntervalDuration = 1000 * 60 * 1;
relockingIntervalDuration = 1000 * 10 * 1; // TODO: set back to 60s
filter = { includesString: '', onlyUnsaved: false };
initialFilter = this.filter;
isLoaded = false;
@Output() createEvent = new EventEmitter();
@Output() editEvent = new EventEmitter();
@Output() lockEvent = new EventEmitter();
@Output() saveEvent = new EventEmitter();
@Output() cancelEvent = new EventEmitter();
@Output() deleteEvent = new EventEmitter();
@Output() relockEvent = new EventEmitter();
constructor(private schemaService: SchemaService, public dialog: MatDialog) {}
@ -128,6 +127,7 @@ export class TableComponent {
} else if (!(oldRow.isLockedByMe && row.isLockedByMe)) {
tempDataSource.push(flatten(row));
} else if (!!oldRow) {
//old row is getting edited
tempDataSource.push(oldRow);
}
}
@ -143,7 +143,7 @@ export class TableComponent {
this.relockingInterval = setInterval(() => {
for (const row of this.data.data) {
if (row.isLockedByMe) {
this.relock(row);
this.lock(row);
}
}
}, this.relockingIntervalDuration);
@ -253,12 +253,8 @@ export class TableComponent {
this.createEvent.emit(newRow);
}
edit(row: any) {
this.editEvent.emit(row);
}
relock(row: any) {
this.relockEvent.emit(row);
lock(row: any) {
this.lockEvent.emit(deepen(row));
}
countUnsavedRows(): number {

@ -1,20 +1,9 @@
<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>
</div>
<div class="page-wrapper" *ngIf="data">
<h1>{{ data[headlineDataPath] }}</h1>
<div *ngFor="let prop of propertiesInfo">
<app-cell
[editable]="data.isLockedByMe && !prop.readonly"
[(value)]="data[prop.name]"
[label]="prop.translation || prop.name"
[inputType]="prop.type"
></app-cell>
</div>
</div>
<app-data-page
[dataService]="dataService"
[propertiesInfo]="propertiesInfo"
[headlineDataPath]="headlineDataPath"
[pageDataGQLType]="pageDataGQLType"
[pageDataGQLUpdateInputType]="pageDataGQLUpdateInputType"
(lockEvent)="lock($event)"
(saveEvent)="save($event)"
></app-data-page>

@ -1,6 +0,0 @@
.page-loading-spinner {
margin: 0 auto;
}
.page-wrapper {
padding: 1em;
}

@ -10,12 +10,7 @@ import { SchemaService } from 'src/app/services/schema.service';
styleUrls: ['./bike.component.scss'],
})
export class BikeComponent implements OnInit {
propertiesInfo: {
name: string;
translation: string;
readonly?: boolean;
type?: string;
}[] = [
propertiesInfo = [
{ name: 'name', translation: 'Name' },
{ name: 'id', translation: 'ID', readonly: true },
{ name: 'group', translation: 'Gruppe' },
@ -105,46 +100,22 @@ export class BikeComponent implements OnInit {
];
headlineDataPath = 'name';
pageDataGQLType: string = "CargoBike";
pageDataGQLUpdateInputType: string = "CargoBikeUpdateInput"
pageDataGQLType: string = 'CargoBike';
pageDataGQLUpdateInputType: string = 'CargoBikeUpdateInput';
id: string;
data: any;
isLoading: boolean = false;
dataService: any;
constructor(
private route: ActivatedRoute,
private bikesService: BikesService,
private schemaService: SchemaService
) {
this.addPropertiesFromGQLSchemaToPropertiesInfo();
}
constructor(private bikesService: BikesService) {}
ngOnInit(): void {
this.id = this.route.snapshot.paramMap.get('id');
this.bikesService.loadPageData({ id: this.id });
this.bikesService.pageData.subscribe((data) => {
this.data = flatten(data);
});
this.bikesService.loadingBike.subscribe(
(isLoading) => (this.isLoading = isLoading)
);
this.dataService = this.bikesService;
}
lock(row: any) {
this.bikesService.lockBike({id: row.id});
}
addPropertiesFromGQLSchemaToPropertiesInfo() {
for (const column of this.propertiesInfo) {
const typeInformation = this.schemaService.getTypeInformation(
this.pageDataGQLType,
column.name
);
column.type = column.type || typeInformation.type;
}
for (const column of this.propertiesInfo) {
const typeInformation = this.schemaService.getTypeInformation(
this.pageDataGQLUpdateInputType,
column.name
);
column.readonly = column.readonly || !typeInformation.isPartOfType;
}
save(row: any) {
this.bikesService.updateBike({bike: row})
}
}

@ -5,8 +5,7 @@
[tableDataGQLCreateInputType]="tableDataGQLCreateInputType"
[tableDataGQLUpdateInputType]="tableDataGQLUpdateInputType"
(createEvent)="create($event)"
(editEvent)="edit($event)"
(relockEvent)="relock($event)"
(lockEvent)="lock($event)"
(saveEvent)="save($event)"
(cancelEvent)="cancel($event)"
(deleteEvent)="delete($event)"

@ -128,14 +128,10 @@ export class BikesComponent {
this.bikesService.createBike({ bike: row });
}
edit(row: any) {
lock(row: any) {
this.bikesService.lockBike({ id: row.id });
}
relock(row: any) {
this.bikesService.relockBike({ id: row.id });
}
save(row: any) {
this.bikesService.updateBike({ bike: row });
}

@ -33,8 +33,7 @@ export class BikesService {
tableData: BehaviorSubject<CargoBikeResult[]> = new BehaviorSubject([]);
loadingRowIds: BehaviorSubject<string[]> = new BehaviorSubject([]);
pageData: BehaviorSubject<any> = new BehaviorSubject([]);
loadingBike: BehaviorSubject<boolean> = new BehaviorSubject(false);
isLoadingPageData: BehaviorSubject<boolean> = new BehaviorSubject(false);
constructor(
private getCargoBikesGQL: GetCargoBikesGQL,
@ -44,7 +43,7 @@ export class BikesService {
private lockCargoBikeGQL: LockCargoBikeGQL,
private unlockCargoBikeGQL: UnlockCargoBikeGQL,
private createCargoBikeGQL: CreateCargoBikeGQL,
private deleteCargoBikeGQL: DeleteCargoBikeGQL,
private deleteCargoBikeGQL: DeleteCargoBikeGQL
) {}
addLoadingRowId(id: string) {
@ -57,25 +56,25 @@ export class BikesService {
this.loadingRowIds.value.splice(index, 1);
}
});
this.loadingRowIds.next(this.loadingRowIds.value);
}
loadTableData() {
this.getCargoBikesGQL.fetch().subscribe((result) => {
this.tableData.next(result.data.cargoBikes);
});
}
loadPageData(variables: GetCargoBikeByIdQueryVariables) {
this.pageData.next(null);
this.loadingBike.next(true);
this.isLoadingPageData.next(true);
this.getCargoBikeByIdGQL
.fetch(variables)
.subscribe((result) => {
this.pageData.next(result.data.cargoBikeById);
})
.add(() => {
this.loadingBike.next(false);
this.isLoadingPageData.next(false);
});
}
@ -84,12 +83,7 @@ export class BikesService {
this.reloadCargoBikeByIdGQL
.fetch(variables)
.subscribe((result) => {
const newBike = result.data.cargoBikeById;
this.tableData.next(
this.tableData.value.map((bike) =>
newBike.id === bike.id ? newBike : bike
)
);
this.updateDataRowFromResponse(result.data.cargoBikeById)
})
.add(() => {
this.removeLoadingRowId(variables.id);
@ -97,14 +91,10 @@ export class BikesService {
}
createBike(variables: CreateCargoBikeMutationVariables) {
this.createCargoBikeGQL
.mutate(variables)
.subscribe((result) => {
const newBike = result.data.createCargoBike;
this.tableData.next(
[newBike, ...this.tableData.value]
);
})
this.createCargoBikeGQL.mutate(variables).subscribe((result) => {
const newBike = result.data.createCargoBike;
this.tableData.next([newBike, ...this.tableData.value]);
});
}
updateBike(variables: UpdateCargoBikeMutationVariables) {
@ -112,12 +102,7 @@ export class BikesService {
this.updateCargoBikeGQL
.mutate(variables)
.subscribe((result) => {
const newBike = result.data.updateCargoBike;
this.tableData.next(
this.tableData.value.map((bike) =>
newBike.id === bike.id ? newBike : bike
)
);
this.updateDataRowFromResponse(result.data.updateCargoBike);
})
.add(() => {
this.removeLoadingRowId(variables.bike.id);
@ -129,12 +114,7 @@ export class BikesService {
this.lockCargoBikeGQL
.mutate(variables)
.subscribe((result) => {
const lockedBike = result.data.lockCargoBike;
this.tableData.next(
this.tableData.value.map((bike) =>
lockedBike.id === bike.id ? lockedBike : bike
)
);
this.updateDataRowFromResponse(result.data.lockCargoBike);
})
.add(() => {
this.removeLoadingRowId(variables.id);
@ -146,12 +126,7 @@ export class BikesService {
this.unlockCargoBikeGQL
.mutate(variables)
.subscribe((result) => {
const unlockedBike = result.data.unlockCargoBike;
this.tableData.next(
this.tableData.value.map((bike) =>
unlockedBike.id === bike.id ? unlockedBike : bike
)
);
this.updateDataRowFromResponse(result.data.unlockCargoBike);
})
.add(() => {
this.removeLoadingRowId(variables.id);
@ -163,8 +138,10 @@ export class BikesService {
this.deleteCargoBikeGQL
.mutate(variables)
.subscribe((result) => {
if(result.data.deleteCargoBike) {
this.tableData.next([...this.tableData.value].filter(bike => bike.id !== variables.id));
if (result.data.deleteCargoBike) {
this.tableData.next(
[...this.tableData.value].filter((bike) => bike.id !== variables.id)
);
}
})
.add(() => {
@ -172,7 +149,13 @@ export class BikesService {
});
}
relockBike(variables: LockCargoBikeMutationVariables) {
this.lockCargoBikeGQL.mutate(variables).subscribe();
private updateDataRowFromResponse(rowFromResponse: any) {
const newTableData = this.tableData.value.map((row) =>
rowFromResponse.id === row.id ? rowFromResponse : row
);
this.tableData.next(newTableData);
if ((rowFromResponse.id === this.pageData?.value?.id)) {
this.pageData.next(rowFromResponse);
}
}
}

Loading…
Cancel
Save