From 314fb3c33d0d827f9d274890ade8b769b4344adb Mon Sep 17 00:00:00 2001 From: Max Ehrlicher-Schmidt Date: Mon, 23 Nov 2020 13:43:05 +0100 Subject: [PATCH] Create table template component --- src/app/app.module.ts | 8 +- .../table}/delete-confirmation-dialog.html | 0 src/app/components/table/table.component.html | 237 ++++++++++++ src/app/components/table/table.component.scss | 53 +++ src/app/components/table/table.component.ts | 364 ++++++++++++++++++ .../pages/dataPages/bike/bike.component.ts | 4 +- .../pages/tables/bikes/bikes.component.html | 249 +----------- .../pages/tables/bikes/bikes.component.scss | 52 --- src/app/pages/tables/bikes/bikes.component.ts | 327 ++-------------- src/app/services/bikes.service.ts | 37 +- 10 files changed, 718 insertions(+), 613 deletions(-) rename src/app/{pages/tables/bikes => components/table}/delete-confirmation-dialog.html (100%) create mode 100644 src/app/components/table/table.component.html create mode 100644 src/app/components/table/table.component.scss create mode 100644 src/app/components/table/table.component.ts diff --git a/src/app/app.module.ts b/src/app/app.module.ts index c120085..2d7fee4 100644 --- a/src/app/app.module.ts +++ b/src/app/app.module.ts @@ -34,7 +34,7 @@ import { AppRoutingModule } from './app-routing.module'; import { AppComponent } from './app.component'; import { BrowserAnimationsModule } from '@angular/platform-browser/animations'; import { LoginComponent } from './pages/login/login.component'; -import { BikesComponent, DeleteConfirmationDialog } from './pages/tables/bikes/bikes.component'; +import { BikesComponent } from './pages/tables/bikes/bikes.component'; import { GraphQLModule } from './graphql.module'; import { ParticipantsComponent } from './pages/tables/participants/participants.component'; import { LendingStationsComponent } from './pages/tables/lending-stations/lending-stations.component'; @@ -44,7 +44,8 @@ import { MenuListItemComponent } from './components/menu-list-item/menu-list-ite import {SidenavProfileComponent} from './components/sidenav-profile/sidenav-profile.component'; import { NavService }from './components/menu-list-item/nav.service'; import { TokenInterceptor } from './helper/token.interceptor'; -import { BikeComponent } from './pages/dataPages/bike/bike.component' +import { BikeComponent } from './pages/dataPages/bike/bike.component'; +import { TableComponent, DeleteConfirmationDialog } from './components/table/table.component' @NgModule({ @@ -59,7 +60,8 @@ import { BikeComponent } from './pages/dataPages/bike/bike.component' TableOverviewComponent, CellComponent, DeleteConfirmationDialog, - BikeComponent + BikeComponent, + TableComponent ], imports: [ BrowserModule, diff --git a/src/app/pages/tables/bikes/delete-confirmation-dialog.html b/src/app/components/table/delete-confirmation-dialog.html similarity index 100% rename from src/app/pages/tables/bikes/delete-confirmation-dialog.html rename to src/app/components/table/delete-confirmation-dialog.html diff --git a/src/app/components/table/table.component.html b/src/app/components/table/table.component.html new file mode 100644 index 0000000..dc4faff --- /dev/null +++ b/src/app/components/table/table.component.html @@ -0,0 +1,237 @@ +
+
+ + + + + Filter + + + + + nur ungespeicherte Elemente anzeigen + + +
+
+ + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + + + + {{ getTranslation(column.name) }} + + + + {{ element[column.name] }} + {{ + element[column.name] + }} + + + +
+ + + + + + + + + + + + + locked +
+
+ + +
+
+ + + + +
+
+ diff --git a/src/app/components/table/table.component.scss b/src/app/components/table/table.component.scss new file mode 100644 index 0000000..8907e62 --- /dev/null +++ b/src/app/components/table/table.component.scss @@ -0,0 +1,53 @@ +.table-page-wrapper { + display: flex; + flex-direction: column; + height: 100%; + .table-control { + margin: 0.5em; + flex: none; + .table-control-button { + margin: 0.25em; + } + .filter { + margin-right: 0.5em; + } + .mat-paginator { + display: inline-block; + width: 50em; + margin-right: 0.5em; + } + } + .table-container { + flex: 1; + width: auto; + margin-left: 0.5em; + margin-right: 0.5em; + max-width: 100%; + overflow: auto; + table { + max-width: 100%; + margin: 0 auto; + .mat-header-cell, + .mat-footer-cell, + .mat-cell { + min-width: 3em; + box-sizing: border-box; + padding: 0 0.25em; + } + ::ng-deep.mat-form-field { + width: 100%; + } + + .mat-table-sticky { + filter: brightness(90%); + //opacity: 1; + } + + .button-wrapper { + display: flex; + flex-direction: row; + } + } + } + } + \ No newline at end of file diff --git a/src/app/components/table/table.component.ts b/src/app/components/table/table.component.ts new file mode 100644 index 0000000..c628f5b --- /dev/null +++ b/src/app/components/table/table.component.ts @@ -0,0 +1,364 @@ +import { SelectionModel } from '@angular/cdk/collections'; +import { + Component, + EventEmitter, + Input, + Output, + ViewChild, +} from '@angular/core'; +import { CdkDragDrop, moveItemInArray } from '@angular/cdk/drag-drop'; +import { BikesService, 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'; + +import { logArrayInColumnInfoForm } from 'src/app/helperFunctions/logArrayInColumnInfoForm'; +import { MatTableDataSource } from '@angular/material/table'; +import { MatPaginator } from '@angular/material/paginator'; +import { MatSort } from '@angular/material/sort'; +import { MatDialog, MatDialogRef } from '@angular/material/dialog'; + +@Component({ + selector: 'app-table', + templateUrl: './table.component.html', + styleUrls: ['./table.component.scss'], +}) +export class TableComponent { + /** this array defines the columns and translations of the table and the order they are displayed */ + @Input() + columnInfo: { + name: string; + translation: string; + acceptedForCreation?: boolean; + requiredForCreation?: boolean; + sticky?: boolean; + readonly?: boolean; + type?: string; + link?: (row: any) => string; + }[] = []; + + @Input() + dataService: any; + + @Input() + tableDataGQLType: string; + @Input() + tableDataGQLCreateInputType: string; + @Input() + tableDataGQLUpdateInputType: string; + + @ViewChild(MatPaginator) paginator: MatPaginator; + @ViewChild(MatSort) sort: MatSort; + + additionalColumnsFront: string[] = ['select']; + additionalColumnsBack: string[] = ['buttons']; + displayedColumns: string[] = []; + + loadingRowIds: string[] = []; + + /** data source of the table */ + data: MatTableDataSource = new MatTableDataSource(); + selection = new SelectionModel(true, []); + + reloadingTable = false; + + relockingInterval = null; + + @Input() + relockingIntervalDuration = 1000 * 60 * 1; + filter = { includesString: '', onlyUnsaved: false }; + initialFilter = this.filter; + isLoaded = false; + + @Output() createEvent = new EventEmitter(); + @Output() editEvent = 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) {} + + ngAfterViewInit() { + this.addColumnPropertiesFromGQLSchemaToColumnInfo(); + this.data.paginator = this.paginator; + this.data.sortingDataAccessor = (item, columnName) => { + if (typeof item[columnName] === 'string') { + return item[columnName].toLocaleLowerCase(); + } + return item[columnName]; + }; + this.data.sort = this.sort; + + this.data.filter = (this.filter as unknown) as string; + this.data.filterPredicate = (data, filter: any) => { + const a = !filter.onlyUnsaved || data.newObject || data.isLockedByMe; + const b = + !filter.includesString || + Object.keys(data).some( + (k) => + data[k] != null && + data[k] + .toString() + .toLowerCase() + .includes(filter.includesString.toLowerCase()) + ); + return a && b; + }; + + this.columnInfo.forEach((column) => + this.displayedColumns.push(column.name) + ); + this.displayedColumns.unshift(this.additionalColumnsFront[0]); + this.displayedColumns.push(this.additionalColumnsBack[0]); + + this.dataService.loadingRowIds.subscribe((rowIds) => { + this.loadingRowIds = rowIds; + }); + + this.dataService.tableData.subscribe((newTableDataSource) => { + this.reloadingTable = false; + const tempDataSource = []; + for (const row of newTableDataSource) { + this.isLoaded = true; + const oldRow = this.getRowById(row.id); + /** make sure to not overwrite a row that is being edited */ + if (!oldRow) { + tempDataSource.push(flatten(row)); + } else if (!(oldRow.isLockedByMe && row.isLockedByMe)) { + tempDataSource.push(flatten(row)); + } else if (!!oldRow) { + tempDataSource.push(oldRow); + } + } + for (const oldRow of this.data.data) { + if (oldRow.newObject) { + tempDataSource.unshift(oldRow); + } + } + this.data.data = tempDataSource; + }); + this.dataService.loadTableData(); + + this.relockingInterval = setInterval(() => { + for (const row of this.data.data) { + if (row.isLockedByMe) { + this.relock(row); + } + } + }, this.relockingIntervalDuration); + } + + ngOnDestroy() { + clearInterval(this.relockingInterval); + } + + addColumnPropertiesFromGQLSchemaToColumnInfo() { + for (const column of this.columnInfo) { + const typeInformation = this.schemaService.getTypeInformation( + this.tableDataGQLType, + column.name + ); + column.type = column.type || typeInformation.type; + } + for (const column of this.columnInfo) { + const typeInformation = this.schemaService.getTypeInformation( + this.tableDataGQLUpdateInputType, + column.name + ); + column.readonly = column.readonly || !typeInformation.isPartOfType; + } + for (const column of this.columnInfo) { + const typeInformation = this.schemaService.getTypeInformation( + this.tableDataGQLCreateInputType, + column.name + ); + column.requiredForCreation = typeInformation.isRequired; + column.acceptedForCreation = typeInformation.isPartOfType; + } + } + + getTranslation(propertyName: string) { + return ( + this.columnInfo.find((column) => column.name === propertyName) + ?.translation || propertyName + ); + } + + isStickyColumn(propertyName: string) { + return ( + this.columnInfo.find((column) => column.name === propertyName)?.sticky || + false + ); + } + + isLoading(id: string) { + return this.loadingRowIds.includes(id); + } + + validityChange(row: any, columnName: string, isValid: Event) { + if (!row.FieldsValidity) { + row['FieldsValidity'] = {}; + } + row['FieldsValidity'][columnName] = isValid; + } + + countUnvalidFields(row: any) { + let unvalidFieldsCount = 0; + if (!row.FieldsValidity) { + return 99; + } + for (const prop in row.FieldsValidity) { + if (!row.FieldsValidity[prop]) { + unvalidFieldsCount++; + } + } + return unvalidFieldsCount; + } + + reloadTable() { + this.reloadingTable = true; + this.isLoaded = false; + this.data.data = []; + this.dataService.loadTableData(); + } + + addNewObject() { + this.paginator.firstPage(); + this.setFilter({ ...this.filter, includesString: '' }); + this.resetSorting(); + this.data.data = [ + { newObject: true, id: this.getNewId() }, + ...this.data.data, + ]; + } + + getNewId(): string { + let id = -1; + while (this.getRowById(id.toString())) { + id--; + } + return id.toString(); + } + + deleteNewObject(row: any) { + this.data.data = this.data.data.filter((element) => row.id !== element.id); + } + + create(row: any) { + const newRow = this.schemaService.filterObject( + this.tableDataGQLCreateInputType, + deepen(row) + ); + this.createEvent.emit(newRow); + } + + edit(row: any) { + this.editEvent.emit(row); + } + + relock(row: any) { + this.relockEvent.emit(row); + } + + countUnsavedRows(): number { + let unsavedCount = 0; + for (const row of this.data.data) { + if (row.isLockedByMe || row.newObject) { + unsavedCount++; + } + } + return unsavedCount; + } + + save(row: any) { + const deepenRow = this.schemaService.filterObject( + this.tableDataGQLUpdateInputType, + deepen(row) + ); + this.saveEvent.emit(deepenRow); + } + + cancel(row: any) { + this.cancelEvent.emit(row); + } + + delete(row: any) { + this.deleteEvent.emit(row); + } + + openDeleteConfirmationDialog(row: any) { + const dialogRef = this.dialog.open(DeleteConfirmationDialog, { + width: '250px', + }); + + dialogRef.afterClosed().subscribe((result) => { + if (result === true) { + this.delete(row); + } + }); + } + + getRowById(id: string) { + return this.data.data.find((row) => row.id === id); + } + + drop(event: CdkDragDrop) { + moveItemInArray( + this.displayedColumns, + event.previousIndex + 2, + event.currentIndex + 2 + ); // +2 because the first 2 (selection + name) columns are not dragable + } + + /** Whether the number of selected elements matches the total number of rows. */ + isAllSelected() { + const numSelected = this.selection.selected.length; + const numRows = this.data.data.length; + return numSelected === numRows; + } + + /** Selects all rows if they are not all selected; otherwise clear selection. */ + masterToggle() { + this.isAllSelected() + ? this.selection.clear() + : this.data.data.forEach((row) => this.selection.select(row)); + } + + showOnlyUnsavedElements(value: boolean) { + this.filter.onlyUnsaved = value; + this.filter.includesString = ''; + this.applyFilter(); + } + + applyFilter() { + this.data.filter = ({ + ...this.filter, + includesString: this.filter.includesString.trim().toLowerCase(), + } as unknown) as string; + } + + setFilter(filterObject) { + this.filter = filterObject; + this.applyFilter(); + } + + resetSorting() { + this.sort.sort({ id: null, start: 'asc', disableClear: false }); + } +} + +@Component({ + selector: 'delete-confirmation-dialog', + templateUrl: 'delete-confirmation-dialog.html', +}) +export class DeleteConfirmationDialog { + constructor(public dialogRef: MatDialogRef) {} + + onConfirmClick(): void { + this.dialogRef.close(true); + } + onNoClick(): void { + this.dialogRef.close(false); + } +} diff --git a/src/app/pages/dataPages/bike/bike.component.ts b/src/app/pages/dataPages/bike/bike.component.ts index 711e96f..f33c831 100644 --- a/src/app/pages/dataPages/bike/bike.component.ts +++ b/src/app/pages/dataPages/bike/bike.component.ts @@ -122,8 +122,8 @@ export class BikeComponent implements OnInit { ngOnInit(): void { this.id = this.route.snapshot.paramMap.get('id'); - this.bikesService.loadCargoBike({ id: this.id }); - this.bikesService.bike.subscribe((data) => { + this.bikesService.loadPageData({ id: this.id }); + this.bikesService.pageData.subscribe((data) => { this.data = flatten(data); }); this.bikesService.loadingBike.subscribe( diff --git a/src/app/pages/tables/bikes/bikes.component.html b/src/app/pages/tables/bikes/bikes.component.html index 73979b0..57af263 100644 --- a/src/app/pages/tables/bikes/bikes.component.html +++ b/src/app/pages/tables/bikes/bikes.component.html @@ -1,236 +1,13 @@ -
-
- - - - - Filter - - - - - nur ungespeicherte Elemente anzeigen - - -
-
- - - - - - - - - - - - - - - - - - - - - - - - - -
- - - - - - - {{ getTranslation(column.name) }} - - - - {{ element[column.name] }} - {{ - element[column.name] - }} - - - -
- - - - - - - - - - - - - locked -
-
- - -
-
- - - - -
-
+ diff --git a/src/app/pages/tables/bikes/bikes.component.scss b/src/app/pages/tables/bikes/bikes.component.scss index b16788b..e69de29 100644 --- a/src/app/pages/tables/bikes/bikes.component.scss +++ b/src/app/pages/tables/bikes/bikes.component.scss @@ -1,52 +0,0 @@ -.table-page-wrapper { - display: flex; - flex-direction: column; - height: 100%; - .table-control { - margin: 0.5em; - flex: none; - .table-control-button { - margin: 0.25em; - } - .filter { - margin-right: 0.5em; - } - .mat-paginator { - display: inline-block; - width: 50em; - margin-right: 0.5em; - } - } - .table-container { - flex: 1; - width: auto; - margin-left: 0.5em; - margin-right: 0.5em; - max-width: 100%; - overflow: auto; - table { - max-width: 100%; - margin: 0 auto; - .mat-header-cell, - .mat-footer-cell, - .mat-cell { - min-width: 3em; - box-sizing: border-box; - padding: 0 0.25em; - } - ::ng-deep.mat-form-field { - width: 100%; - } - - .mat-table-sticky { - filter: brightness(90%); - //opacity: 1; - } - - .button-wrapper { - display: flex; - flex-direction: row; - } - } - } -} diff --git a/src/app/pages/tables/bikes/bikes.component.ts b/src/app/pages/tables/bikes/bikes.component.ts index bd7e7ed..de85d65 100644 --- a/src/app/pages/tables/bikes/bikes.component.ts +++ b/src/app/pages/tables/bikes/bikes.component.ts @@ -5,11 +5,6 @@ import { BikesService, 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'; - -import { logArrayInColumnInfoForm } from 'src/app/helperFunctions/logArrayInColumnInfoForm'; -import { MatTableDataSource } from '@angular/material/table'; -import { MatPaginator } from '@angular/material/paginator'; -import { MatSort } from '@angular/material/sort'; import { MatDialog, MatDialogRef } from '@angular/material/dialog'; @Component({ @@ -18,17 +13,7 @@ import { MatDialog, MatDialogRef } from '@angular/material/dialog'; styleUrls: ['./bikes.component.scss'], }) export class BikesComponent { - /** this array defines the columns and translations of the table and the order they are displayed */ - columnInfo: { - name: string; - translation: string; - acceptedForCreation?: boolean; - requiredForCreation?: boolean; - sticky?: boolean; - readonly?: boolean; - type?: string; - link?: (row: any) => string; - }[] = [ + columnInfo = [ { name: 'name', translation: 'Name', @@ -74,7 +59,10 @@ export class BikesComponent { name: 'dimensionsAndLoad.maxWeightLuggageRack', translation: 'max Zuladung Gepäckträger', }, - { name: 'dimensionsAndLoad.maxWeightTotal', translation: 'max Gesamtgewicht' }, + { + name: 'dimensionsAndLoad.maxWeightTotal', + translation: 'max Gesamtgewicht', + }, { name: 'numberOfChildren', translation: 'Anzahl Kinder' }, { name: 'numberOfWheels', translation: 'Anzahl Räder' }, { name: 'forCargo', translation: 'für Lasten j/n' }, @@ -92,8 +80,14 @@ export class BikesComponent { { name: 'security.policeCoding', translation: 'Polizei Codierung' }, { name: 'technicalEquipment.bicycleShift', translation: 'Schaltung' }, { name: 'technicalEquipment.isEBike', translation: 'E-Bike j/n' }, - { name: 'technicalEquipment.hasLightSystem', translation: 'Lichtanlage j/n' }, - { name: 'technicalEquipment.specialFeatures', translation: 'Besonderheiten' }, + { + name: 'technicalEquipment.hasLightSystem', + translation: 'Lichtanlage j/n', + }, + { + name: 'technicalEquipment.specialFeatures', + translation: 'Besonderheiten', + }, { name: 'stickerBikeNameState', translation: 'Aufkleber Status' }, { name: 'note', translation: 'Aufkleber Kommentar' }, { name: 'taxes.costCenter', translation: 'Steuern Kostenstelle' }, @@ -115,313 +109,42 @@ export class BikesComponent { { name: 'lendingStation.address.zip', translation: '' }, ]; + dataService: any; + tableDataGQLType: string = 'CargoBike'; tableDataGQLCreateInputType: string = 'CargoBikeCreateInput'; tableDataGQLUpdateInputType: string = 'CargoBikeUpdateInput'; - @ViewChild(MatPaginator) paginator: MatPaginator; - @ViewChild(MatSort) sort: MatSort; - - additionalColumnsFront: string[] = ['select']; - additionalColumnsBack: string[] = ['buttons']; - displayedColumns: string[] = []; - loadingRowIds: string[] = []; - - /** data source of the table */ - data: MatTableDataSource = new MatTableDataSource(); - selection = new SelectionModel(true, []); - - reloadingTable = false; - - relockingInterval = null; - relockingDuration = 1000 * 60 * 1; - filter = { includesString: '', onlyUnsaved: false }; - initialFilter = this.filter; - isLoaded = false; - constructor( - private bikesService: BikesService, - private schemaService: SchemaService, - public dialog: MatDialog + private bikesService: BikesService ) {} - ngAfterViewInit() { - this.addColumnPropertiesFromGQLSchemaToColumnInfo(); - this.data.paginator = this.paginator; - this.data.sortingDataAccessor = (item, columnName) => { - if (typeof item[columnName] === 'string') { - return item[columnName].toLocaleLowerCase(); - } - return item[columnName]; - }; - this.data.sort = this.sort; - - this.data.filter = (this.filter as unknown) as string; - this.data.filterPredicate = (data, filter: any) => { - const a = !filter.onlyUnsaved || data.newObject || data.isLockedByMe; - const b = - !filter.includesString || - Object.keys(data).some( - (k) => - data[k] != null && - data[k] - .toString() - .toLowerCase() - .includes(filter.includesString.toLowerCase()) - ); - return a && b; - }; - - this.columnInfo.forEach((column) => - this.displayedColumns.push(column.name) - ); - this.displayedColumns.unshift(this.additionalColumnsFront[0]); - this.displayedColumns.push(this.additionalColumnsBack[0]); - - this.bikesService.loadingRowIds.subscribe((rowIds) => { - this.loadingRowIds = rowIds; - }); - - this.bikesService.bikes.subscribe((newTableDataSource) => { - this.reloadingTable = false; - const tempDataSource = []; - for (const row of newTableDataSource) { - this.isLoaded = true; - const oldRow = this.getRowById(row.id); - /** make sure to not overwrite a row that is being edited */ - if (!oldRow) { - tempDataSource.push(flatten(row)); - } else if (!(oldRow.isLockedByMe && row.isLockedByMe)) { - tempDataSource.push(flatten(row)); - } else if (!!oldRow) { - tempDataSource.push(oldRow); - } - } - for (const oldRow of this.data.data) { - if (oldRow.newObject) { - tempDataSource.unshift(oldRow); - } - } - this.data.data = tempDataSource; - }); - this.bikesService.loadBikes(); - - this.relockingInterval = setInterval(() => { - for (const row of this.data.data) { - if (row.isLockedByMe) { - this.bikesService.relockBike({ id: row.id }); - } - } - }, this.relockingDuration); - } - - ngOnDestroy() { - clearInterval(this.relockingInterval); - } - - addColumnPropertiesFromGQLSchemaToColumnInfo() { - for (const column of this.columnInfo) { - const typeInformation = this.schemaService.getTypeInformation( - this.tableDataGQLType, - column.name - ); - column.type = column.type || typeInformation.type; - } - for (const column of this.columnInfo) { - const typeInformation = this.schemaService.getTypeInformation( - this.tableDataGQLUpdateInputType, - column.name - ); - column.readonly = column.readonly || !typeInformation.isPartOfType; - } - for (const column of this.columnInfo) { - const typeInformation = this.schemaService.getTypeInformation( - this.tableDataGQLCreateInputType, - column.name - ); - column.requiredForCreation = typeInformation.isRequired; - column.acceptedForCreation = typeInformation.isPartOfType; - } - } - - getTranslation(propertyName: string) { - return ( - this.columnInfo.find((column) => column.name === propertyName)?.translation || - propertyName - ); - } - - isStickyColumn(propertyName: string) { - return ( - this.columnInfo.find((column) => column.name === propertyName)?.sticky || - false - ); - } - - isLoading(id: string) { - return this.loadingRowIds.includes(id); - } - - validityChange(row: any, columnName: string, isValid: Event) { - if (!row.FieldsValidity) { - row['FieldsValidity'] = {}; - } - row['FieldsValidity'][columnName] = isValid; - } - - countUnvalidFields(row: any) { - let unvalidFieldsCount = 0; - if (!row.FieldsValidity) { - return 99; - } - for (const prop in row.FieldsValidity) { - if (!row.FieldsValidity[prop]) { - unvalidFieldsCount++; - } - } - return unvalidFieldsCount; - } - - reloadTable() { - this.reloadingTable = true; - this.isLoaded = false; - this.data.data = []; - this.bikesService.loadBikes(); - } - - addNewObject() { - this.paginator.firstPage(); - this.setFilter({ ...this.filter, includesString: '' }); - this.resetSorting(); - this.data.data = [ - { newObject: true, id: this.getNewId() }, - ...this.data.data, - ]; - } - - getNewId(): string { - let id = -1; - while (this.getRowById(id.toString())) { - id--; - } - return id.toString(); - } - - deleteNewObject(row: any) { - this.data.data = this.data.data.filter((element) => row.id !== element.id); + ngOnInit() { + this.dataService = this.bikesService; } create(row: any) { - const newBike = this.schemaService.filterObject( - this.tableDataGQLCreateInputType, - deepen(row) - ); - this.bikesService.createBike({ bike: newBike }); + this.bikesService.createBike({ bike: row }); } - edit(row: CargoBikeResult) { + edit(row: any) { this.bikesService.lockBike({ id: row.id }); } - countUnsavedRows(): number { - let unsavedCount = 0; - for (const row of this.data.data) { - if (row.isLockedByMe || row.newObject) { - unsavedCount++; - } - } - return unsavedCount; + relock(row: any) { + this.bikesService.relockBike({ id: row.id }); } - save(row: CargoBikeResult) { - const deepenRow = this.schemaService.filterObject( - this.tableDataGQLUpdateInputType, - deepen(row) - ); - this.bikesService.updateBike({ bike: deepenRow }); + save(row: any) { + this.bikesService.updateBike({ bike: row }); } - cancel(row: CargoBikeResult) { + cancel(row: any) { this.bikesService.unlockBike({ id: row.id }); } delete(row: any) { this.bikesService.deleteBike({ id: row.id }); } - - openDeleteConfirmationDialog(row: any) { - const dialogRef = this.dialog.open(DeleteConfirmationDialog, { - width: '250px', - }); - - dialogRef.afterClosed().subscribe((result) => { - if (result === true) { - this.delete(row); - } - }); - } - - getRowById(id: string) { - return this.data.data.find((row) => row.id === id); - } - - drop(event: CdkDragDrop) { - moveItemInArray( - this.displayedColumns, - event.previousIndex + 2, - event.currentIndex + 2 - ); // +2 because the first 2 (selection + name) columns are not dragable - } - - /** Whether the number of selected elements matches the total number of rows. */ - isAllSelected() { - const numSelected = this.selection.selected.length; - const numRows = this.data.data.length; - return numSelected === numRows; - } - - /** Selects all rows if they are not all selected; otherwise clear selection. */ - masterToggle() { - this.isAllSelected() - ? this.selection.clear() - : this.data.data.forEach((row) => this.selection.select(row)); - } - - showOnlyUnsavedElements(value: boolean) { - this.filter.onlyUnsaved = value; - this.filter.includesString = ''; - this.applyFilter(); - } - - applyFilter() { - this.data.filter = ({ - ...this.filter, - includesString: this.filter.includesString.trim().toLowerCase(), - } as unknown) as string; - } - - setFilter(filterObject) { - this.filter = filterObject; - this.applyFilter(); - } - - resetSorting() { - this.sort.sort({ id: null, start: 'asc', disableClear: false }); - } -} - -@Component({ - selector: 'delete-confirmation-dialog', - templateUrl: 'delete-confirmation-dialog.html', -}) -export class DeleteConfirmationDialog { - constructor(public dialogRef: MatDialogRef) {} - - onConfirmClick(): void { - this.dialogRef.close(true); - } - onNoClick(): void { - this.dialogRef.close(false); - } } diff --git a/src/app/services/bikes.service.ts b/src/app/services/bikes.service.ts index 83d3a89..d5a532d 100644 --- a/src/app/services/bikes.service.ts +++ b/src/app/services/bikes.service.ts @@ -29,9 +29,10 @@ export type CargoBikeResult = DeepExtractTypeSkipArrays< providedIn: 'root', }) export class BikesService { - bikes: BehaviorSubject = new BehaviorSubject([]); + /** CargoBikes Array */ + tableData: BehaviorSubject = new BehaviorSubject([]); loadingRowIds: BehaviorSubject = new BehaviorSubject([]); - bike: BehaviorSubject = new BehaviorSubject([]); + pageData: BehaviorSubject = new BehaviorSubject([]); loadingBike: BehaviorSubject = new BehaviorSubject(false); @@ -58,20 +59,20 @@ export class BikesService { }); } - loadBikes() { + loadTableData() { this.getCargoBikesGQL.fetch().subscribe((result) => { - this.bikes.next(result.data.cargoBikes); + this.tableData.next(result.data.cargoBikes); }); } - loadCargoBike(variables: GetCargoBikeByIdQueryVariables) { - this.bike.next(null); + loadPageData(variables: GetCargoBikeByIdQueryVariables) { + this.pageData.next(null); this.loadingBike.next(true); this.getCargoBikeByIdGQL .fetch(variables) .subscribe((result) => { - this.bike.next(result.data.cargoBikeById); + this.pageData.next(result.data.cargoBikeById); }) .add(() => { this.loadingBike.next(false); @@ -84,8 +85,8 @@ export class BikesService { .fetch(variables) .subscribe((result) => { const newBike = result.data.cargoBikeById; - this.bikes.next( - this.bikes.value.map((bike) => + this.tableData.next( + this.tableData.value.map((bike) => newBike.id === bike.id ? newBike : bike ) ); @@ -100,8 +101,8 @@ export class BikesService { .mutate(variables) .subscribe((result) => { const newBike = result.data.createCargoBike; - this.bikes.next( - [newBike, ...this.bikes.value] + this.tableData.next( + [newBike, ...this.tableData.value] ); }) } @@ -112,8 +113,8 @@ export class BikesService { .mutate(variables) .subscribe((result) => { const newBike = result.data.updateCargoBike; - this.bikes.next( - this.bikes.value.map((bike) => + this.tableData.next( + this.tableData.value.map((bike) => newBike.id === bike.id ? newBike : bike ) ); @@ -129,8 +130,8 @@ export class BikesService { .mutate(variables) .subscribe((result) => { const lockedBike = result.data.lockCargoBike; - this.bikes.next( - this.bikes.value.map((bike) => + this.tableData.next( + this.tableData.value.map((bike) => lockedBike.id === bike.id ? lockedBike : bike ) ); @@ -146,8 +147,8 @@ export class BikesService { .mutate(variables) .subscribe((result) => { const unlockedBike = result.data.unlockCargoBike; - this.bikes.next( - this.bikes.value.map((bike) => + this.tableData.next( + this.tableData.value.map((bike) => unlockedBike.id === bike.id ? unlockedBike : bike ) ); @@ -163,7 +164,7 @@ export class BikesService { .mutate(variables) .subscribe((result) => { if(result.data.deleteCargoBike) { - this.bikes.next([...this.bikes.value].filter(bike => bike.id !== variables.id)); + this.tableData.next([...this.tableData.value].filter(bike => bike.id !== variables.id)); } }) .add(() => {