Adds custom filter predicates for native object data

Supports properties like motion submitter and more.

Does at the moment "not" support tunneled properties, i.e filters
that need to go over a repository
(i.w workflow-state-label)

Fixes some small issues
This commit is contained in:
Sean Engelhardt 2019-06-24 16:30:27 +02:00
parent 582be687eb
commit 2758441552
12 changed files with 103 additions and 134 deletions

View File

@ -148,6 +148,9 @@ export class ListViewTableComponent<V extends BaseViewModel, M extends BaseModel
@Input()
public columns: PblColumnDefinition[] = [];
@Input()
public filterProps: string[];
/**
* Key to restore scroll position after navigating
*/
@ -299,12 +302,13 @@ export class ListViewTableComponent<V extends BaseViewModel, M extends BaseModel
})
.create();
const filterPredicate = (item: any): boolean => {
const filterPredicate = (item: V): boolean => {
if (!this.inputValue) {
return true;
}
if (this.inputValue) {
// filter by ID
const trimmedInput = this.inputValue.trim().toLowerCase();
const idString = '' + item.id;
const foundId =
@ -316,20 +320,21 @@ export class ListViewTableComponent<V extends BaseViewModel, M extends BaseModel
return true;
}
for (const column of this.columns) {
const col = this.dataSource.hostGrid.columnApi.findColumn(column.prop);
const value = col.getValue(item);
// custom filter predicates
if (this.filterProps && this.filterProps.length) {
for (const prop of this.filterProps) {
const propertyAsString = '' + item[prop];
if (!!value) {
const valueAsString = '' + value;
const foundValue =
valueAsString
.trim()
.toLocaleLowerCase()
.indexOf(trimmedInput) !== -1;
if (!!propertyAsString) {
const foundProp =
propertyAsString
.trim()
.toLowerCase()
.indexOf(trimmedInput) !== -1;
if (foundValue) {
return true;
if (foundProp) {
return true;
}
}
}
}

View File

@ -18,7 +18,9 @@
[filterService]="filterService"
[columns]="tableColumnDefinition"
[multiSelect]="isMultiSelect"
[restricted]="restrictedColumns"
[hiddenInMobile]="['info']"
[filterProps]="filterProps"
scrollKey="agenda"
[(selectedRows)]="selectedRows"
(dataSourceChange)="onDataSourceChange($event)"

View File

@ -6,10 +6,12 @@ import { Title } from '@angular/platform-browser';
import { TranslateService } from '@ngx-translate/core';
import { PblColumnDefinition } from '@pebula/ngrid';
import { _ } from 'app/core/translate/translation-marker';
import { AgendaCsvExportService } from '../../services/agenda-csv-export.service';
import { AgendaFilterListService } from '../../services/agenda-filter-list.service';
import { AgendaPdfService } from '../../services/agenda-pdf.service';
import { ConfigService } from 'app/core/ui-services/config.service';
import { ColumnRestriction } from 'app/shared/components/list-view-table/list-view-table.component';
import { DurationService } from 'app/core/ui-services/duration.service';
import { ItemInfoDialogComponent } from '../item-info-dialog/item-info-dialog.component';
import { ItemRepositoryService } from 'app/core/repositories/agenda/item-repository.service';
@ -20,11 +22,10 @@ import { ProjectorElementBuildDeskriptor } from 'app/site/base/projectable';
import { PromptService } from 'app/core/ui-services/prompt.service';
import { PdfDocumentService } from 'app/core/ui-services/pdf-document.service';
import { StorageService } from 'app/core/core-services/storage.service';
import { TopicRepositoryService } from 'app/core/repositories/topics/topic-repository.service';
import { ViewportService } from 'app/core/ui-services/viewport.service';
import { ViewItem } from '../../models/view-item';
import { ViewListOfSpeakers } from '../../models/view-list-of-speakers';
import { _ } from 'app/core/translate/translation-marker';
import { TopicRepositoryService } from 'app/core/repositories/topics/topic-repository.service';
import { ViewTopic } from '../../models/view-topic';
/**
@ -80,7 +81,7 @@ export class AgendaListComponent extends ListViewBaseComponent<ViewItem> impleme
},
{
prop: 'speaker',
width: this.singleButtonWidth
width: this.badgeButtonWidth
},
{
prop: 'menu',
@ -88,6 +89,22 @@ export class AgendaListComponent extends ListViewBaseComponent<ViewItem> impleme
}
];
public restrictedColumns: ColumnRestriction[] = [
{
columnName: 'menu',
permission: 'agenda.can_manage'
},
{
columnName: 'speaker',
permission: 'agenda.can_see_list_of_speakers'
}
];
/**
* Define extra filter properties
*/
public filterProps = ['itemNumber', 'comment'];
/**
* The usual constructor for components
* @param titleService Setting the browser tab title
@ -322,26 +339,4 @@ export class AgendaListComponent extends ListViewBaseComponent<ViewItem> impleme
return result;
}
}
/**
* Overwrites the dataSource's string filter with a case-insensitive search
* in the item number and title
*
* TODO: Filter predicates will be missed :(
*/
// private setFulltextFilter(): void {
// this.dataSource.filterPredicate = (data, filter) => {
// if (!data) {
// return false;
// }
// filter = filter ? filter.toLowerCase() : '';
// return (
// data.itemNumber.toLowerCase().includes(filter) ||
// data
// .getListTitle()
// .toLowerCase()
// .includes(filter)
// );
// };
// }
}

View File

@ -24,6 +24,7 @@
[filterService]="filterService"
[sortService]="sortService"
[columns]="tableColumnDefinition"
[filterProps]="filterProps"
[multiSelect]="isMultiSelect"
scrollKey="assignments"
[(selectedRows)]="selectedRows"

View File

@ -49,6 +49,11 @@ export class AssignmentListComponent extends ListViewBaseComponent<ViewAssignmen
}
];
/**
* Define extra filter properties
*/
public filterProps = ['title', 'candidates', 'assignmentRelatedUsers', 'tags', 'candidateAmount'];
/**
* Constructor.
*

View File

@ -43,6 +43,11 @@ export abstract class ListViewBaseComponent<V extends BaseViewModel> extends Bas
*/
public singleButtonWidth = '40px';
/**
* NGrid column width for single buttons with badge
*/
public badgeButtonWidth = '45px';
/**
* @param titleService the title service
* @param translate the translate service

View File

@ -24,6 +24,8 @@
[sortService]="sortService"
[columns]="tableColumnDefinition"
[multiSelect]="isMultiSelect"
[restricted]="restrictedColumns"
[filterProps]="filterProps"
scrollKey="user"
[(selectedRows)]="selectedRows"
(dataSourceChange)="onDataSourceChange($event)"

View File

@ -1,22 +1,23 @@
import { Component, OnInit, ViewChild, TemplateRef } from '@angular/core';
import { Router, ActivatedRoute } from '@angular/router';
import { FormGroup, Validators, FormBuilder } from '@angular/forms';
import { Title } from '@angular/platform-browser';
import { MatSnackBar, MatDialog } from '@angular/material';
import { Router, ActivatedRoute } from '@angular/router';
import { Title } from '@angular/platform-browser';
import { TranslateService } from '@ngx-translate/core';
import { PblColumnDefinition } from '@pebula/ngrid';
import { ListViewBaseComponent } from '../../../base/list-view-base';
import { ViewMediafile } from '../../models/view-mediafile';
import { ColumnRestriction } from 'app/shared/components/list-view-table/list-view-table.component';
import { ListViewBaseComponent } from 'app/site/base/list-view-base';
import { MediafileRepositoryService } from 'app/core/repositories/mediafiles/mediafile-repository.service';
import { MediaManageService } from 'app/core/ui-services/media-manage.service';
import { PromptService } from 'app/core/ui-services/prompt.service';
import { MediafileFilterListService } from '../../services/mediafile-filter.service';
import { MediafilesSortListService } from '../../services/mediafiles-sort-list.service';
import { ViewportService } from 'app/core/ui-services/viewport.service';
import { OperatorService } from 'app/core/core-services/operator.service';
import { PromptService } from 'app/core/ui-services/prompt.service';
import { StorageService } from 'app/core/core-services/storage.service';
import { ViewportService } from 'app/core/ui-services/viewport.service';
import { ViewMediafile } from '../../models/view-mediafile';
/**
* Lists all the uploaded files.
@ -95,6 +96,25 @@ export class MediafileListComponent extends ListViewBaseComponent<ViewMediafile>
}
];
/**
* Restricted Columns
*/
public restrictedColumns: ColumnRestriction[] = [
{
columnName: 'indicator',
permission: 'mediafiles.can_manage'
},
{
columnName: 'menu',
permission: 'mediafiles.can_manage'
}
];
/**
* Define extra filter properties
*/
public filterProps = ['title', 'type'];
/**
* Constructs the component
*
@ -301,20 +321,4 @@ export class MediafileListComponent extends ListViewBaseComponent<ViewMediafile>
this.editFile = false;
}
}
/**
* Overwrites the dataSource's string filter with a case-insensitive search
* in the file name property
*
* TODO: Filter predicates will be missed :(
*/
// private setFulltextFilter(): void {
// this.dataSource.filterPredicate = (data, filter) => {
// if (!data || !data.title) {
// return false;
// }
// filter = filter ? filter.toLowerCase() : '';
// return data.title.toLowerCase().indexOf(filter) >= 0;
// };
// }
}

View File

@ -48,6 +48,7 @@
[columns]="tableColumnDefinition"
[multiSelect]="isMultiSelect"
[restricted]="restrictedColumns"
[filterProps]="filterProps"
[hiddenInMobile]="['state']"
scrollKey="motion"
[(selectedRows)]="selectedRows"

View File

@ -107,7 +107,8 @@ export class MotionListComponent extends ListViewBaseComponent<ViewMotion> imple
minWidth: 160
},
{
prop: 'speaker'
prop: 'speaker',
width: this.badgeButtonWidth
}
];
@ -129,13 +130,23 @@ export class MotionListComponent extends ListViewBaseComponent<ViewMotion> imple
public categories: ViewCategory[] = [];
public motionBlocks: ViewMotionBlock[] = [];
/**
* Columns that demand certain permissions
*/
public restrictedColumns: ColumnRestriction[] = [
{
columnName: 'speaker',
permission: 'agenda.can_see'
permission: 'agenda.can_see_list_of_speakers'
}
];
/**
* Define extra filter properties
*
* TODO: repo.getExtendedStateLabel(), repo.getExtendedRecommendationLabel()
*/
public filterProps = ['submitters', 'motion_block', 'title', 'identifier'];
/**
* List of `TileCategoryInformation`.
* Necessary to not iterate over the values of the map below.
@ -394,58 +405,6 @@ export class MotionListComponent extends ListViewBaseComponent<ViewMotion> imple
);
}
/**
* Overwrites the dataSource's string filter with a case-insensitive search
* in the identifier, title, state, recommendations, submitters, motion blocks and id
*
* TODO: Does currently not work with virtual scrolling tables. Filter predicates will be missed :(
*/
// private setFulltextFilter(): void {
// this.dataSource.filterPredicate = (data, filter) => {
// if (!data) {
// return false;
// }
// filter = filter ? filter.toLowerCase() : '';
// if (data.submitters.length && data.submitters.find(user => user.full_name.toLowerCase().includes(filter))) {
// return true;
// }
// if (data.motion_block && data.motion_block.title.toLowerCase().includes(filter)) {
// return true;
// }
// if (data.title.toLowerCase().includes(filter)) {
// return true;
// }
// if (data.identifier && data.identifier.toLowerCase().includes(filter)) {
// return true;
// }
// if (
// this.getStateLabel(data) &&
// this.getStateLabel(data)
// .toLocaleLowerCase()
// .includes(filter)
// ) {
// return true;
// }
// if (
// this.getRecommendationLabel(data) &&
// this.getRecommendationLabel(data)
// .toLocaleLowerCase()
// .includes(filter)
// ) {
// return true;
// }
// const dataid = '' + data.id;
// if (dataid.includes(filter)) {
// return true;
// }
// return false;
// };
// }
/**
* This function saves the selected view by changes.
*
@ -478,7 +437,7 @@ export class MotionListComponent extends ListViewBaseComponent<ViewMotion> imple
* @param ev a MouseEvent.
*/
public async openEditInfo(motion: ViewMotion): Promise<void> {
if (!this.isMultiSelect) {
if (!this.isMultiSelect && this.perms.isAllowed('change_metadata')) {
// The interface holding the current information from motion.
this.infoDialog = {
title: motion.title,

View File

@ -19,6 +19,7 @@
[filterService]="filterService"
[sortService]="sortService"
[columns]="tableColumnDefinition"
[filterProps]="filterProps"
[multiSelect]="isMultiSelect"
[hiddenInMobile]="['group']"
scrollKey="user"
@ -35,11 +36,11 @@
matTooltip="{{ getUserTooltip(user) | translate }}"
></a>
<div>
<span *ngIf="user.is_active">
<span *ngIf="user.is_active !== false">
{{ name }}
</span>
<span *ngIf="!user.is_active">
<span *ngIf="user.is_active === false">
<os-icon-container icon="block" swap="true">
{{ name }}
</os-icon-container>
@ -96,7 +97,7 @@
class="checkbox-ripple-padding"
(change)="setPresent(user)"
[checked]="user.is_present"
[disabled]="isMultiSelect"
[disabled]="isMultiSelect || !this.operator.hasPerms('users.can_manage')"
>
<span translate>Present</span>
</mat-checkbox>

View File

@ -126,6 +126,11 @@ export class UserListComponent extends ListViewBaseComponent<ViewUser> implement
}
];
/**
* Define extra filter properties
*/
public filterProps = ['full_name', 'groups', 'structure_level', 'number'];
/**
* The usual constructor for components
* @param titleService Serivce for setting the title
@ -413,20 +418,4 @@ export class UserListComponent extends ListViewBaseComponent<ViewUser> implement
viewUser.user.is_present = !viewUser.user.is_present;
await this.repo.update(viewUser.user, viewUser);
}
/**
* Overwrites the dataSource's string filter with a case-insensitive search
* in the full_name property
*
* TODO: Filter predicates will be missed :(
*/
// private setFulltextFilter(): void {
// this.dataSource.filterPredicate = (data, filter) => {
// if (!data || !data.full_name) {
// return false;
// }
// filter = filter ? filter.toLowerCase() : '';
// return data.full_name.toLowerCase().indexOf(filter) >= 0;
// };
// }
}