index.tsx 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387
  1. /*
  2. * <<
  3. * Davinci
  4. * ==
  5. * Copyright (C) 2016 - 2017 EDP
  6. * ==
  7. * Licensed under the Apache License, Version 2.0 (the "License");
  8. * you may not use this file except in compliance with the License.
  9. * You may obtain a copy of the License at
  10. *
  11. * http://www.apache.org/licenses/LICENSE-2.0
  12. *
  13. * Unless required by applicable law or agreed to in writing, software
  14. * distributed under the License is distributed on an "AS IS" BASIS,
  15. * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  16. * See the License for the specific language governing permissions and
  17. * limitations under the License.
  18. * >>
  19. */
  20. import React, { PureComponent, GetDerivedStateFromProps } from 'react'
  21. import {
  22. IControl,
  23. OnGetControlOptions,
  24. IMapControlOptions,
  25. IRenderTreeItem
  26. } from '../types'
  27. import { IFormedViews, IShareFormedViews } from 'app/containers/View/types'
  28. import {
  29. getVariableParams,
  30. getFilterParams,
  31. getAllChildren,
  32. getParents,
  33. getPanelRenderState,
  34. getCustomOptionVariableParams,
  35. cleanInvisibleConditionalControlValues
  36. } from '../util'
  37. import {
  38. SHOULD_LOAD_OPTIONS,
  39. CHANGE_IMMEDIATELY,
  40. ControlPanelTypes,
  41. ControlPanelLayoutTypes,
  42. ControlQueryMode,
  43. ControlFieldTypes,
  44. ControlOptionTypes
  45. } from '../constants'
  46. import DashboardControlPanelLayout from './Layouts/Dashboard'
  47. import DashboardItemControlPanelLayout from './Layouts/DashboardItem'
  48. import FullScreenControlPanelLayout from './Layouts/FullScreen'
  49. interface IControlPanelProps {
  50. controls: IControl[]
  51. formedViews: IFormedViews | IShareFormedViews
  52. items: string
  53. type: ControlPanelTypes
  54. layoutType: ControlPanelLayoutTypes
  55. viewId?: number
  56. reload: boolean
  57. queryMode: ControlQueryMode
  58. formValues: object
  59. mapOptions: IMapControlOptions
  60. onGetOptions: OnGetControlOptions
  61. onChange: (formValues: object) => void
  62. onSearch: (formValues?: object) => void
  63. }
  64. interface IControlPanelStates {
  65. renderTree: IRenderTreeItem[]
  66. flatTree: {
  67. [key: string]: IRenderTreeItem
  68. }
  69. defaultValues: object
  70. prevControls: IControl[]
  71. prevItems: string
  72. }
  73. class ControlPanel extends PureComponent<
  74. IControlPanelProps,
  75. IControlPanelStates
  76. > {
  77. public state: IControlPanelStates = {
  78. renderTree: [],
  79. flatTree: {},
  80. defaultValues: {},
  81. prevControls: [],
  82. prevItems: ''
  83. }
  84. public static getDerivedStateFromProps: GetDerivedStateFromProps<
  85. IControlPanelProps,
  86. IControlPanelStates
  87. > = (props, state) => {
  88. const { type, controls, items } = props
  89. let nextState: Partial<IControlPanelStates> = {
  90. prevControls: controls,
  91. prevItems: items
  92. }
  93. if (state.prevControls !== controls || state.prevItems !== items) {
  94. nextState = {
  95. ...nextState,
  96. ...getPanelRenderState(type, controls, items)
  97. }
  98. }
  99. return nextState
  100. }
  101. public componentDidMount() {
  102. this.initFormValuesAndSelectOptions(
  103. this.props,
  104. this.state,
  105. Object.keys(this.props.formValues).length > 0
  106. )
  107. }
  108. public componentDidUpdate(prevProps: IControlPanelProps) {
  109. const { controls, reload, onSearch } = this.props
  110. const { defaultValues } = this.state
  111. if (prevProps.controls !== controls) {
  112. this.initFormValuesAndSelectOptions(this.props, this.state, false)
  113. }
  114. if (reload) {
  115. onSearch(defaultValues)
  116. }
  117. }
  118. private initFormValuesAndSelectOptions(
  119. props: IControlPanelProps,
  120. state: IControlPanelStates,
  121. initiated: boolean
  122. ) {
  123. const { formValues, onChange } = props
  124. const { flatTree, defaultValues } = state
  125. const initialFormValues = initiated ? formValues : defaultValues
  126. onChange(initialFormValues)
  127. Object.values(flatTree).forEach((control) => {
  128. if (SHOULD_LOAD_OPTIONS[control.type]) {
  129. this.loadOptions(control, flatTree, initialFormValues)
  130. }
  131. })
  132. }
  133. private loadOptions = (
  134. renderControl: IRenderTreeItem,
  135. flatTree: { [key: string]: IRenderTreeItem },
  136. controlValues: { [key: string]: any }
  137. ) => {
  138. const { formedViews, type, items, onGetOptions } = this.props
  139. const {
  140. key,
  141. parent,
  142. cache,
  143. expired,
  144. optionType,
  145. valueViewId,
  146. valueField,
  147. textField,
  148. parentField,
  149. customOptions,
  150. relatedViews
  151. } = renderControl
  152. if (optionType === ControlOptionTypes.Custom) {
  153. onGetOptions(
  154. key,
  155. true,
  156. customOptions,
  157. type === ControlPanelTypes.Local ? Number(items) : void 0
  158. )
  159. } else {
  160. const parents = getParents(parent, flatTree)
  161. const requestParams = {}
  162. // get cascading conditions
  163. Object.entries(relatedViews).forEach(([viewId, relatedView]) => {
  164. let filters = []
  165. let variables = []
  166. parents.forEach((parentControl) => {
  167. const parentValue = controlValues[parentControl.key]
  168. Object.entries(parentControl.relatedViews).forEach(
  169. ([parentViewId, parentRelatedView]) => {
  170. if (viewId === parentViewId) {
  171. let cascadeRelatedViewId: string | number
  172. let cascadeRelatedFields: string[]
  173. let onlyFilters = false
  174. switch (optionType) {
  175. case ControlOptionTypes.Auto:
  176. cascadeRelatedViewId = parentViewId
  177. cascadeRelatedFields = parentRelatedView.fields
  178. break
  179. case ControlOptionTypes.Manual:
  180. if (
  181. parentControl.optionType === ControlOptionTypes.Auto &&
  182. valueViewId === Number(parentViewId)
  183. ) {
  184. cascadeRelatedViewId = parentViewId
  185. cascadeRelatedFields = parentRelatedView.fields
  186. } else if (
  187. parentControl.optionType === ControlOptionTypes.Manual &&
  188. valueViewId === parentControl.valueViewId
  189. ) {
  190. cascadeRelatedViewId = parentControl.valueViewId
  191. cascadeRelatedFields = [parentControl.valueField]
  192. onlyFilters = true
  193. }
  194. break
  195. }
  196. if (
  197. cascadeRelatedViewId &&
  198. cascadeRelatedFields &&
  199. formedViews[cascadeRelatedViewId]
  200. ) {
  201. const { model, variable } = formedViews[cascadeRelatedViewId]
  202. if (onlyFilters) {
  203. filters = filters.concat(
  204. getFilterParams(
  205. parentControl,
  206. cascadeRelatedFields,
  207. parentValue,
  208. model
  209. )
  210. )
  211. } else {
  212. if (parentControl.optionWithVariable) {
  213. variables = variables.concat(
  214. getCustomOptionVariableParams(
  215. parentControl,
  216. Number(cascadeRelatedViewId),
  217. parentValue,
  218. variable
  219. )
  220. )
  221. } else {
  222. if (
  223. parentRelatedView.fieldType === ControlFieldTypes.Column
  224. ) {
  225. filters = filters.concat(
  226. getFilterParams(
  227. parentControl,
  228. cascadeRelatedFields,
  229. parentValue,
  230. model
  231. )
  232. )
  233. } else {
  234. variables = variables.concat(
  235. getVariableParams(
  236. parentControl,
  237. cascadeRelatedFields,
  238. parentValue,
  239. variable
  240. )
  241. )
  242. }
  243. }
  244. }
  245. }
  246. }
  247. }
  248. )
  249. })
  250. switch (optionType) {
  251. case ControlOptionTypes.Auto:
  252. if (relatedView.fieldType === ControlFieldTypes.Column) {
  253. requestParams[viewId] = {
  254. columns: relatedView.fields,
  255. filters,
  256. variables,
  257. cache,
  258. expired
  259. }
  260. }
  261. break
  262. case ControlOptionTypes.Manual:
  263. requestParams[valueViewId] = {
  264. columns: [valueField, textField, parentField].filter((f) => !!f),
  265. filters,
  266. variables,
  267. cache,
  268. expired
  269. }
  270. break
  271. }
  272. })
  273. if (Object.keys(requestParams).length) {
  274. onGetOptions(
  275. key,
  276. false,
  277. requestParams,
  278. type === ControlPanelTypes.Local ? Number(items) : void 0
  279. )
  280. }
  281. }
  282. }
  283. private change = (control: IControl, val) => {
  284. const { controls, queryMode, formValues, onChange, onSearch } = this.props
  285. const { flatTree } = this.state
  286. const { key, type } = control
  287. const childrenKeys = getAllChildren(key, flatTree)
  288. const controlValue = {
  289. [key]: val
  290. }
  291. const cleanedInvisibleValues = cleanInvisibleConditionalControlValues(
  292. controls,
  293. control,
  294. formValues
  295. )
  296. const updatedFormValues = {
  297. ...formValues,
  298. ...controlValue,
  299. ...cleanedInvisibleValues
  300. }
  301. if (childrenKeys.length) {
  302. childrenKeys.forEach((childKey) => {
  303. const child = flatTree[childKey]
  304. if (SHOULD_LOAD_OPTIONS[child.type]) {
  305. this.loadOptions(child, flatTree, updatedFormValues)
  306. }
  307. })
  308. }
  309. onChange(updatedFormValues)
  310. if (
  311. queryMode === ControlQueryMode.Immediately &&
  312. CHANGE_IMMEDIATELY[type]
  313. ) {
  314. onSearch(controlValue)
  315. }
  316. }
  317. private reset = () => {
  318. const { queryMode, onChange, onSearch } = this.props
  319. const { defaultValues } = this.state
  320. onChange(defaultValues)
  321. if (queryMode === ControlQueryMode.Immediately) {
  322. onSearch(defaultValues)
  323. }
  324. }
  325. public render() {
  326. const {
  327. layoutType,
  328. queryMode,
  329. formValues,
  330. mapOptions,
  331. onSearch
  332. } = this.props
  333. const { renderTree } = this.state
  334. const layoutProps = {
  335. queryMode,
  336. renderTree,
  337. formValues,
  338. mapOptions,
  339. onChange: this.change,
  340. onSearch,
  341. onReset: this.reset
  342. }
  343. switch (layoutType) {
  344. case ControlPanelLayoutTypes.Dashboard:
  345. return <DashboardControlPanelLayout {...layoutProps} />
  346. case ControlPanelLayoutTypes.DashboardItem:
  347. return <DashboardItemControlPanelLayout {...layoutProps} />
  348. case ControlPanelLayoutTypes.Fullscreen:
  349. return <FullScreenControlPanelLayout {...layoutProps} />
  350. default:
  351. return null
  352. }
  353. }
  354. }
  355. export default ControlPanel