diff --git a/client/src/app/admin/containers/attribute/attribute-list.component.html b/client/src/app/admin/containers/attribute/attribute-list.component.html new file mode 100644 index 0000000000000000000000000000000000000000..f476ec3ad5cde560e6ef0ca194c606787ce4cfef --- /dev/null +++ b/client/src/app/admin/containers/attribute/attribute-list.component.html @@ -0,0 +1,49 @@ +<div class="container-fluid"> + <nav aria-label="breadcrumb"> + <ol class="breadcrumb"> + <li class="breadcrumb-item"> + <a routerLink="/instance-list">Instances</a> + </li> + <li class="breadcrumb-item"> + <a routerLink="/configure-instance/{{ instanceSelected | async }}"> + Configure instance {{ instanceSelected | async }} + </a> + </li> + <li class="breadcrumb-item active" aria-current="page"> + Configure dataset {{ datasetSelected | async }} + </li> + </ol> + </nav> + + <app-spinner *ngIf="(attributeListIsLoading | async) || (datasetListIsLoading | async)"></app-spinner> + + <div *ngIf="(attributeListIsLoaded | async) && (datasetListIsLoaded | async)" class="row mt-1"> + <div class="col-12"> + <app-form-attribute-list + [dataset]="dataset | async" + [attributeList]="attributeList | async" + [columnList]="columnList | async" + [optionListGenerated]="optionListGenerated | async" + [criteriaFamilyList]="criteriaFamilyList | async" + [outputFamilyList]="outputFamilyList | async" + [outputCategoryList]="outputCategoryList | async" + [settingsSelectList]="settingsSelectList | async" + [settingsSelectOptionList]="settingsSelectOptionList | async" + [tabSelected]="tabSelected | async" + (addCriteriaFamily)="addCriteriaFamily($event)" + (editCriteriaFamily)="editCriteriaFamily($event)" + (deleteCriteriaFamily)="deleteCriteriaFamily($event)" + (addOutputFamily)="addOutputFamily($event)" + (editOutputFamily)="editOutputFamily($event)" + (deleteOutputFamily)="deleteOutputFamily($event)" + (addAttribute)="addAttribute($event)" + (editAttribute)="editAttribute($event)" + (deleteAttribute)="deleteAttribute($event)" + (addOutputCategory)="addOutputCategory($event)" + (editOutputCategory)="editOutputCategory($event)" + (deleteOutputCategory)="deleteOutputCategory($event)" + (generateAttributeOptionList)="generateAttributeOptionList($event)"> + </app-form-attribute-list> + </div> + </div> +</div> diff --git a/client/src/app/admin/containers/attribute/attribute-list.component.scss b/client/src/app/admin/containers/attribute/attribute-list.component.scss new file mode 100644 index 0000000000000000000000000000000000000000..6b917133e64d49ce00365302ae03f1e02101b4cc --- /dev/null +++ b/client/src/app/admin/containers/attribute/attribute-list.component.scss @@ -0,0 +1,18 @@ +.attributes { + margin-right: 20px; + margin-left: 20px; + padding-top: 15px; + padding-bottom: 15px; + border-top: 1px solid; + border-color: #ccc; +} + +th { + background-color: #a8c96e; + color: whitesmoke; + text-align: center; +} + +.td-att-name { + background-color: #dcdada; +} diff --git a/client/src/app/admin/containers/attribute/attribute-list.component.ts b/client/src/app/admin/containers/attribute/attribute-list.component.ts new file mode 100644 index 0000000000000000000000000000000000000000..0d9315262e237fa0323834244050feb5c78507b8 --- /dev/null +++ b/client/src/app/admin/containers/attribute/attribute-list.component.ts @@ -0,0 +1,136 @@ +import { Component, OnInit } from '@angular/core'; +import { ActivatedRoute } from '@angular/router'; + +import { Observable } from 'rxjs'; +import { map } from 'rxjs/operators'; +import { Store } from '@ngrx/store'; + +import { Select, SelectOption, Dataset, Attribute, Column, CriteriaFamily, OutputCategory, OutputFamily } from 'src/app/metamodel/store/models'; +import * as instanceSelector from 'src/app/metamodel/store/selectors/instance.selector'; +import * as datasetActions from 'src/app/metamodel/store/actions/dataset.actions'; +import * as datasetSelector from 'src/app/metamodel/store/selectors/dataset.selector'; +import * as attributeActions from 'src/app/metamodel/store/actions/attribute.actions'; +import * as attributeSelector from 'src/app/metamodel/store/selectors/attribute.selector'; +import * as criteriaFamilyActions from 'src/app/metamodel/store/actions/criteria-family.actions'; +import * as criteriaFamilySelector from 'src/app/metamodel/store/selectors/criteria-family.selector'; +import * as outputFamilyActions from 'src/app/metamodel/store/actions/output-family.actions'; +import * as outputFamilySelector from 'src/app/metamodel/store/selectors/output-family.selector'; +import * as outputCategoryActions from 'src/app/metamodel/store/actions/output-category.actions'; +import * as outputCategorySelector from 'src/app/metamodel/store/selectors/output-category.selector'; +import * as selectActions from 'src/app/metamodel/store/actions/select.actions'; +import * as selectSelector from 'src/app/metamodel/store/selectors/select.selector'; +import * as optionActions from 'src/app/metamodel/store/actions/select-option.actions'; +import * as optionSelector from 'src/app/metamodel/store/selectors/select-option.selector'; +import * as columnActions from 'src/app/metamodel/store/actions/column.actions'; +import * as columnSelector from 'src/app/metamodel/store/selectors/column.selector'; + +@Component({ + selector: 'app-attribute', + templateUrl: 'attribute-list.component.html', + styleUrls: [ 'attribute-list.component.scss' ] +}) +export class AttributeComponent implements OnInit { + public instanceName: Observable<string>; + public datasetName: Observable<string>; + public dataset: Observable<Dataset>; + public datasetListIsLoading: Observable<boolean>; + public datasetListIsLoaded: Observable<boolean>; + public tabSelected: Observable<string>; + public attributeList: Observable<Attribute[]>; + public attributeListIsLoading: Observable<boolean>; + public attributeListIsLoaded: Observable<boolean>; + public columnList: Observable<Column[]>; + public columnListIsLoading: Observable<boolean>; + public columnListIsLoaded: Observable<boolean>; + public optionListGenerated: Observable<string[]>; + public criteriaFamilyList: Observable<CriteriaFamily[]>; + public outputFamilyList: Observable<OutputFamily[]>; + public outputCategoryList: Observable<OutputCategory[]>; + public settingsSelectList: Observable<Select[]>; + public settingsSelectOptionList: Observable<SelectOption[]>; + + constructor(private store: Store<{ }>, private route: ActivatedRoute) { + this.instanceName = store.select(instanceSelector.selectInstanceNameByRoute); + this.datasetName = store.select(datasetSelector.selectDatasetNameByRoute); + this.dataset = store.select(datasetSelector.selectDatasetByRouteName); + this.datasetListIsLoading = store.select(datasetSelector.selectDatasetListIsLoading); + this.datasetListIsLoaded = store.select(datasetSelector.selectDatasetListIsLoaded); + this.attributeList = store.select(attributeSelector.selectAllAttributes); + this.attributeListIsLoading = store.select(attributeSelector.selectAttributeListIsLoading); + this.attributeListIsLoaded = store.select(attributeSelector.selectAttributeListIsLoaded); + this.columnList = store.select(columnSelector.selectAllColumns); + this.columnListIsLoading = store.select(columnSelector.selectColumnListIsLoading); + this.columnListIsLoaded = store.select(columnSelector.selectColumnListIsLoaded); + this.optionListGenerated = store.select(attributeSelector.getOptionListGenerated); + this.criteriaFamilyList = store.select(criteriaFamilySelector.selectAllCriteriaFamilys); + this.outputFamilyList = store.select(outputFamilySelector.selectAllOutputFamilys); + this.outputCategoryList = store.select(outputCategorySelector.selectAllOutputCategorys); + this.settingsSelectList = store.select(selectSelector.selectAllSelects); + this.settingsSelectOptionList = store.select(optionSelector.selectAllSelectOptions); + } + + ngOnInit() { + this.store.dispatch(datasetActions.loadDatasetList()); + this.store.dispatch(attributeActions.loadAttributeList()); + this.store.dispatch(selectActions.loadSelectList()); + this.store.dispatch(optionActions.loadSelectOptionList()); + this.store.dispatch(criteriaFamilyActions.loadCriteriaFamilyList()); + this.store.dispatch(outputFamilyActions.loadOutputFamilyList()); + this.store.dispatch(outputCategoryActions.loadOutputCategoryList()); + this.tabSelected = this.route.queryParamMap.pipe( + map(params => params.get('tab_selected')) + ); + } + + addCriteriaFamily(criteriaFamily: CriteriaFamily) { + this.store.dispatch(criteriaFamilyActions.addCriteriaFamily({ criteriaFamily })); + } + + editCriteriaFamily(criteriaFamily: CriteriaFamily) { + this.store.dispatch(criteriaFamilyActions.editCriteriaFamily({ criteriaFamily })); + } + + deleteCriteriaFamily(criteriaFamily: CriteriaFamily) { + this.store.dispatch(criteriaFamilyActions.deleteCriteriaFamily({ criteriaFamily })); + } + + addOutputFamily(outputFamily: OutputFamily) { + this.store.dispatch(outputFamilyActions.addOutputFamily({ outputFamily })); + } + + editOutputFamily(outputFamily: OutputFamily) { + this.store.dispatch(outputFamilyActions.editOutputFamily({ outputFamily })); + } + + deleteOutputFamily(outputFamily: OutputFamily) { + this.store.dispatch(outputFamilyActions.deleteOutputFamily({ outputFamily })); + } + + addOutputCategory(outputCategory: OutputCategory) { + this.store.dispatch(outputCategoryActions.addOutputCategory({ outputCategory })); + } + + editOutputCategory(outputCategory: OutputCategory) { + this.store.dispatch(outputCategoryActions.editOutputCategory({ outputCategory })); + } + + deleteOutputCategory(outputCategory: OutputCategory) { + this.store.dispatch(outputCategoryActions.deleteOutputCategory({ outputCategory })); + } + + addAttribute(attribute: Attribute) { + this.store.dispatch(attributeActions.addAttribute({ attribute })); + } + + editAttribute(attribute: Attribute) { + this.store.dispatch(attributeActions.editAttribute({ attribute })); + } + + deleteAttribute(attribute: Attribute) { + this.store.dispatch(attributeActions.deleteAttribute({ attribute })); + } + + generateAttributeOptionList(attribute: Attribute) { + this.store.dispatch(new attributeActions.GenerateOptionListAction(attribute)); + } +} diff --git a/client/src/app/metamodel/store/actions/attribute.actions.ts b/client/src/app/metamodel/store/actions/attribute.actions.ts index e26b4b230bd8c58a6c6be0478679d20357158b3d..01012dcb53eaa97012dbc0523b2720db0749ef6a 100644 --- a/client/src/app/metamodel/store/actions/attribute.actions.ts +++ b/client/src/app/metamodel/store/actions/attribute.actions.ts @@ -5,3 +5,12 @@ import { Attribute } from '../models'; export const loadAttributeList = createAction('[Metamodel] Load Attribute List'); export const loadAttributeListSuccess = createAction('[Metamodel] Load Attribute List Success', props<{ attributes: Attribute[] }>()); export const loadAttributeListFail = createAction('[Metamodel] Load Attribute List Fail'); +export const addAttribute = createAction('[Metamodel] Add Attribute', props<{ attribute: Attribute }>()); +export const addAttributeSuccess = createAction('[Metamodel] Add Attribute Success', props<{ attribute: Attribute }>()); +export const addAttributeFail = createAction('[Metamodel] Add Attribute Fail'); +export const editAttribute = createAction('[Metamodel] Edit Attribute', props<{ attribute: Attribute }>()); +export const editAttributeSuccess = createAction('[Metamodel] Edit Attribute Success', props<{ attribute: Attribute }>()); +export const editAttributeFail = createAction('[Metamodel] Edit Attribute Fail'); +export const deleteAttribute = createAction('[Metamodel] Delete Attribute', props<{ attribute: Attribute }>()); +export const deleteAttributeSuccess = createAction('[Metamodel] Delete Attribute Success', props<{ attribute: Attribute }>()); +export const deleteAttributeFail = createAction('[Metamodel] Delete Attribute Fail'); diff --git a/client/src/app/metamodel/store/actions/column.actions.ts b/client/src/app/metamodel/store/actions/column.actions.ts new file mode 100644 index 0000000000000000000000000000000000000000..1aa0d0ec7e33827040631c7c18b3d388e51dc330 --- /dev/null +++ b/client/src/app/metamodel/store/actions/column.actions.ts @@ -0,0 +1,7 @@ +import { createAction, props } from '@ngrx/store'; + +import { Column } from '../models'; + +export const loadColumnList = createAction('[Metamodel] Load Column List'); +export const loadColumnListSuccess = createAction('[Metamodel] Load Column List Success', props<{ columns: Column[] }>()); +export const loadColumnListFail = createAction('[Metamodel] Load Column List Fail'); diff --git a/client/src/app/metamodel/store/effects/attribute.effects.ts b/client/src/app/metamodel/store/effects/attribute.effects.ts new file mode 100644 index 0000000000000000000000000000000000000000..dfb8c6892b703b9d5e69a8d0fc0b9583073e458b --- /dev/null +++ b/client/src/app/metamodel/store/effects/attribute.effects.ts @@ -0,0 +1,123 @@ +import { Injectable } from '@angular/core'; +import { Router } from '@angular/router'; + +import { Actions, createEffect, ofType, concatLatestFrom } from '@ngrx/effects'; +import { Store } from '@ngrx/store'; +import { of } from 'rxjs'; +import { map, tap, mergeMap, catchError } from 'rxjs/operators'; +import { ToastrService } from 'ngx-toastr'; + +import * as attributeActions from '../actions/attribute.actions'; +import { AttributeService } from '../services/attribute.service'; +import * as datasetSelector from '../selectors/dataset.selector'; + +@Injectable() +export class AttributeEffects { + loadAttributes$ = createEffect(() => + this.actions$.pipe( + ofType(attributeActions.loadAttributeList), + concatLatestFrom(() => this.store.select(datasetSelector.selectDatasetNameByRoute)), + mergeMap(([action, datasetName]) => this.attributeService.retrieveAttributeList(datasetName) + .pipe( + map(attributes => attributeActions.loadAttributeListSuccess({ attributes })), + catchError(() => of(attributeActions.loadAttributeListFail())) + ) + ) + ) + ); + + addAttribute$ = createEffect(() => + this.actions$.pipe( + ofType(attributeActions.addAttribute), + concatLatestFrom(() => this.store.select(datasetSelector.selectDatasetNameByRoute)), + mergeMap(([action, datasetName]) => this.attributeService.addAttribute(datasetName, action.attribute) + .pipe( + map(attribute => attributeActions.addAttributeSuccess({ attribute })), + catchError(() => of(attributeActions.addAttributeFail())) + ) + ) + ) + ); + + addAttributeSuccess$ = createEffect(() => + this.actions$.pipe( + ofType(attributeActions.addAttributeSuccess), + tap(() => { + this.router.navigate(['/admin/attribute/attribute-list']); + this.toastr.success('Attribute successfully added', 'The new attribute was added into the database') + }) + ), { dispatch: false} + ); + + addAttributeFail$ = createEffect(() => + this.actions$.pipe( + ofType(attributeActions.addAttributeFail), + tap(() => this.toastr.error('Failure to add attribute', 'The new attribute could not be added into the database')) + ), { dispatch: false} + ); + + editAttribute$ = createEffect(() => + this.actions$.pipe( + ofType(attributeActions.editAttribute), + concatLatestFrom(() => this.store.select(datasetSelector.selectDatasetNameByRoute)), + mergeMap(([action, datasetName]) => this.attributeService.editAttribute(datasetName, action.attribute) + .pipe( + map(attribute => attributeActions.editAttributeSuccess({ attribute })), + catchError(() => of(attributeActions.editAttributeFail())) + ) + ) + ) + ); + + editAttributeSuccess$ = createEffect(() => + this.actions$.pipe( + ofType(attributeActions.editAttributeSuccess), + tap(() => { + this.router.navigate(['/admin/attribute/attribute-list']); + this.toastr.success('Attribute successfully edited', 'The existing attribute has been edited into the database') + }) + ), { dispatch: false} + ); + + editAttributeFail$ = createEffect(() => + this.actions$.pipe( + ofType(attributeActions.editAttributeFail), + tap(() => this.toastr.error('Failure to edit attribute', 'The existing attribute could not be edited into the database')) + ), { dispatch: false} + ); + + deleteAttribute$ = createEffect(() => + this.actions$.pipe( + ofType(attributeActions.deleteAttribute), + concatLatestFrom(() => this.store.select(datasetSelector.selectDatasetNameByRoute)), + mergeMap(([action, datasetName]) => this.attributeService.deleteAttribute(datasetName, action.attribute) + .pipe( + map(() => attributeActions.deleteAttributeSuccess({ attribute: action.attribute })), + catchError(() => of(attributeActions.deleteAttributeFail())) + ) + ) + ) + ); + + deleteAttributeSuccess$ = createEffect(() => + this.actions$.pipe( + ofType(attributeActions.deleteAttributeSuccess), + tap(() => this.toastr.success('Attribute successfully deleted', 'The existing attribute has been deleted')) + ), { dispatch: false} + ); + + deleteAttributeFail$ = createEffect(() => + this.actions$.pipe( + ofType(attributeActions.deleteAttributeFail), + tap(() => this.toastr.error('Failure to delete attribute', 'The existing attribute could not be deleted from the database')) + ), { dispatch: false} + ); + + constructor( + private actions$: Actions, + private attributeService: AttributeService, + private router: Router, + private toastr: ToastrService, + private store: Store<{ }> + ) {} +} diff --git a/client/src/app/metamodel/store/effects/column.effects.ts b/client/src/app/metamodel/store/effects/column.effects.ts new file mode 100644 index 0000000000000000000000000000000000000000..cc6c7304d452378b4b09e66d73d655913ebf22ff --- /dev/null +++ b/client/src/app/metamodel/store/effects/column.effects.ts @@ -0,0 +1,32 @@ +import { Injectable } from '@angular/core'; + +import { Actions, createEffect, ofType, concatLatestFrom } from '@ngrx/effects'; +import { Store } from '@ngrx/store'; +import { of } from 'rxjs'; +import { map, mergeMap, catchError } from 'rxjs/operators'; + +import * as columnActions from '../actions/column.actions'; +import { ColumnService } from '../services/column.service'; +import * as datasetSelector from '../selectors/dataset.selector'; + +@Injectable() +export class ColumnEffects { + loadColumns$ = createEffect(() => + this.actions$.pipe( + ofType(columnActions.loadColumnList), + concatLatestFrom(() => this.store.select(datasetSelector.selectDatasetNameByRoute)), + mergeMap(([action, datasetName]) => this.columnService.retrieveColumns(datasetName) + .pipe( + map(columns => columnActions.loadColumnListSuccess({ columns })), + catchError(() => of(columnActions.loadColumnListFail())) + ) + ) + ) + ); + + constructor( + private actions$: Actions, + private columnService: ColumnService, + private store: Store<{ }> + ) {} +} diff --git a/client/src/app/metamodel/store/effects/index.ts b/client/src/app/metamodel/store/effects/index.ts index 5b5a627d1c463a8e84ae16d2b4e617e59f0ab9d0..7fa91a8f0f1fdead70a6904845baf15aee06df30 100644 --- a/client/src/app/metamodel/store/effects/index.ts +++ b/client/src/app/metamodel/store/effects/index.ts @@ -1,9 +1,11 @@ import { DatabaseEffects } from './database.effects'; import { TableEffects } from './table.effects'; +import { ColumnEffects } from './column.effects'; import { SurveyEffects } from './survey.effects'; import { InstanceEffects } from './instance.effects'; import { DatasetFamilyEffects } from './dataset-family.effects'; import { DatasetEffects } from './dataset.effects'; +import { AttributeEffects } from './attribute.effects'; import { GroupEffects } from './group.effects'; import { CriteriaFamilyEffects } from './criteria-family.effects'; import { OutputCategoryEffects } from './output-category.effects'; @@ -15,10 +17,12 @@ import { SelectOptionEffects } from './select-option.effects'; export const metamodelEffects = [ DatabaseEffects, TableEffects, + ColumnEffects, SurveyEffects, InstanceEffects, DatasetFamilyEffects, DatasetEffects, + AttributeEffects, GroupEffects, CriteriaFamilyEffects, OutputCategoryEffects, diff --git a/client/src/app/metamodel/store/effects/output-family.effects.ts b/client/src/app/metamodel/store/effects/output-family.effects.ts index e5b5d0622b3ab4c57205d6f892b7f3b075ad1e5c..86140a8fb48bd2385aee3ea0ec0d222aba544c78 100644 --- a/client/src/app/metamodel/store/effects/output-family.effects.ts +++ b/client/src/app/metamodel/store/effects/output-family.effects.ts @@ -12,7 +12,7 @@ import * as datasetSelector from '../selectors/dataset.selector'; @Injectable() export class OutputFamilyEffects { - loadOutputFamilys$ = createEffect(() => + loadOutputFamilies$ = createEffect(() => this.actions$.pipe( ofType(outputFamilyActions.loadOutputFamilyList), concatLatestFrom(() => this.store.select(datasetSelector.selectDatasetNameByRoute)), @@ -25,7 +25,7 @@ export class OutputFamilyEffects { ) ); - addOutputFamily$ = createEffect(() => + addOutputFamilies$ = createEffect(() => this.actions$.pipe( ofType(outputFamilyActions.addOutputFamily), concatLatestFrom(() => this.store.select(datasetSelector.selectDatasetNameByRoute)), diff --git a/client/src/app/metamodel/store/models/db-table-column.model.ts b/client/src/app/metamodel/store/models/column.model.ts similarity index 54% rename from client/src/app/metamodel/store/models/db-table-column.model.ts rename to client/src/app/metamodel/store/models/column.model.ts index 10fc6f8931e5b1a60d778de137364e700f968219..91d30c9af4415413e3714858be2bc0680b9f7c97 100644 --- a/client/src/app/metamodel/store/models/db-table-column.model.ts +++ b/client/src/app/metamodel/store/models/column.model.ts @@ -1,4 +1,4 @@ -export interface DbTableColumn { +export interface Column { name: string; type: string; }; diff --git a/client/src/app/metamodel/store/models/index.ts b/client/src/app/metamodel/store/models/index.ts index d55be226c231db4f978fba312334540ad2f15847..c2af97131216e59b1aadddd0ee5a2be18764d61f 100644 --- a/client/src/app/metamodel/store/models/index.ts +++ b/client/src/app/metamodel/store/models/index.ts @@ -1,5 +1,4 @@ export * from './database.model'; -export * from './db-table-column.model'; export * from './survey.model'; export * from './group.model'; export * from './dataset.model'; @@ -12,3 +11,4 @@ export * from './output-family.model'; export * from './select.model'; export * from './select-option.model'; export * from './file-info.model'; +export * from './column.model'; diff --git a/client/src/app/metamodel/store/reducers/attribute.reducer.ts b/client/src/app/metamodel/store/reducers/attribute.reducer.ts index e57fde5a349f37895559259df59eef56b5afb571..964a1829dfce8060fa8e4e09e08df36a9383c578 100644 --- a/client/src/app/metamodel/store/reducers/attribute.reducer.ts +++ b/client/src/app/metamodel/store/reducers/attribute.reducer.ts @@ -39,6 +39,15 @@ export const attributeReducer = createReducer( ...state, attributeListIsLoading: false } + }), + on(attributeActions.addAttributeSuccess, (state, { attribute }) => { + return adapter.addOne(attribute, state) + }), + on(attributeActions.editAttributeSuccess, (state, { attribute }) => { + return adapter.setOne(attribute, state) + }), + on(attributeActions.deleteAttributeSuccess, (state, { attribute }) => { + return adapter.removeOne(attribute.name, state) }) ); @@ -53,3 +62,6 @@ export const selectAttributeIds = selectIds; export const selectAttributeEntities = selectEntities; export const selectAllAttributes = selectAll; export const selectAttributeTotal = selectTotal; + +export const selectAttributeListIsLoading = (state: State) => state.attributeListIsLoading; +export const selectAttributeListIsLoaded = (state: State) => state.attributeListIsLoaded; diff --git a/client/src/app/metamodel/store/reducers/column.reducer.ts b/client/src/app/metamodel/store/reducers/column.reducer.ts new file mode 100644 index 0000000000000000000000000000000000000000..2f8aa770a132239a27edfce7379a83d8468a647f --- /dev/null +++ b/client/src/app/metamodel/store/reducers/column.reducer.ts @@ -0,0 +1,61 @@ +import { createReducer, on } from '@ngrx/store'; +import { EntityState, EntityAdapter, createEntityAdapter } from '@ngrx/entity'; + +import { Column } from '../models'; +import * as columnActions from '../actions/column.actions'; + +export interface State extends EntityState<Column> { + columnListIsLoading: boolean; + columnListIsLoaded: boolean; +} + +export const adapter: EntityAdapter<Column> = createEntityAdapter<Column>({ + selectId: (column: Column) => column.name, + sortComparer: (a: Column, b: Column) => a.name.localeCompare(b.name) +}); + +export const initialState: State = adapter.getInitialState({ + columnListIsLoading: false, + columnListIsLoaded: false +}); + +export const columnReducer = createReducer( + initialState, + on(columnActions.loadColumnList, (state) => { + return { + ...state, + columnListIsLoading: true + } + }), + on(columnActions.loadColumnListSuccess, (state, { columns }) => { + return adapter.setAll( + columns, + { + ...state, + columnListIsLoading: false, + columnListIsLoaded: true + } + ); + }), + on(columnActions.loadColumnListFail, (state) => { + return { + ...state, + columnListIsLoading: false + } + }) +); + +const { + selectIds, + selectEntities, + selectAll, + selectTotal, +} = adapter.getSelectors(); + +export const selectColumnIds = selectIds; +export const selectColumnEntities = selectEntities; +export const selectAllColumns = selectAll; +export const selectColumnTotal = selectTotal; + +export const selectColumnListIsLoading = (state: State) => state.columnListIsLoading; +export const selectColumnListIsLoaded = (state: State) => state.columnListIsLoaded; diff --git a/client/src/app/metamodel/store/reducers/index.ts b/client/src/app/metamodel/store/reducers/index.ts index a37e1a627deffbd7ad0e36177f507973cea5458a..f187b7bd3046ad7cfb85c8921843647d5f160824 100644 --- a/client/src/app/metamodel/store/reducers/index.ts +++ b/client/src/app/metamodel/store/reducers/index.ts @@ -3,6 +3,7 @@ import { combineReducers, createFeatureSelector } from '@ngrx/store'; import { RouterReducerState } from 'src/app/custom-route-serializer'; import * as database from './database.reducer'; import * as table from './table.reducer'; +import * as column from './column.reducer'; import * as survey from './survey.reducer'; import * as group from './group.reducer'; import * as dataset from './dataset.reducer'; @@ -19,6 +20,7 @@ import * as selectOption from './select-option.reducer'; export interface State { database: database.State; table: table.State; + column: column.State; survey: survey.State; group: group.State; dataset: dataset.State; @@ -36,6 +38,7 @@ export interface State { const reducers = { database: database.databaseReducer, table: table.tableReducer, + column: column.columnReducer, survey: survey.surveyReducer, group: group.groupReducer, dataset: dataset.datasetReducer, diff --git a/client/src/app/metamodel/store/selectors/attribute.selector.ts b/client/src/app/metamodel/store/selectors/attribute.selector.ts index 8dcfa5940ebf2461b316b580c9ede8fe98fde756..d53216d586122a2d842a8d645412563327956116 100644 --- a/client/src/app/metamodel/store/selectors/attribute.selector.ts +++ b/client/src/app/metamodel/store/selectors/attribute.selector.ts @@ -27,3 +27,13 @@ export const selectAttributeTotal = createSelector( selectAttributeState, fromAttribute.selectAttributeTotal ); + +export const selectAttributeListIsLoading = createSelector( + selectAttributeState, + fromAttribute.selectAttributeListIsLoading +); + +export const selectAttributeListIsLoaded = createSelector( + selectAttributeState, + fromAttribute.selectAttributeListIsLoaded +); diff --git a/client/src/app/metamodel/store/selectors/column.selector.ts b/client/src/app/metamodel/store/selectors/column.selector.ts new file mode 100644 index 0000000000000000000000000000000000000000..1f3da9e9d671f934399b0a2f9daeebf57064003c --- /dev/null +++ b/client/src/app/metamodel/store/selectors/column.selector.ts @@ -0,0 +1,45 @@ +import { createSelector } from '@ngrx/store'; + +import * as reducer from '../reducers'; +import * as fromColumn from '../reducers/column.reducer'; + +export const selectColumnState = createSelector( + reducer.getMetamodelState, + (state: reducer.State) => state.column +); + +export const selectColumnIds = createSelector( + selectColumnState, + fromColumn.selectColumnIds +); + +export const selectColumnEntities = createSelector( + selectColumnState, + fromColumn.selectColumnEntities +); + +export const selectAllColumns = createSelector( + selectColumnState, + fromColumn.selectAllColumns +); + +export const selectColumnTotal = createSelector( + selectColumnState, + fromColumn.selectColumnTotal +); + +export const selectColumnListIsLoading = createSelector( + selectColumnState, + fromColumn.selectColumnListIsLoading +); + +export const selectColumnListIsLoaded = createSelector( + selectColumnState, + fromColumn.selectColumnListIsLoaded +); + +export const selectColumnByRouteName = createSelector( + selectColumnEntities, + reducer.selectRouterState, + (entities, router) => entities[router.state.params.name] +); diff --git a/client/src/app/metamodel/store/services/column.service.ts b/client/src/app/metamodel/store/services/column.service.ts new file mode 100644 index 0000000000000000000000000000000000000000..7b637838c9cd6b934a64b41299a4e0f2a33347b1 --- /dev/null +++ b/client/src/app/metamodel/store/services/column.service.ts @@ -0,0 +1,18 @@ +import { Injectable } from '@angular/core'; +import { HttpClient } from '@angular/common/http'; + +import { Observable } from 'rxjs'; + +import { Column } from '../models'; +import { environment } from 'src/environments/environment'; + +@Injectable() +export class ColumnService { + private API_PATH: string = environment.apiUrl + '/'; + + constructor(private http: HttpClient) { } + + retrieveColumns(datasetName: string): Observable<Column[]> { + return this.http.get<Column[]>(this.API_PATH + 'dataset/' + datasetName + '/column'); + } +} diff --git a/client/src/app/metamodel/store/services/database.service.ts b/client/src/app/metamodel/store/services/database.service.ts index 74f5ca325e9a2cd2d0f2940743209176d571afec..828c7c6f305f5ad9f4963ff3b16505c6af0a6417 100644 --- a/client/src/app/metamodel/store/services/database.service.ts +++ b/client/src/app/metamodel/store/services/database.service.ts @@ -3,7 +3,7 @@ import { HttpClient } from '@angular/common/http'; import { Observable } from 'rxjs'; -import { Database, DbTableColumn } from '../models'; +import { Database } from '../models'; import { environment } from 'src/environments/environment'; @Injectable() @@ -15,10 +15,6 @@ export class DatabaseService { retrieveDatabaseList(): Observable<Database[]> { return this.http.get<Database[]>(this.API_PATH + 'database'); } - - retrieveColumns(datasetName: string) { - return this.http.get<DbTableColumn[]>(this.API_PATH + 'dataset/' + datasetName + '/column'); - } addDatabase(newDatabase: Database): Observable<Database> { return this.http.post<Database>(this.API_PATH + 'database', newDatabase); diff --git a/client/src/app/metamodel/store/services/index.ts b/client/src/app/metamodel/store/services/index.ts index fe9f8660b644928767f0bca7549305a1d3d488b3..c04a44fd0f0d07748bb07730d9940cc9579ecb40 100644 --- a/client/src/app/metamodel/store/services/index.ts +++ b/client/src/app/metamodel/store/services/index.ts @@ -1,5 +1,6 @@ import { DatabaseService } from './database.service'; import { TableService } from './table.service'; +import { ColumnService } from './column.service'; import { SurveyService } from './survey.service'; import { GroupService } from './group.service'; import { DatasetService } from './dataset.service'; @@ -16,6 +17,7 @@ import { SelectOptionService } from './select-option.service'; export const metamodelServices = [ DatabaseService, TableService, + ColumnService, SurveyService, GroupService, DatasetService,