diff --git a/src/app/app-routing.module.ts b/src/app/app-routing.module.ts index bca7386..aab32fe 100644 --- a/src/app/app-routing.module.ts +++ b/src/app/app-routing.module.ts @@ -43,7 +43,7 @@ const routes: Routes = [ { path: 'table/equipmentTypes', component: EquipmentTypesComponent, canActivate: [AuthGuard] }, { path: 'table/engagementTypes', component: EngagementTypesComponent, canActivate: [AuthGuard] }, { path: 'table/engagements', component: EngagementsComponent, canActivate: [AuthGuard] }, - { path: 'table/equipment', component: EquipmentComponent, canActivate: [AuthGuard] }, + { path: 'table/equipments', component: EquipmentComponent, canActivate: [AuthGuard] }, { path: 'table/timeFrames', component: TimeFramesComponent, canActivate: [AuthGuard] }, { path: 'table/persons', component: PersonsComponent, canActivate: [AuthGuard] }, { path: 'table/contactInformation', component: ContactInformationComponent, canActivate: [AuthGuard] }, diff --git a/src/app/app.component.html b/src/app/app.component.html index 1eaaeec..7331aed 100644 --- a/src/app/app.component.html +++ b/src/app/app.component.html @@ -31,16 +31,23 @@ - - + - + + + {{ link.iconName }} + {{ link.displayName }} + + - + diff --git a/src/app/app.component.scss b/src/app/app.component.scss index 8eccf92..23e55dd 100644 --- a/src/app/app.component.scss +++ b/src/app/app.component.scss @@ -1,4 +1,5 @@ @import '../theme'; +$primary-color: mat-color($light-primary, 300); #page-wrapper { display: flex; @@ -20,8 +21,15 @@ padding-top:0px } +.primary-color { + color: $primary-color !important; +} + mat-nav-list .nav-profile-list-item { height:200px; - background: mat-color($light-primary, 300);; - //color:white; + background: $primary-color; +} + +.nav-list-icon { + margin-right: 1em; } \ No newline at end of file diff --git a/src/app/app.component.ts b/src/app/app.component.ts index e12be28..a84bf6c 100644 --- a/src/app/app.component.ts +++ b/src/app/app.component.ts @@ -5,7 +5,7 @@ import { Router } from '@angular/router'; import { ViewChild } from '@angular/core'; import { MatSidenav } from '@angular/material/sidenav'; import { NavService } from './components/menu-list-item/nav.service'; -import { NavItem } from './components/menu-list-item/nav-item'; +import { tableLinks } from 'src/app/tableLinks'; @Component({ selector: 'app-root', @@ -20,29 +20,7 @@ export class AppComponent { @ViewChild('sidenav') public sideNav:MatSidenav; @ViewChild('appDrawer') appDrawer: ElementRef; - - navItems: NavItem[] = [ - { - displayName: 'Tabellen', - iconName: 'recent_actors', - route: 'tableOverview', - children: [ - { - displayName: 'Lastenräder', - iconName: 'directions_bike', - route: 'table/bikes' - }, - { - displayName: 'Beteiligte', - iconName: 'group', - route: 'table/participants' - }, - { - displayName: 'Standorte', - iconName: 'place', - route: 'table/lendingStations' - }] - }]; + tableLinks = tableLinks constructor( diff --git a/src/app/app.module.ts b/src/app/app.module.ts index 5c273c0..fa0b764 100644 --- a/src/app/app.module.ts +++ b/src/app/app.module.ts @@ -44,7 +44,6 @@ import { ParticipantsComponent } from './pages/tables/participants/participants. import { LendingStationsComponent } from './pages/tables/lending-stations/lending-stations.component'; import { TableOverviewComponent } from './pages/table-overview/table-overview.component'; import { CellComponent } from './components/tableComponents/cell/cell.component'; -import { MenuListItemComponent } from './components/menu-list-item/menu-list-item.component'; import { SidenavProfileComponent } from './components/sidenav-profile/sidenav-profile.component'; import { NavService } from './components/menu-list-item/nav.service'; import { TokenInterceptor } from './helper/token.interceptor'; @@ -91,7 +90,6 @@ import { FilterRowComponent } from './components/tableComponents/filter-row/filt @NgModule({ declarations: [ AppComponent, - MenuListItemComponent, SidenavProfileComponent, LoginComponent, BikesComponent, diff --git a/src/app/components/menu-list-item/menu-list-item.component.html b/src/app/components/menu-list-item/menu-list-item.component.html deleted file mode 100644 index 34abf60..0000000 --- a/src/app/components/menu-list-item/menu-list-item.component.html +++ /dev/null @@ -1,15 +0,0 @@ - - - {{item.iconName}} - {{item.displayName}} - - - - - expand_more - - - -
- -
diff --git a/src/app/components/menu-list-item/menu-list-item.component.scss b/src/app/components/menu-list-item/menu-list-item.component.scss deleted file mode 100644 index 2629bc7..0000000 --- a/src/app/components/menu-list-item/menu-list-item.component.scss +++ /dev/null @@ -1,33 +0,0 @@ -@import '../../../theme'; - -:host { - display: flex; - flex-direction: column; - outline: none; - width: 100%; - - .mat-list-item.active { - //background-color: mat-color($dark-primary, 50); - } - &:hover, - &:focus { - >.mat-list-item:not(.expanded) { - //background-color: mat-color($dark-primary, 100) !important; - } - } -} - -.mat-list-item { - padding: 8px 0; - display: flex; - width: auto; - - .routeIcon { - margin-right: 40px; - } -} - -span { - display: inline-flex; - vertical-align: middle; -} \ No newline at end of file diff --git a/src/app/components/menu-list-item/menu-list-item.component.ts b/src/app/components/menu-list-item/menu-list-item.component.ts deleted file mode 100644 index 8974e85..0000000 --- a/src/app/components/menu-list-item/menu-list-item.component.ts +++ /dev/null @@ -1,62 +0,0 @@ -import {Component, HostBinding, Input, OnInit} from '@angular/core'; -import {NavItem} from './nav-item'; -import {Router} from '@angular/router'; -import {NavService} from './nav.service'; -import {animate, state, style, transition, trigger} from '@angular/animations'; - -@Component({ - selector: 'app-menu-list-item', - templateUrl: './menu-list-item.component.html', - styleUrls: ['./menu-list-item.component.scss'], - animations: [ - trigger('indicatorRotate', [ - state('collapsed', style({transform: 'rotate(0deg)'})), - state('expanded', style({transform: 'rotate(180deg)'})), - transition('expanded <=> collapsed', - animate('225ms cubic-bezier(0.4,0.0,0.2,1)') - ), - ]) - ] -}) -export class MenuListItemComponent implements OnInit { - expanded: boolean = false; - @HostBinding('attr.aria-expanded') ariaExpanded = this.expanded; - @Input() item: NavItem; - @Input() depth: number; - - constructor(public navService: NavService, - public router: Router, - ) { - if (this.depth === undefined) { - this.depth = 0; - } - } - - ngOnInit() { - this.navService.currentUrl.subscribe((url: string) => { - if (this.item.route && url) { - // console.log(`Checking '/${this.item.route}' against '${url}'`); - this.expanded = url.indexOf(`/${this.item.route}`) === 0; - this.ariaExpanded = this.expanded; - // console.log(`${this.item.route} is expanded: ${this.expanded}`); - } - }); - } - - onItemSelected(item: NavItem) { - /*if (!item.children || !item.children.length) { - this.router.navigate([item.route]); - this.navService.closeNav(); - } - if (item.children && item.children.length) { - this.expanded = !this.expanded; - }*/ - - this.router.navigate([item.route]); - this.navService.closeNav(); - } - - expand(item: NavItem) { - this.expanded = !this.expanded; - } -} diff --git a/src/app/components/menu-list-item/nav-item.ts b/src/app/components/menu-list-item/nav-item.ts deleted file mode 100644 index c19ddfd..0000000 --- a/src/app/components/menu-list-item/nav-item.ts +++ /dev/null @@ -1,8 +0,0 @@ -export interface NavItem { - displayName: string; - disabled?: boolean; - iconName: string; - route?: string; - children?: NavItem[]; - } - \ No newline at end of file diff --git a/src/app/components/reference-table/reference-table.component.html b/src/app/components/reference-table/reference-table.component.html index c335d46..3d87cea 100644 --- a/src/app/components/reference-table/reference-table.component.html +++ b/src/app/components/reference-table/reference-table.component.html @@ -1,5 +1,15 @@
+
- - Tabelle filtern - - - + + + + + + + + + + + - + +
diff --git a/src/app/components/reference-table/reference-table.component.ts b/src/app/components/reference-table/reference-table.component.ts index d520807..641047c 100644 --- a/src/app/components/reference-table/reference-table.component.ts +++ b/src/app/components/reference-table/reference-table.component.ts @@ -1,5 +1,6 @@ import { Component, + ElementRef, EventEmitter, Input, Output, @@ -11,7 +12,7 @@ import { SchemaService } from 'src/app/services/schema.service'; import { MatTableDataSource } from '@angular/material/table'; import { MatPaginator } from '@angular/material/paginator'; import { MatSort } from '@angular/material/sort'; -import { FormControl, FormGroup } from '@angular/forms'; +import { customTableFilterFunction } from 'src/app/helperFunctions/customTableFilterFunction'; import { Subject } from 'rxjs/internal/Subject'; import { debounceTime } from 'rxjs/internal/operators/debounceTime'; @@ -28,6 +29,7 @@ export class ReferenceTableComponent { translation: string; sticky?: boolean; type?: string; + list?: boolean; //whether the type is a list link?: (row: any) => string; }[] = []; @@ -77,6 +79,9 @@ export class ReferenceTableComponent { @ViewChild(MatPaginator) paginator: MatPaginator; @ViewChild(MatSort) sort: MatSort; + @ViewChild('filterRow', { read: ElementRef }) filterRow: ElementRef; + @ViewChild('headerRow', { read: ElementRef }) headerRow: ElementRef; + displayedColumns: string[] = []; /** data source of the table */ @@ -86,8 +91,9 @@ export class ReferenceTableComponent { reloadingTable = false; - tableFilterString = ''; - filterStringChanged: Subject = new Subject(); + displayedFilterColumns = []; + filters: any = {}; + filterChanged: Subject = new Subject(); constructor(private schemaService: SchemaService) {} @@ -99,16 +105,26 @@ export class ReferenceTableComponent { if (this.editableReferences) { this.displayedColumns.push('buttons'); } + this.displayedFilterColumns = this.displayedColumns.map( + (columnName) => columnName + '.filter' + ); - this.filterStringChanged - .pipe(debounceTime(400)) - .subscribe(() => this.applyTableFilter()); + this.dataSource.filterPredicate = customTableFilterFunction; + this.resetFilters(); } ngAfterViewInit() { + this.setTableFilterRowHeight(); + this.dataSource.paginator = this.paginator; this.dataSource.sort = this.sort; + this.dataSource.filter = (this.filters as unknown) as string; + + this.filterChanged.pipe(debounceTime(400)).subscribe(() => { + this.applyFilters(); + }); + this.dataSource.sortingDataAccessor = (item, columnName) => { if (typeof item[columnName] === 'string') { return item[columnName].toLocaleLowerCase(); @@ -152,9 +168,9 @@ export class ReferenceTableComponent { (element) => element.id === row.id ); if (index !== -1) { - this.dataSource.data.splice(index, 1); - this.dataSource.data = this.dataSource.data; //needed to trigger update lol - this.onReferenceChange(); + this.dataSource.data.splice(index, 1); + this.dataSource.data = this.dataSource.data; //needed to trigger update lol + this.onReferenceChange(); } // show it again in the selection @@ -166,8 +182,7 @@ export class ReferenceTableComponent { addReference(row: any) { this.dataSource.data = [flatten(row), ...this.dataSource.data]; this.idsOfObjectsToHide = [row.id, ...this.idsOfObjectsToHide]; - this.tableFilterString = ''; - this.applyTableFilter(); + this.resetFilters(); this.onReferenceChange(); } @@ -175,14 +190,6 @@ export class ReferenceTableComponent { return this.dataSource.data.find((row) => row.id === id); } - newFilterStringValue(): void { - this.filterStringChanged.next(this.tableFilterString); - } - - applyTableFilter() { - this.dataSource.filter = this.tableFilterString; - } - onReferenceChange() { const ids = []; for (const element of this.data) { @@ -190,4 +197,58 @@ export class ReferenceTableComponent { } this.referenceIds.emit(ids); } + + /** Filter functions **************************************************************/ + newFilterValue(): void { + this.filterChanged.next(this.filters); + } + + applyFilters(): void { + this.dataSource.filter = (this.filters as unknown) as string; + } + + columnFiltersAreSet(): boolean { + for (const filterObject of Object.keys(this.filters.columnFilters)) { + if (this.filters.columnFilters[filterObject].isSet) { + return true; + } + } + return false; + } + + resetColumnFilters() { + this.filters['columnFilters'] = []; + for (const column of this.columnInfo) { + this.filters.columnFilters[column.dataPath] = { + isSet: false, + value: null, + minValue: {}, + maxValue: {}, + fromValue: {}, + toValue: {}, + type: column.type, + list: column.list, + options: {}, + }; + } + this.setTableFilterRowHeight(); + this.applyFilters(); + } + + resetFilters() { + this.filters = []; + this.resetColumnFilters(); + } + + setTableFilterRowHeight() { + setTimeout(() => { + const filterRowHeight = this.filterRow.nativeElement.clientHeight; + const headerRowCells = Array.from( + this.headerRow.nativeElement.children as HTMLCollectionOf + ); + for (let i = 0; i < headerRowCells.length; i++) { + headerRowCells[i].style.top = filterRowHeight + 'px'; + } + }); + } } diff --git a/src/app/components/table/table.component.html b/src/app/components/table/table.component.html index b7639a8..c521f89 100644 --- a/src/app/components/table/table.component.html +++ b/src/app/components/table/table.component.html @@ -48,11 +48,23 @@ > nur ungespeicherte Elemente anzeigen +
+ +
@@ -73,12 +86,29 @@ [sticky]="isStickyColumn(column.dataPath)" > + + + + + + + - -
+ @@ -254,7 +284,12 @@ > deleteLöschen - @@ -327,16 +362,23 @@
- - - -
+ diff --git a/src/app/components/table/table.component.scss b/src/app/components/table/table.component.scss index fd8874b..c208199 100644 --- a/src/app/components/table/table.component.scss +++ b/src/app/components/table/table.component.scss @@ -26,8 +26,17 @@ margin-right: 0.5em; } } + .idle-overlay { + filter: blur(5px); + } + .idle-spinner { + position: absolute; + top: 50%; + left: 50%; + transform: translate(-50%, -50%); + z-index: 999; + } .table-container { - flex: 1; width: auto; margin-left: 0.5em; margin-right: 0.5em; diff --git a/src/app/components/table/table.component.ts b/src/app/components/table/table.component.ts index f861c01..7ec5b1e 100644 --- a/src/app/components/table/table.component.ts +++ b/src/app/components/table/table.component.ts @@ -7,6 +7,7 @@ import { ViewChild, AfterViewInit, ChangeDetectorRef, + ElementRef, } from '@angular/core'; import { CdkDragDrop, moveItemInArray } from '@angular/cdk/drag-drop'; import { flatten } from 'src/app/helperFunctions/flattenObject'; @@ -62,9 +63,15 @@ export class TableComponent implements AfterViewInit { @Input() tableDataGQLUpdateInputType: string; + @Input() + copyableRows = false; + @ViewChild(MatPaginator) paginator: MatPaginator; @ViewChild(MatSort) sort: MatSort; + @ViewChild('filterRow', { read: ElementRef }) filterRow: ElementRef; + @ViewChild('headerRow', { read: ElementRef }) headerRow: ElementRef; + additionalColumnsFront: string[] = []; additionalColumnsBack: string[] = ['buttons']; displayedColumns: string[] = []; @@ -88,10 +95,12 @@ export class TableComponent implements AfterViewInit { filterChanged: Subject = new Subject(); isLoaded = false; + isProcessing = false; @Output() createEvent = new EventEmitter(); @Output() lockEvent = new EventEmitter(); @Output() saveEvent = new EventEmitter(); + @Output() copyEvent = new EventEmitter(); @Output() cancelEvent = new EventEmitter(); @Output() deleteEvent = new EventEmitter(); @@ -108,20 +117,20 @@ export class TableComponent implements AfterViewInit { ngOnInit() { this.addColumnPropertiesFromGQLSchemaToColumnInfo(); - console.log(this.columnInfo); this.columnInfo.forEach((column) => this.displayedColumns.push(column.dataPath) ); + this.displayedColumns.unshift(...this.additionalColumnsFront); + this.displayedColumns.push(...this.additionalColumnsBack); this.displayedFilterColumns = this.displayedColumns.map( (columnName) => columnName + '.filter' ); - this.displayedColumns.unshift(...this.additionalColumnsFront); - this.displayedColumns.push(...this.additionalColumnsBack); this.resetFilters(); } ngAfterViewInit(): void { + this.setTableFilterRowHeight(); this.data.paginator = this.paginator; this.data.sortingDataAccessor = (item, columnName) => { if (typeof item[columnName] === 'string') { @@ -147,13 +156,19 @@ export class TableComponent implements AfterViewInit { }); this.dataService.tableData.subscribe((newTableDataSource) => { - this.reloadingTable = false; const tempDataSource = []; if (newTableDataSource === null) { return; } + this.reloadingTable = false; this.isLoaded = true; for (const row of newTableDataSource) { + if (row.newObject) { + // its a copied object + row.id = this.getNewId(); + tempDataSource.push(flatten(row)); + continue; + } const oldRow = this.getRowById(row.id); /** make sure to not overwrite a row that is being edited */ if (!oldRow) { @@ -335,6 +350,14 @@ export class TableComponent implements AfterViewInit { this.saveEvent.emit(deepenRow); } + copy(row: any) { + const deepenRow = this.schemaService.filterObject( + this.tableDataGQLUpdateInputType, + deepen(row) + ); + this.copyEvent.emit(deepenRow); + } + cancel(row: any) { this.cancelEvent.emit(row); } @@ -419,32 +442,60 @@ export class TableComponent implements AfterViewInit { } newFilterValue(): void { - console.log(this.filters); this.filterChanged.next(this.filters); } applyFilters(): void { - this.isLoaded = false; + this.isProcessing = true; setTimeout(() => { this.data.filter = (this.filters as unknown) as string; - this.isLoaded = true; + this.isProcessing = false; }); } - resetFilters() { - this.filters = []; + columnFiltersAreSet(): boolean { + for (const filterObject of Object.keys(this.filters.columnFilters)) { + if (this.filters.columnFilters[filterObject].isSet) { + return true; + } + } + return false; + } + + resetColumnFilters() { this.filters['columnFilters'] = []; for (const column of this.columnInfo) { this.filters.columnFilters[column.dataPath] = { + isSet: false, value: null, minValue: {}, maxValue: {}, fromValue: {}, toValue: {}, type: column.type, + list: column.list, options: {}, }; } + this.setTableFilterRowHeight(); + this.applyFilters(); + } + + resetFilters() { + this.filters = []; + this.resetColumnFilters(); + } + + setTableFilterRowHeight() { + setTimeout(() => { + const filterRowHeight = this.filterRow.nativeElement.clientHeight; + const headerRowCells = Array.from( + this.headerRow.nativeElement.children as HTMLCollectionOf + ); + for (let i = 0; i < headerRowCells.length; i++) { + headerRowCells[i].style.top = filterRowHeight + 'px'; + } + }); } resetSorting() { diff --git a/src/app/components/tableComponents/date-range-cell/date-range-cell.component.html b/src/app/components/tableComponents/date-range-cell/date-range-cell.component.html index e4781dc..6102ef1 100644 --- a/src/app/components/tableComponents/date-range-cell/date-range-cell.component.html +++ b/src/app/components/tableComponents/date-range-cell/date-range-cell.component.html @@ -31,7 +31,7 @@ + + + + + \ No newline at end of file diff --git a/src/app/graphqlOperations/bike.graphql b/src/app/graphqlOperations/bike.graphql index d96a3cf..e5af105 100644 --- a/src/app/graphqlOperations/bike.graphql +++ b/src/app/graphqlOperations/bike.graphql @@ -10,6 +10,12 @@ query GetCargoBikeById($id: ID!) { } } +query copyCargoBikeById($id: ID!) { + copyCargoBikeById(id: $id) { + ...CargoBikeFieldsForTable + } +} + query ReloadCargoBikeById($id: ID!) { cargoBikeById(id: $id) { ...CargoBikeFieldsForTable diff --git a/src/app/helperFunctions/customTableFilterFunction.ts b/src/app/helperFunctions/customTableFilterFunction.ts index 5053515..8cd429c 100644 --- a/src/app/helperFunctions/customTableFilterFunction.ts +++ b/src/app/helperFunctions/customTableFilterFunction.ts @@ -1,159 +1,180 @@ export function customTableFilterFunction(data: any, filter: any) { if (data.newObject) { - return true; // always show new objects + return true; // always show newly created objects } if (filter.onlyUnsaved && !data.isLockedByMe) { return false; } for (const filterElementName of Object.keys(filter.columnFilters)) { const filterElement = filter.columnFilters[filterElementName]; - // String Filter - if (filterElement.value) { - if (filterElement.type === 'String' || filterElement.type === 'Id') { - let searchString = filterElement.value.trim(); - let dataElement = data[filterElementName]?.trim(); - if (dataElement == null) { + // List Filter - ignore types if column is list + if (filterElement.list && filterElement.values?.length > 0) { + let dataElement: Array = data[filterElementName]; + if (dataElement.length !== filterElement.values.length) { + return false; + } + for (const element of filterElement.values) { + if (!dataElement.includes(element)) { return false; } - if (!filterElement.options.caseSensitive) { - searchString = searchString.toLowerCase(); - dataElement = dataElement.toLowerCase(); + } + } else { + // String Filter + if (filterElement.value) { + if (filterElement.type === 'String' || filterElement.type === 'Id') { + let searchString = filterElement.value.trim(); + let dataElement = data[filterElementName]?.trim(); + if (dataElement == null) { + return false; + } + if (!filterElement.options.caseSensitive) { + searchString = searchString.toLowerCase(); + dataElement = dataElement.toLowerCase(); + } + if ( + (filterElement.options.exact && dataElement !== searchString) || + !dataElement.includes(searchString) + ) { + return false; + } } + } + // Number Filter + if (filterElement.min != null || filterElement.max != null) { if ( - (filterElement.options.exact && dataElement !== searchString) || - !dataElement.includes(searchString) + filterElement.type === 'Float' || + filterElement.type === 'Int' || + filterElement.type === 'Money' ) { - return false; + let dataElement = data[filterElementName]; + if (dataElement == null) { + return false; + } + if (filterElement.min != null && dataElement < filterElement.min) { + return false; + } + if (filterElement.max != null && dataElement > filterElement.max) { + return false; + } } } - } - // Number Filter - if (filterElement.min != null || filterElement.max != null) { + // NumberRange Filter + if (filterElement.type === 'NumRange') { + if ( + filterElement.minValue.min != null || + filterElement.minValue.max != null || + filterElement.maxValue.min != null || + filterElement.maxValue.max != null + ) { + let dataElementMin = data[filterElementName + '.min']; + let dataElementMax = data[filterElementName + '.max']; + if (dataElementMin == null && dataElementMax == null) { + return false; + } + if ( + filterElement.minValue.min != null && + dataElementMin < filterElement.minValue.min + ) { + return false; + } + if ( + filterElement.minValue.max != null && + dataElementMin > filterElement.minValue.max + ) { + return false; + } + if ( + filterElement.maxValue.min != null && + dataElementMax < filterElement.maxValue.min + ) { + return false; + } + if ( + filterElement.maxValue.max != null && + dataElementMax > filterElement.maxValue.max + ) { + return false; + } + } + } + // Date Filter if ( - filterElement.type === 'Float' || - filterElement.type === 'Int' || - filterElement.type === 'Money' + filterElement.type === 'Date' && + (filterElement.from != null || filterElement.to != null) ) { let dataElement = data[filterElementName]; if (dataElement == null) { return false; } - if (filterElement.min != null && dataElement < filterElement.min) { + if ( + filterElement.from != null && + new Date(dataElement) < new Date(filterElement.from) + ) { return false; } - if (filterElement.max != null && dataElement > filterElement.max) { + if ( + filterElement.to != null && + new Date(dataElement) > new Date(filterElement.to) + ) { return false; } } - } - // NumberRange Filter - if (filterElement.type === 'NumRange') { + // DateRange Filter if ( - filterElement.minValue.min != null || - filterElement.minValue.max != null || - filterElement.maxValue.min != null || - filterElement.maxValue.max != null + filterElement.type === 'DateRange' && + (filterElement.fromValue.from != null || + filterElement.fromValue.to != null || + filterElement.toValue.from != null || + filterElement.toValue.to != null) ) { - let dataElementMin = data[filterElementName + '.min']; - let dataElementMax = data[filterElementName + '.max']; - if (dataElementMin == null && dataElementMax == null) { + let dataElementFrom = data[filterElementName + '.from']; + let dataElementTo = data[filterElementName + '.to']; + if (!dataElementFrom && !dataElementTo) { return false; } if ( - filterElement.minValue.min != null && - dataElementMin < filterElement.minValue.min + filterElement.fromValue.from != null && + (!dataElementFrom || + new Date(dataElementFrom) < new Date(filterElement.fromValue.from)) ) { return false; } if ( - filterElement.minValue.max != null && - dataElementMin > filterElement.minValue.max + filterElement.fromValue.to != null && + new Date(dataElementFrom) > new Date(filterElement.fromValue.to) ) { return false; } if ( - filterElement.maxValue.min != null && - dataElementMax < filterElement.maxValue.min + filterElement.toValue.from != null && + new Date(dataElementTo) < new Date(filterElement.toValue.from) ) { return false; } if ( - filterElement.maxValue.max != null && - dataElementMax > filterElement.maxValue.max + filterElement.toValue.to != null && + (!dataElementTo || + new Date(dataElementTo) > new Date(filterElement.toValue.to)) ) { return false; } } - } - // Date Filter - if ( - filterElement.type === 'Date' && - (filterElement.from != null || filterElement.to != null) - ) { - let dataElement = data[filterElementName]; - if (dataElement == null) { - return false; - } - if ( - filterElement.from != null && - new Date(dataElement) < new Date(filterElement.from) - ) { - return false; - } + // Enum Filter if ( - filterElement.to != null && - new Date(dataElement) > new Date(filterElement.to) + filterElement.type.startsWith('Enum') && + filterElement.value != null && + filterElement.value !== data[filterElementName] ) { return false; } - } - // DateRange Filter - if ( - filterElement.type === 'DateRange' && - (filterElement.fromValue.from != null || - filterElement.fromValue.to != null || - filterElement.toValue.from != null || - filterElement.toValue.to != null) - ) { - let dataElementFrom = data[filterElementName + '.from']; - let dataElementTo = data[filterElementName + '.to']; - if (!dataElementFrom && !dataElementTo) { - return false; - } + // Boolean Filter if ( - filterElement.fromValue.from != null && - (!dataElementFrom || - new Date(dataElementFrom) < new Date(filterElement.fromValue.from)) + filterElement.type === 'Boolean' && + ((filterElement.value === 'Ja' && !data[filterElementName]) || + (filterElement.value === 'Nein' && data[filterElementName])) ) { return false; } - if ( - filterElement.fromValue.to != null && - new Date(dataElementFrom) > new Date(filterElement.fromValue.to) - ) { - return false; - } - if ( - filterElement.toValue.from != null && - new Date(dataElementTo) < new Date(filterElement.toValue.from) - ) { - return false; - } - if ( - filterElement.toValue.to != null && - (!dataElementTo || - new Date(dataElementTo) > new Date(filterElement.toValue.to)) - ) { - return false; - } - } - // Enum Filter - if (filterElement.type.startsWith('Enum') && filterElement.value != null && filterElement.value !== data[filterElementName]) { - return false; - } - // Boolean Filter - if (filterElement.type === "Boolean" && ((filterElement.value === "Ja" && !data[filterElementName])||(filterElement.value === "Nein" && data[filterElementName]))) { - return false; } } /*const b = diff --git a/src/app/pages/dataPages/bike/bike.component.ts b/src/app/pages/dataPages/bike/bike.component.ts index edbd52a..4685a83 100644 --- a/src/app/pages/dataPages/bike/bike.component.ts +++ b/src/app/pages/dataPages/bike/bike.component.ts @@ -250,7 +250,7 @@ export class BikeComponent implements OnInit { }, { type: 'ReferenceTable', - title: 'Equipment', + title: 'Equipments', dataPath: 'equipment', dataService: null, columnInfo: [ @@ -269,7 +269,7 @@ export class BikeComponent implements OnInit { : '') ); }, - linkToTable: (element) => '/table/equipment', + linkToTable: (element) => '/table/equipments', linkToTableParams: (bike) => { return { filter: bike.name }; }, diff --git a/src/app/pages/table-overview/table-overview.component.html b/src/app/pages/table-overview/table-overview.component.html index 7f2da50..d37fd6b 100644 --- a/src/app/pages/table-overview/table-overview.component.html +++ b/src/app/pages/table-overview/table-overview.component.html @@ -1,27 +1,21 @@ - -
-
- - Lastenräder - Aktive - Standorte - Personen - -
- - Equipmenttypen - Equipment - Zeitscheiben - Organisation - Kontaktinformation - Anbieter - Engagements - Engagementtypen - Lastenradevents - Lastenradeventtypen - Workshops - Workshoptypen - +
+ +
- - diff --git a/src/app/pages/table-overview/table-overview.component.scss b/src/app/pages/table-overview/table-overview.component.scss index 76d2043..6c770a5 100644 --- a/src/app/pages/table-overview/table-overview.component.scss +++ b/src/app/pages/table-overview/table-overview.component.scss @@ -1,12 +1,34 @@ -mat-grid-tile { - cursor: pointer; - background: grey; -} - -.grid-list-spacer { - margin: 15px; -} - -.big-list { - margin-bottom: 30px; +.page-wrapper { + display: flex; + height: 100%; + .link-box { + flex-basis: 50%; + height: 100%; + overflow: auto; + .text-wrapper { + padding: 1.5em 3em; + } + } + .banner-box { + flex-basis: 50%; + overflow: hidden; + display: flex; + align-items: center; + justify-content: center; + text-align: center; + .center-text { + margin: 0.5em; + position: absolute; + word-break: break-word; + color: white; + z-index: 5; + } + .banner-img-box { + height: 100%; + filter: blur(2px); + img { + height: 100%; + } + } + } } diff --git a/src/app/pages/table-overview/table-overview.component.ts b/src/app/pages/table-overview/table-overview.component.ts index b494e94..1617649 100644 --- a/src/app/pages/table-overview/table-overview.component.ts +++ b/src/app/pages/table-overview/table-overview.component.ts @@ -1,4 +1,6 @@ import { Component, OnInit } from '@angular/core'; +import { AuthService } from 'src/app/services/auth.service'; +import { tableLinks } from 'src/app/tableLinks'; @Component({ selector: 'app-table-overview', @@ -7,9 +9,17 @@ import { Component, OnInit } from '@angular/core'; }) export class TableOverviewComponent implements OnInit { - constructor() { } + username = "fLotte Nutzer" + tableLinks = tableLinks; + + constructor(private auth: AuthService) { + } ngOnInit(): void { + this.auth.currentUser.subscribe(user => { + const name = user?.user?.name; + this.username = name || this.username + }); } } diff --git a/src/app/pages/tables/bike-events/bike-events.component.ts b/src/app/pages/tables/bike-events/bike-events.component.ts index 9ace369..a1b7d3c 100644 --- a/src/app/pages/tables/bike-events/bike-events.component.ts +++ b/src/app/pages/tables/bike-events/bike-events.component.ts @@ -118,7 +118,7 @@ export class BikeEventsComponent implements OnInit { tableDataGQLCreateInputType: string = 'BikeEventCreateInput'; tableDataGQLUpdateInputType: string = 'BikeEventUpdateInput'; - headline = 'Lastenradevent'; + headline = 'Lastenradevents'; headlineIconName = 'event'; loadingRowIds: string[] = []; diff --git a/src/app/pages/tables/bikes/bikes.component.html b/src/app/pages/tables/bikes/bikes.component.html index 8c18b6a..4f26be9 100644 --- a/src/app/pages/tables/bikes/bikes.component.html +++ b/src/app/pages/tables/bikes/bikes.component.html @@ -2,6 +2,7 @@ [headline]="headline" [headlineIconName]="headlineIconName" [columnInfo]="columnInfo" + [copyableRows]="copyableRows" [dataService]="dataService" [tableDataGQLType]="tableDataGQLType" [tableDataGQLCreateInputType]="tableDataGQLCreateInputType" @@ -9,6 +10,7 @@ (createEvent)="create($event)" (lockEvent)="lock($event)" (saveEvent)="save($event)" + (copyEvent)="copy($event)" (cancelEvent)="cancel($event)" (deleteEvent)="delete($event)" > diff --git a/src/app/pages/tables/bikes/bikes.component.ts b/src/app/pages/tables/bikes/bikes.component.ts index 9b6d8e2..1dd25a5 100644 --- a/src/app/pages/tables/bikes/bikes.component.ts +++ b/src/app/pages/tables/bikes/bikes.component.ts @@ -132,6 +132,7 @@ export class BikesComponent implements OnInit { headline = 'Lastenräder'; headlineIconName = 'directions_bike'; + copyableRows = true; loadingRowIds: string[] = []; constructor(private bikesService: BikesService) {} @@ -152,6 +153,10 @@ export class BikesComponent implements OnInit { this.bikesService.updateBike({ bike: row }); } + copy(row: any) { + this.bikesService.copyBikeById({ id: row.id }); + } + cancel(row: any) { this.bikesService.unlockBike({ id: row.id }); } diff --git a/src/app/pages/tables/equipment/equipment.component.ts b/src/app/pages/tables/equipment/equipment.component.ts index fb202e6..2022d90 100644 --- a/src/app/pages/tables/equipment/equipment.component.ts +++ b/src/app/pages/tables/equipment/equipment.component.ts @@ -7,7 +7,7 @@ import { EquipmentService } from 'src/app/services/equipment.service'; styleUrls: ['./equipment.component.scss'], }) export class EquipmentComponent implements OnInit { - headline = 'Equipment'; + headline = 'Equipments'; headlineIconName = 'battery_full'; columnInfo = [ diff --git a/src/app/pages/tables/organisations/organisations.component.ts b/src/app/pages/tables/organisations/organisations.component.ts index 6814652..da98814 100644 --- a/src/app/pages/tables/organisations/organisations.component.ts +++ b/src/app/pages/tables/organisations/organisations.component.ts @@ -50,7 +50,7 @@ export class OrganisationsComponent implements OnInit { tableDataGQLCreateInputType: string = 'OrganisationCreateInput'; tableDataGQLUpdateInputType: string = 'OrganisationUpdateInput'; - headline = 'Organisation'; + headline = 'Organisationen'; headlineIconName = 'business'; loadingRowIds: string[] = []; diff --git a/src/app/services/bikes.service.ts b/src/app/services/bikes.service.ts index 263205f..97d4839 100644 --- a/src/app/services/bikes.service.ts +++ b/src/app/services/bikes.service.ts @@ -16,6 +16,8 @@ import { DeleteCargoBikeMutationVariables, GetCargoBikeByIdGQL, GetCargoBikeByIdQueryVariables, + CopyCargoBikeByIdGQL, + CopyCargoBikeByIdQueryVariables } from 'src/generated/graphql'; @Injectable({ @@ -34,6 +36,7 @@ export class BikesService { private getCargoBikeByIdGQL: GetCargoBikeByIdGQL, private reloadCargoBikeByIdGQL: ReloadCargoBikeByIdGQL, private updateCargoBikeGQL: UpdateCargoBikeGQL, + private copyCargoBikeByIdGQL: CopyCargoBikeByIdGQL, private lockCargoBikeGQL: LockCargoBikeGQL, private unlockCargoBikeGQL: UnlockCargoBikeGQL, private createCargoBikeGQL: CreateCargoBikeGQL, @@ -73,6 +76,19 @@ export class BikesService { }); } + copyBikeById(variables: CopyCargoBikeByIdQueryVariables) { + + this.copyCargoBikeByIdGQL + .fetch(variables) + .subscribe((result) => { + const newBike = result.data.copyCargoBikeById; + newBike["newObject"] = true; + const currentTableData = this.tableData.getValue(); + this.tableData.next([newBike, ...this.tableData.getValue()]); + this.tableData.next(currentTableData); + }) + } + reloadBike(variables: ReloadCargoBikeByIdQueryVariables) { this.addLoadingRowId(variables.id); this.reloadCargoBikeByIdGQL diff --git a/src/app/tableLinks.ts b/src/app/tableLinks.ts new file mode 100644 index 0000000..c02e91a --- /dev/null +++ b/src/app/tableLinks.ts @@ -0,0 +1,83 @@ + +export const tableLinks = [ + { + displayName: 'Lastenräder', + iconName: 'directions_bike', + route: '/table/bikes' + }, + { + displayName: 'Aktive', + iconName: 'directions_run', + route: '/table/participants' + }, + { + displayName: 'Standorte', + iconName: 'location_on', + route: '/table/lendingStations' + }, + { + displayName: 'Personen', + iconName: 'person', + route: '/table/persons' + }, + { + displayName: 'Kontaktinformationen', + iconName: 'contact_page', + route: '/table/contactInformation' + }, + { + displayName: 'Zeitscheiben', + iconName: 'access_time', + route: '/table/timeFrames' + }, + { + displayName: 'Lastenfahrrad-Eventtypen', + iconName: 'build', + route: '/table/bikeEventTypes' + }, + { + displayName: 'Lastenfahrrad-Events', + iconName: 'event', + route: '/table/bikeEvents' + }, + { + displayName: 'Equipmenttypen', + iconName: 'toys', + route: '/table/equipmentTypes' + }, + { + displayName: 'Equipment', + iconName: 'battery_full', + route: '/table/equipments' + }, + { + displayName: 'Engagementypen', + iconName: 'track_changes', + route: '/table/engagementTypes' + }, + { + displayName: 'Engagements', + iconName: 'update', + route: '/table/engagements' + }, + { + displayName: 'Organisationen', + iconName: 'business', + route: '/table/organisations' + }, + { + displayName: 'Anbieter', + iconName: 'people', + route: '/table/providers' + }, + { + displayName: 'Workshops', + iconName: 'school', + route: '/table/workshops' + }, + { + displayName: 'Workshoptypen', + iconName: 'multiline_chart', + route: '/table/workshopTypes' + }, + ] \ No newline at end of file diff --git a/src/assets/rain.png b/src/assets/rain.png new file mode 100644 index 0000000..9a01b9f Binary files /dev/null and b/src/assets/rain.png differ diff --git a/src/generated/graphql.ts b/src/generated/graphql.ts index b047b52..f3218db 100644 --- a/src/generated/graphql.ts +++ b/src/generated/graphql.ts @@ -1813,6 +1813,16 @@ export type GetCargoBikeByIdQuery = { __typename?: 'Query', cargoBikeById?: Mayb & CargoBikeFieldsForPageFragment )> }; +export type CopyCargoBikeByIdQueryVariables = Exact<{ + id: Scalars['ID']; +}>; + + +export type CopyCargoBikeByIdQuery = { __typename?: 'Query', copyCargoBikeById?: Maybe<( + { __typename?: 'CargoBike' } + & CargoBikeFieldsForTableFragment + )> }; + export type ReloadCargoBikeByIdQueryVariables = Exact<{ id: Scalars['ID']; }>; @@ -3744,6 +3754,24 @@ export const GetCargoBikeByIdDocument = gql` export class GetCargoBikeByIdGQL extends Apollo.Query { document = GetCargoBikeByIdDocument; + constructor(apollo: Apollo.Apollo) { + super(apollo); + } + } +export const CopyCargoBikeByIdDocument = gql` + query copyCargoBikeById($id: ID!) { + copyCargoBikeById(id: $id) { + ...CargoBikeFieldsForTable + } +} + ${CargoBikeFieldsForTableFragmentDoc}`; + + @Injectable({ + providedIn: 'root' + }) + export class CopyCargoBikeByIdGQL extends Apollo.Query { + document = CopyCargoBikeByIdDocument; + constructor(apollo: Apollo.Apollo) { super(apollo); }