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.
- package/index.ts +0 -3
- package/package.json +1 -1
- package/src/components/common/custom-hooks/use-check-element-conditions.hook.ts +20 -6
- package/src/components/common/custom-hooks/use-window-width.hook.ts +31 -0
- package/src/components/form/1-list/index.tsx +11 -4
- package/src/components/form/1-list/table-header.tsx +29 -40
- package/src/components/form/1-list/table.tsx +37 -20
- package/src/components/form/2-details/index.tsx +21 -9
- package/src/components/form/3-preview/index.tsx +5 -5
- package/src/components/form/layout-renderer/1-row/header.tsx +1 -1
- package/src/components/form/layout-renderer/1-row/index.tsx +17 -19
- package/src/components/form/layout-renderer/1-row/repeatable-render.tsx +5 -5
- package/src/components/form/layout-renderer/2-col/index.tsx +11 -18
- package/src/components/form/layout-renderer/3-element/1-dynamic-button/index.tsx +67 -22
- package/src/components/form/layout-renderer/3-element/1-dynamic-button/use-create-data.hook.ts +8 -9
- package/src/components/form/layout-renderer/3-element/1-dynamic-button/use-delete-data.hook.ts +2 -5
- package/src/components/form/layout-renderer/3-element/1-dynamic-button/use-duplicate-data.hook.ts +2 -5
- package/src/components/form/layout-renderer/3-element/1-dynamic-button/use-generate-report.hook.tsx +13 -11
- package/src/components/form/layout-renderer/3-element/1-dynamic-button/use-publish-data.hook.ts +2 -5
- package/src/components/form/layout-renderer/3-element/1-dynamic-button/use-save-data.hook.ts +4 -5
- package/src/components/form/layout-renderer/3-element/1-dynamic-button/use-save-signature.hook.ts +2 -2
- package/src/components/form/layout-renderer/3-element/10-currency.tsx +2 -2
- package/src/components/form/layout-renderer/3-element/2-field-element.tsx +4 -4
- package/src/components/form/layout-renderer/3-element/3-read-field-data.tsx +2 -2
- package/src/components/form/layout-renderer/3-element/7-file-upload.tsx +1 -1
- package/src/components/form/layout-renderer/3-element/8-fields-with-options.tsx +25 -13
- package/src/components/form/layout-renderer/3-element/9-form-data-render.tsx +160 -157
- package/src/components/form/layout-renderer/3-element/index.tsx +24 -13
- package/src/constants.ts +13 -9
- package/src/enums/form.enum.ts +244 -0
- package/src/enums/index.ts +42 -0
- package/src/functions/companies/FontSelector.tsx +67 -0
- package/src/functions/{conditional-rule-validator.ts → forms/conditional-rule-validator.ts} +5 -2
- package/src/functions/{create-form-rules.ts → forms/create-form-rules.ts} +3 -4
- package/src/functions/{data-render-functions.tsx → forms/data-render-functions.tsx} +6 -6
- package/src/functions/{evaluate-condition.ts → forms/evaluate-condition.ts} +1 -1
- package/src/functions/{evaluate-value.ts → forms/evaluate-value.ts} +3 -3
- package/src/functions/{form-schema-validator.ts → forms/form-schema-validator.ts} +4 -4
- package/src/functions/{form.ts → forms/form.ts} +1 -1
- package/src/functions/{get-element-props.ts → forms/get-element-props.ts} +3 -3
- package/src/functions/forms/index.ts +247 -0
- package/src/functions/index.ts +1 -301
- package/src/types/companies/index.ts +157 -32
- package/src/types/companies/site-layout/authenticated/index.tsx +439 -349
- package/src/types/{data-list → forms/data-list}/filter-config.ts +2 -2
- package/src/types/{data-list → forms/data-list}/index.ts +7 -5
- package/src/types/{generate → forms/generate}/index.ts +1 -1
- package/src/types/forms/index.ts +108 -0
- package/src/types/forms/layout-elements/button.ts +47 -0
- package/src/types/{layout-elements → forms/layout-elements}/conditions.ts +8 -1
- package/src/types/{layout-elements → forms/layout-elements}/data-render-config.ts +3 -3
- package/src/types/{layout-elements → forms/layout-elements}/evaluation-config.ts +1 -1
- package/src/types/{layout-elements → forms/layout-elements}/field-option-source.ts +1 -1
- package/src/types/{layout-elements → forms/layout-elements}/index.ts +14 -47
- package/src/types/{layout-elements → forms/layout-elements}/sanitization.ts +1 -1
- package/src/types/forms/layout-elements/style.ts +20 -0
- package/src/types/{layout-elements → forms/layout-elements}/validation.ts +1 -1
- package/src/types/{relationship → forms/relationship}/index.ts +9 -8
- package/src/types/index.ts +1 -128
- package/src/enums.ts +0 -298
- package/src/types/layout-elements/style.ts +0 -40
- /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,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(
|
|
21
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
25
|
-
IFormLayoutRow,
|
|
23
|
+
IFormDetailsConfig,
|
|
26
24
|
} from '../../../types'
|
|
27
25
|
|
|
28
26
|
export default function FormDataListHeaderComponent({
|
|
29
|
-
|
|
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(
|
|
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,
|
|
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
|
-
|
|
89
|
+
dynamicFilters: [...curr.dynamicFilters, parsedDFilter],
|
|
85
90
|
}
|
|
86
91
|
},
|
|
87
|
-
{ customFilter: {},
|
|
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:
|
|
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:
|
|
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
|
-
|
|
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
|
|
186
|
+
const lastRel = config.relationshipPath[config.relationshipPath.length - 1]
|
|
198
187
|
|
|
199
188
|
return {
|
|
200
189
|
customFilter: {},
|
|
201
|
-
dynamicFilter: JSON.stringify({ [
|
|
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,
|
|
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
|
-
|
|
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 [
|
|
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 },
|
|
60
|
+
async (config: IFormDataListConfig & { configForFormId?: number }, layoutConfig?: IFormDetailsConfig) => {
|
|
54
61
|
if (config.configForFormId && config.configForFormId !== formId) return
|
|
55
62
|
|
|
56
63
|
const {
|
|
57
|
-
|
|
58
|
-
header = {
|
|
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 =
|
|
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(
|
|
78
|
+
optionKeyValuePair = await extractElementsOptions(layoutConfig?.elements ?? {}, elementsWithOptions_key)
|
|
70
79
|
|
|
71
|
-
setTableColumns(generateTableColumns(
|
|
72
|
-
|
|
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,
|
|
103
|
-
}, [dataListConfig,
|
|
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
|
-
|
|
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={
|
|
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
|
-
|
|
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 [
|
|
28
|
-
|
|
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 {
|
|
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(
|
|
111
|
+
const dateFields = extractDateFields(elements)
|
|
101
112
|
|
|
102
113
|
fetchFormData(formId, dateFields)
|
|
103
114
|
}
|
|
104
|
-
|
|
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
|
|
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
|
-
|
|
11
|
+
elements = {},
|
|
12
12
|
titleComponent,
|
|
13
13
|
}: {
|
|
14
14
|
layout: IFormLayoutRow[]
|
|
15
|
-
|
|
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 {
|
|
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
|
|
23
|
-
return kebabCaseToCamelCase(rowData.style
|
|
24
|
-
}, [rowData.style
|
|
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(
|
|
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
|
-
|
|
42
|
+
elements={elements}
|
|
46
43
|
formContext={formContext}
|
|
47
|
-
colStyle={
|
|
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
|
-
|
|
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
|
}
|