added description filtering (ui)

This commit is contained in:
Sofia Papacharalampous 2024-06-18 16:06:15 +03:00
parent 6fca0402fb
commit 775a9a232d
18 changed files with 365 additions and 422 deletions

View File

@ -3,6 +3,9 @@ import { Guid } from '@common/types/guid';
import { IsActive } from '../common/enum/is-active.enum';
import { DescriptionStatus } from '../common/enum/description-status';
import { DmpLookup } from './dmp.lookup';
import { DmpUserRole } from '../common/enum/dmp-user-role';
import { DescriptionTag } from '../model/description/description';
import { UntypedFormBuilder, UntypedFormControl, UntypedFormGroup } from '@angular/forms';
export class DescriptionLookup extends Lookup implements DescriptionFilter {
ids: Guid[];
@ -16,9 +19,37 @@ export class DescriptionLookup extends Lookup implements DescriptionFilter {
isActive: IsActive[];
statuses: DescriptionStatus[];
showAllVersions: boolean; //TODO
descriptionTemplateIds: Guid[]; //TODO
associatedDmpIds: Guid[]; //TODO
roleInDmp: DmpUserRole[]; //TODO
tags: DescriptionTag[]; //TODO
constructor() {
super();
}
from(formGroup: UntypedFormGroup): void {
if (!formGroup) return;
this.statuses = formGroup.get("status")?.value ? [formGroup.get("status")?.value] : [];
this.showAllVersions = formGroup.get("showAllVersions")?.value ?? false;
this.roleInDmp = formGroup.get("role")?.value ? [formGroup.get("role")?.value] : [];
this.tags = formGroup.get("tags")?.value ?? [];
this.associatedDmpIds = formGroup.get("associatedDmpIds")?.value ?? [];
this.descriptionTemplateIds = formGroup.get("descriptionTemplates")?.value ?? [];
}
buildForm(): UntypedFormGroup {
return (new UntypedFormBuilder()).group({
status: [this.statuses?.length > 0 ? this.statuses[0] : null],
role: [this.roleInDmp?.length > 0 ? this.roleInDmp[0] : null],
showAllVersions: [this.showAllVersions ?? false],
descriptionTemplates: [this.descriptionTemplateIds],
associatedDmpIds: [this.associatedDmpIds],
tags: [this.tags],
});
}
}
export interface DescriptionFilter {
@ -32,4 +63,11 @@ export interface DescriptionFilter {
dmpSubQuery: DmpLookup;
isActive: IsActive[];
statuses: DescriptionStatus[];
showAllVersions: boolean; //TODO
descriptionTemplateIds: Guid[]; //TODO
associatedDmpIds: Guid[]; //TODO
roleInDmp: DmpUserRole[]; //TODO
tags: DescriptionTag[]; //TODO
//TODO: dynamic reference-types
}

View File

@ -26,7 +26,7 @@ export class AnalyticsService {
public static RecentEditedActivity: string = 'Recent DMP Activity';
public static DescriptionEditor: string = 'Description Editor';
public static DescriptionListing: string = 'Descriptions';
public static DatasetCriteriaDialog: string = 'Dataset Criteria';
public static DescriptionFilterDialog: string = 'Dataset Criteria';
public static DescriptionListingItem: string = 'Description Listing Item';
public static DescriptionOverview: string = 'Description Overview';
public static DmpEditor: string = 'DMP Editor';

View File

@ -74,8 +74,8 @@ export class TagService {
initialItems: (excludedItems: any[], data?: any) => this.query(this.buildAutocompleteLookup(null, excludedItems ? excludedItems : null)).pipe(map(x => x.items)),
filterFn: (searchQuery: string, excludedItems: any[]) => this.query(this.buildAutocompleteLookup(searchQuery, excludedItems)).pipe(map(x => x.items)),
getSelectedItems: (selectedItems: any[]) => this.query(this.buildAutocompleteLookup(null, null, selectedItems)).pipe(map(x => x.items)),
displayFn: (item: Tag) => JSON.stringify(item),
titleFn: (item: Tag) => JSON.stringify(item),
displayFn: (item: Tag) => item.label,
titleFn: (item: Tag) => item.label,
valueAssign: (item: Tag) => item.id,
};

View File

@ -1,2 +0,0 @@
<a class="col-auto d-flex pointer" (click)="onClose()"><span class="ml-auto mt-3 material-icons clear-icon">clear</span></a>
<app-dataset-criteria-component [isPublic]="data.isPublic" [status]="data.status" [criteriaFormGroup]="data.formGroup" (filtersChanged)="onFiltersChanged($event)"></app-dataset-criteria-component>

View File

@ -1,44 +0,0 @@
import { HttpClient } from '@angular/common/http';
import { Component, Inject, OnInit, ViewChild } from '@angular/core';
import { UntypedFormGroup } from '@angular/forms';
import { MAT_DIALOG_DATA, MatDialogRef } from '@angular/material/dialog';
import { DatasetCriteria } from '@app/core/query/dataset/dataset-criteria';
import { DatasetCriteriaComponent } from '../dataset-criteria.component';
import { AnalyticsService } from '@app/core/services/matomo/analytics-service';
@Component({
selector: 'dataset-criteria-dialog-component',
templateUrl: './dataset-criteria-dialog.component.html',
styleUrls: ['./dataset-criteria-dialog.component.scss']
})
export class DatasetCriteriaDialogComponent implements OnInit {
@ViewChild(DatasetCriteriaComponent, { static: true }) criteria: DatasetCriteriaComponent;
constructor(
public dialogRef: MatDialogRef<DatasetCriteriaDialogComponent>,
private httpClient: HttpClient,
private analyticsService: AnalyticsService,
@Inject(MAT_DIALOG_DATA) public data: { isPublic: boolean, status: Number, criteria: DatasetCriteria, formGroup: UntypedFormGroup, updateDataFn: Function }
) {
}
ngOnInit() {
this.analyticsService.trackPageView(AnalyticsService.DatasetCriteriaDialog);
this.criteria.setCriteria(this.data.criteria);
}
onNoClick(): void {
this.dialogRef.close();
}
onClose(): void {
this.dialogRef.close();
}
onFiltersChanged(event) {
this.data.updateDataFn(this.criteria);
}
}

View File

@ -1,306 +0,0 @@
import { Component, EventEmitter, Input, OnInit, Output } from '@angular/core';
import { UntypedFormBuilder, UntypedFormControl, UntypedFormGroup } from '@angular/forms';
import { MatDialog } from '@angular/material/dialog';
import { MatSnackBar } from '@angular/material/snack-bar';
import { Router } from '@angular/router';
import { RequestItem } from '@app/core/query/request-item';
import { AuthService } from '@app/core/services/auth/auth.service';
import { DmpService } from '@app/core/services/dmp/dmp.service';
import { SnackBarNotificationLevel, UiNotificationService } from '@app/core/services/notification/ui-notification-service';
import { EnumUtils } from '@app/core/services/utilities/enum-utils.service';
import { MultipleAutoCompleteConfiguration } from '@app/library/auto-complete/multiple/multiple-auto-complete-configuration';
import { BaseCriteriaComponent } from '@app/ui/misc/criteria/base-criteria.component';
import { ValidationErrorModel } from '@common/forms/validation/error-model/validation-error-model';
import { TranslateService } from '@ngx-translate/core';
import { Observable } from 'rxjs';
import { map, takeUntil } from 'rxjs/operators';
@Component({
selector: 'app-dataset-criteria-component',
templateUrl: './dataset-criteria.component.html',
styleUrls: ['./dataset-criteria.component.scss']
})
export class DatasetCriteriaComponent extends BaseCriteriaComponent implements OnInit {
@Input() dmpSearchEnabled;
@Input() status;
@Input() isPublic: boolean;
@Input() criteriaFormGroup: UntypedFormGroup;
@Output() filtersChanged: EventEmitter<any> = new EventEmitter();
public criteria: any;
public filteringTagsAsync = false;
public filteredTags: ExternalSourceItemModel[];
statuses = DatasetStatus;
options: UntypedFormGroup;
public formGroup = new UntypedFormBuilder().group({
like: new UntypedFormControl(),
groupIds: new UntypedFormControl(),
grants: new UntypedFormControl(),
status: new UntypedFormControl(),
role: new UntypedFormControl(),
organisations: new UntypedFormControl(),
collaborators: new UntypedFormControl(),
datasetTemplates: new UntypedFormControl(),
tags: new UntypedFormControl(),
allVersions: new UntypedFormControl(),
grantStatus: new UntypedFormControl()
});
tagsAutoCompleteConfiguration = {
filterFn: this.filterTags.bind(this),
initialItems: (excludedItems: any[]) => this.filterTags('').pipe(map(result => result.filter(resultItem => (excludedItems || []).map(x => x.id).indexOf(resultItem.id) === -1))),
displayFn: (item) => item['name'],
titleFn: (item) => item['name']
};
datasetTemplateAutoCompleteConfiguration: MultipleAutoCompleteConfiguration = {
filterFn: this.filterDatasetTemplate.bind(this),
initialItems: (excludedItems: any[]) => this.filterDatasetTemplate('').pipe(map(result => result.filter(resultItem => (excludedItems || []).map(x => x.id).indexOf(resultItem.id) === -1))),
displayFn: (item) => item['label'],
titleFn: (item) => item['label'],
subtitleFn: (item) => item['description']
};
dmpAutoCompleteConfiguration = {
filterFn: (x, excluded) => this.filterDmps(x).pipe(map(x => x.data)),
initialItems: (extraData) => this.filterDmps('').pipe(map(x => x.data)),
displayFn: (item) => item['label'],
titleFn: (item) => item['label']
};
collaboratorsAutoCompleteConfiguration: MultipleAutoCompleteConfiguration = {
filterFn: this.filterCollaborators.bind(this),
initialItems: (excludedItems: any[]) => this.filterCollaborators('').pipe(map(result => result.filter(resultItem => (excludedItems || []).map(x => x.id).indexOf(resultItem.id) === -1))),
displayFn: (item) => item['name'],
titleFn: (item) => item['name']
};
grantAutoCompleteConfiguration: MultipleAutoCompleteConfiguration = {
filterFn: this.filterGrant.bind(this),
initialItems: (excludedItems: any[]) => this.filterGrant('').pipe(map(result => result.filter(resultItem => (excludedItems || []).map(x => x.id).indexOf(resultItem.id) === -1))),
displayFn: (item) => item['label'],
titleFn: (item) => item['label']
};
organisationAutoCompleteConfiguration: MultipleAutoCompleteConfiguration = {
filterFn: this.filterOrganisations.bind(this),
initialItems: (excludedItems: any[]) => this.filterOrganisations('').pipe(map(result => result.filter(resultItem => (excludedItems || []).map(x => x.id).indexOf(resultItem.id) === -1))),
displayFn: (item) => item['name'],
titleFn: (item) => item['name']
}
constructor(
private externalSourcesService: ExternalSourcesService,
public enumUtils: EnumUtils,
public dmpService: DmpService,
public datasetWizardService: DatasetWizardService,
private dialog: MatDialog,
private snackBar: MatSnackBar,
private uiNotificationService: UiNotificationService,
private router: Router,
private language: TranslateService,
public grantService: GrantService,
private organisationService: OrganisationService,
private userService: UserServiceOld,
private datasetService: DatasetService,
fb: UntypedFormBuilder,
private authService: AuthService
) {
super(new ValidationErrorModel());
// this.options = fb.group({
// status: new FormControl(),
// floatLabel: 'auto',
// });
}
ngOnInit() {
super.ngOnInit();
// This if is just for passing label on chips of dialog
if (this.formGroup && this.criteriaFormGroup) {
this.formGroup.get('datasetTemplates').setValue(this.criteriaFormGroup.get('datasetTemplates').value);
this.formGroup.get('groupIds').setValue(this.criteriaFormGroup.get('groupIds').value);
this.formGroup.get('grants').setValue(this.criteriaFormGroup.get('grants').value);
this.formGroup.get('collaborators').setValue(this.criteriaFormGroup.get('collaborators').value);
this.formGroup.get('organisations').setValue(this.criteriaFormGroup.get('organisations').value);
this.formGroup.get('tags').setValue(this.criteriaFormGroup.get('tags').value);
}
this.formGroup.get('like').valueChanges
.pipe(takeUntil(this._destroyed))
.subscribe(x => this.controlModified());
this.formGroup.get('groupIds').valueChanges
.pipe(takeUntil(this._destroyed))
.subscribe(x => this.controlModified());
this.formGroup.get('grants').valueChanges
.pipe(takeUntil(this._destroyed))
.subscribe(x => this.controlModified());
this.formGroup.get('status').valueChanges
.pipe(takeUntil(this._destroyed))
.subscribe(x => this.controlModified());
this.formGroup.get('role').valueChanges
.pipe(takeUntil(this._destroyed))
.subscribe(x => this.controlModified());
this.formGroup.get('organisations').valueChanges
.pipe(takeUntil(this._destroyed))
.subscribe(x => this.controlModified());
this.formGroup.get('collaborators').valueChanges
.pipe(takeUntil(this._destroyed))
.subscribe(x => this.controlModified());
this.formGroup.get('datasetTemplates').valueChanges
.pipe(takeUntil(this._destroyed))
.subscribe(x => this.controlModified());
this.formGroup.get('allVersions').valueChanges
.pipe(takeUntil(this._destroyed))
.subscribe(x => this.controlModified());
this.formGroup.get('tags').valueChanges
.pipe(takeUntil(this._destroyed))
.subscribe(x => this.controlModified());
this.formGroup.get('grantStatus').valueChanges
.pipe(takeUntil(this._destroyed))
.subscribe(x => this.controlModified());
// if (this.criteria == null) { this.criteria = {}; }
// this.formGroup.patchValue({'status': this.status !== undefined ? this.status : 'null'});
}
setCriteria(criteria: DatasetCriteria): void {
this.formGroup.get('like').patchValue(criteria.like);
this.formGroup.get('groupIds').patchValue(criteria.groupIds);
this.formGroup.get('grants').patchValue(criteria.grants);
this.formGroup.get('status').patchValue(criteria.status);
this.formGroup.get('role').patchValue(criteria.role);
this.formGroup.get('collaborators').patchValue(criteria.collaborators);
this.formGroup.get('datasetTemplates').patchValue(criteria.datasetTemplates);
this.formGroup.get('allVersions').patchValue(criteria.allVersions);
this.formGroup.get('tags').patchValue(criteria.tags);
this.formGroup.get('grantStatus').patchValue(criteria.grantStatus);
// this.criteria = criteria;
}
controlModified(): void {
this.clearErrorModel();
this.filtersChanged.emit();
if (this.refreshCallback != null &&
(this.formGroup.get('like').value == null || this.formGroup.get('like').value.length === 0 || this.formGroup.get('like').value.length > 2)
) {
setTimeout(() => this.refreshCallback(true));
}
}
filterTags(value: string): Observable<ExternalSourceItemModel[]> {
this.filteredTags = undefined;
this.filteringTagsAsync = true;
const requestItem: RequestItem<TagCriteria> = new RequestItem();
const criteria: TagCriteria = new TagCriteria();
criteria.like = value;
requestItem.criteria = criteria;
return this.externalSourcesService.searchDatasetTags(requestItem);
}
filterDatasetTemplate(query: string): Observable<any[]> {
const fields: Array<string> = new Array<string>();
fields.push('asc');
const datasetTemplateRequestItem: DataTableRequest<DatasetProfileCriteria> = new DataTableRequest(0, null, { fields: fields });
datasetTemplateRequestItem.criteria = new DatasetProfileCriteria();
datasetTemplateRequestItem.criteria.like = query;
if (this.isPublic) {
return this.datasetService.getDatasetProfiles(datasetTemplateRequestItem);
} else {
return this.datasetService.getDatasetProfilesUsedPaged(datasetTemplateRequestItem).pipe(map(x => x.data));
}
}
filterDmps(value: string): Observable<DmpListingModel> {
const fields: Array<string> = new Array<string>();
fields.push('asc');
const dmpDataTableRequest: DataTableRequest<DmpCriteria> = new DataTableRequest(0, null, { fields: fields });
dmpDataTableRequest.criteria = new DmpCriteria();
dmpDataTableRequest.criteria.like = value;
if (this.isPublic) {
dmpDataTableRequest.criteria.isPublic = true;
dmpDataTableRequest.criteria.onlyPublic = true;
}
return this.dmpService.getPaged(dmpDataTableRequest, "autocomplete");
// }
}
filterGrant(query: string) {
const fields: Array<string> = new Array<string>();
fields.push('asc');
const grantRequestItem: DataTableRequest<GrantCriteria> = new DataTableRequest(0, null, { fields: fields });
grantRequestItem.criteria = new GrantCriteria();
grantRequestItem.criteria.like = query;
if (this.isPublic) {
return this.grantService.getPublicPaged(grantRequestItem).pipe(map(x => x.data));
} else {
return this.grantService.getPaged(grantRequestItem, "autocomplete").pipe(map(x => x.data));
}
}
filterOrganisations(value: string) {
const fields: Array<string> = new Array<string>();
fields.push('asc');
const dataTableRequest: DataTableRequest<OrganisationCriteria> = new DataTableRequest(0, null, { fields: fields });
dataTableRequest.criteria = new OrganisationCriteria();
dataTableRequest.criteria.labelLike = value;
if (this.isPublic) {
return this.organisationService.searchPublicOrganisations(dataTableRequest).pipe(map(x => x.data));
} else {
return this.organisationService.searchInternalOrganisations(dataTableRequest).pipe(map(x => x.data));
}
}
filterCollaborators(query: string) {
const fields: Array<string> = new Array<string>();
fields.push('asc');
const collaboratorsRequestItem: DataTableRequest<UserCriteria> = new DataTableRequest(0, null, { fields: fields });
collaboratorsRequestItem.criteria = new UserCriteria();
collaboratorsRequestItem.criteria.collaboratorLike = query;
return this.userService.getCollaboratorsPaged(collaboratorsRequestItem).pipe(map(x => x.data));
}
fileImport(event) {
const dialogRef = this.dialog.open(DatasetUploadDialogue, {
data: {
fileList: FileList,
success: Boolean,
datasetTitle: String,
dmpId: String,
datasetProfileId: String,
dmpSearchEnabled: this.dmpSearchEnabled,
criteria: this.criteria
}
});
dialogRef.afterClosed().pipe(takeUntil(this._destroyed)).subscribe(result => {
if (result && result.success) {
this.datasetWizardService.uploadXml(result.fileList, result.datasetTitle, result.dmpId, result.datasetProfileId)
.pipe(takeUntil(this._destroyed))
.subscribe(
complete => this.onCallbackSuccess(),
error => this.onCallbackError(error)
);
}
})
}
onCallbackSuccess(): void {
this.uiNotificationService.snackBarNotification(this.language.instant('DATASET-UPLOAD.SNACK-BAR.SUCCESSFUL-CREATION'), SnackBarNotificationLevel.Success);
this.router.navigate(['/plans']);
}
onCallbackError(error: any) {
this.uiNotificationService.snackBarNotification(this.language.instant('DATASET-UPLOAD.SNACK-BAR.UNSUCCESSFUL'), SnackBarNotificationLevel.Success);
}
isAuthenticated(): boolean {
return this.authService.currentAccountIsAuthenticated();
}
}

View File

@ -1,7 +1,7 @@
<div class="main-content listing-main-container h-100">
<div class="container-fluid">
<div class="row">
<div *ngIf="hasListingItems && listingItems && listingItems.length === 0 && !hasLikeCriteria()" class="card mt-0">
<div *ngIf="hasListingItems && listingItems && listingItems.length === 0 && !hasLikeFilters()" class="card mt-0">
<!-- <div class="card mt-0" [style.display]="isVisible ? 'block' : 'none'"> -->
<!-- <a class="col-auto d-flex" (click)="closeCard()"><span class="ml-auto pt-3 material-icons clear-icon">clear</span></a> -->
<div class="card-content info-text mb-0">
@ -29,12 +29,11 @@
</button>
</div>
</div>
<!-- TODO: implement filters -->
<!-- <div *ngIf="listingItems && listingItems.length > 0 || this.lookup.like" class="filter-btn" [style.right]="dialog.getDialogById('filters') ? '446px' : '0px'" [style.width]="scrollbar ? '57px' : '37px'" (click)="openFiltersDialog()">
<div *ngIf="listingItems && listingItems.length > 0 || this.lookup.like" class="filter-btn" [style.right]="dialog.getDialogById('filters') ? '446px' : '0px'" [style.width]="listingItems.length > 2 ? '57px' : '37px'" (click)="openFiltersDialog()">
<button mat-raised-button class="p-0">
<mat-icon class="mr-4">filter_alt</mat-icon>
<mat-icon class="mr-4 filter-icon">filter_alt</mat-icon>
</button>
</div> -->
</div>
</div>
<div>
<div class="listing row pb-2">

View File

@ -123,6 +123,12 @@
right: 0px;
z-index: 100;
width: 37px;
.filter-icon {
width: 1.5rem;
height: 1.5rem;
font-size: 1.5rem;
}
}
.filter-btn button {

View File

@ -17,7 +17,7 @@ import { Dmp, DmpDescriptionTemplate, DmpUser } from '@app/core/model/dmp/dmp';
import { DmpReference } from '@app/core/model/dmp/dmp-reference';
import { ReferenceType } from '@app/core/model/reference-type/reference-type';
import { Reference } from '@app/core/model/reference/reference';
import { DescriptionLookup } from '@app/core/query/description.lookup';
import { DescriptionFilter, DescriptionLookup } from '@app/core/query/description.lookup';
import { DmpLookup } from '@app/core/query/dmp.lookup';
import { AuthService } from '@app/core/services/auth/auth.service';
import { DescriptionService } from '@app/core/services/description/description.service';
@ -35,6 +35,7 @@ import { debounceTime, map, takeUntil } from 'rxjs/operators';
import { nameof } from 'ts-simple-nameof';
import { StartNewDescriptionDialogComponent } from '../start-new-description-dialog/start-new-description-dialog.component';
import { RouterUtilsService } from '@app/core/services/router/router-utils.service';
import { DescriptionFilterDialogComponent } from './filtering/description-filter-dialogue/description-filter-dialog.component';
@Component({
selector: 'app-description-listing-component',
@ -59,7 +60,6 @@ export class DescriptionListingComponent extends BaseComponent implements OnInit
pageSize: number = 5;
lookup: DescriptionLookup = new DescriptionLookup();
criteriaFormGroup: UntypedFormGroup;
public formGroup = new UntypedFormBuilder().group({
like: new UntypedFormControl(),
order: new UntypedFormControl()
@ -135,6 +135,7 @@ export class DescriptionListingComponent extends BaseComponent implements OnInit
//const dmp = await this.dmpService.getSingle(this.dmpId).toPromise();
this.lookup.dmpSubQuery = new DmpLookup();
this.lookup.dmpSubQuery.ids = [Guid.parse(this.dmpId)];
this.lookup.associatedDmpIds.push(Guid.parse(this.dmpId));
if (params['dmpLabel'] !== undefined) {
this.titlePrefix = 'for ' + params['dmpLabel'];
}
@ -280,8 +281,28 @@ export class DescriptionListingComponent extends BaseComponent implements OnInit
openFiltersDialog(): void {
//TODO: Add filters dialog
const dialogRef = this.dialog.open(DescriptionFilterDialogComponent, {
width: '456px',
height: '100%',
id: 'filters',
restoreFocus: false,
position: { right: '0px;' },
panelClass: 'dialog-side-panel',
data: {
isPublic: this.isPublic ?? true,
filterForm: this.lookup.buildForm(),
updateDataFn: this.updateDataFn.bind(this)
}
});
}
updateDataFn(filterForm: UntypedFormGroup): void {
let testLookup = this.lookup;
testLookup.from(filterForm);
console.log('testLookup: ', testLookup);
// this.lookup.from(filterForm);
// this.refresh(this.lookup);
}
hasScrollbar(): boolean {
return document.getElementById("main-page").scrollHeight > document.documentElement.clientHeight
@ -346,7 +367,7 @@ export class DescriptionListingComponent extends BaseComponent implements OnInit
});
}
hasLikeCriteria(): boolean {
hasLikeFilters(): boolean {
return this.lookup.like !== undefined && this.lookup.like !== null;
}

View File

@ -7,6 +7,9 @@ import { CommonUiModule } from '@common/ui/common-ui.module';
import { DescriptionListingRoutingModule } from './description-listing.routing';
import { DescriptionCopyDialogModule } from '../description-copy-dialog/description-copy-dialog.module';
import { StartNewDescriptionDialogModule } from '../start-new-description-dialog/start-new-description-dialog.module';
import { DescriptionFilterDialogComponent } from './filtering/description-filter-dialogue/description-filter-dialog.component';
import { DescriptionFilterComponent } from './filtering/description-filter.component';
import { AutoCompleteModule } from '@app/library/auto-complete/auto-complete.module';
@NgModule({
imports: [
@ -15,11 +18,14 @@ import { StartNewDescriptionDialogModule } from '../start-new-description-dialog
FormattingModule,
DescriptionCopyDialogModule,
StartNewDescriptionDialogModule,
DescriptionListingRoutingModule
DescriptionListingRoutingModule,
AutoCompleteModule,
],
declarations: [
DescriptionListingComponent,
DescriptionListingItemComponent,
DescriptionFilterDialogComponent,
DescriptionFilterComponent,
],
exports: [
DescriptionListingItemComponent

View File

@ -0,0 +1,7 @@
<a class="col-auto d-flex pointer" (click)="onClose()"><span class="ml-auto mt-3 material-icons clear-icon">clear</span></a>
<app-description-filter-component
[filterFormGroup]="data.filterForm"
[isPublic]="data.isPublic"
(filterChanged)="onFilterChanged($event)"
></app-description-filter-component>

View File

@ -0,0 +1,44 @@
import { Component, Inject, OnInit, ViewChild } from '@angular/core';
import { UntypedFormGroup } from '@angular/forms';
import { MAT_DIALOG_DATA, MatDialogRef } from '@angular/material/dialog';
import { AnalyticsService } from '@app/core/services/matomo/analytics-service';
import { DescriptionFilterComponent } from '../description-filter.component';
@Component({
selector: 'description-filter-dialog-component',
templateUrl: './description-filter-dialog.component.html',
styleUrls: ['./description-filter-dialog.component.scss']
})
export class DescriptionFilterDialogComponent implements OnInit {
@ViewChild(DescriptionFilterComponent, { static: true }) filter: DescriptionFilterComponent;
constructor(
public dialogRef: MatDialogRef<DescriptionFilterComponent>,
private analyticsService: AnalyticsService,
@Inject(MAT_DIALOG_DATA) public data: {
isPublic: boolean,
status: Number,
filterForm: UntypedFormGroup,
updateDataFn: Function,
}
) {
}
ngOnInit() {
this.analyticsService.trackPageView(AnalyticsService.DescriptionFilterDialog);
}
onNoClick(): void {
this.dialogRef.close();
}
onClose(): void {
this.dialogRef.close();
}
onFilterChanged(formGroup: UntypedFormGroup) {
this.data.updateDataFn(formGroup);
}
}

View File

@ -1,11 +1,13 @@
{{formGroup?.value | json}}
<div class="dmp-criteria">
<div class="filters">
<div class="filters container-fluid">
<div class="row justify-content-center">
<div class="col-10">
<h6 class="criteria-title">{{'CRITERIA.FILTERS'| translate}}</h6>
<h6 class="criteria-title">{{'DESCRIPTION-LISTING.FILTERS.NAME'| translate}}</h6>
</div>
</div>
<div class="row justify-content-center">
<div class="row justify-content-center" *ngIf="formGroup">
<!-- Search Filter-->
<!-- <mat-form-field class="col-11 search">
<input matInput placeholder="{{'CRITERIA.GRANTS.LIKE'| translate}}" name="grantCriteriaLike"
@ -18,24 +20,19 @@
<!-- Status Filter-->
<div class="col-10" *ngIf="!isPublic">
<h6 class="category-title">{{'CRITERIA.DATA-SETS.STATUS'| translate}}</h6>
<mat-radio-group aria-label="Select an option" [formControl]="formGroup.get('status')">
<mat-list-item>
<mat-radio-button value="null" [checked]="!formGroup.get('status').value">{{ 'TYPES.DATASET-STATUS.ANY' | translate }}</mat-radio-button>
</mat-list-item>
<mat-list-item>
<mat-radio-button value="0">{{ 'TYPES.DATASET-STATUS.DRAFT' | translate }}</mat-radio-button>
</mat-list-item>
<mat-list-item>
<mat-radio-button value="1">{{ 'TYPES.DATASET-STATUS.FINALISED' | translate }}</mat-radio-button>
</mat-list-item>
<h6 class="category-title">{{ 'DESCRIPTION-LISTING.FILTERS.STATUS.NAME' | translate}}</h6>
<mat-radio-group aria-label="Select an option" [formControl]="formGroup.get('status')" class="row">
<mat-radio-button value="null" [checked]="!formGroup.get('status').value" class="col-12">{{ 'DESCRIPTION-LISTING.FILTERS.STATUS.TYPES.ANY' | translate }}</mat-radio-button>
<mat-radio-button value="0" class="col-12">{{ 'DESCRIPTION-LISTING.FILTERS.STATUS.TYPES.DRAFT' | translate }}</mat-radio-button>
<mat-radio-button value="1" class="col-12">{{ 'DESCRIPTION-LISTING.FILTERS.STATUS.TYPES.FINALIZED' | translate }}</mat-radio-button>
</mat-radio-group>
<hr>
</div>
<!-- End of Status Filter-->
<!-- Grant Status -->
<div class="col-10" *ngIf="isPublic">
<!-- TODO -->
<!-- <div class="col-10" *ngIf="isPublic">
<h6 class="category-title">{{ 'FACET-SEARCH.GRANT-STATUS.TITLE' | translate }}</h6>
<mat-radio-group [formControl]="formGroup.get('grantStatus')">
<mat-list-item>
@ -49,16 +46,15 @@
</mat-list-item>
</mat-radio-group>
<hr>
</div>
</div> -->
<!-- End of Grant Status -->
<!-- Related Dataset Templates Filters -->
<div class="col-10">
<h6 class="category-title">{{'CRITERIA.DATA-SETS.RELATED-DATASET-TEMPLATES' | translate}}</h6>
<mat-form-field class="mb-4">
<mat-label>{{'CRITERIA.DATA-SETS.SELECT-DATASET-TEMPLATES' | translate }}</mat-label>
<app-multiple-auto-complete [formControl]="formGroup.get('datasetTemplates')" [configuration]="datasetTemplateAutoCompleteConfiguration">
</app-multiple-auto-complete>
<h6 class="category-title">{{'DESCRIPTION-LISTING.FILTERS.RELATED-DESCRIPTION-TEMPLATES.NAME' | translate}}</h6>
<mat-form-field class="w-100">
<mat-label>{{'DESCRIPTION-LISTING.FILTERS.RELATED-DESCRIPTION-TEMPLATES.PLACEHOLDER' | translate }}</mat-label>
<app-multiple-auto-complete [formControl]="formGroup.get('descriptionTemplates')" [configuration]="descriptionTemplateAutoCompleteConfiguration"></app-multiple-auto-complete>
</mat-form-field>
<hr>
</div>
@ -66,26 +62,26 @@
<!-- Related DMP Filters -->
<div class="col-10">
<h6 class="category-title">{{'CRITERIA.DATA-SETS.RELATED-DMP' | translate}}</h6>
<mat-form-field class="mb-4">
<mat-label>{{'CRITERIA.DATA-SETS.SELECT-DMP' | translate }}</mat-label>
<app-multiple-auto-complete [formControl]="formGroup.get('groupIds')" [configuration]="dmpAutoCompleteConfiguration">
</app-multiple-auto-complete>
<h6 class="category-title">{{'DESCRIPTION-LISTING.FILTERS.ASSOCIATED-DMPS.NAME' | translate}}</h6>
<mat-form-field class="w-100">
<mat-label>{{'DESCRIPTION-LISTING.FILTERS.ASSOCIATED-DMPS.PLACEHOLDER' | translate }}</mat-label>
<app-multiple-auto-complete [formControl]="formGroup.get('associatedDmpIds')" [configuration]="dmpAutoCompleteConfiguration"></app-multiple-auto-complete>
</mat-form-field>
<hr>
</div>
<!-- End of Related DMP Filters -->
<!-- All Versions Filter-->
<!-- TODO -->
<div class="col-10" *ngIf="!isPublic">
<h6 class="category-title">{{'CRITERIA.DATA-SETS.ALL-VERSIONS'| translate}}</h6>
<mat-slide-toggle [formControl]="formGroup.get('allVersions')"></mat-slide-toggle>
<h6 class="category-title">{{'DESCRIPTION-LISTING.FILTERS.ALL-VERSIONS'| translate}}</h6>
<mat-slide-toggle [formControl]="formGroup.get('showAllVersions')"></mat-slide-toggle>
<hr>
</div>
<!-- End of All Versions Filter-->
<!-- Related Grant Filters -->
<div class="col-10">
<!-- <div class="col-10">
<h6 class="category-title">{{'CRITERIA.DATA-SETS.RELATED-GRANT' | translate}}</h6>
<mat-form-field class="mb-4">
<mat-label>{{'CRITERIA.DATA-SETS.SELECT-GRANTS' | translate }}</mat-label>
@ -93,11 +89,12 @@
</app-multiple-auto-complete>
</mat-form-field>
<hr>
</div>
</div> -->
<!-- End of Related Grants Filters -->
<!-- Related Collaborators Filters -->
<div class="col-10" *ngIf="!isPublic">
<!-- TODO -->
<!-- <div class="col-10" *ngIf="!isPublic">
<h6 class="category-title">{{'CRITERIA.DATA-SETS.RELATED-COLLABORATORS' | translate}}</h6>
<mat-form-field class="mb-4">
<mat-label>{{'CRITERIA.DATA-SETS.SELECT-COLLABORATORS' | translate }}</mat-label>
@ -105,29 +102,23 @@
</app-multiple-auto-complete>
</mat-form-field>
<hr>
</div>
</div> -->
<!-- End of Related Collaborators Filters -->
<!-- Role Filter -->
<div class="col-10" *ngIf="isAuthenticated()">
<h6 class="category-title">{{'CRITERIA.DATA-SETS.ROLE' | translate }}</h6>
<mat-radio-group aria-label="Select an option" [formControl]="formGroup.get('role')">
<mat-list-item>
<mat-radio-button checked value="null" [checked]="!formGroup.get('role').value">{{ 'TYPES.DATASET-ROLE.ANY' | translate }}</mat-radio-button>
</mat-list-item>
<mat-list-item>
<mat-radio-button value="0">{{ 'TYPES.DATASET-ROLE.OWNER' | translate }}</mat-radio-button>
</mat-list-item>
<mat-list-item>
<mat-radio-button value="1">{{ 'TYPES.DATASET-ROLE.MEMBER' | translate }}</mat-radio-button>
</mat-list-item>
<h6 class="category-title">{{'DESCRIPTION-LISTING.FILTERS.ROLE.NAME' | translate }}</h6>
<mat-radio-group aria-label="Select an option" [formControl]="formGroup.get('role')" class="row">
<mat-radio-button value="null" [checked]="!formGroup.get('role').value" class="col-12">{{ 'DESCRIPTION-LISTING.FILTERS.ROLE.TYPES.ANY' | translate }}</mat-radio-button>
<mat-radio-button value="0" class="col-12">{{ 'DESCRIPTION-LISTING.FILTERS.ROLE.TYPES.OWNER' | translate }}</mat-radio-button>
<mat-radio-button value="1" class="col-12">{{ 'DESCRIPTION-LISTING.FILTERS.ROLE.TYPES.MEMBER' | translate }}</mat-radio-button>
</mat-radio-group>
<hr>
</div>
<!-- End of Role Filter -->
<!-- Related Organization Filter -->
<div class="col-10">
<!-- <div class="col-10">
<h6 class="category-title">{{'CRITERIA.DATA-SETS.ORGANIZATION' | translate }}</h6>
<mat-form-field class="mb-4">
<mat-label>{{'CRITERIA.DATA-SETS.SELECT-ORGANIZATIONS' | translate}}</mat-label>
@ -135,15 +126,15 @@
</app-multiple-auto-complete>
</mat-form-field>
<hr>
</div>
</div> -->
<!-- End of Related Organization Filter -->
<!-- Tags Filter -->
<div class="col-10">
<h6 class="category-title">{{'CRITERIA.DATA-SETS.TAGS' | translate }}</h6>
<mat-form-field class="mb-4">
<mat-label>{{'CRITERIA.DATA-SETS.SELECT-TAGS' | translate}}</mat-label>
<app-multiple-auto-complete [formControl]="formGroup.get('tags')" [configuration]="tagsAutoCompleteConfiguration">
<h6 class="category-title">{{'DESCRIPTION-LISTING.FILTERS.TAGS.NAME' | translate }}</h6>
<mat-form-field class="w-100 mb-4">
<mat-label>{{'DESCRIPTION-LISTING.FILTERS.TAGS.PLACEHOLDER' | translate}}</mat-label>
<app-multiple-auto-complete [formControl]="formGroup.get('tags')" [configuration]="tagAutoCompleteConfiguration">
</app-multiple-auto-complete>
</mat-form-field>
</div>

View File

@ -15,12 +15,7 @@
.import {
margin: 10px;
padding: 0px;
}
.filters {
// border: 1px solid #e4e4e4;
// border-radius: 5px;
}
}
.criteria-title {
color: black;
@ -54,3 +49,7 @@
::ng-deep .mat-form-field-appearance-outline .mat-form-field-infix {
padding: 0.3rem 0rem 0.6rem 0rem !important;
}
::ng-deep label {
margin: 0;
}

View File

@ -0,0 +1,97 @@
import { Component, EventEmitter, Input, OnChanges, OnInit, Output, SimpleChanges } from '@angular/core';
import { UntypedFormGroup } from '@angular/forms';
import { DescriptionStatus } from '@app/core/common/enum/description-status';
import { AuthService } from '@app/core/services/auth/auth.service';
import { DescriptionTemplateTypeService } from '@app/core/services/description-template-type/description-template-type.service';
import { DmpService } from '@app/core/services/dmp/dmp.service';
import { TagService } from '@app/core/services/tag/tag.service';
import { EnumUtils } from '@app/core/services/utilities/enum-utils.service';
import { MultipleAutoCompleteConfiguration } from '@app/library/auto-complete/multiple/multiple-auto-complete-configuration';
import { BaseCriteriaComponent } from '@app/ui/misc/criteria/base-criteria.component';
import { ValidationErrorModel } from '@common/forms/validation/error-model/validation-error-model';
import { takeUntil } from 'rxjs/operators';
@Component({
selector: 'app-description-filter-component',
templateUrl: './description-filter.component.html',
styleUrls: ['./description-filter.component.scss']
})
export class DescriptionFilterComponent extends BaseCriteriaComponent implements OnInit, OnChanges {
@Input() status;
@Input() isPublic: boolean;
@Input() filterFormGroup: UntypedFormGroup;
@Output() filterChanged: EventEmitter<any> = new EventEmitter();
public criteria: any;
public filteringTagsAsync = false;
statuses = DescriptionStatus;
options: UntypedFormGroup;
get tagAutoCompleteConfiguration(): MultipleAutoCompleteConfiguration {
return this.tagService.multipleAutocompleteConfiguration;
};
get descriptionTemplateAutoCompleteConfiguration(): MultipleAutoCompleteConfiguration {
return this.descriptionTemplateTypeService.getMultipleAutoCompleteSearchConfiguration();
};
get dmpAutoCompleteConfiguration(): MultipleAutoCompleteConfiguration {
return this.dmpService.multipleAutocompleteConfiguration;
};
constructor(
public enumUtils: EnumUtils,
private authentication: AuthService,
private descriptionTemplateTypeService: DescriptionTemplateTypeService,
private dmpService: DmpService,
private tagService: TagService,
) {
super(new ValidationErrorModel());
}
ngOnInit() {
super.ngOnInit();
}
ngOnChanges(changes: SimpleChanges): void {
if (changes['filterFormGroup']) {
this.formGroup = this.filterFormGroup;
this.formGroup.get('status')?.valueChanges
.pipe(takeUntil(this._destroyed))
.subscribe(x => this.controlModified());
this.formGroup.get('role')?.valueChanges
.pipe(takeUntil(this._destroyed))
.subscribe(x => this.controlModified());
this.formGroup.get('descriptionTemplates')?.valueChanges
.pipe(takeUntil(this._destroyed))
.subscribe(x => this.controlModified());
this.formGroup.get('associatedDmpIds')?.valueChanges
.pipe(takeUntil(this._destroyed))
.subscribe(x => this.controlModified());
this.formGroup.get('showAllVersions')?.valueChanges
.pipe(takeUntil(this._destroyed))
.subscribe(x => this.controlModified());
this.formGroup.get('tags')?.valueChanges
.pipe(takeUntil(this._destroyed))
.subscribe(x => this.controlModified());
}
}
controlModified(): void {
this.clearErrorModel();
this.filterChanged.emit(this.formGroup);
if (this.refreshCallback != null &&
(this.formGroup.get('like')?.value == null || this.formGroup.get('like')?.value.length === 0 || this.formGroup.get('like')?.value.length > 2)
) {
setTimeout(() => this.refreshCallback(true));
}
}
isAuthenticated(): boolean {
return this.authentication.currentAccountIsAuthenticated();
}
}

View File

@ -0,0 +1,55 @@
import { HttpClient } from '@angular/common/http';
import { Component, Inject, OnInit, ViewChild } from '@angular/core';
import { UntypedFormBuilder, UntypedFormControl, UntypedFormGroup } from '@angular/forms';
import { MAT_DIALOG_DATA, MatDialogRef } from '@angular/material/dialog';
import { AnalyticsService } from '@app/core/services/matomo/analytics-service';
import { DescriptionFiltersComponent } from '../description-filter.component';
@Component({
selector: 'description-filter-dialog-component',
templateUrl: './description-filter-dialog.component.html',
styleUrls: ['./description-filter-dialog.component.scss']
})
export class DescriptionFilterDialogComponent implements OnInit {
@ViewChild(DescriptionFiltersComponent, { static: true }) filter: DescriptionFiltersComponent;
public filtersFormGroup = new UntypedFormBuilder().group({
status: new UntypedFormControl(),
role: new UntypedFormControl(),
allVersions: new UntypedFormControl(),
descriptionTemplates: new UntypedFormControl(),
relatedDmps: new UntypedFormControl(),
tags: new UntypedFormControl(),
});
constructor(
public dialogRef: MatDialogRef<DescriptionFiltersComponent>,
private httpClient: HttpClient,
private analyticsService: AnalyticsService,
@Inject(MAT_DIALOG_DATA) public data: {
isPublic: boolean,
status: Number,
filterForm: UntypedFormGroup,
updateDataFn: Function }
) {
}
ngOnInit() {
this.analyticsService.trackPageView(AnalyticsService.DescriptionFiltersDialog);
// this.filters.setfilters(this.data.filters);
}
onNoClick(): void {
this.dialogRef.close();
}
onClose(): void {
this.dialogRef.close();
}
onFiltersChanged(event) {
this.data.updateDataFn(this.filter);
}
}

View File

@ -840,6 +840,9 @@
"DESCRIPTION": "Description",
"GRANT": "Grant",
"LOCKED": "Description is Locked by another user",
"PART-OF": "Part of",
"DMP": "Plan",
"EMPTY-LIST": "Nothing here yet.",
"ACTIONS": {
"ADD-DESCRIPTION": "Add Description",
"TAKE-A-TOUR": "Do you need help? Take a tour..",
@ -860,9 +863,38 @@
"COPY": "Copy",
"CANCEL": "Cancel"
},
"PART-OF": "Part of",
"DMP": "Plan",
"EMPTY-LIST": "Nothing here yet."
"FILTERS": {
"NAME": "Filters",
"STATUS": {
"NAME": "Status",
"TYPES": {
"ANY": "Any",
"DRAFT": "Draft",
"FINALIZED": "Finalized"
}
},
"RELATED-DESCRIPTION-TEMPLATES": {
"NAME": "Related Description Templates",
"PLACEHOLDER": "Select Description Templates"
},
"ASSOCIATED-DMPS": {
"NAME": "Related DMPs",
"PLACEHOLDER": "Select DMPs"
},
"ALL-VERSIONS": "From All Versions",
"ROLE": {
"NAME": "Role",
"TYPES": {
"ANY": "Any",
"OWNER": "Owner",
"MEMBER": "Member"
}
},
"TAGS": {
"NAME": "Tags",
"PLACEHOLDER": "Select Tags"
}
}
},
"DESCRIPTION-EDITOR": {
"TITLE-NEW": "New Description",