Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

refactor: decouple default search UI interface logic from backend #1578

8 changes: 7 additions & 1 deletion src/app/app-config.service.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,11 @@
import { HttpClient } from "@angular/common/http";
import { Injectable } from "@angular/core";
import { timeout } from "rxjs/operators";
import { TableColumn } from "state-management/models";
import {
DatasetsListSettings,
LabelMaps,
TableColumn,
} from "state-management/models";

export interface OAuth2Endpoint {
authURL: string;
Expand Down Expand Up @@ -91,6 +95,8 @@ export interface AppConfig {
notificationInterceptorEnabled: boolean;
pidSearchMethod?: string;
metadataEditingUnitListDisabled?: boolean;
defaultDatasetsListSettings: DatasetsListSettings;
labelMaps: LabelMaps;
}

@Injectable()
Expand Down
5 changes: 0 additions & 5 deletions src/app/app.component.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,6 @@ import {
fetchCurrentUserAction,
loadDefaultSettings,
logoutAction,
setDatasetTableColumnsAction,
} from "state-management/actions/user.actions";
import { MatSnackBar } from "@angular/material/snack-bar";
import { Meta, Title } from "@angular/platform-browser";
Expand Down Expand Up @@ -74,10 +73,6 @@ export class AppComponent implements OnDestroy, OnInit, AfterViewChecked {

this.store.dispatch(loadDefaultSettings());

this.store.dispatch(
setDatasetTableColumnsAction({ columns: this.config.localColumns }),
);

this.store.dispatch(fetchCurrentUserAction());
if (window.location.pathname.indexOf("logout") !== -1) {
this.logout();
Expand Down
6 changes: 5 additions & 1 deletion src/app/datasets/dashboard/dashboard.component.ts
Original file line number Diff line number Diff line change
Expand Up @@ -160,7 +160,11 @@ export class DashboardComponent implements OnInit, OnDestroy {
updateColumnSubscription(): void {
this.subscriptions.push(
this.loggedIn$.subscribe((status) => {
const columns = this.appConfig.localColumns;
// NOTE: this.appConfig.localColumns is for backward compatibility.
// it should be removed once localColumns is removed from the appConfig
const columns =
this.appConfig.defaultDatasetsListSettings.columns ||
this.appConfig.localColumns;
this.store.dispatch(setDatasetTableColumnsAction({ columns }));
if (!status) {
this.tableColumns$ = this.store
Expand Down
22 changes: 14 additions & 8 deletions src/app/datasets/datasets-filter/datasets-filter.component.html
Original file line number Diff line number Diff line change
Expand Up @@ -6,16 +6,22 @@
<mat-card-content>
<ng-container *ngIf="filterConfigs$ | async as filterConfigs">
<ng-container *ngFor="let filterConfig of filterConfigs">
<ng-container *ngIf="filterConfig.visible">
<ng-container *ngComponentOutlet="resolveComponentType(filterConfig.type); inputs: { 'clear': clearSearchBar}"></ng-container>
<ng-container *ngIf="filterConfig">
<ng-container
*ngComponentOutlet="
renderComponent(filterConfig);
inputs: { clear: clearSearchBar }
"
></ng-container>
</ng-container>
</ng-container>
</ng-container>


<div class="section-container" *ngIf="appConfig.scienceSearchEnabled">
<ng-container *ngFor="let condition of scientificConditions$ | async">
<ng-container *ngComponentOutlet="ConditionFilterComponent; inputs: { condition }"></ng-container>
<ng-container
*ngComponentOutlet="ConditionFilterComponent; inputs: { condition }"
></ng-container>
</ng-container>
</div>

Expand All @@ -29,7 +35,7 @@
</button>
</div>

<div class="section-container">
<div class="section-container">
<button
mat-raised-button
color="primary"
Expand All @@ -46,9 +52,9 @@
class="datasets-filters-clear-all-button"
(click)="reset()"
>
<mat-icon>undo</mat-icon>
Reset Filters
</button>
<mat-icon>undo</mat-icon>
Reset Filters
</button>
</div>
</mat-card-content>
</mat-card>
140 changes: 95 additions & 45 deletions src/app/datasets/datasets-filter/datasets-filter.component.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,13 @@
import { Component, OnDestroy } from "@angular/core";
import {
Component,
OnDestroy,
OnInit,
Type,
ViewContainerRef,
} from "@angular/core";
import { MatDialog } from "@angular/material/dialog";
import { Store } from "@ngrx/store";

import { cloneDeep, isEqual } from "lodash-es";
import {
selectHasAppliedFilters,
selectScientificConditions,
Expand All @@ -12,11 +18,8 @@ import {
fetchDatasetsAction,
fetchFacetCountsAction,
} from "state-management/actions/datasets.actions";
import { Subscription } from "rxjs";
import {
deselectAllCustomColumnsAction,
updateConditionsConfigs,
updateFilterConfigs,
updateUserSettingsAction,
} from "state-management/actions/user.actions";
import { AppConfigService } from "app-config.service";
Expand All @@ -36,28 +39,31 @@ import { TypeFilterComponent } from "../../shared/modules/filters/type-filter.co
import { KeywordFilterComponent } from "../../shared/modules/filters/keyword-filter.component";
import { DateRangeFilterComponent } from "../../shared/modules/filters/date-range-filter.component";
import { TextFilterComponent } from "../../shared/modules/filters/text-filter.component";

const COMPONENT_MAP: { [key: string]: any } = {
PidFilterComponent: PidFilterComponent,
PidFilterContainsComponent: PidFilterContainsComponent,
PidFilterStartsWithComponent: PidFilterStartsWithComponent,
LocationFilterComponent: LocationFilterComponent,
GroupFilterComponent: GroupFilterComponent,
TypeFilterComponent: TypeFilterComponent,
KeywordFilterComponent: KeywordFilterComponent,
DateRangeFilterComponent: DateRangeFilterComponent,
TextFilterComponent: TextFilterComponent,
ConditionFilterComponent: ConditionFilterComponent,
import { Filters, FilterConfig } from "shared/modules/filters/filters.module";
import { FilterComponentInterface } from "shared/modules/filters/interface/filter-component.interface";
import { Subscription } from "rxjs";
import { take } from "rxjs/operators";

const COMPONENT_MAP: { [K in Filters]: Type<any> } = {
PidFilter: PidFilterComponent,
PidFilterContains: PidFilterContainsComponent,
PidFilterStartsWith: PidFilterStartsWithComponent,
LocationFilter: LocationFilterComponent,
GroupFilter: GroupFilterComponent,
TypeFilter: TypeFilterComponent,
KeywordFilter: KeywordFilterComponent,
DateRangeFilter: DateRangeFilterComponent,
TextFilter: TextFilterComponent,
ConditionFilter: ConditionFilterComponent,
};

@Component({
selector: "datasets-filter",
templateUrl: "datasets-filter.component.html",
styleUrls: ["datasets-filter.component.scss"],
})
export class DatasetsFilterComponent implements OnDestroy {
export class DatasetsFilterComponent implements OnInit, OnDestroy {
private subscriptions: Subscription[] = [];

protected readonly ConditionFilterComponent = ConditionFilterComponent;

filterConfigs$ = this.store.select(selectFilters);
Expand All @@ -74,13 +80,34 @@ export class DatasetsFilterComponent implements OnDestroy {

isInEditMode = false;

labelMaps: { [key: string]: string } = {};

constructor(
public appConfigService: AppConfigService,
public dialog: MatDialog,
private store: Store,
private asyncPipe: AsyncPipe,
private viewContainerRef: ViewContainerRef,
) {}

ngOnInit() {
this.getAllComponentLabels();
}

getAllComponentLabels() {
Object.entries(COMPONENT_MAP).forEach(([key, component]) => {
const componentRef = this.viewContainerRef.createComponent(component);

const instance = componentRef.instance as FilterComponentInterface;

const label = instance.label;

this.labelMaps[key] = label;

componentRef.destroy();
});
}

reset() {
this.clearSearchBar = true;

Expand All @@ -93,40 +120,57 @@ export class DatasetsFilterComponent implements OnDestroy {
}, 0);
}

showDatasetsFilterSettingsDialog() {
async showDatasetsFilterSettingsDialog() {
// Get initial filter and condition configs
// to compare with the updated ones
// and dispatch the updated ones if they changed
// This is to prevent unnecessary API calls
const initialFilterConfigs = await this.filterConfigs$
.pipe(take(1))
.toPromise();
const initialConditionConfigs = await this.conditionConfigs$
.pipe(take(1))
.toPromise();

const initialFilterConfigsCopy = cloneDeep(initialFilterConfigs);
const initialConditionConfigsCopy = cloneDeep(initialConditionConfigs);

const dialogRef = this.dialog.open(DatasetsFilterSettingsComponent, {
width: "60%",
width: "60vw",
data: {
filterConfigs: this.asyncPipe.transform(this.filterConfigs$),
conditionConfigs: this.asyncPipe.transform(this.conditionConfigs$),
labelMaps: this.labelMaps,
},
});

dialogRef.afterClosed().subscribe((result) => {
console.log("The dialog was closed");
if (result) {
// Handle the selected filter
console.log(`Selected filter: ${result}`);
this.store.dispatch(
updateFilterConfigs({ filterConfigs: result.filterConfigs }),
);
this.store.dispatch(
updateUserSettingsAction({
property: { filters: result.filterConfigs },
}),
const filtersChanged = !isEqual(
initialFilterConfigsCopy,
result.filterConfigs,
);
this.store.dispatch(
updateConditionsConfigs({
conditionConfigs: result.conditionConfigs,
}),
);
this.store.dispatch(
updateUserSettingsAction({
property: { conditions: result.conditionConfigs },
}),
const conditionsChanged = !isEqual(
initialConditionConfigsCopy,
result.conditionConfigs,
);

// this.cdr.detectChanges();
if (filtersChanged || conditionsChanged) {
const updatedProperty = {};

if (filtersChanged) {
updatedProperty["filters"] = result.filterConfigs;
}

if (conditionsChanged) {
updatedProperty["conditions"] = result.conditionConfigs;
}
this.store.dispatch(
updateUserSettingsAction({
property: updatedProperty,
}),
);
}
}
});
}
Expand All @@ -137,11 +181,17 @@ export class DatasetsFilterComponent implements OnDestroy {
this.store.dispatch(fetchFacetCountsAction());
}

renderComponent(filterObj: FilterConfig): any {
const key = Object.keys(filterObj)[0];
const isEnabled = filterObj[key];

if (!isEnabled || !COMPONENT_MAP[key]) {
return null;
}

return COMPONENT_MAP[key];
}
ngOnDestroy() {
this.subscriptions.forEach((subscription) => subscription.unsubscribe());
}

resolveComponentType(typeAsString: string): any {
return COMPONENT_MAP[typeAsString];
}
}
Loading