form-craft-package 1.5.6 → 1.6.1

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 (64) 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-preserved-form-items.hook.ts +0 -2
  5. package/src/components/common/custom-hooks/use-window-width.hook.ts +31 -0
  6. package/src/components/form/1-list/index.tsx +12 -5
  7. package/src/components/form/1-list/table-header.tsx +29 -40
  8. package/src/components/form/1-list/table.tsx +39 -21
  9. package/src/components/form/2-details/index.tsx +24 -13
  10. package/src/components/form/3-preview/index.tsx +5 -5
  11. package/src/components/form/layout-renderer/1-row/header.tsx +1 -1
  12. package/src/components/form/layout-renderer/1-row/index.tsx +18 -19
  13. package/src/components/form/layout-renderer/1-row/repeatable-render.tsx +5 -5
  14. package/src/components/form/layout-renderer/2-col/index.tsx +11 -18
  15. package/src/components/form/layout-renderer/3-element/1-dynamic-button/index.tsx +68 -24
  16. package/src/components/form/layout-renderer/3-element/1-dynamic-button/use-create-data.hook.ts +12 -14
  17. package/src/components/form/layout-renderer/3-element/1-dynamic-button/use-delete-data.hook.ts +2 -5
  18. package/src/components/form/layout-renderer/3-element/1-dynamic-button/use-duplicate-data.hook.ts +2 -5
  19. package/src/components/form/layout-renderer/3-element/1-dynamic-button/use-generate-report.hook.tsx +56 -63
  20. package/src/components/form/layout-renderer/3-element/1-dynamic-button/use-publish-data.hook.ts +2 -5
  21. package/src/components/form/layout-renderer/3-element/1-dynamic-button/use-save-data.hook.ts +4 -5
  22. package/src/components/form/layout-renderer/3-element/1-dynamic-button/use-save-signature.hook.ts +4 -4
  23. package/src/components/form/layout-renderer/3-element/10-currency.tsx +2 -2
  24. package/src/components/form/layout-renderer/3-element/2-field-element.tsx +4 -4
  25. package/src/components/form/layout-renderer/3-element/3-read-field-data.tsx +4 -4
  26. package/src/components/form/layout-renderer/3-element/6-signature.tsx +2 -2
  27. package/src/components/form/layout-renderer/3-element/7-file-upload.tsx +2 -5
  28. package/src/components/form/layout-renderer/3-element/8-fields-with-options.tsx +25 -13
  29. package/src/components/form/layout-renderer/3-element/9-form-data-render.tsx +157 -158
  30. package/src/components/form/layout-renderer/3-element/index.tsx +24 -13
  31. package/src/constants.ts +13 -9
  32. package/src/enums/form.enum.ts +242 -0
  33. package/src/enums/index.ts +42 -0
  34. package/src/functions/companies/FontSelector.tsx +57 -0
  35. package/src/functions/{conditional-rule-validator.ts → forms/conditional-rule-validator.ts} +5 -2
  36. package/src/functions/{create-form-rules.ts → forms/create-form-rules.ts} +3 -4
  37. package/src/functions/{data-render-functions.tsx → forms/data-render-functions.tsx} +12 -11
  38. package/src/functions/{evaluate-condition.ts → forms/evaluate-condition.ts} +1 -1
  39. package/src/functions/{evaluate-value.ts → forms/evaluate-value.ts} +3 -3
  40. package/src/functions/{form-schema-validator.ts → forms/form-schema-validator.ts} +4 -4
  41. package/src/functions/{form.ts → forms/form.ts} +1 -1
  42. package/src/functions/{get-element-props.ts → forms/get-element-props.ts} +3 -3
  43. package/src/functions/forms/index.ts +247 -0
  44. package/src/functions/index.ts +1 -301
  45. package/src/types/companies/index.ts +164 -32
  46. package/src/types/companies/site-layout/authenticated/index.tsx +493 -350
  47. package/src/types/{data-list → forms/data-list}/filter-config.ts +2 -2
  48. package/src/types/{data-list → forms/data-list}/index.ts +7 -5
  49. package/src/types/{generate → forms/generate}/index.ts +1 -1
  50. package/src/types/forms/index.ts +116 -0
  51. package/src/types/forms/layout-elements/button.ts +47 -0
  52. package/src/types/{layout-elements → forms/layout-elements}/conditions.ts +8 -1
  53. package/src/types/{layout-elements → forms/layout-elements}/data-render-config.ts +3 -3
  54. package/src/types/{layout-elements → forms/layout-elements}/evaluation-config.ts +1 -1
  55. package/src/types/{layout-elements → forms/layout-elements}/field-option-source.ts +1 -1
  56. package/src/types/{layout-elements → forms/layout-elements}/index.ts +14 -47
  57. package/src/types/{layout-elements → forms/layout-elements}/sanitization.ts +1 -1
  58. package/src/types/forms/layout-elements/style.ts +20 -0
  59. package/src/types/{layout-elements → forms/layout-elements}/validation.ts +1 -1
  60. package/src/types/{relationship → forms/relationship}/index.ts +9 -8
  61. package/src/types/index.ts +1 -128
  62. package/src/enums.ts +0 -298
  63. package/src/types/layout-elements/style.ts +0 -40
  64. /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.1",
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
  }
@@ -3,7 +3,6 @@ import { FormPreservedItemKeys } from '../../../enums'
3
3
 
4
4
  export const useFormPreservedItemValues = (formRef?: FormInstance): IFormValues => {
5
5
  const baseServerUrl = Form.useWatch(FormPreservedItemKeys.BaseServerUrl, { form: formRef, preserve: true })
6
- const companyKey = Form.useWatch(FormPreservedItemKeys.CompanyKey, { form: formRef, preserve: true })
7
6
  const isGeneratingPdf = Form.useWatch(FormPreservedItemKeys.IsGeneratingPDF, { form: formRef, preserve: true })
8
7
  const hasSignature = Form.useWatch(FormPreservedItemKeys.HasSignature, { form: formRef, preserve: true })
9
8
  const inPreviewMode = Form.useWatch(FormPreservedItemKeys.InPreviewMode, { form: formRef, preserve: true })
@@ -15,7 +14,6 @@ export const useFormPreservedItemValues = (formRef?: FormInstance): IFormValues
15
14
 
16
15
  return {
17
16
  [FormPreservedItemKeys.BaseServerUrl]: baseServerUrl,
18
- [FormPreservedItemKeys.CompanyKey]: companyKey,
19
17
  [FormPreservedItemKeys.SubmissionPdfConfig]: submissionPdfConfig,
20
18
  [FormPreservedItemKeys.IsGeneratingPDF]: isGeneratingPdf,
21
19
  [FormPreservedItemKeys.HasSignature]: hasSignature,
@@ -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,13 +20,16 @@ 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
  >()
26
30
 
27
31
  const attachmentBaseUrl = useMemo(() => `${baseServerUrl}/api/attachment/${companyKey}`, [baseServerUrl, companyKey])
28
- const dataListHeaderContext = { formId, userId, attachmentBaseUrl, formName }
32
+ const dataListHeaderContext = { formId, userId, attachmentBaseUrl, formName, companyKey }
29
33
 
30
34
  useEffect(() => {
31
35
  if (formId) {
@@ -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
 
@@ -39,7 +46,7 @@ export default function FormDataListTableComponent({
39
46
  pagination: DEFAULT_PAGINATION,
40
47
  })
41
48
 
42
- const { attachmentBaseUrl, userId, formId, formName, formRef } = dataListHeaderContext
49
+ const { attachmentBaseUrl, userId, formId } = dataListHeaderContext
43
50
 
44
51
  useEffect(() => {
45
52
  setIsHandlingSchema(true)
@@ -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, dataListHeaderContext))
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, userId, formId, dataListHeaderContext],
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
@@ -208,4 +225,5 @@ export interface IDataListHeaderContext {
208
225
  formName?: string
209
226
  parentInfo?: IDataListParentInfo
210
227
  formRef?: FormInstance
228
+ companyKey?: string
211
229
  }
@@ -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
@@ -39,11 +50,10 @@ export default function FormDataDetailsComponent({
39
50
  if (formDataRef)
40
51
  formDataRef.setFieldsValue({
41
52
  [FormPreservedItemKeys.BaseServerUrl]: baseServerUrl,
42
- [FormPreservedItemKeys.CompanyKey]: companyKey,
43
53
  [FormPreservedItemKeys.IsPublic]: isPublic,
44
54
  [FormPreservedItemKeys.InPreviewMode]: false,
45
55
  })
46
- }, [formDataRef, baseServerUrl, companyKey, isPublic])
56
+ }, [formDataRef, baseServerUrl, isPublic])
47
57
 
48
58
  const fetchFormData = useCallback(
49
59
  (dFormId?: number, dateFields: string[] = []) => {
@@ -87,7 +97,7 @@ export default function FormDataDetailsComponent({
87
97
  const parsedData: IFormSchema | null = parseJSON(res.data.data)
88
98
  if (parsedData) {
89
99
  console.log('FORM LAYOUT FETCH (PARSED)', parsedData)
90
- const { layout } = parsedData.detailsConfig
100
+ const { layouts: rawLayouts, elements } = parsedData.detailsConfig
91
101
 
92
102
  if (isPublic) {
93
103
  if (parsedData.generateConfig?.submissionPdf?.enabled)
@@ -97,11 +107,11 @@ export default function FormDataDetailsComponent({
97
107
  )
98
108
  setLoadings((c) => ({ ...c, data: false }))
99
109
  } else {
100
- const dateFields = extractDateFields(layout)
110
+ const dateFields = extractDateFields(elements)
101
111
 
102
112
  fetchFormData(formId, dateFields)
103
113
  }
104
- setLayout(layout)
114
+ setLayoutConfig({ elements, layouts: rawLayouts })
105
115
  }
106
116
  }
107
117
  })
@@ -110,8 +120,8 @@ export default function FormDataDetailsComponent({
110
120
  }, [formId, formKey, isPublic, formDataRef, fetchFormData])
111
121
 
112
122
  const formContext = useMemo(
113
- () => ({ formId, formKey, formDataId, formRef: formDataRef, formName }),
114
- [formDataId, formDataRef, formName, formId, formKey],
123
+ () => ({ formId, formKey, formDataId, formRef: formDataRef, formName, companyKey }),
124
+ [formDataId, formDataRef, formName, formId, formKey, companyKey],
115
125
  )
116
126
 
117
127
  if (loadings.layout || loadings.data) return <FormDataListSkeleton_Details />
@@ -123,6 +133,7 @@ export default function FormDataDetailsComponent({
123
133
  key={rowIdx}
124
134
  rowData={row}
125
135
  formContext={formContext}
136
+ elements={layoutConfig.elements}
126
137
  renderButton={(btnProps, conditions) => (
127
138
  <DynamicFormButtonRender
128
139
  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({