diff --git a/src/app/datasets/dashboard/dashboard.component.ts b/src/app/datasets/dashboard/dashboard.component.ts index b38de06bc..37f823f78 100644 --- a/src/app/datasets/dashboard/dashboard.component.ts +++ b/src/app/datasets/dashboard/dashboard.component.ts @@ -14,6 +14,7 @@ import { addDatasetAction, fetchDatasetCompleteAction, fetchMetadataKeysAction, + fetchMetadataTypesAction, } from "state-management/actions/datasets.actions"; import { @@ -23,7 +24,7 @@ import { selectSelectedDatasets, selectPagination, } from "state-management/selectors/datasets.selectors"; -import { distinctUntilChanged, filter, map, take } from "rxjs/operators"; +import { distinctUntilChanged, filter, map, skip, take } from "rxjs/operators"; import { MatDialog } from "@angular/material/dialog"; import { MatSidenav } from "@angular/material/sidenav"; import { AddDatasetDialogComponent } from "datasets/add-dataset-dialog/add-dataset-dialog.component"; @@ -180,7 +181,7 @@ export class DashboardComponent implements OnInit, OnDestroy { ngOnInit() { this.store.dispatch(prefillBatchAction()); this.store.dispatch(fetchMetadataKeysAction()); - this.store.dispatch(fetchDatasetsAction()); + this.store.dispatch(fetchMetadataTypesAction()); this.updateColumnSubscription(); @@ -189,6 +190,7 @@ export class DashboardComponent implements OnInit, OnDestroy { .pipe( map(([pagination, _, loggedIn]) => [pagination, loggedIn]), distinctUntilChanged(deepEqual), + skip(1), // TODO avoid this hack ) .subscribe((obj) => { this.store.dispatch(fetchDatasetsAction()); diff --git a/src/app/datasets/dataset-details-dashboard/dataset-details-dashboard.component.ts b/src/app/datasets/dataset-details-dashboard/dataset-details-dashboard.component.ts index e022a675b..635be1d63 100644 --- a/src/app/datasets/dataset-details-dashboard/dataset-details-dashboard.component.ts +++ b/src/app/datasets/dataset-details-dashboard/dataset-details-dashboard.component.ts @@ -23,6 +23,8 @@ import { fetchAttachmentsAction, fetchDatablocksAction, fetchDatasetAction, + fetchMetadataKeysAction, + fetchMetadataTypesAction, fetchOrigDatablocksAction, fetchRelatedDatasetsAction, } from "state-management/actions/datasets.actions"; @@ -119,6 +121,9 @@ export class DatasetDetailsDashboardComponent ) {} ngOnInit() { + this.store.dispatch(fetchMetadataKeysAction()); + this.store.dispatch(fetchMetadataTypesAction()); + this.subscriptions.push( this.route.params.pipe(pluck("id")).subscribe((id: string) => { if (id) { diff --git a/src/app/datasets/dataset-table/dataset-table.component.html b/src/app/datasets/dataset-table/dataset-table.component.html index def257e90..2e31cd99f 100644 --- a/src/app/datasets/dataset-table/dataset-table.component.html +++ b/src/app/datasets/dataset-table/dataset-table.component.html @@ -1,4 +1,15 @@ -
+
+ +
+
+

No data loaded. Please click the button below to load data.

+ +
+
+ +
@@ -407,3 +418,4 @@
+
diff --git a/src/app/datasets/dataset-table/dataset-table.component.scss b/src/app/datasets/dataset-table/dataset-table.component.scss index 3b81e2ad9..6bf826c65 100644 --- a/src/app/datasets/dataset-table/dataset-table.component.scss +++ b/src/app/datasets/dataset-table/dataset-table.component.scss @@ -1,3 +1,33 @@ +.dataset-container { + position: relative; /* Container to position the overlay absolutely */ + + .opaque { + opacity: 0.5; /* Make the table semi-transparent when overlay is active */ + } + + .overlay { + position: absolute; + top: 0; + left: 0; + right: 0; + bottom: 0; + background-color: rgba(255, 255, 255, 0.8); /* Semi-transparent background */ + display: flex; + justify-content: center; + align-items: center; + z-index: 10; /* Ensure the overlay is above the table */ + pointer-events: all; /* Make sure the overlay captures the button click */ + } + + .overlay-content { + text-align: center; + background: white; + padding: 20px; + box-shadow: 0 2px 10px rgba(0, 0, 0, 0.2); + border-radius: 8px; + } +} + .dataset-table { mat-table { overflow-x: scroll; diff --git a/src/app/datasets/dataset-table/dataset-table.component.ts b/src/app/datasets/dataset-table/dataset-table.component.ts index ca15bf4c2..760534278 100644 --- a/src/app/datasets/dataset-table/dataset-table.component.ts +++ b/src/app/datasets/dataset-table/dataset-table.component.ts @@ -12,7 +12,7 @@ import { import { Dataset, TableColumn } from "state-management/models"; import { MatCheckboxChange } from "@angular/material/checkbox"; import { Subscription } from "rxjs"; -import { Store } from "@ngrx/store"; +import { ActionsSubject, Store } from "@ngrx/store"; import { clearSelectionAction, selectDatasetAction, @@ -20,6 +20,9 @@ import { selectAllDatasetsAction, changePageAction, sortByColumnAction, + fetchDatasetsAction, + fetchDatasetCompleteAction, + fetchFacetCountsAction, } from "state-management/actions/datasets.actions"; import { @@ -28,6 +31,8 @@ import { selectPage, selectTotalSets, selectDatasetsInBatch, + selectLoadData, + selectCurrentDataset, } from "state-management/selectors/datasets.selectors"; import { PageChangeEvent } from "shared/modules/table/table.component"; import { @@ -37,6 +42,13 @@ import { import { get } from "lodash"; import { AppConfigService } from "app-config.service"; import { selectCurrentUser } from "state-management/selectors/user.selectors"; +import { + MatDialog, + MatDialogModule, + MatDialogRef, +} from "@angular/material/dialog"; +import { MatIconModule } from "@angular/material/icon"; +import { MatButtonModule } from "@angular/material/button"; export interface SortChangeEvent { active: string; direction: "asc" | "desc" | ""; @@ -47,6 +59,61 @@ export interface SortChangeEvent { // derivedDatasetsNum: number; // } +@Component({ + selector: "app-guard-dialog", + template: ` +

+ warning + Loading All Data +

+
+

+ You are about to load all available data. This action might take a long + time and consume significant resources. +

+

+ It's recommended to specify search or filter criteria for better + performance. +

+
+
+ + +
+ `, + styles: [ + ` + mat-dialog-content { + font-size: 16px; + line-height: 1.6; + } + + mat-dialog-actions { + padding-right: 8px; + } + + h1 mat-icon { + margin-right: 8px; + } + `, + ], + standalone: true, + imports: [MatIconModule, MatDialogModule, MatButtonModule], +}) +class GuardDialogComponent { + constructor(public dialogRef: MatDialogRef) {} + + onConfirm(): void { + this.dialogRef.close(true); // User confirmed loading all data + } + + onCancel(): void { + this.dialogRef.close(false); // User canceled, to refine search + } +} + @Component({ selector: "dataset-table", templateUrl: "dataset-table.component.html", @@ -59,6 +126,8 @@ export class DatasetTableComponent implements OnInit, OnDestroy, OnChanges { appConfig = this.appConfigService.getConfig(); + dataLoaded$ = this.store.select(selectLoadData); + lodashGet = get; currentPage$ = this.store.select(selectPage); datasetsPerPage$ = this.store.select(selectDatasetsPerPage); @@ -77,6 +146,7 @@ export class DatasetTableComponent implements OnInit, OnDestroy, OnChanges { constructor( public appConfigService: AppConfigService, + public dialog: MatDialog, private store: Store, ) {} doSettingsClick(event: MouseEvent) { @@ -268,4 +338,19 @@ export class DatasetTableComponent implements OnInit, OnDestroy, OnChanges { ngOnDestroy() { this.subscriptions.forEach((subscription) => subscription.unsubscribe()); } + + loadData() { + const dialogRef = this.dialog.open(GuardDialogComponent); + + dialogRef.afterClosed().subscribe((result) => { + if (result === true) { + // User confirmed, load all data + this.store.dispatch(fetchDatasetsAction()); + this.store.dispatch(fetchFacetCountsAction()); + } else { + // User canceled, show a message or allow them to filter + console.log("User chose to refine search or filter."); + } + }); + } } diff --git a/src/app/datasets/datasets-filter/settings/datasets-filter-settings.component.ts b/src/app/datasets/datasets-filter/settings/datasets-filter-settings.component.ts index f55af1307..3bd56876e 100644 --- a/src/app/datasets/datasets-filter/settings/datasets-filter-settings.component.ts +++ b/src/app/datasets/datasets-filter/settings/datasets-filter-settings.component.ts @@ -1,4 +1,4 @@ -import { ChangeDetectorRef, Component, Inject } from "@angular/core"; +import { Component, Inject } from "@angular/core"; import { MAT_DIALOG_DATA, MatDialog, @@ -16,14 +16,16 @@ import { selectColumnAction, } from "../../../state-management/actions/user.actions"; import { Store } from "@ngrx/store"; -import { selectMetadataKeys } from "../../../state-management/selectors/datasets.selectors"; +import { + selectMetadataKeys, + selectMetadataTypes, +} from "../../../state-management/selectors/datasets.selectors"; import { CdkDragDrop, moveItemInArray } from "@angular/cdk/drag-drop"; import { ConditionConfig, FilterConfig, } from "../../../shared/modules/filters/filters.module"; import { getFilterLabel } from "../../../shared/modules/filters/utils"; -import { ScientificCondition } from "../../../state-management/models"; @Component({ selector: "app-type-datasets-filter-settings", @@ -34,6 +36,7 @@ export class DatasetsFilterSettingsComponent { protected readonly getFilterLabel = getFilterLabel; metadataKeys$ = this.store.select(selectMetadataKeys); + metadataTypes$ = this.store.select(selectMetadataTypes); appConfig = this.appConfigService.getConfig(); @@ -51,6 +54,7 @@ export class DatasetsFilterSettingsComponent { .open(SearchParametersDialogComponent, { data: { parameterKeys: this.asyncPipe.transform(this.metadataKeys$), + parameterTypes: this.asyncPipe.transform(this.metadataTypes$), }, }) .afterClosed() @@ -80,6 +84,7 @@ export class DatasetsFilterSettingsComponent { .open(SearchParametersDialogComponent, { data: { parameterKeys: this.asyncPipe.transform(this.metadataKeys$), + parameterTypes: this.asyncPipe.transform(this.metadataTypes$), condition: condition.condition, }, }) diff --git a/src/app/shared/modules/scientific-metadata/metadata-edit/metadata-edit.component.html b/src/app/shared/modules/scientific-metadata/metadata-edit/metadata-edit.component.html index 85e2b624f..66339118b 100644 --- a/src/app/shared/modules/scientific-metadata/metadata-edit/metadata-edit.component.html +++ b/src/app/shared/modules/scientific-metadata/metadata-edit/metadata-edit.component.html @@ -29,14 +29,24 @@ + + + {{ key }} + + Name is required diff --git a/src/app/shared/modules/scientific-metadata/metadata-edit/metadata-edit.component.ts b/src/app/shared/modules/scientific-metadata/metadata-edit/metadata-edit.component.ts index 82821861e..9c1d0fc05 100644 --- a/src/app/shared/modules/scientific-metadata/metadata-edit/metadata-edit.component.ts +++ b/src/app/shared/modules/scientific-metadata/metadata-edit/metadata-edit.component.ts @@ -20,9 +20,14 @@ import { import { debounceTime, distinctUntilChanged } from "rxjs/operators"; import { UnitsService } from "shared/services/units.service"; import { startWith, map } from "rxjs/operators"; -import { Observable } from "rxjs"; +import { Observable, Subscription } from "rxjs"; import { ScientificMetadata } from "../scientific-metadata.module"; import { AppConfigService } from "app-config.service"; +import { Store } from "@ngrx/store"; +import { + selectMetadataKeys, + selectMetadataTypes, +} from "../../../../state-management/selectors/datasets.selectors"; @Component({ selector: "metadata-edit", @@ -38,6 +43,8 @@ export class MetadataEditComponent implements OnInit, OnChanges { appConfig = this.appConfigService.getConfig(); filteredUnits$: Observable | undefined = new Observable(); + metadataKeys$ = this.store.select(selectMetadataKeys); + metadataTypes$ = this.store.select(selectMetadataTypes); invalidUnitWarning: string[] = []; @Input() metadata: Record | undefined; @Output() save = new EventEmitter>(); @@ -46,6 +53,7 @@ export class MetadataEditComponent implements OnInit, OnChanges { private formBuilder: FormBuilder, private unitsService: UnitsService, private appConfigService: AppConfigService, + private store: Store, ) {} get formControlFields() { @@ -77,6 +85,7 @@ export class MetadataEditComponent implements OnInit, OnChanges { this.items.push(field); + this.setupFieldNameValueChangeListener(); this.setupFieldUnitValueChangeListeners(); } @@ -178,6 +187,7 @@ export class MetadataEditComponent implements OnInit, OnChanges { }); } this.items.push(field); + this.setupFieldNameValueChangeListener(); this.setupFieldUnitValueChangeListeners(); this.detectType(index); @@ -255,6 +265,47 @@ export class MetadataEditComponent implements OnInit, OnChanges { return this.metadataForm.get("items") as FormArray; } + setupFieldNameValueChangeListener() { + // Listen for changes in 'fieldName' to dynamically update 'fieldType' + this.items.controls.forEach((control, index) => { + control + .get("fieldName") + ?.valueChanges.pipe( + debounceTime(600), + distinctUntilChanged(), + startWith(""), + ) + .subscribe((selectedKey) => { + this.metadataTypes$.subscribe((types) => { + // Update the fieldType if the selectedKey exists in metadataTypes + const foundType = types.find( + (type) => type.metadataKey === selectedKey, + )?.metadataType; + if (foundType) { + this.items + .at(index) + .get("fieldType") + ?.setValue( + // TODO we should think about consistent types naming, see BE#1428 + (function (foundType, that) { + switch (foundType) { + case "mixed": + return that.typeValues[0]; // quantity + case "double": + case "int": + return that.typeValues[1]; // number + default: + return that.typeValues[2]; // string + } + })(foundType, this), + ); + this.detectType(index); + } + }); + }); + }); + } + setupFieldUnitValueChangeListeners() { this.items.controls.forEach((control, index) => { control @@ -278,6 +329,7 @@ export class MetadataEditComponent implements OnInit, OnChanges { this.addCurrentMetadata(); this.units = this.unitsService.getUnits(); + this.setupFieldNameValueChangeListener(); this.setupFieldUnitValueChangeListeners(); } diff --git a/src/app/shared/modules/search-parameters-dialog/search-parameters-dialog.component.html b/src/app/shared/modules/search-parameters-dialog/search-parameters-dialog.component.html index 61c2149a3..a46489066 100644 --- a/src/app/shared/modules/search-parameters-dialog/search-parameters-dialog.component.html +++ b/src/app/shared/modules/search-parameters-dialog/search-parameters-dialog.component.html @@ -26,18 +26,17 @@

Add Characteristic

+ Operator - is greater than - is less than - is equal to (numeric) - is equal to (string) + (selectionChange)="toggleUnitField()"> + + {{ operator.label }} + diff --git a/src/app/shared/modules/search-parameters-dialog/search-parameters-dialog.component.ts b/src/app/shared/modules/search-parameters-dialog/search-parameters-dialog.component.ts index d2680dc1a..97678959b 100644 --- a/src/app/shared/modules/search-parameters-dialog/search-parameters-dialog.component.ts +++ b/src/app/shared/modules/search-parameters-dialog/search-parameters-dialog.component.ts @@ -1,10 +1,18 @@ -import { ChangeDetectorRef, Component, Inject } from "@angular/core"; +import { Component, Inject } from "@angular/core"; import { FormControl, FormGroup, Validators } from "@angular/forms"; import { MatDialogRef, MAT_DIALOG_DATA } from "@angular/material/dialog"; import { AppConfigService } from "app-config.service"; -import { map, startWith } from "rxjs/operators"; +import { + debounceTime, + distinctUntilChanged, + filter, + map, + mergeMap, + startWith, +} from "rxjs/operators"; import { UnitsService } from "shared/services/units.service"; import { ScientificCondition } from "../../../state-management/models"; +import { Observable, of } from "rxjs"; @Component({ selector: "search-parameters-dialog", @@ -14,7 +22,9 @@ export class SearchParametersDialogComponent { appConfig = this.appConfigService.getConfig(); unitsEnabled = this.appConfig.scienceSearchUnitsEnabled; - parameterKeys = this.data.parameterKeys; + parameterKeys = this.data.parameterTypes.map((type) => type.metadataKey); + parameterTypes = this.data.parameterTypes; + filteredOperators$: Observable<{ value: string; label: string }[]> = of([]); // TODO default set of operators units: string[] = []; parametersForm = new FormGroup({ @@ -56,13 +66,94 @@ export class SearchParametersDialogComponent { @Inject(MAT_DIALOG_DATA) public data: { parameterKeys: string[]; + parameterTypes: Record[]; condition?: ScientificCondition; }, public dialogRef: MatDialogRef, private unitsService: UnitsService, ) { + function findMetadataTypeByKey(key) { + return ( + this.parameterTypes.find((type) => type.metadataKey.includes(key)) || + ({ metadataType: "mixed" } as Record) + ); + } + if (this.data.condition?.lhs) { this.getUnits(this.data.condition.lhs); + this.filteredOperators$ = of( + this.getOperatorsByType( + this.extractFieldType( + findMetadataTypeByKey.bind(this, this.data.condition.lhs), + ), + ), + ); + } + + // Dynamically update operators based on the field selected by the user + this.parametersForm + .get("lhs")! + .valueChanges.pipe( + debounceTime(300), + filter((selectedLhs: string) => selectedLhs && selectedLhs.length > 2), + distinctUntilChanged(), + mergeMap((selectedLhs: string) => { + return of(findMetadataTypeByKey.bind(this, selectedLhs)); + }), + // take(1), + map((field) => { + return this.extractFieldType(field.metadataType); + }), + ) + .subscribe((type) => { + this.filteredOperators$ = of(this.getOperatorsByType(type)); + }); + } + + // Helper to extract the field type from the dataset + private extractFieldType(type: string): string { + // Assuming metadata structure contains field type info + if (type === "string") { + return "string"; + } else if (type === "int" || type === "double") { + return "number"; + } else if (type === "Date") { + return "date"; + } + + return "mixed"; // Default to string if type can't be inferred + } + + // Dynamically fetch operators based on field type + private getOperatorsByType( + fieldType: string, + ): { value: string; label: string }[] { + const forString = [ + { value: "EQUAL_TO_STRING", label: "is equal to (string)" }, + { value: "CONTAINS", label: "contains" }, + ]; + const forNumber = [ + { value: "GREATER_THAN", label: "is greater than" }, + { value: "LESS_THAN", label: "is less than" }, + { value: "EQUAL_TO_NUMERIC", label: "is equal to (numeric)" }, + ]; + const forDate = [ + { value: "BEFORE", label: "is before" }, + { value: "AFTER", label: "is after" }, + ]; + const all = [{ value: "EQUAL", label: "is equal to" }] + .concat(forString) + .concat(forNumber) + .concat(forDate); + switch (fieldType) { + case "string": + return forString; + case "number": + return forNumber; + case "date": + return forDate; + default: + return all; } } diff --git a/src/app/shared/sdk/services/custom/Dataset.ts b/src/app/shared/sdk/services/custom/Dataset.ts index d26e78d1e..f8cdbec9c 100644 --- a/src/app/shared/sdk/services/custom/Dataset.ts +++ b/src/app/shared/sdk/services/custom/Dataset.ts @@ -2979,6 +2979,25 @@ export class DatasetApi extends BaseLoopBackApi { return result; } + public metadataTypes(): Observable { + let _method: string = "GET"; + let _url: string = + LoopBackConfig.getPath() + + "/" + + LoopBackConfig.getApiVersion() + + "/Datasets/metadataTypes"; + let result = this.request( + _method, + _url, + undefined, + undefined, + undefined, + null, + undefined, + ); + return result; + } + /** * The name of the model represented by this $resource, * i.e. `Dataset`. diff --git a/src/app/state-management/actions/datasets.actions.ts b/src/app/state-management/actions/datasets.actions.ts index 5c6ce0d93..635f865eb 100644 --- a/src/app/state-management/actions/datasets.actions.ts +++ b/src/app/state-management/actions/datasets.actions.ts @@ -38,14 +38,29 @@ export const fetchFacetCountsFailedAction = createAction( export const fetchMetadataKeysAction = createAction( "[Dataset] Fetch Metadata Keys", ); + +export const fetchMetadataTypesAction = createAction( + "[Dataset] Fetch Metadata Types", +); + export const fetchMetadataKeysCompleteAction = createAction( "[Dataset] Fetch Metadata Keys Complete", props<{ metadataKeys: string[] }>(), ); + export const fetchMetadataKeysFailedAction = createAction( "[Dataset] Fetch Metadata Keys Failed", ); +export const fetchMetadataTypesCompleteAction = createAction( + "[Dataset] Fetch Metadata Types Complete", + props<{ metadataTypes: Record[] }>(), +); + +export const fetchMetadataTypesFailedAction = createAction( + "[Dataset] Fetch Metadata Types Failed", +); + export const fetchDatasetAction = createAction( "[Dataset] Fetch Dataset", props<{ pid: string; filters?: any }>(), diff --git a/src/app/state-management/effects/datasets.effects.ts b/src/app/state-management/effects/datasets.effects.ts index 2784b9ebf..7db968ad3 100644 --- a/src/app/state-management/effects/datasets.effects.ts +++ b/src/app/state-management/effects/datasets.effects.ts @@ -35,6 +35,7 @@ import { loadingCompleteAction, updateUserSettingsAction, } from "state-management/actions/user.actions"; +import { fetchMetadataTypesAction } from "state-management/actions/datasets.actions"; @Injectable() export class DatasetEffects { @@ -89,7 +90,7 @@ export class DatasetEffects { map(([action, params]) => params), mergeMap(({ query }) => { const parsedQuery = JSON.parse(query); - parsedQuery.metadataKey = ""; + parsedQuery.scientificMetadata = { $exists: true, $ne: "" }; return this.datasetApi.metadataKeys(JSON.stringify(parsedQuery)).pipe( map((metadataKeys) => fromActions.fetchMetadataKeysCompleteAction({ metadataKeys }), @@ -100,6 +101,20 @@ export class DatasetEffects { ); }); + fetchMetadataTypes$ = createEffect(() => { + return this.actions$.pipe( + ofType(fromActions.fetchMetadataTypesAction), + mergeMap(() => { + return this.datasetApi.metadataTypes().pipe( + map((metadataTypes) => + fromActions.fetchMetadataTypesCompleteAction({ metadataTypes }), + ), + catchError(() => of(fromActions.fetchMetadataTypesFailedAction())), + ); + }), + ); + }); + updateUserDatasetsLimit$ = createEffect(() => { return this.actions$.pipe( ofType(fromActions.changePageAction), diff --git a/src/app/state-management/reducers/datasets.reducer.ts b/src/app/state-management/reducers/datasets.reducer.ts index 7c6783d88..23c6a667a 100644 --- a/src/app/state-management/reducers/datasets.reducer.ts +++ b/src/app/state-management/reducers/datasets.reducer.ts @@ -4,7 +4,11 @@ import { DatasetState, } from "state-management/state/datasets.store"; import * as fromActions from "state-management/actions/datasets.actions"; -import { ArchViewMode, Dataset } from "state-management/models"; +import { + ArchViewMode, + Dataset, + ScientificCondition, +} from "state-management/models"; const reducer = createReducer( initialDatasetState, @@ -30,6 +34,14 @@ const reducer = createReducer( (state, { metadataKeys }): DatasetState => ({ ...state, metadataKeys }), ), + on( + fromActions.fetchMetadataTypesCompleteAction, + (state, { metadataTypes }): DatasetState => ({ + ...state, + metadataTypes, + }), + ), + on( fromActions.fetchDatasetCompleteAction, (state, { dataset }): DatasetState => ({ @@ -453,9 +465,30 @@ const reducer = createReducer( (state, { condition }): DatasetState => { const currentFilters = state.filters; const currentScientific = currentFilters.scientific; + + // Custom comparison function to check if two conditions are equal + const areConditionsEqual = ( + cond1: ScientificCondition, + cond2: ScientificCondition, + ) => { + return ( + cond1.lhs === cond2.lhs && + cond1.relation === cond2.relation && + cond1.rhs === cond2.rhs && + cond1.unit === cond2.unit + ); + }; + + // Check if the condition already exists in the scientific array + const conditionExists = currentScientific.some((existingCondition) => + areConditionsEqual(existingCondition, condition), + ); + const filters = { ...currentFilters, - scientific: [...currentScientific, condition], + scientific: conditionExists + ? currentScientific + : [...currentScientific, condition], }; return { ...state, filters }; }, @@ -471,6 +504,14 @@ const reducer = createReducer( return { ...state, filters }; }, ), + on(fromActions.fetchDatasetsCompleteAction, (state): DatasetState => { + state.dataLoaded = true; + return state; + }), + on(fromActions.fetchDatasetsFailedAction, (state): DatasetState => { + state.dataLoaded = false; + return state; + }), ); export const datasetsReducer = ( diff --git a/src/app/state-management/selectors/datasets.selectors.ts b/src/app/state-management/selectors/datasets.selectors.ts index 756f75b69..0d74fd779 100644 --- a/src/app/state-management/selectors/datasets.selectors.ts +++ b/src/app/state-management/selectors/datasets.selectors.ts @@ -18,6 +18,11 @@ export const selectMetadataKeys = createSelector( (state) => state.metadataKeys, ); +export const selectMetadataTypes = createSelector( + selectDatasetState, + (state) => state.metadataTypes, +); + export const selectCurrentDataset = createSelector( selectDatasetState, (state) => state.currentSet, @@ -287,3 +292,8 @@ export const selectRelatedDatasetsPerPage = createSelector( selectRelatedDatasetsFilters, (filters) => filters.limit, ); + +export const selectLoadData = createSelector( + selectDatasetState, + (state) => state.dataLoaded, +); diff --git a/src/app/state-management/state/datasets.store.ts b/src/app/state-management/state/datasets.store.ts index ad84c0ea0..8b4b5173d 100644 --- a/src/app/state-management/state/datasets.store.ts +++ b/src/app/state-management/state/datasets.store.ts @@ -30,6 +30,7 @@ export interface DatasetState { facetCounts: FacetCounts; metadataKeys: string[]; + metadataTypes: Record[]; hasPrefilledFilters: boolean; searchTerms: string; keywordsTerms: string; @@ -46,6 +47,8 @@ export interface DatasetState { batch: Dataset[]; openwhiskResult: Record | undefined; + + dataLoaded: boolean; } export const initialDatasetState: DatasetState = { @@ -58,6 +61,7 @@ export const initialDatasetState: DatasetState = { facetCounts: {}, metadataKeys: [], + metadataTypes: [], hasPrefilledFilters: false, searchTerms: "", keywordsTerms: "", @@ -91,4 +95,6 @@ export const initialDatasetState: DatasetState = { batch: [], openwhiskResult: undefined, + + dataLoaded: false, };