Merge remote-tracking branch 'origin/master'

master
FlayInAHook 4 years ago
commit 21d185ff79

@ -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] },

@ -31,16 +31,23 @@
<mat-sidenav #appDrawer mode="over" opened="false">
<mat-nav-list class="nav-profile-list">
<mat-list-item class="nav-profile-list-item">
<sidenav-profile>
</sidenav-profile>
<sidenav-profile> </sidenav-profile>
</mat-list-item>
</mat-nav-list>
<mat-nav-list>
<app-menu-list-item *ngFor="let item of navItems" [item]="item"></app-menu-list-item>
<a
[routerLink]="link.route"
*ngFor="let link of tableLinks"
>
<mat-list-item [ngClass]="{'primary-color': link.route === router.url.split('?')[0] }">
<mat-icon class="nav-list-icon">{{ link.iconName }}</mat-icon>
{{ link.displayName }}
</mat-list-item>
</a>
</mat-nav-list>
</mat-sidenav>
<mat-sidenav-content>
<router-outlet></router-outlet>
<router-outlet></router-outlet>
</mat-sidenav-content>
</mat-sidenav-container>
</div>

@ -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;
}

@ -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(

@ -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,

@ -1,15 +0,0 @@
<a mat-list-item [ngStyle]="{'padding-left': (depth * 12) + 'px'}" [ngClass]="{'active': item.route ? router.isActive(item.route, true): false, 'expanded': expanded}" class="menu-list-item">
<span (click)="onItemSelected(item)">
<mat-icon class="routeIcon">{{item.iconName}}</mat-icon>
{{item.displayName}}
</span>
<span fxFlex *ngIf="item.children && item.children.length" (click)="expand(item)">
<span fxFlex></span>
<mat-icon [@indicatorRotate]="expanded ? 'expanded': 'collapsed'">
expand_more
</mat-icon>
</span>
</a>
<div *ngIf="expanded">
<app-menu-list-item *ngFor="let child of item.children" [item]="child" [depth]="depth+1"> </app-menu-list-item>
</div>

@ -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;
}

@ -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;
}
}

@ -1,8 +0,0 @@
export interface NavItem {
displayName: string;
disabled?: boolean;
iconName: string;
route?: string;
children?: NavItem[];
}

@ -1,5 +1,15 @@
<div class="table-page-wrapper">
<div class="table-control">
<button
mat-raised-button
color="primary"
class="table-control-button"
*ngIf="columnFiltersAreSet()"
i18n
(click)="resetColumnFilters()"
>
Filter zurücksetzen
</button>
<div *ngIf="editableReferences">
<app-autocomplete-select
[editable]="editable"
@ -10,26 +20,6 @@
(selectedElementChange)="addReference($event)"
></app-autocomplete-select>
</div>
<mat-form-field class="filter">
<mat-label>Tabelle filtern</mat-label>
<input
matInput
[(ngModel)]="tableFilterString"
(ngModelChange)="newFilterStringValue()"
placeholder="Suchbegriff eingeben..."
/>
<button
mat-button
*ngIf="tableFilterString"
matSuffix
mat-icon-button
aria-label="Clear"
(click)="tableFilterString = ''; applyTableFilter()"
color="accent"
>
<mat-icon>close</mat-icon>
</button>
</mat-form-field>
<mat-paginator
[pageSizeOptions]="[15, 25]"
showFirstLastButtons
@ -40,6 +30,36 @@
<!--- Note that these columns can be defined in any order.
The actual rendered columns are set as a property on the row definition" -->
<!-- Filter Header Row -->
<ng-container
*ngFor="let column of columnInfo"
[matColumnDef]="column.dataPath + '.filter'"
[sticky]="isStickyColumn(column.dataPath)"
><th mat-header-cell *matHeaderCellDef>
<app-filter-row
*ngIf="filters.columnFilters[column.dataPath]?.isSet"
[column]="column"
[(filter)]="filters.columnFilters[column.dataPath]"
(filtersChange)="newFilterValue()"
></app-filter-row>
<button
mat-icon-button
matTooltip="Filter hinzufügen"
*ngIf="!filters.columnFilters[column.dataPath]?.isSet"
(click)="
filters.columnFilters[column.dataPath].isSet = true;
setTableFilterRowHeight()
"
>
<mat-icon>filter_list</mat-icon>
</button>
</th>
</ng-container>
<!-- empty filter cell -->
<ng-container matColumnDef="buttons.filter">
<th mat-header-cell *matHeaderCellDef></th>
</ng-container>
<!-- Other Columns -->
<ng-container
*ngFor="let column of columnInfo"
@ -96,7 +116,16 @@
</ng-container>
<!-- Table Definition -->
<tr mat-header-row *matHeaderRowDef="displayedColumns; sticky: true"></tr>
<tr
mat-header-row
*matHeaderRowDef="displayedFilterColumns; sticky: true"
#filterRow
></tr>
<tr
mat-header-row
#headerRow
*matHeaderRowDef="displayedColumns; sticky: true"
></tr>
<tr mat-row *matRowDef="let row; columns: displayedColumns"></tr>
</table>
</div>

@ -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<string> = new Subject<string>();
displayedFilterColumns = [];
filters: any = {};
filterChanged: Subject<any> = new Subject<any>();
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<HTMLElement>
);
for (let i = 0; i < headerRowCells.length; i++) {
headerRowCells[i].style.top = filterRowHeight + 'px';
}
});
}
}

@ -48,11 +48,23 @@
>
nur ungespeicherte Elemente anzeigen
</mat-checkbox>
<button
mat-raised-button
color="primary"
class="table-control-button"
*ngIf="columnFiltersAreSet()"
i18n
(click)="resetColumnFilters()"
>
Spaltenfilterwerte zurücksetzen
</button>
<mat-paginator
[pageSizeOptions]="[15, 25, 30]"
showFirstLastButtons
></mat-paginator>
</div>
<!-- Table -->
<div class="table-container">
<table
mat-table
@ -62,6 +74,7 @@
cdkDropListOrientation="horizontal"
(cdkDropListDropped)="drop($event)"
[dataSource]="data"
[ngClass]="{ 'idle-overlay': !isLoaded }"
>
<!--- Note that these columns can be defined in any order.
The actual rendered columns are set as a property on the row definition" -->
@ -73,12 +86,29 @@
[sticky]="isStickyColumn(column.dataPath)"
><th mat-header-cell *matHeaderCellDef>
<app-filter-row
*ngIf="filters.columnFilters[column.dataPath]?.isSet"
[column]="column"
[(filter)]="filters.columnFilters[column.dataPath]"
(filtersChange)="newFilterValue()"
></app-filter-row>
<button
mat-icon-button
matTooltip="Filter hinzufügen"
*ngIf="!filters.columnFilters[column.dataPath]?.isSet"
(click)="
filters.columnFilters[column.dataPath].isSet = true;
setTableFilterRowHeight()
"
>
<mat-icon>filter_list</mat-icon>
</button>
</th>
</ng-container>
<!-- empty filter cell -->
<ng-container matColumnDef="buttons.filter">
<th mat-header-cell *matHeaderCellDef></th>
</ng-container>
<!-- Checkbox Column -->
<ng-container matColumnDef="select" sticky>
<th mat-header-cell *matHeaderCellDef>
@ -254,7 +284,12 @@
>
<mat-icon>delete</mat-icon>Löschen
</button>
<button mat-menu-item disabled>
<button
mat-menu-item
*ngIf="copyableRows"
[disabled]="false"
(click)="copy(element)"
>
<mat-icon>content_copy</mat-icon>Duplizieren
</button>
</mat-menu>
@ -327,16 +362,23 @@
<tr
mat-header-row
*matHeaderRowDef="displayedFilterColumns; sticky: true"
#filterRow
></tr>
<tr
mat-header-row
*matHeaderRowDef="displayedColumns; sticky: true"
#headerRow
></tr>
<tr
[ngClass]="{ 'idle-overlay': isProcessing }"
mat-row
*matRowDef="let row; columns: displayedColumns"
></tr>
<tr mat-header-row *matHeaderRowDef="displayedColumns; sticky: true"></tr>
<tr mat-row *matRowDef="let row; columns: displayedColumns"></tr>
</table>
<mat-card
*ngIf="!isLoaded"
style="display: flex; justify-content: center; align-items: center"
>
<mat-spinner [diameter]="32"></mat-spinner>
</mat-card>
</div>
<mat-spinner
class="idle-spinner"
*ngIf="!isLoaded || isProcessing"
[diameter]="48"
></mat-spinner>
</div>

@ -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;

@ -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<any> = new Subject<any>();
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<HTMLElement>
);
for (let i = 0; i < headerRowCells.length; i++) {
headerRowCells[i].style.top = filterRowHeight + 'px';
}
});
}
resetSorting() {

@ -31,7 +31,7 @@
<mat-form-field class="date-range-input">
<input
matInput
[placeholder]="placeholderFrom"
[placeholder]="placeholderTo"
[matDatepicker]="toPicker"
[value]="stringToDate(to)"
disabled

@ -1,3 +1,15 @@
<!-- List Filter -->
<app-cell
*ngIf="column.list"
[isList]="true"
[editable]="true"
[(value)]="filter.values"
(valueChange)="newFilterValue()"
[inputType]="'String'"
>
</app-cell>
<ng-container *ngIf="!column.list">
<!-- String Filter -->
<mat-form-field *ngIf="column.type === 'String' || column.type === 'Id'">
<input
@ -134,3 +146,4 @@
[inputType]="'Enum//Ja//Nein'"
>
</app-cell>
</ng-container>

@ -10,6 +10,12 @@ query GetCargoBikeById($id: ID!) {
}
}
query copyCargoBikeById($id: ID!) {
copyCargoBikeById(id: $id) {
...CargoBikeFieldsForTable
}
}
query ReloadCargoBikeById($id: ID!) {
cargoBikeById(id: $id) {
...CargoBikeFieldsForTable

@ -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<any> = 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 =

@ -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 };
},

@ -1,27 +1,21 @@
<div class="grid-list-spacer">
<div class="big-list">
<mat-grid-list class="big-list" cols="2" rowHeight="3:1">
<mat-grid-tile class="mat-elevation-z4" routerLink = "/table/bikes">Lastenräder</mat-grid-tile>
<mat-grid-tile class="mat-elevation-z4" routerLink = "/table/participants">Aktive</mat-grid-tile>
<mat-grid-tile class="mat-elevation-z4" routerLink = "/table/lendingStations">Standorte</mat-grid-tile>
<mat-grid-tile class="mat-elevation-z4" routerLink = "/table/persons">Personen</mat-grid-tile>
</mat-grid-list>
<div>
<mat-grid-list cols="4" rowHeight="60px" >
<mat-grid-tile class="mat-elevation-z4" routerLink = "/table/equipmentTypes">Equipmenttypen</mat-grid-tile>
<mat-grid-tile class="mat-elevation-z4" routerLink = "/table/equipment">Equipment</mat-grid-tile>
<mat-grid-tile class="mat-elevation-z4" routerLink = "/table/timeFrames">Zeitscheiben</mat-grid-tile>
<mat-grid-tile class="mat-elevation-z4" routerLink = "/table/organisations">Organisation</mat-grid-tile>
<mat-grid-tile class="mat-elevation-z4" routerLink = "/table/contactInformation">Kontaktinformation</mat-grid-tile>
<mat-grid-tile class="mat-elevation-z4" routerLink = "/table/providers">Anbieter</mat-grid-tile>
<mat-grid-tile class="mat-elevation-z4" routerLink = "/table/engagements">Engagements</mat-grid-tile>
<mat-grid-tile class="mat-elevation-z4" routerLink = "/table/engagementTypes">Engagementtypen</mat-grid-tile>
<mat-grid-tile class="mat-elevation-z4" routerLink = "/table/bikeEvents">Lastenradevents</mat-grid-tile>
<mat-grid-tile class="mat-elevation-z4" routerLink = "/table/bikeEventTypes">Lastenradeventtypen</mat-grid-tile>
<mat-grid-tile class="mat-elevation-z4" routerLink = "/table/workshops">Workshops</mat-grid-tile>
<mat-grid-tile class="mat-elevation-z4" routerLink = "/table/workshopTypes">Workshoptypen</mat-grid-tile>
</mat-grid-list>
<div class="page-wrapper">
<div class="link-box">
<div class="text-wrapper">
<h1>Wohin soll es gehen?</h1>
<mat-list>
<mat-list-item *ngFor="let link of tableLinks">
<a mat-button color="primary" [routerLink]="link.route">
<mat-icon>{{ link.iconName }}</mat-icon>
{{ link.displayName }}
</a>
</mat-list-item>
</mat-list>
</div>
</div>
<div class="banner-box mat-elevation-z8">
<span class="center-text mat-display-3">Willkommen, {{ username }}!</span>
<div class="banner-img-box">
<img class="banner-img" src="../../../assets/rain.png" />
</div>
</div>
</div>

@ -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%;
}
}
}
}

@ -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
});
}
}

@ -118,7 +118,7 @@ export class BikeEventsComponent implements OnInit {
tableDataGQLCreateInputType: string = 'BikeEventCreateInput';
tableDataGQLUpdateInputType: string = 'BikeEventUpdateInput';
headline = 'Lastenradevent';
headline = 'Lastenradevents';
headlineIconName = 'event';
loadingRowIds: string[] = [];

@ -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)"
></app-table>

@ -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 });
}

@ -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 = [

@ -50,7 +50,7 @@ export class OrganisationsComponent implements OnInit {
tableDataGQLCreateInputType: string = 'OrganisationCreateInput';
tableDataGQLUpdateInputType: string = 'OrganisationUpdateInput';
headline = 'Organisation';
headline = 'Organisationen';
headlineIconName = 'business';
loadingRowIds: string[] = [];

@ -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

@ -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'
},
]

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 MiB

@ -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<GetCargoBikeByIdQuery, GetCargoBikeByIdQueryVariables> {
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<CopyCargoBikeByIdQuery, CopyCargoBikeByIdQueryVariables> {
document = CopyCargoBikeByIdDocument;
constructor(apollo: Apollo.Apollo) {
super(apollo);
}

Loading…
Cancel
Save