Commit c53f81e6 authored by Tifenn Guillas's avatar Tifenn Guillas
Browse files

Merge branch '59-action-serveur-checkbox-result' into 'develop'

Resolve "Action serveur pour checkbox dans result page"

Closes #59

See merge request !54
parents b86a88bf 85075efa
Pipeline #1529 passed with stages
in 7 minutes and 40 seconds
......@@ -14,7 +14,13 @@
</div>
<div *ngIf="searchMeta">
<div class="mb-2">
<button [disabled]="noSelectedData()" class="btn btn-sm btn-outline-primary">Action</button>
<button [disabled]="noSelectedData() || processWip" (click)="fireAction('csv')"
class="btn btn-sm btn-outline-primary">To CSV</button>
<span *ngIf="processWip" class="float-right mr-2">
<i class="fas fa-circle-notch fa-spin fa-2x"></i>
</span>
<a *ngIf="processDone" href="http://0.0.0.0:8085/{{ processId }}.csv" target="_blank"
class="btn btn-sm btn-outline-secondary float-right">Download your CSV</a>
</div>
<div class="table-responsive">
<table class="table table-bordered table-hover">
......@@ -27,7 +33,8 @@
</thead>
<tbody>
<tr *ngFor="let datum of searchData">
<td *ngIf="getDataset().selectable_row" class="data-selected" (click)="toggleSelection(datum)">
<td *ngIf="getDataset().selectable_row" class="data-selected"
(click)="toggleSelection(datum)">
<button class="btn btn-block text-left p-0 m-0">
<div *ngIf="!isSelected(datum)">
<i class="far fa-square fa-lg text-secondary"></i>
......
......@@ -17,10 +17,14 @@ export class DatatableComponent {
@Input() searchMeta: SearchMeta;
@Input() searchData: any[];
@Input() selectedData: any[];
@Input() processWip: boolean;
@Input() processDone: boolean;
@Input() processId: string;
@Output() initSearchMeta: EventEmitter<{}> = new EventEmitter();
@Output() getSearchData: EventEmitter<number> = new EventEmitter();
@Output() addSelectedData: EventEmitter<any> = new EventEmitter();
@Output() deleteSelectedData: EventEmitter<any> = new EventEmitter();
@Output() executeAction: EventEmitter<string> = new EventEmitter();
initDatatable() {
this.initSearchMeta.emit();
......@@ -73,4 +77,8 @@ export class DatatableComponent {
noSelectedData() {
return this.selectedData.length < 1;
}
fireAction(typeAction: string): void {
this.executeAction.emit(typeAction);
}
}
......@@ -22,7 +22,11 @@
(initSearchMeta)="getSearchMeta()"
(getSearchData)="getSearchData($event)"
(addSelectedData)="addSearchData($event)"
(deleteSelectedData)="deleteSearchData($event)">
(deleteSelectedData)="deleteSearchData($event)"
[processWip]="processWip | async"
[processDone]="processDone | async"
[processId]="processId | async"
(executeAction)="executeProcess($event)">
</app-datatable>
</div>
<div class="col-12 col-md-4 pt-2">
......
import { Component, OnInit } from '@angular/core';
import { Component, OnInit, Query } from '@angular/core';
import { Observable } from 'rxjs';
import { Store } from '@ngrx/store';
......@@ -40,6 +40,9 @@ export class ResultComponent implements OnInit {
public searchData: Observable<any[]>;
public selectedData: Observable<any[]>;
public queryParams: Observable<SearchQueryParams>;
public processWip: Observable<boolean>;
public processDone: Observable<boolean>;
public processId: Observable<string>;
constructor(private store: Store<StoreState>) {
this.datasetSearchMetaIsLoading = store.select(metamodelSelector.getDatasetSearchMetaIsLoading);
......@@ -56,6 +59,9 @@ export class ResultComponent implements OnInit {
this.searchData = this.store.select(searchSelector.getSearchData);
this.selectedData = this.store.select(searchSelector.getSelectedData);
this.queryParams = this.store.select(searchSelector.getQueryParams);
this.processWip = this.store.select(searchSelector.getProcessWip);
this.processDone = this.store.select(searchSelector.getProcessDone);
this.processId = this.store.select(searchSelector.getProcessId);
}
ngOnInit() {
......@@ -75,11 +81,15 @@ export class ResultComponent implements OnInit {
this.store.dispatch(new searchActions.RetrieveDataAction(page));
}
addSearchData(data: any) {
addSearchData(data: any): void {
this.store.dispatch(new searchActions.AddSelectedDataAction(data));
}
deleteSearchData(data: any) {
deleteSearchData(data: any): void {
this.store.dispatch(new searchActions.DeleteSelectedDataAction(data));
}
executeProcess(typeAction: string): void {
this.store.dispatch(new searchActions.ExecuteProcessAction(typeAction));
}
}
......@@ -21,6 +21,10 @@ export const RETRIEVE_DATA_SUCCESS = '[Search] Retrieve Data Success';
export const RETRIEVE_DATA_FAIL = '[Search] Retrieve Data Fail';
export const ADD_SELECTED_DATA = '[Search] Add Selected Data';
export const DELETE_SELECTED_DATA = '[Search] Delete Selected Data';
export const EXECUTE_PROCESS = '[Search] Execute Process';
export const EXECUTE_PROCESS_WIP = '[Search] Execute Process WIP';
export const EXECUTE_PROCESS_SUCCESS = '[Search] Execute Process Success';
export const EXECUTE_PROCESS_FAIL = '[Search] Execute Process Fail';
export class InitSearchByUrl implements Action {
type = INIT_SEARCH_BY_URL;
......@@ -136,6 +140,30 @@ export class DeleteSelectedDataAction implements Action {
constructor(public payload: any) { }
}
export class ExecuteProcessAction implements Action {
type = EXECUTE_PROCESS;
constructor(public payload: string) { }
}
export class ExecuteProcessWipAction implements Action {
type = EXECUTE_PROCESS_WIP;
constructor(public payload: {} = null) { }
}
export class ExecuteProcessSuccessAction implements Action {
type = EXECUTE_PROCESS_SUCCESS;
constructor(public payload: any) { }
}
export class ExecuteProcessFailAction implements Action {
type = EXECUTE_PROCESS_FAIL;
constructor(public payload: {} = null) { }
}
export type Actions
= InitSearchByUrl
| ChangeStepAction
......@@ -155,4 +183,8 @@ export type Actions
| RetrieveDataSuccessAction
| RetrieveDataFailAction
| AddSelectedDataAction
| DeleteSelectedDataAction;
| DeleteSelectedDataAction
| ExecuteProcessAction
| ExecuteProcessWipAction
| ExecuteProcessSuccessAction
| ExecuteProcessFailAction;
import { Injectable } from '@angular/core';
import { HttpErrorResponse } from '@angular/common/http';
import { ToastrService } from 'ngx-toastr';
import { Effect, Actions, ofType } from '@ngrx/effects';
import { Store, Action } from '@ngrx/store';
import { of } from 'rxjs';
import { map, tap, switchMap, withLatestFrom, catchError } from 'rxjs/operators';
import { map, tap, switchMap, withLatestFrom, catchError, delay } from 'rxjs/operators';
import * as attributeActions from '../../metamodel/action/attribute.action';
import * as fromMetamodel from '../../metamodel/reducers';
import * as searchActions from './search.action';
import * as fromRouter from '@ngrx/router-store';
import * as fromSearch from './search.reducer';
......@@ -20,7 +22,11 @@ export class SearchEffects {
private actions$: Actions,
private searchService: SearchService,
private toastr: ToastrService,
private store$: Store<{ router: fromRouter.RouterReducerState<utils.RouterStateUrl>, search: fromSearch.State }>
private store$: Store<{
router: fromRouter.RouterReducerState<utils.RouterStateUrl>,
search: fromSearch.State,
metamodel: fromMetamodel.State
}>
) { }
@Effect()
......@@ -164,4 +170,49 @@ export class SearchEffects {
ofType(searchActions.RETRIEVE_DATA_FAIL),
tap(_ => this.toastr.error('Loading Failed!', 'The search data loading failed'))
);
@Effect()
executeProcessAction$ = this.actions$.pipe(
ofType(searchActions.EXECUTE_PROCESS),
withLatestFrom(this.store$),
switchMap(([action, state]) => {
const executeProcessAction = action as searchActions.ExecuteProcessAction;
let query = state.search.datasetName;
query += '?a=' + state.search.outputList.join(';');
query += '&c=';
query += state.metamodel.attribute.datasetAttributeList.find(a => a.search_flag === 'ID').id;
query += '::in::';
query += state.search.selectedData.join('|');
return this.searchService.executeProcess(executeProcessAction.payload, query).pipe(
map((res: any) => new searchActions.ExecuteProcessWipAction(res.message)),
catchError(() => of(new searchActions.ExecuteProcessFailAction()))
);
})
);
@Effect()
executeProcessWipAction$ = this.actions$.pipe(
ofType(searchActions.EXECUTE_PROCESS_WIP),
withLatestFrom(this.store$),
switchMap(([action, state]) => {
const executeProcessWipAction = action as searchActions.ExecuteProcessWipAction;
const idProcess = executeProcessWipAction.payload as string;
return this.searchService.checkProcess(idProcess).pipe(
map(_ => {
return new searchActions.ExecuteProcessSuccessAction(idProcess);
}),
catchError((err: HttpErrorResponse) => {
if (err.status === 404) {
return of(new searchActions.ExecuteProcessWipAction(idProcess)).pipe(delay(5000));
}
return of(new searchActions.ExecuteProcessFailAction());
}));
})
);
@Effect({ dispatch: false })
executeProcessFailAction$ = this.actions$.pipe(
ofType(searchActions.EXECUTE_PROCESS_FAIL),
tap(_ => this.toastr.error('Action Failed!', 'The process failed'))
);
}
......@@ -16,6 +16,9 @@ export interface State {
searchMeta: SearchMeta;
searchData: any[];
selectedData: any[];
processWip: boolean;
processDone: boolean;
processId: string;
}
const initialState: State = {
......@@ -31,7 +34,10 @@ const initialState: State = {
searchMetaIsLoaded: false,
searchMeta: null,
searchData: null,
selectedData: []
selectedData: [],
processWip: false,
processDone: false,
processId: null
};
export function reducer(state: State = initialState, action: actions.Actions): State {
......@@ -147,7 +153,7 @@ export function reducer(state: State = initialState, action: actions.Actions): S
return {
...state,
selectedData: [...state.selectedData, addData]
}
};
case actions.DELETE_SELECTED_DATA:
const deleteData = action.payload as any;
......@@ -155,7 +161,37 @@ export function reducer(state: State = initialState, action: actions.Actions): S
return {
...state,
selectedData: [...state.selectedData.filter(d => d !== deleteData)]
}
};
case actions.EXECUTE_PROCESS:
return {
...state,
processWip: true,
processDone: false
};
case actions.EXECUTE_PROCESS_WIP:
const processId = action.payload as string;
return {
...state,
processId
};
case actions.EXECUTE_PROCESS_SUCCESS:
return {
...state,
processWip: false,
processDone: true
};
case actions.EXECUTE_PROCESS_FAIL:
return {
...state,
processWip: false,
processDone: false,
processId: null
};
default:
return state;
......@@ -175,3 +211,6 @@ export const getSearchMetaIsLoaded = (state: State) => state.searchMetaIsLoaded;
export const getSearchMeta = (state: State) => state.searchMeta;
export const getSearchData = (state: State) => state.searchData;
export const getSelectedData = (state: State) => state.selectedData;
export const getProcessWip = (state: State) => state.processWip;
export const getProcessDone = (state: State) => state.processDone;
export const getProcessId = (state: State) => state.processId;
......@@ -8,7 +8,7 @@ export const getSearchState = createFeatureSelector<search.State>('search');
export const getPristine = createSelector(
getSearchState,
search.getPristine
)
);
export const getCurrentStep = createSelector(
getSearchState,
......@@ -100,3 +100,18 @@ export const getQueryParams = createSelector(
}
}
);
export const getProcessWip = createSelector(
getSearchState,
search.getProcessWip
);
export const getProcessDone = createSelector(
getSearchState,
search.getProcessDone
);
export const getProcessId = createSelector(
getSearchState,
search.getProcessId
);
......@@ -6,15 +6,25 @@ import { environment } from '../../../environments/environment';
@Injectable()
export class SearchService {
private API_PATH: string = environment.apiUrl + '/search/' + environment.instanceName;
private API_PATH: string = environment.apiUrl;
private instanceName: string = environment.instanceName;
constructor(private http: HttpClient) { }
retrieveMeta(query: string) {
return this.http.get<SearchMeta>(this.API_PATH + '/meta/' + query);
return this.http.get<SearchMeta>(this.API_PATH + '/search/' + this.instanceName + '/meta/' + query);
}
retrieveData(query: string) {
return this.http.get<any[]>(this.API_PATH + '/data/' + query);
return this.http.get<any[]>(this.API_PATH + '/search/' + this.instanceName + '/data/' + query);
}
executeProcess(typeAction: string, query: string) {
const url = this.API_PATH + '/service/' + this.instanceName + '/' + query;
return this.http.get<any>(url);
}
checkProcess(id: string) {
return this.http.head<any>('http://0.0.0.0:8085/' + id + '.csv');
}
}
Supports Markdown
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment