form-craft-package 1.5.6 → 1.6.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (62) hide show
  1. package/index.ts +0 -3
  2. package/package.json +1 -1
  3. package/src/components/common/custom-hooks/use-check-element-conditions.hook.ts +20 -6
  4. package/src/components/common/custom-hooks/use-window-width.hook.ts +31 -0
  5. package/src/components/form/1-list/index.tsx +11 -4
  6. package/src/components/form/1-list/table-header.tsx +29 -40
  7. package/src/components/form/1-list/table.tsx +37 -20
  8. package/src/components/form/2-details/index.tsx +21 -9
  9. package/src/components/form/3-preview/index.tsx +5 -5
  10. package/src/components/form/layout-renderer/1-row/header.tsx +1 -1
  11. package/src/components/form/layout-renderer/1-row/index.tsx +17 -19
  12. package/src/components/form/layout-renderer/1-row/repeatable-render.tsx +5 -5
  13. package/src/components/form/layout-renderer/2-col/index.tsx +11 -18
  14. package/src/components/form/layout-renderer/3-element/1-dynamic-button/index.tsx +67 -22
  15. package/src/components/form/layout-renderer/3-element/1-dynamic-button/use-create-data.hook.ts +8 -9
  16. package/src/components/form/layout-renderer/3-element/1-dynamic-button/use-delete-data.hook.ts +2 -5
  17. package/src/components/form/layout-renderer/3-element/1-dynamic-button/use-duplicate-data.hook.ts +2 -5
  18. package/src/components/form/layout-renderer/3-element/1-dynamic-button/use-generate-report.hook.tsx +13 -11
  19. package/src/components/form/layout-renderer/3-element/1-dynamic-button/use-publish-data.hook.ts +2 -5
  20. package/src/components/form/layout-renderer/3-element/1-dynamic-button/use-save-data.hook.ts +4 -5
  21. package/src/components/form/layout-renderer/3-element/1-dynamic-button/use-save-signature.hook.ts +2 -2
  22. package/src/components/form/layout-renderer/3-element/10-currency.tsx +2 -2
  23. package/src/components/form/layout-renderer/3-element/2-field-element.tsx +4 -4
  24. package/src/components/form/layout-renderer/3-element/3-read-field-data.tsx +2 -2
  25. package/src/components/form/layout-renderer/3-element/7-file-upload.tsx +1 -1
  26. package/src/components/form/layout-renderer/3-element/8-fields-with-options.tsx +25 -13
  27. package/src/components/form/layout-renderer/3-element/9-form-data-render.tsx +160 -157
  28. package/src/components/form/layout-renderer/3-element/index.tsx +24 -13
  29. package/src/constants.ts +13 -9
  30. package/src/enums/form.enum.ts +244 -0
  31. package/src/enums/index.ts +42 -0
  32. package/src/functions/companies/FontSelector.tsx +67 -0
  33. package/src/functions/{conditional-rule-validator.ts → forms/conditional-rule-validator.ts} +5 -2
  34. package/src/functions/{create-form-rules.ts → forms/create-form-rules.ts} +3 -4
  35. package/src/functions/{data-render-functions.tsx → forms/data-render-functions.tsx} +6 -6
  36. package/src/functions/{evaluate-condition.ts → forms/evaluate-condition.ts} +1 -1
  37. package/src/functions/{evaluate-value.ts → forms/evaluate-value.ts} +3 -3
  38. package/src/functions/{form-schema-validator.ts → forms/form-schema-validator.ts} +4 -4
  39. package/src/functions/{form.ts → forms/form.ts} +1 -1
  40. package/src/functions/{get-element-props.ts → forms/get-element-props.ts} +3 -3
  41. package/src/functions/forms/index.ts +247 -0
  42. package/src/functions/index.ts +1 -301
  43. package/src/types/companies/index.ts +157 -32
  44. package/src/types/companies/site-layout/authenticated/index.tsx +439 -349
  45. package/src/types/{data-list → forms/data-list}/filter-config.ts +2 -2
  46. package/src/types/{data-list → forms/data-list}/index.ts +7 -5
  47. package/src/types/{generate → forms/generate}/index.ts +1 -1
  48. package/src/types/forms/index.ts +108 -0
  49. package/src/types/forms/layout-elements/button.ts +47 -0
  50. package/src/types/{layout-elements → forms/layout-elements}/conditions.ts +8 -1
  51. package/src/types/{layout-elements → forms/layout-elements}/data-render-config.ts +3 -3
  52. package/src/types/{layout-elements → forms/layout-elements}/evaluation-config.ts +1 -1
  53. package/src/types/{layout-elements → forms/layout-elements}/field-option-source.ts +1 -1
  54. package/src/types/{layout-elements → forms/layout-elements}/index.ts +14 -47
  55. package/src/types/{layout-elements → forms/layout-elements}/sanitization.ts +1 -1
  56. package/src/types/forms/layout-elements/style.ts +20 -0
  57. package/src/types/{layout-elements → forms/layout-elements}/validation.ts +1 -1
  58. package/src/types/{relationship → forms/relationship}/index.ts +9 -8
  59. package/src/types/index.ts +1 -128
  60. package/src/enums.ts +0 -298
  61. package/src/types/layout-elements/style.ts +0 -40
  62. /package/src/functions/{json-handlers.ts → forms/json-handlers.ts} +0 -0
package/index.ts CHANGED
@@ -2,8 +2,5 @@ export * from './src/enums'
2
2
  export * from './src/types'
3
3
  export * from './src/constants'
4
4
  export * from './src/functions'
5
- export * from './src/functions/json-handlers'
6
- export * from './src/functions/form-schema-validator'
7
- export * from './src/functions/get-element-props'
8
5
  export * from './src/components'
9
6
  export * from './src/components/common/custom-hooks'
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "form-craft-package",
3
- "version": "1.5.6",
3
+ "version": "1.6.0",
4
4
  "main": "index.ts",
5
5
  "scripts": {
6
6
  "test": "echo \"Error: no test specified\" && exit 1",
@@ -1,9 +1,10 @@
1
1
  import { Form, FormInstance } from 'antd'
2
2
  import { useEffect, useState } from 'react'
3
3
  import { IConditionalValidation_Field, IFormLayoutElementConditions } from '../../../types'
4
- import { FormElementConditionalKeyEnum, FormStateEnum } from '../../../enums'
5
- import { getValidationConfigs, validateConditions } from '../../../functions/conditional-rule-validator'
4
+ import { DeviceBreakpointEnum, FormElementConditionalKeyEnum, FormStateEnum } from '../../../enums'
5
+ import { getValidationConfigs, validateConditions } from '../../../functions/forms/conditional-rule-validator'
6
6
  import { NEW_FORM_DATA_IDENTIFIER } from '../../../constants'
7
+ import useGetCurrentBreakpoint from './use-window-width.hook'
7
8
 
8
9
  export const useCheckElementConditions = (
9
10
  formRef: FormInstance | undefined,
@@ -12,13 +13,20 @@ export const useCheckElementConditions = (
12
13
  ) => {
13
14
  const [isDisabled, setIsDisabled] = useState(defaultDisabled)
14
15
  const [isHidden, setIsHidden] = useState(false)
16
+ const currentBreakpoint = useGetCurrentBreakpoint()
15
17
 
16
18
  const formValues = Form.useWatch([], { form: formRef, preserve: true })
17
19
 
18
20
  useEffect(() => {
19
21
  if (!!formValues) {
20
- setIsDisabled(!isValidationsMet(FormElementConditionalKeyEnum.EnableIf, formValues, conditionalConfigs))
21
- setIsHidden(!isValidationsMet(FormElementConditionalKeyEnum.ShowIf, formValues, conditionalConfigs))
22
+ setIsDisabled(
23
+ !isValidationsMet(FormElementConditionalKeyEnum.EnableIf, formValues, conditionalConfigs, {
24
+ currentBreakpoint,
25
+ }),
26
+ )
27
+ setIsHidden(
28
+ !isValidationsMet(FormElementConditionalKeyEnum.ShowIf, formValues, conditionalConfigs, { currentBreakpoint }),
29
+ )
22
30
  }
23
31
  }, [formValues, conditionalConfigs])
24
32
 
@@ -29,12 +37,13 @@ const isValidationsMet = (
29
37
  type: FormElementConditionalKeyEnum,
30
38
  dataValues: { [key: string]: any },
31
39
  configs?: IFormLayoutElementConditions,
40
+ contextValues?: { currentBreakpoint?: DeviceBreakpointEnum },
32
41
  ) => {
33
42
  const validations = configs?.[type] ?? []
34
43
 
35
44
  if (validations.length === 0) return true
36
45
 
37
- const { fieldValidations, authUserValidations, hasDataIdValidation, alwaysDisabled } =
46
+ const { fieldValidations, authUserValidations, hasDataIdValidation, alwaysDisabled, hideScreens } =
38
47
  getValidationConfigs(validations)
39
48
 
40
49
  if (alwaysDisabled) return false
@@ -55,5 +64,10 @@ const isValidationsMet = (
55
64
  : dataValues.formDataId !== NEW_FORM_DATA_IDENTIFIER
56
65
  : true
57
66
 
58
- return isFieldsConditionsMet && isAuthUserConditionsMet && isDataIdConditionsMet
67
+ // Dependent on screen
68
+ const isScreenConditionsMet = contextValues?.currentBreakpoint
69
+ ? !hideScreens.includes(contextValues.currentBreakpoint)
70
+ : true
71
+
72
+ return isFieldsConditionsMet && isAuthUserConditionsMet && isDataIdConditionsMet && isScreenConditionsMet
59
73
  }
@@ -0,0 +1,31 @@
1
+ import { useState, useEffect } from 'react'
2
+ import { DeviceBreakpointEnum } from '../../../enums'
3
+ import { DEVICE_BREAKPOINTS } from '../../../constants'
4
+
5
+ const useGetCurrentBreakpoint = (): DeviceBreakpointEnum => {
6
+ const [width, setWidth] = useState<number>(typeof window !== 'undefined' ? window.innerWidth : 0)
7
+
8
+ useEffect(() => {
9
+ const handleResize = () => setWidth(window.innerWidth)
10
+
11
+ window.addEventListener('resize', handleResize)
12
+
13
+ return () => window.removeEventListener('resize', handleResize)
14
+ }, [])
15
+
16
+ if (!width) return DeviceBreakpointEnum.Default
17
+
18
+ // if (width < parseInt(DEVICE_BREAKPOINTS[DeviceBreakpointEnum.Mobile])) return DeviceBreakpointEnum.Mobile
19
+
20
+ if (width < parseInt(DEVICE_BREAKPOINTS[DeviceBreakpointEnum.TabletPortrait]))
21
+ return DeviceBreakpointEnum.TabletPortrait
22
+
23
+ if (width < parseInt(DEVICE_BREAKPOINTS[DeviceBreakpointEnum.TabletLandscape]))
24
+ return DeviceBreakpointEnum.TabletLandscape
25
+
26
+ if (width < parseInt(DEVICE_BREAKPOINTS[DeviceBreakpointEnum.Laptop])) return DeviceBreakpointEnum.Laptop
27
+
28
+ return DeviceBreakpointEnum.Default
29
+ }
30
+
31
+ export default useGetCurrentBreakpoint
@@ -1,10 +1,11 @@
1
1
  import { useCallback, useEffect, useMemo, useState } from 'react'
2
2
  import client from '../../../functions/axios-handler'
3
- import { parseJSON } from '../../../functions/json-handlers'
3
+ import { parseJSON } from '../../../functions/forms/json-handlers'
4
4
  import FormDataListSkeleton_Table from '../../common/loading-skeletons/table'
5
- import { IFormData, IFormDataListConfig, IFormLayoutRow, IFormSchema } from '../../../types'
5
+ import { IFormData, IFormDataListConfig, IFormLayoutElement, IFormLayoutRow, IFormSchema } from '../../../types'
6
6
  import { ICustomFunctionCall } from '../layout-renderer/3-element/1-dynamic-button'
7
7
  import FormDataListTableComponent, { IReqDataConfig } from './table'
8
+ import { DeviceBreakpointEnum } from '../../../enums'
8
9
 
9
10
  function FormDataListComponent({
10
11
  formName,
@@ -19,7 +20,10 @@ function FormDataListComponent({
19
20
  const [dataListConfig, setDataListConfig] = useState<
20
21
  | {
21
22
  dataListConfig: IFormDataListConfig & { configForFormId: number }
22
- detailsLayout: IFormLayoutRow[]
23
+ detailsLayoutConfig: {
24
+ elements: { [key: string]: IFormLayoutElement }
25
+ layouts: { [key in DeviceBreakpointEnum]?: IFormLayoutRow[] }
26
+ }
23
27
  }
24
28
  | undefined
25
29
  >()
@@ -36,7 +40,10 @@ function FormDataListComponent({
36
40
  // configForFormId is passed to ensure that the table shows the correct data list when the forms are switched quickly
37
41
  setDataListConfig({
38
42
  dataListConfig: { ...parsedData.dataListConfig, configForFormId: formId },
39
- detailsLayout: parsedData.detailsConfig.layout,
43
+ detailsLayoutConfig: {
44
+ elements: parsedData.detailsConfig.elements,
45
+ layouts: parsedData.detailsConfig.layouts,
46
+ },
40
47
  })
41
48
  }
42
49
  }
@@ -1,11 +1,12 @@
1
1
  import { Form } from 'antd'
2
2
  import { ReactNode, useCallback, useEffect, useMemo, useState } from 'react'
3
3
  import dayjs from 'dayjs'
4
- import { extractFiltersFromLayout } from '../../../functions'
5
- import { FilterConfigTypeEnum, FormPreservedItemKeys } from '../../../enums'
4
+ import { extractFiltersFromLayout } from '../../../functions/forms'
5
+ import { DeviceBreakpointEnum, FilterConfigTypeEnum, FormPreservedItemKeys } from '../../../enums'
6
6
  import { LayoutRendererRow } from '../layout-renderer/1-row'
7
7
  import { VALUE_REPLACEMENT_PLACEHOLDER } from '../../../constants'
8
8
  import { DynamicFormButtonRender, ICustomFunctionCall } from '../layout-renderer/3-element/1-dynamic-button'
9
+ import useGetCurrentBreakpoint from '../../common/custom-hooks/use-window-width.hook'
9
10
  import {
10
11
  DEFAULT_NO_FILTER,
11
12
  DEFAULT_NO_SORTER,
@@ -13,20 +14,17 @@ import {
13
14
  IDataListHeaderContext,
14
15
  IReqDataConfig,
15
16
  } from './table'
16
- import client from '../../../functions/axios-handler'
17
17
  import {
18
18
  IFilterByAuthUser,
19
- IFilterByLinkedForm,
20
19
  IFilterConfig,
21
20
  IFilterCustom,
22
21
  IFilterNested,
23
22
  IFilterSimple,
24
- IFormData,
25
- IFormLayoutRow,
23
+ IFormDetailsConfig,
26
24
  } from '../../../types'
27
25
 
28
26
  export default function FormDataListHeaderComponent({
29
- layout,
27
+ layoutConfig,
30
28
  titleComponent,
31
29
  defaultFilter,
32
30
  startLoading,
@@ -36,7 +34,14 @@ export default function FormDataListHeaderComponent({
36
34
  }: IFormDataListHeaderComponent) {
37
35
  const [dataListHeaderFormRef] = Form.useForm()
38
36
  const [filterConfigs, setFilterConfigs] = useState<IFilterNested>({})
39
- const { userId, formId, formName, parentInfo } = dataListHeaderContext
37
+ const { userId, formId, formName, parentInfo, formDataId } = dataListHeaderContext
38
+
39
+ const currentBreakpoint = useGetCurrentBreakpoint()
40
+
41
+ const layout = useMemo(
42
+ () => layoutConfig.layouts[currentBreakpoint] ?? layoutConfig.layouts[DeviceBreakpointEnum.Default] ?? [],
43
+ [layoutConfig.layouts, currentBreakpoint],
44
+ )
40
45
 
41
46
  const filterValues = Form.useWatch([], dataListHeaderFormRef)
42
47
 
@@ -44,7 +49,7 @@ export default function FormDataListHeaderComponent({
44
49
  if (dataListHeaderFormRef) dataListHeaderFormRef.setFieldsValue({ [FormPreservedItemKeys.InPreviewMode]: false })
45
50
  }, [dataListHeaderFormRef])
46
51
 
47
- useEffect(() => setFilterConfigs(extractFiltersFromLayout(layout)), [layout])
52
+ useEffect(() => setFilterConfigs(extractFiltersFromLayout(layoutConfig.elements)), [layoutConfig])
48
53
 
49
54
  const organizeFilterData = useCallback(
50
55
  async (values: { [key: string]: any }) => {
@@ -75,25 +80,28 @@ export default function FormDataListHeaderComponent({
75
80
  }
76
81
  }
77
82
 
78
- const { customFilter, dynamicFilter } = filtersToApply.reduce(
79
- (curr, next) => {
83
+ const { customFilter, dynamicFilters } = filtersToApply.reduce(
84
+ (curr: { customFilter: { [key: string]: any }; dynamicFilters: { [key: string]: any }[] }, next) => {
80
85
  if (!next) return curr
81
86
  const parsedDFilter = JSON.parse(next.dynamicFilter)
82
87
  return {
83
88
  customFilter: { ...curr.customFilter, ...next.customFilter },
84
- dynamicFilter: { ...curr.dynamicFilter, ...parsedDFilter },
89
+ dynamicFilters: [...curr.dynamicFilters, parsedDFilter],
85
90
  }
86
91
  },
87
- { customFilter: {}, dynamicFilter: {} },
92
+ { customFilter: {}, dynamicFilters: [] },
88
93
  )
89
94
 
95
+ const updatedDynamicFilter = JSON.stringify(
96
+ dynamicFilters.length === 1 ? dynamicFilters[0] : { $and: dynamicFilters },
97
+ )
90
98
  updateReqData((c) => {
91
99
  if (!c)
92
100
  return {
93
101
  ...DEFAULT_NO_SORTER,
94
102
  pagination: DEFAULT_PAGINATION,
95
103
  customFilter,
96
- dynamicFilter: JSON.stringify(dynamicFilter),
104
+ dynamicFilter: updatedDynamicFilter,
97
105
  }
98
106
 
99
107
  return {
@@ -101,7 +109,7 @@ export default function FormDataListHeaderComponent({
101
109
  sort: defaultFilter.sort,
102
110
  pagination: { ...defaultFilter.pagination, current: 1 },
103
111
  customFilter,
104
- dynamicFilter: JSON.stringify(dynamicFilter),
112
+ dynamicFilter: updatedDynamicFilter,
105
113
  }
106
114
  })
107
115
  } else updateReqData(defaultFilter)
@@ -114,8 +122,8 @@ export default function FormDataListHeaderComponent({
114
122
  }, [filterValues])
115
123
 
116
124
  const formContext = useMemo(
117
- () => ({ formRef: dataListHeaderFormRef, formName, formId }),
118
- [dataListHeaderFormRef, formName, formId],
125
+ () => ({ formRef: dataListHeaderFormRef, formName, formId, formDataId, linkedParentsDataIds: parentInfo?.dataIds }),
126
+ [dataListHeaderFormRef, formName, formId, formDataId, parentInfo],
119
127
  )
120
128
 
121
129
  return (
@@ -126,6 +134,7 @@ export default function FormDataListHeaderComponent({
126
134
  rowData={row}
127
135
  titleComponent={titleComponent}
128
136
  formContext={formContext}
137
+ elements={layoutConfig.elements}
129
138
  renderButton={(btnProps, conditions) => (
130
139
  <DynamicFormButtonRender
131
140
  displayStateProps={{ btnProps, conditions, stateToPass: { parentInfo } }}
@@ -140,7 +149,7 @@ export default function FormDataListHeaderComponent({
140
149
  }
141
150
 
142
151
  type IFormDataListHeaderComponent = {
143
- layout: IFormLayoutRow[]
152
+ layoutConfig: IFormDetailsConfig
144
153
  updateReqData: React.Dispatch<React.SetStateAction<IReqDataConfig | undefined>>
145
154
  titleComponent?: ReactNode
146
155
  defaultFilter: IReqDataConfig
@@ -148,26 +157,6 @@ type IFormDataListHeaderComponent = {
148
157
  } & ICustomFunctionCall &
149
158
  IDataListHeaderContext
150
159
 
151
- const getLinkedFormFilter = async (
152
- config: IFilterByLinkedForm,
153
- parentDataIds: string[],
154
- lastRelIndex: number,
155
- ): Promise<{ dataIds: string[]; foreignKey: string }> => {
156
- const lastRel = config.relationshipPath[lastRelIndex]
157
-
158
- if (lastRelIndex - 1 >= 0) {
159
- const childOfLastRel = config.relationshipPath[lastRelIndex - 1]
160
- const childFormDataRes = await client.post(`/api/formdata/list/${childOfLastRel.formId}`, {
161
- dynamicFilter: JSON.stringify({ [`Data.${lastRel.foreignKey}`]: { $in: parentDataIds } }),
162
- })
163
- if (childFormDataRes.status === 200) {
164
- const childDataIds = childFormDataRes.data.data.map((d: IFormData) => d.id)
165
-
166
- return getLinkedFormFilter(config, childDataIds, lastRelIndex - 1)
167
- } else return { dataIds: parentDataIds, foreignKey: childOfLastRel.foreignKey }
168
- } else return { dataIds: parentDataIds, foreignKey: lastRel.foreignKey }
169
- }
170
-
171
160
  const handleFilterValues = async (
172
161
  config: IFilterConfig,
173
162
  value: any,
@@ -194,11 +183,11 @@ const handleFilterValues = async (
194
183
  }
195
184
 
196
185
  if (config.type === FilterConfigTypeEnum.ByLinkedForm && value) {
197
- const finalFilter = await getLinkedFormFilter(config, [value], config.relationshipPath.length - 1)
186
+ const lastRel = config.relationshipPath[config.relationshipPath.length - 1]
198
187
 
199
188
  return {
200
189
  customFilter: {},
201
- dynamicFilter: JSON.stringify({ [`Data.${finalFilter.foreignKey}`]: { $in: finalFilter.dataIds } }),
190
+ dynamicFilter: JSON.stringify({ $expr: { $eq: [`$${lastRel.formId}._id`, { $toObjectId: value }] } }),
202
191
  }
203
192
  }
204
193
  }
@@ -1,17 +1,23 @@
1
- import { useCallback, useEffect, useRef, useState } from 'react'
2
- import { IDataListParentInfo, IFormData, IFormDataListConfig, IFormLayoutRow, ITableColumn } from '../../../types'
1
+ import { useCallback, useEffect, useMemo, useRef, useState } from 'react'
2
+ import { IDataListParentInfo, IFormData, IFormDataListConfig, IFormDetailsConfig, ITableColumn } from '../../../types'
3
3
  import FormDataListHeaderComponent from './table-header'
4
4
  import useDebounced from '../../common/custom-hooks/use-debounce.hook'
5
5
  import { FormInstance, Table } from 'antd'
6
- import { FilterConfigTypeEnum, FormDataListViewTypeEnum, MongoDbSortOrderEnum } from '../../../enums'
7
6
  import { ICustomFunctionCall } from '../layout-renderer/3-element/1-dynamic-button'
8
7
  import { SorterResult } from 'antd/es/table/interface'
9
- import { appendOptionsFromDetailsLayout, extractElementsOptions, generateTableColumns } from '../../../functions'
8
+ import { appendOptionsFromDetailsLayout, extractElementsOptions, generateTableColumns } from '../../../functions/forms'
10
9
  import { useLocation } from 'react-router-dom'
10
+ import useGetCurrentBreakpoint from '../../common/custom-hooks/use-window-width.hook'
11
+ import {
12
+ DeviceBreakpointEnum,
13
+ FilterConfigTypeEnum,
14
+ FormDataListViewTypeEnum,
15
+ MongoDbSortOrderEnum,
16
+ } from '../../../enums'
11
17
 
12
18
  export default function FormDataListTableComponent({
13
19
  dataListConfig,
14
- detailsLayout,
20
+ detailsLayoutConfig,
15
21
  parentLoading,
16
22
  dataList,
17
23
  loadingBlock,
@@ -20,8 +26,9 @@ export default function FormDataListTableComponent({
20
26
  onCustomFunctionCall,
21
27
  ...dataListHeaderContext
22
28
  }: IFormDataListTableComponent) {
29
+ const currentBreakpoint = useGetCurrentBreakpoint()
23
30
  const location = useLocation()
24
- const [tableColumns, setTableColumns] = useState<ITableColumn[]>([])
31
+ const [tableColumns, setTableColumns] = useState<(ITableColumn & { hideScreens?: DeviceBreakpointEnum[] })[]>([])
25
32
  const [filterReqData, setFilterReqData, debouncedFilterReqData] = useDebounced<IReqDataConfig | undefined>(
26
33
  undefined,
27
34
  250,
@@ -29,7 +36,7 @@ export default function FormDataListTableComponent({
29
36
  const [otherConfigs, setOtherConfigs] = useState<
30
37
  (Partial<IFormDataListConfig> & { hasNoPagination: boolean }) | undefined
31
38
  >(undefined)
32
- const [headerLayout, setHeaderLayout] = useState<IFormLayoutRow[]>([])
39
+ const [headerLayoutConfig, setHeaderLayoutConfig] = useState<IFormDetailsConfig>({ elements: {}, layouts: {} })
33
40
  const [isHandlingSchema, setIsHandlingSchema] = useState(true)
34
41
  const isFinishedHandlingRef = useRef(false)
35
42
 
@@ -50,26 +57,31 @@ export default function FormDataListTableComponent({
50
57
  }, [debouncedFilterReqData])
51
58
 
52
59
  const handleDataListConfig = useCallback(
53
- async (config: IFormDataListConfig & { configForFormId?: number }, layout: IFormLayoutRow[] = []) => {
60
+ async (config: IFormDataListConfig & { configForFormId?: number }, layoutConfig?: IFormDetailsConfig) => {
54
61
  if (config.configForFormId && config.configForFormId !== formId) return
55
62
 
56
63
  const {
57
- elements = [],
58
- header = { layout: [] },
64
+ columns = [],
65
+ header = { elements: {}, layouts: {} },
59
66
  pagination,
60
67
  defaultFilter,
61
68
  defaultSorter,
62
69
  ...restConfigs
63
70
  } = config
64
71
 
65
- const elementsWithOptions_key = elements.filter((op) => op.isSelectOption).map((op) => op.key)
72
+ const elementsWithOptions_key = columns
73
+ .filter((op) => op.isSelectOption)
74
+ .map((op) => (op.key.includes('Data.') ? op.key.replace('Data.', '') : op.key))
66
75
 
67
76
  let optionKeyValuePair = {}
68
77
  if (elementsWithOptions_key.length > 0)
69
- optionKeyValuePair = await extractElementsOptions(layout, elementsWithOptions_key)
78
+ optionKeyValuePair = await extractElementsOptions(layoutConfig?.elements ?? {}, elementsWithOptions_key)
70
79
 
71
- setTableColumns(generateTableColumns(elements, optionKeyValuePair, { attachmentBaseUrl, formName, formRef }))
72
- setHeaderLayout(appendOptionsFromDetailsLayout(header.layout, layout))
80
+ setTableColumns(generateTableColumns(columns, optionKeyValuePair, { attachmentBaseUrl, formName, formRef }))
81
+ setHeaderLayoutConfig({
82
+ layouts: header.layouts,
83
+ elements: appendOptionsFromDetailsLayout(header.elements, layoutConfig?.elements),
84
+ })
73
85
  setOtherConfigs({ ...restConfigs, hasNoPagination: pagination?.hasNoPagination ?? false })
74
86
 
75
87
  if (defaultFilter && defaultFilter.type !== FilterConfigTypeEnum.NoFilter)
@@ -95,19 +107,24 @@ export default function FormDataListTableComponent({
95
107
  setIsHandlingSchema(false)
96
108
  isFinishedHandlingRef.current = true
97
109
  },
98
- [attachmentBaseUrl, formName, userId, formId, formRef],
110
+ [attachmentBaseUrl, formName, userId, formId, formRef, currentBreakpoint],
99
111
  )
100
112
 
101
113
  useEffect(() => {
102
- if (dataListConfig) handleDataListConfig(dataListConfig, detailsLayout)
103
- }, [dataListConfig, detailsLayout])
114
+ if (dataListConfig) handleDataListConfig(dataListConfig, detailsLayoutConfig)
115
+ }, [dataListConfig, detailsLayoutConfig])
116
+
117
+ const tableColumnsFiltered = useMemo(
118
+ () => tableColumns.filter((op) => !Array.isArray(op.hideScreens) || !op.hideScreens.includes(currentBreakpoint)),
119
+ [tableColumns, currentBreakpoint],
120
+ )
104
121
 
105
122
  if (isHandlingSchema && loadingBlock) return loadingBlock
106
123
 
107
124
  return (
108
125
  <>
109
126
  <FormDataListHeaderComponent
110
- layout={headerLayout}
127
+ layoutConfig={headerLayoutConfig}
111
128
  updateReqData={(req) => {
112
129
  if (!isFinishedHandlingRef.current) return
113
130
 
@@ -128,7 +145,7 @@ export default function FormDataListTableComponent({
128
145
  {dataListConfig?.listType === FormDataListViewTypeEnum.Table && (
129
146
  <Table<IFormData>
130
147
  dataSource={dataList.data}
131
- columns={tableColumns}
148
+ columns={tableColumnsFiltered}
132
149
  rowKey={(record) => record.id}
133
150
  pagination={
134
151
  otherConfigs?.hasNoPagination
@@ -166,7 +183,7 @@ export default function FormDataListTableComponent({
166
183
 
167
184
  type IFormDataListTableComponent = {
168
185
  dataListConfig?: IFormDataListConfig & { configForFormId?: number }
169
- detailsLayout?: IFormLayoutRow[]
186
+ detailsLayoutConfig?: IFormDetailsConfig
170
187
  dataList: { data: IFormData[]; total: number }
171
188
  parentLoading: boolean
172
189
  loadingBlock?: JSX.Element
@@ -1,16 +1,17 @@
1
1
  import { Form } from 'antd'
2
2
  import { useCallback, useEffect, useMemo, useState } from 'react'
3
- import { IFormLayoutRow, IFormSchema } from '../../../types'
3
+ import { IFormLayoutElement, IFormLayoutRow, IFormSchema } from '../../../types'
4
4
  import { NEW_FORM_DATA_IDENTIFIER } from '../../../constants'
5
5
  import client from '../../../functions/axios-handler'
6
- import { parseJSON } from '../../../functions/json-handlers'
6
+ import { parseJSON } from '../../../functions/forms/json-handlers'
7
7
  import { LayoutRendererRow } from '../layout-renderer/1-row'
8
8
  import { DynamicFormButtonRender, ICustomFunctionCall } from '../layout-renderer/3-element/1-dynamic-button'
9
9
  import FormDataListSkeleton_Details from '../../common/loading-skeletons/details'
10
10
  import { useLocation } from 'react-router-dom'
11
- import { extractDateFields, queryParamsToObject } from '../../../functions'
12
- import { FormPreservedItemKeys } from '../../../enums'
11
+ import { extractDateFields, queryParamsToObject } from '../../../functions/forms'
12
+ import { DeviceBreakpointEnum, FormPreservedItemKeys } from '../../../enums'
13
13
  import dayjs from 'dayjs'
14
+ import useGetCurrentBreakpoint from '../../common/custom-hooks/use-window-width.hook'
14
15
 
15
16
  export default function FormDataDetailsComponent({
16
17
  isPublic,
@@ -22,10 +23,20 @@ export default function FormDataDetailsComponent({
22
23
  baseServerUrl,
23
24
  onCustomFunctionCall,
24
25
  }: IFormDataDetailsComponent) {
26
+ const location = useLocation()
25
27
  const [formDataRef] = Form.useForm()
26
28
  const [loadings, setLoadings] = useState({ layout: true, data: true })
27
- const [layout, setLayout] = useState<IFormLayoutRow[]>([])
28
- const location = useLocation()
29
+ const [layoutConfig, setLayoutConfig] = useState<{
30
+ elements: { [key: string]: IFormLayoutElement }
31
+ layouts: { [key in DeviceBreakpointEnum]?: IFormLayoutRow[] }
32
+ }>({ elements: {}, layouts: {} })
33
+
34
+ const currentBreakpoint = useGetCurrentBreakpoint()
35
+
36
+ const layout = useMemo(
37
+ () => layoutConfig.layouts[currentBreakpoint] ?? layoutConfig.layouts[DeviceBreakpointEnum.Default] ?? [],
38
+ [layoutConfig.layouts, currentBreakpoint],
39
+ )
29
40
 
30
41
  useEffect(() => {
31
42
  // this is for converting the screen to PDF
@@ -87,7 +98,7 @@ export default function FormDataDetailsComponent({
87
98
  const parsedData: IFormSchema | null = parseJSON(res.data.data)
88
99
  if (parsedData) {
89
100
  console.log('FORM LAYOUT FETCH (PARSED)', parsedData)
90
- const { layout } = parsedData.detailsConfig
101
+ const { layouts: rawLayouts, elements } = parsedData.detailsConfig
91
102
 
92
103
  if (isPublic) {
93
104
  if (parsedData.generateConfig?.submissionPdf?.enabled)
@@ -97,11 +108,11 @@ export default function FormDataDetailsComponent({
97
108
  )
98
109
  setLoadings((c) => ({ ...c, data: false }))
99
110
  } else {
100
- const dateFields = extractDateFields(layout)
111
+ const dateFields = extractDateFields(elements)
101
112
 
102
113
  fetchFormData(formId, dateFields)
103
114
  }
104
- setLayout(layout)
115
+ setLayoutConfig({ elements, layouts: rawLayouts })
105
116
  }
106
117
  }
107
118
  })
@@ -123,6 +134,7 @@ export default function FormDataDetailsComponent({
123
134
  key={rowIdx}
124
135
  rowData={row}
125
136
  formContext={formContext}
137
+ elements={layoutConfig.elements}
126
138
  renderButton={(btnProps, conditions) => (
127
139
  <DynamicFormButtonRender
128
140
  displayStateProps={{ btnProps, conditions, defaulDisabled: true }}
@@ -1,6 +1,6 @@
1
1
  import { Form } from 'antd'
2
- import { IFormLayoutRow } from '../../../types'
3
- import { FormPreservedItemKeys, ResponsivenessDeviceEnum } from '../../../enums'
2
+ import { IFormLayoutElement, IFormLayoutRow } from '../../../types'
3
+ import { FormPreservedItemKeys } from '../../../enums'
4
4
  import { LayoutRendererRow } from '../layout-renderer/1-row'
5
5
  import { DynamicFormButtonRender } from '../layout-renderer/3-element/1-dynamic-button'
6
6
  import { useNotification } from '../../common/custom-hooks'
@@ -8,11 +8,11 @@ import { ReactNode, useEffect, useMemo } from 'react'
8
8
 
9
9
  export const FormPreviewComponent = ({
10
10
  layout,
11
- breakpointDevice,
11
+ elements = {},
12
12
  titleComponent,
13
13
  }: {
14
14
  layout: IFormLayoutRow[]
15
- breakpointDevice?: ResponsivenessDeviceEnum
15
+ elements: { [key: string]: IFormLayoutElement }
16
16
  titleComponent?: ReactNode
17
17
  }) => {
18
18
  const [previewFormRef] = Form.useForm()
@@ -30,9 +30,9 @@ export const FormPreviewComponent = ({
30
30
  <LayoutRendererRow
31
31
  key={rowIdx}
32
32
  rowData={row}
33
- breakpointDevice={breakpointDevice}
34
33
  titleComponent={titleComponent}
35
34
  formContext={formContext}
35
+ elements={elements}
36
36
  renderButton={(btnProps, conditions) => (
37
37
  <DynamicFormButtonRender
38
38
  displayStateProps={{ btnProps, conditions }}
@@ -1,6 +1,6 @@
1
1
  import { useMemo } from 'react'
2
2
  import { IFormLayoutRowHeader } from '../../../../types'
3
- import { kebabCaseToCamelCase } from '../../../../functions'
3
+ import { kebabCaseToCamelCase } from '../../../../functions/forms'
4
4
  import { FaCaretDown, FaCaretUp } from 'react-icons/fa6'
5
5
 
6
6
  export default function RowHeader({
@@ -1,27 +1,24 @@
1
1
  import { FormInstance } from 'antd'
2
- import { ResponsivenessDeviceEnum } from '../../../../enums'
3
- import { getFlexContainerStyle, getFlexItemStyle, kebabCaseToCamelCase } from '../../../../functions'
4
- import { IDataRender_ButtonProps, IFormLayoutElementConditions, IFormLayoutRow } from '../../../../types'
2
+ import { getFlexContainerStyle, getColumnStyle, kebabCaseToCamelCase } from '../../../../functions/forms'
5
3
  import LayoutRendererCol from '../2-col'
6
4
  import { memo, ReactElement, ReactNode, useMemo, useState } from 'react'
7
5
  import { LayoutRowConditionalHeaderRenderer } from './header-render'
8
6
  import { LayoutRowRepeatableRenderer } from './repeatable-render'
7
+ import {
8
+ IButtonElementProps,
9
+ IFormLayoutElement,
10
+ IFormLayoutElementConditions,
11
+ IFormLayoutRow,
12
+ } from '../../../../types'
9
13
 
10
14
  export const LayoutRendererRow = memo(
11
- ({
12
- basePath = [],
13
- rowData,
14
- titleComponent,
15
- breakpointDevice = ResponsivenessDeviceEnum.Default,
16
- formContext,
17
- renderButton,
18
- }: ILayoutRendererRow) => {
15
+ ({ basePath = [], rowData, titleComponent, formContext, elements, renderButton }: ILayoutRendererRow) => {
19
16
  const [hiddenElementCount, setHiddenElementCount] = useState(0)
20
17
 
21
18
  const styleConfig = useMemo(() => {
22
- if (!rowData.style || !rowData.style[breakpointDevice]) return {}
23
- return kebabCaseToCamelCase(rowData.style[breakpointDevice])
24
- }, [rowData.style, breakpointDevice])
19
+ if (!rowData.style || !rowData.style) return {}
20
+ return kebabCaseToCamelCase(rowData.style)
21
+ }, [rowData.style])
25
22
 
26
23
  return (
27
24
  <>
@@ -30,7 +27,7 @@ export const LayoutRendererRow = memo(
30
27
  {(formListItemProps) => {
31
28
  const style: { [key: string]: any } = {
32
29
  ...styleConfig,
33
- ...getFlexContainerStyle(breakpointDevice, rowData.responsiveness),
30
+ ...getFlexContainerStyle(rowData.display),
34
31
  }
35
32
  if (hiddenElementCount === rowData.children.length) style.display = 'none'
36
33
 
@@ -42,9 +39,9 @@ export const LayoutRendererRow = memo(
42
39
  basePath={formListItemProps ? formListItemProps.updatedBasePath : basePath}
43
40
  colData={col}
44
41
  titleComponent={titleComponent}
45
- breakpointDevice={breakpointDevice}
42
+ elements={elements}
46
43
  formContext={formContext}
47
- colStyle={getFlexItemStyle(breakpointDevice, colIdx, rowData.responsiveness)}
44
+ colStyle={getColumnStyle(colIdx, rowData.display)}
48
45
  renderButton={renderButton}
49
46
  hideRow={(isHidden) =>
50
47
  setHiddenElementCount((c) => {
@@ -72,9 +69,9 @@ interface ILayoutRendererRow {
72
69
  basePath?: (string | number)[]
73
70
  rowData: IFormLayoutRow
74
71
  titleComponent?: ReactNode
75
- breakpointDevice?: ResponsivenessDeviceEnum
76
72
  formContext: IFormContext
77
- renderButton?: (props: IDataRender_ButtonProps, conditions?: IFormLayoutElementConditions) => ReactElement
73
+ elements: { [key: string]: IFormLayoutElement }
74
+ renderButton?: (props: IButtonElementProps, conditions?: IFormLayoutElementConditions) => ReactElement
78
75
  }
79
76
 
80
77
  export interface IFormContext {
@@ -83,4 +80,5 @@ export interface IFormContext {
83
80
  formName?: string
84
81
  formKey?: string
85
82
  formId?: number
83
+ linkedParentsDataIds?: { [key: string]: string[] }
86
84
  }