/* * << * Davinci * == * Copyright (C) 2016 - 2017 EDP * == * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. * >> */ import React, { PureComponent, GetDerivedStateFromProps } from 'react' import { IControl, OnGetControlOptions, IMapControlOptions, IRenderTreeItem } from '../types' import { IFormedViews, IShareFormedViews } from 'app/containers/View/types' import { getVariableParams, getFilterParams, getAllChildren, getParents, getPanelRenderState, getCustomOptionVariableParams, cleanInvisibleConditionalControlValues } from '../util' import { SHOULD_LOAD_OPTIONS, CHANGE_IMMEDIATELY, ControlPanelTypes, ControlPanelLayoutTypes, ControlQueryMode, ControlFieldTypes, ControlOptionTypes } from '../constants' import DashboardControlPanelLayout from './Layouts/Dashboard' import DashboardItemControlPanelLayout from './Layouts/DashboardItem' import FullScreenControlPanelLayout from './Layouts/FullScreen' interface IControlPanelProps { controls: IControl[] formedViews: IFormedViews | IShareFormedViews items: string type: ControlPanelTypes layoutType: ControlPanelLayoutTypes viewId?: number reload: boolean queryMode: ControlQueryMode formValues: object mapOptions: IMapControlOptions onGetOptions: OnGetControlOptions onChange: (formValues: object) => void onSearch: (formValues?: object) => void } interface IControlPanelStates { renderTree: IRenderTreeItem[] flatTree: { [key: string]: IRenderTreeItem } defaultValues: object prevControls: IControl[] prevItems: string } class ControlPanel extends PureComponent< IControlPanelProps, IControlPanelStates > { public state: IControlPanelStates = { renderTree: [], flatTree: {}, defaultValues: {}, prevControls: [], prevItems: '' } public static getDerivedStateFromProps: GetDerivedStateFromProps< IControlPanelProps, IControlPanelStates > = (props, state) => { const { type, controls, items } = props let nextState: Partial = { prevControls: controls, prevItems: items } if (state.prevControls !== controls || state.prevItems !== items) { nextState = { ...nextState, ...getPanelRenderState(type, controls, items) } } return nextState } public componentDidMount() { this.initFormValuesAndSelectOptions( this.props, this.state, Object.keys(this.props.formValues).length > 0 ) } public componentDidUpdate(prevProps: IControlPanelProps) { const { controls, reload, onSearch } = this.props const { defaultValues } = this.state if (prevProps.controls !== controls) { this.initFormValuesAndSelectOptions(this.props, this.state, false) } if (reload) { onSearch(defaultValues) } } private initFormValuesAndSelectOptions( props: IControlPanelProps, state: IControlPanelStates, initiated: boolean ) { const { formValues, onChange } = props const { flatTree, defaultValues } = state const initialFormValues = initiated ? formValues : defaultValues onChange(initialFormValues) Object.values(flatTree).forEach((control) => { if (SHOULD_LOAD_OPTIONS[control.type]) { this.loadOptions(control, flatTree, initialFormValues) } }) } private loadOptions = ( renderControl: IRenderTreeItem, flatTree: { [key: string]: IRenderTreeItem }, controlValues: { [key: string]: any } ) => { const { formedViews, type, items, onGetOptions } = this.props const { key, parent, cache, expired, optionType, valueViewId, valueField, textField, parentField, customOptions, relatedViews } = renderControl if (optionType === ControlOptionTypes.Custom) { onGetOptions( key, true, customOptions, type === ControlPanelTypes.Local ? Number(items) : void 0 ) } else { const parents = getParents(parent, flatTree) const requestParams = {} // get cascading conditions Object.entries(relatedViews).forEach(([viewId, relatedView]) => { let filters = [] let variables = [] parents.forEach((parentControl) => { const parentValue = controlValues[parentControl.key] Object.entries(parentControl.relatedViews).forEach( ([parentViewId, parentRelatedView]) => { if (viewId === parentViewId) { let cascadeRelatedViewId: string | number let cascadeRelatedFields: string[] let onlyFilters = false switch (optionType) { case ControlOptionTypes.Auto: cascadeRelatedViewId = parentViewId cascadeRelatedFields = parentRelatedView.fields break case ControlOptionTypes.Manual: if ( parentControl.optionType === ControlOptionTypes.Auto && valueViewId === Number(parentViewId) ) { cascadeRelatedViewId = parentViewId cascadeRelatedFields = parentRelatedView.fields } else if ( parentControl.optionType === ControlOptionTypes.Manual && valueViewId === parentControl.valueViewId ) { cascadeRelatedViewId = parentControl.valueViewId cascadeRelatedFields = [parentControl.valueField] onlyFilters = true } break } if ( cascadeRelatedViewId && cascadeRelatedFields && formedViews[cascadeRelatedViewId] ) { const { model, variable } = formedViews[cascadeRelatedViewId] if (onlyFilters) { filters = filters.concat( getFilterParams( parentControl, cascadeRelatedFields, parentValue, model ) ) } else { if (parentControl.optionWithVariable) { variables = variables.concat( getCustomOptionVariableParams( parentControl, Number(cascadeRelatedViewId), parentValue, variable ) ) } else { if ( parentRelatedView.fieldType === ControlFieldTypes.Column ) { filters = filters.concat( getFilterParams( parentControl, cascadeRelatedFields, parentValue, model ) ) } else { variables = variables.concat( getVariableParams( parentControl, cascadeRelatedFields, parentValue, variable ) ) } } } } } } ) }) switch (optionType) { case ControlOptionTypes.Auto: if (relatedView.fieldType === ControlFieldTypes.Column) { requestParams[viewId] = { columns: relatedView.fields, filters, variables, cache, expired } } break case ControlOptionTypes.Manual: requestParams[valueViewId] = { columns: [valueField, textField, parentField].filter((f) => !!f), filters, variables, cache, expired } break } }) if (Object.keys(requestParams).length) { onGetOptions( key, false, requestParams, type === ControlPanelTypes.Local ? Number(items) : void 0 ) } } } private change = (control: IControl, val) => { const { controls, queryMode, formValues, onChange, onSearch } = this.props const { flatTree } = this.state const { key, type } = control const childrenKeys = getAllChildren(key, flatTree) const controlValue = { [key]: val } const cleanedInvisibleValues = cleanInvisibleConditionalControlValues( controls, control, formValues ) const updatedFormValues = { ...formValues, ...controlValue, ...cleanedInvisibleValues } if (childrenKeys.length) { childrenKeys.forEach((childKey) => { const child = flatTree[childKey] if (SHOULD_LOAD_OPTIONS[child.type]) { this.loadOptions(child, flatTree, updatedFormValues) } }) } onChange(updatedFormValues) if ( queryMode === ControlQueryMode.Immediately && CHANGE_IMMEDIATELY[type] ) { onSearch(controlValue) } } private reset = () => { const { queryMode, onChange, onSearch } = this.props const { defaultValues } = this.state onChange(defaultValues) if (queryMode === ControlQueryMode.Immediately) { onSearch(defaultValues) } } public render() { const { layoutType, queryMode, formValues, mapOptions, onSearch } = this.props const { renderTree } = this.state const layoutProps = { queryMode, renderTree, formValues, mapOptions, onChange: this.change, onSearch, onReset: this.reset } switch (layoutType) { case ControlPanelLayoutTypes.Dashboard: return case ControlPanelLayoutTypes.DashboardItem: return case ControlPanelLayoutTypes.Fullscreen: return default: return null } } } export default ControlPanel