form-craft-package 1.10.2 → 1.10.3-dev.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/.env.production +1 -1
- package/package.json +1 -1
- package/src/components/common/custom-hooks/use-node-condition.hook/visibility-utils.ts +18 -138
- package/src/components/common/custom-hooks/use-preserved-form-items.hook.ts +2 -0
- package/src/components/form/1-list/table-header.tsx +15 -4
- package/src/components/form/2-details/index.tsx +12 -1
- package/src/components/form/layout-renderer/1-row/index.tsx +5 -3
- package/src/components/form/layout-renderer/2-col/index.tsx +6 -3
- package/src/components/form/layout-renderer/3-element/1-dynamic-button/use-create-data.hook.ts +127 -66
- package/src/components/form/layout-renderer/3-element/10-currency.tsx +1 -1
- package/src/components/form/layout-renderer/3-element/12-picker-field.tsx +9 -2
- package/src/components/form/layout-renderer/3-element/14-auto-complete.tsx +107 -88
- package/src/components/form/layout-renderer/3-element/{15-form-submission-pdf/index.tsx → 15-form-submission-pdf.tsx} +8 -8
- package/src/components/form/layout-renderer/3-element/16-user-role.tsx +60 -0
- package/src/components/form/layout-renderer/3-element/17-custom-component.tsx +21 -0
- package/src/components/form/layout-renderer/3-element/2-field-element.tsx +74 -10
- package/src/components/form/layout-renderer/3-element/4-rich-text-editor.tsx +31 -13
- package/src/components/form/layout-renderer/3-element/6-signature.tsx +21 -1
- package/src/components/form/layout-renderer/3-element/8-fields-with-options.tsx +1 -1
- package/src/components/form/layout-renderer/3-element/9-form-data-render.tsx +9 -11
- package/src/components/form/layout-renderer/3-element/index.tsx +46 -10
- package/src/constants.ts +3 -2
- package/src/enums/form.enum.ts +14 -0
- package/src/enums/index.ts +6 -2
- package/src/functions/companies/use-company-config.tsx +2 -0
- package/src/functions/forms/conditional-rule.utils.ts +165 -0
- package/src/functions/forms/create-form-rules.ts +20 -4
- package/src/types/companies/index.ts +206 -206
- package/src/types/companies/roles.ts +14 -14
- package/src/types/forms/layout-elements/index.ts +37 -7
- package/src/types/forms/layout-elements/validation.ts +3 -1
- package/src/types/index.ts +6 -0
package/.env.production
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
# .env
|
|
2
|
-
VITE_API_BASE_URL=https://formcraftmaster-backend-ecgtewb6cag8faeq.centralus-01.azurewebsites.net
|
|
2
|
+
VITE_API_BASE_URL=https://formcraftmaster-backend-ecgtewb6cag8faeq.centralus-01.azurewebsites.net
|
|
3
3
|
VITE_LOG_LEVEL=error
|
|
4
4
|
VITE_COOKIE_DOMAIN=localhost
|
|
5
5
|
VITE_ENV=production
|
package/package.json
CHANGED
|
@@ -1,139 +1,19 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import {
|
|
3
|
-
import {
|
|
4
|
-
import {
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
}
|
|
11
|
-
|
|
12
|
-
IConditionalValidation
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
const getValidationConfigs = (validationConfigs: IConditionalValidation[]) =>
|
|
20
|
-
validationConfigs.reduce<{
|
|
21
|
-
fieldValidations: IConditionalValidation_Field[]
|
|
22
|
-
authUserValidations: IConditionalValidation_AuthUser[]
|
|
23
|
-
hasDataIdValidation: IConditionalValidation_FormState | undefined
|
|
24
|
-
alwaysDisabled: boolean
|
|
25
|
-
hideScreens: DeviceBreakpointEnum[]
|
|
26
|
-
}>(
|
|
27
|
-
(curr, v) => {
|
|
28
|
-
if (v.dependentType === ConditionDependencyTypeEnum.Field)
|
|
29
|
-
return { ...curr, fieldValidations: [...curr.fieldValidations, v] }
|
|
30
|
-
else if (v.dependentType === ConditionDependencyTypeEnum.AuthUser)
|
|
31
|
-
return { ...curr, authUserValidations: [...curr.authUserValidations, v] }
|
|
32
|
-
else if (v.dependentType === ConditionDependencyTypeEnum.FormState) return { ...curr, hasDataIdValidation: v }
|
|
33
|
-
else if (v.dependentType === ConditionDependencyTypeEnum.AlwaysFalse) return { ...curr, alwaysDisabled: true }
|
|
34
|
-
else if (v.dependentType === ConditionDependencyTypeEnum.Screen) return { ...curr, hideScreens: v.hideScreens }
|
|
35
|
-
|
|
36
|
-
return curr
|
|
37
|
-
},
|
|
38
|
-
{
|
|
39
|
-
fieldValidations: [],
|
|
40
|
-
authUserValidations: [],
|
|
41
|
-
hasDataIdValidation: undefined,
|
|
42
|
-
alwaysDisabled: false,
|
|
43
|
-
hideScreens: [],
|
|
44
|
-
},
|
|
45
|
-
)
|
|
46
|
-
|
|
47
|
-
/** If any of the validations fails, the function returns false */
|
|
48
|
-
const validateConditions = (validations: IConditionalValidation_Field[], data: Record<string, any>): boolean => {
|
|
49
|
-
return validations.every((validation) => {
|
|
50
|
-
const { field, rule, value: configuredValue } = validation
|
|
51
|
-
const formValue = data[field]
|
|
52
|
-
|
|
53
|
-
switch (rule) {
|
|
54
|
-
case FieldValidationEnum.Required:
|
|
55
|
-
if (!!formValue) return true
|
|
56
|
-
return false
|
|
57
|
-
|
|
58
|
-
case FieldValidationEnum.DataLength:
|
|
59
|
-
return (
|
|
60
|
-
typeof formValue === 'string' && typeof configuredValue === 'number' && formValue.length === configuredValue
|
|
61
|
-
)
|
|
62
|
-
|
|
63
|
-
case FieldValidationEnum.MaxDataLength:
|
|
64
|
-
return (
|
|
65
|
-
typeof formValue === 'string' && typeof configuredValue === 'number' && formValue.length <= configuredValue
|
|
66
|
-
)
|
|
67
|
-
|
|
68
|
-
case FieldValidationEnum.MinDataLength:
|
|
69
|
-
return (
|
|
70
|
-
typeof formValue === 'string' && typeof configuredValue === 'number' && formValue.length >= configuredValue
|
|
71
|
-
)
|
|
72
|
-
|
|
73
|
-
case FieldValidationEnum.MinValue:
|
|
74
|
-
return typeof formValue === 'number' && typeof configuredValue === 'number' && formValue >= configuredValue
|
|
75
|
-
|
|
76
|
-
case FieldValidationEnum.MaxValue:
|
|
77
|
-
return typeof formValue === 'number' && typeof configuredValue === 'number' && formValue <= configuredValue
|
|
78
|
-
|
|
79
|
-
case FieldValidationEnum.Regex:
|
|
80
|
-
return (
|
|
81
|
-
typeof formValue === 'string' &&
|
|
82
|
-
typeof configuredValue === 'string' &&
|
|
83
|
-
new RegExp(configuredValue).test(formValue)
|
|
84
|
-
)
|
|
85
|
-
|
|
86
|
-
case FieldValidationEnum.Email:
|
|
87
|
-
return typeof formValue === 'string' && REGEX_PATTERNS.Email.test(formValue)
|
|
88
|
-
|
|
89
|
-
case FieldValidationEnum.StrictlyEquals:
|
|
90
|
-
if (!configuredValue)
|
|
91
|
-
// equals no value
|
|
92
|
-
return !formValue || [null, undefined, ''].includes(formValue)
|
|
93
|
-
|
|
94
|
-
return Array.isArray(formValue) ? formValue.includes(configuredValue) : formValue === configuredValue
|
|
95
|
-
|
|
96
|
-
default:
|
|
97
|
-
return false
|
|
98
|
-
}
|
|
99
|
-
})
|
|
100
|
-
}
|
|
101
|
-
|
|
102
|
-
export const isValidationsMet = (
|
|
103
|
-
type: FormElementConditionalKeyEnum,
|
|
104
|
-
dataValues: { [key: string]: any },
|
|
105
|
-
configs?: IFormLayoutElementConditions,
|
|
106
|
-
contextValues: { currentBreakpoint?: DeviceBreakpointEnum } & IFormContext = {},
|
|
107
|
-
) => {
|
|
108
|
-
const validations = configs?.[type] ?? []
|
|
109
|
-
|
|
110
|
-
if (validations.length === 0) return true
|
|
111
|
-
|
|
112
|
-
const { fieldValidations, authUserValidations, hasDataIdValidation, alwaysDisabled, hideScreens } =
|
|
113
|
-
getValidationConfigs(validations)
|
|
114
|
-
|
|
115
|
-
if (alwaysDisabled) return false
|
|
116
|
-
|
|
117
|
-
// Dependent on other fields
|
|
118
|
-
const isFieldsConditionsMet =
|
|
119
|
-
dataValues && typeof dataValues === 'object' && fieldValidations.length > 0
|
|
120
|
-
? validateConditions(fieldValidations as IConditionalValidation_Field[], dataValues)
|
|
121
|
-
: true
|
|
122
|
-
|
|
123
|
-
// Dependent on auth user
|
|
124
|
-
const isAuthUserConditionsMet = authUserValidations.length > 0 ? false : true
|
|
125
|
-
|
|
126
|
-
// Dependent on data id
|
|
127
|
-
const isDataIdConditionsMet = hasDataIdValidation
|
|
128
|
-
? hasDataIdValidation.state === FormStateEnum.New
|
|
129
|
-
? isNewFormDataPage(contextValues.formDataId)
|
|
130
|
-
: contextValues.formDataId !== NEW_FORM_DATA_IDENTIFIER
|
|
131
|
-
: true
|
|
132
|
-
|
|
133
|
-
// Dependent on screen
|
|
134
|
-
const isScreenConditionsMet = contextValues?.currentBreakpoint
|
|
135
|
-
? !hideScreens.includes(contextValues.currentBreakpoint)
|
|
136
|
-
: true
|
|
137
|
-
|
|
138
|
-
return isFieldsConditionsMet && isAuthUserConditionsMet && isDataIdConditionsMet && isScreenConditionsMet
|
|
1
|
+
import { IFormContext } from '../../../form/layout-renderer/1-row'
|
|
2
|
+
import { DeviceBreakpointEnum, FormElementConditionalKeyEnum } from '../../../../enums'
|
|
3
|
+
import { IConditionalValidation, IFormLayoutElementConditions } from '../../../../types'
|
|
4
|
+
import { areConditionalRulesMet } from '../../../../functions/forms/conditional-rule.utils'
|
|
5
|
+
|
|
6
|
+
export const isValidationsMet = (
|
|
7
|
+
type: FormElementConditionalKeyEnum,
|
|
8
|
+
dataValues: { [key: string]: any },
|
|
9
|
+
configs?: IFormLayoutElementConditions,
|
|
10
|
+
contextValues: { currentBreakpoint?: DeviceBreakpointEnum } & IFormContext = {},
|
|
11
|
+
) => {
|
|
12
|
+
const validations: IConditionalValidation[] = configs?.[type] ?? []
|
|
13
|
+
|
|
14
|
+
return areConditionalRulesMet(validations, {
|
|
15
|
+
fieldValues: dataValues,
|
|
16
|
+
currentBreakpoint: contextValues.currentBreakpoint,
|
|
17
|
+
formDataId: contextValues.formDataId,
|
|
18
|
+
})
|
|
139
19
|
}
|
|
@@ -7,6 +7,7 @@ export const useFormPreservedItemValues = (formRef?: FormInstance): IFormValues
|
|
|
7
7
|
const signatureFields = Form.useWatch(FormPreservedItemKeys.SignatureFields, { form: formRef, preserve: true })
|
|
8
8
|
const isPublic = Form.useWatch(FormPreservedItemKeys.IsPublic, { form: formRef, preserve: true })
|
|
9
9
|
const formNotifications = Form.useWatch(FormPreservedItemKeys.FormNotifications, { form: formRef, preserve: true })
|
|
10
|
+
const isUserForm = Form.useWatch(FormPreservedItemKeys.IsUserForm, { form: formRef, preserve: true })
|
|
10
11
|
const formTemplateReports = Form.useWatch(FormPreservedItemKeys.FormTemplateReports, {
|
|
11
12
|
form: formRef,
|
|
12
13
|
preserve: true,
|
|
@@ -35,6 +36,7 @@ export const useFormPreservedItemValues = (formRef?: FormInstance): IFormValues
|
|
|
35
36
|
[FormPreservedItemKeys.DuplicateDataFound]:
|
|
36
37
|
duplicateDataMatches && Object.values(duplicateDataMatches).some((result) => result),
|
|
37
38
|
[FormPreservedItemKeys.DuplicateCheckPending]: isDuplicateCheckPending,
|
|
39
|
+
[FormPreservedItemKeys.IsUserForm]: isUserForm,
|
|
38
40
|
}
|
|
39
41
|
}
|
|
40
42
|
|
|
@@ -36,6 +36,7 @@ export default function FormDataListHeaderComponent({
|
|
|
36
36
|
const { formId, detailPageFormId, parentFormJoins, formDataId, manyToManyRelInfo, onCustomFunctionCall } =
|
|
37
37
|
headerLayoutContext
|
|
38
38
|
const isInitialFetchRef = useRef(true)
|
|
39
|
+
const lastSerializedFiltersRef = useRef<string | null>(null)
|
|
39
40
|
|
|
40
41
|
const currentBreakpoint = useGetCurrentBreakpoint()
|
|
41
42
|
|
|
@@ -95,12 +96,22 @@ export default function FormDataListHeaderComponent({
|
|
|
95
96
|
[filterConfigs],
|
|
96
97
|
)
|
|
97
98
|
|
|
99
|
+
|
|
98
100
|
useEffect(() => {
|
|
99
|
-
if (filterValues)
|
|
100
|
-
|
|
101
|
-
|
|
101
|
+
if (!filterValues) return
|
|
102
|
+
|
|
103
|
+
const serialized = JSON.stringify(filterValues)
|
|
104
|
+
if (lastSerializedFiltersRef.current === serialized) return
|
|
105
|
+
|
|
106
|
+
lastSerializedFiltersRef.current = serialized
|
|
107
|
+
|
|
108
|
+
if (isInitialFetchRef.current) {
|
|
109
|
+
isInitialFetchRef.current = false
|
|
110
|
+
return
|
|
102
111
|
}
|
|
103
|
-
|
|
112
|
+
|
|
113
|
+
organizeFilterData(filterValues)
|
|
114
|
+
}, [filterValues, organizeFilterData])
|
|
104
115
|
|
|
105
116
|
const formContext = useMemo(
|
|
106
117
|
() => ({
|
|
@@ -35,6 +35,7 @@ import {
|
|
|
35
35
|
isValidMongoDbId,
|
|
36
36
|
queryParamsToObject,
|
|
37
37
|
} from '../../../functions/forms'
|
|
38
|
+
import { ICustomComponents } from '../layout-renderer/3-element'
|
|
38
39
|
|
|
39
40
|
export default function FormDataDetailsComponent(props: IFormDataDetailsComponent) {
|
|
40
41
|
return (
|
|
@@ -53,6 +54,7 @@ function FormDataDetailsComponentChild({
|
|
|
53
54
|
baseServerUrl,
|
|
54
55
|
initialValues,
|
|
55
56
|
companyKey,
|
|
57
|
+
customComponents,
|
|
56
58
|
onCustomFunctionCall,
|
|
57
59
|
}: IFormDataDetailsComponent) {
|
|
58
60
|
const { pushCrumb, breadcrumbs } = useBreadcrumb()
|
|
@@ -176,6 +178,7 @@ function FormDataDetailsComponentChild({
|
|
|
176
178
|
}
|
|
177
179
|
} else setIsNotFound(true)
|
|
178
180
|
})
|
|
181
|
+
.catch(() => setIsNotFound(true))
|
|
179
182
|
}
|
|
180
183
|
},
|
|
181
184
|
[formDataId, formDataRef, breadcrumbs],
|
|
@@ -206,6 +209,12 @@ function FormDataDetailsComponentChild({
|
|
|
206
209
|
translationStore.setTranslations(cachedConfig.id, cachedConfig.translations)
|
|
207
210
|
}, [cachedConfig])
|
|
208
211
|
|
|
212
|
+
useEffect(() => {
|
|
213
|
+
if (!cachedConfig) return
|
|
214
|
+
|
|
215
|
+
formDataRef.setFieldValue(FormPreservedItemKeys.IsUserForm, true)
|
|
216
|
+
}, [cachedConfig])
|
|
217
|
+
|
|
209
218
|
useEffect(() => {
|
|
210
219
|
if (!cachedConfig?.detailsConfig) return
|
|
211
220
|
|
|
@@ -269,6 +278,7 @@ function FormDataDetailsComponentChild({
|
|
|
269
278
|
formRef: formDataRef,
|
|
270
279
|
companyKey,
|
|
271
280
|
}}
|
|
281
|
+
customComponents={customComponents}
|
|
272
282
|
elements={cachedConfig.detailsConfig.elements}
|
|
273
283
|
renderButton={(btnProps) => (
|
|
274
284
|
<DynamicFormButtonRender
|
|
@@ -298,7 +308,8 @@ type IFormDataDetailsComponent = {
|
|
|
298
308
|
formName?: string
|
|
299
309
|
initialValues?: Record<string, any>
|
|
300
310
|
} & (IDataDetailsPublicProps | IDataDetailsPrivateProps) &
|
|
301
|
-
ICustomFunctionCall
|
|
311
|
+
ICustomFunctionCall &
|
|
312
|
+
ICustomComponents
|
|
302
313
|
|
|
303
314
|
interface IDataDetailsPublicProps {
|
|
304
315
|
isPublic: true
|
|
@@ -9,6 +9,7 @@ import { IDynamicButton_DisplayStateProps } from '../3-element/1-dynamic-button'
|
|
|
9
9
|
import { ELEMENTS_DEFAULT_CLASS } from '../../../../constants'
|
|
10
10
|
import { useHiddenIds } from '../../../common/custom-hooks/use-node-condition.hook/use-node-condition.hook'
|
|
11
11
|
import FormDataListSkeleton_Details from '../../../common/loading-skeletons/details'
|
|
12
|
+
import { ICustomComponents } from '../3-element'
|
|
12
13
|
|
|
13
14
|
export const LayoutRendererRow = memo(
|
|
14
15
|
({
|
|
@@ -18,6 +19,7 @@ export const LayoutRendererRow = memo(
|
|
|
18
19
|
formContext,
|
|
19
20
|
elements,
|
|
20
21
|
isTopLevel = false,
|
|
22
|
+
customComponents,
|
|
21
23
|
renderButton,
|
|
22
24
|
}: ILayoutRendererRow) => {
|
|
23
25
|
const hiddenIds = useHiddenIds()
|
|
@@ -83,6 +85,7 @@ export const LayoutRendererRow = memo(
|
|
|
83
85
|
formContext={formContext}
|
|
84
86
|
colStyle={getColumnStyle(colIdx, rowData.display)}
|
|
85
87
|
renderButton={renderButton}
|
|
88
|
+
customComponents={customComponents}
|
|
86
89
|
/>
|
|
87
90
|
))}
|
|
88
91
|
{formListItemProps?.removeButton}
|
|
@@ -95,7 +98,7 @@ export const LayoutRendererRow = memo(
|
|
|
95
98
|
},
|
|
96
99
|
)
|
|
97
100
|
|
|
98
|
-
|
|
101
|
+
type ILayoutRendererRow = {
|
|
99
102
|
basePath?: (string | number)[]
|
|
100
103
|
rowData: IDndLayoutRow
|
|
101
104
|
isTopLevel?: boolean
|
|
@@ -103,8 +106,7 @@ interface ILayoutRendererRow {
|
|
|
103
106
|
formContext: IFormContext
|
|
104
107
|
elements: { [key: string]: IDndLayoutElement }
|
|
105
108
|
renderButton?: (btnProps: IDynamicButton_DisplayStateProps) => ReactElement
|
|
106
|
-
|
|
107
|
-
}
|
|
109
|
+
} & ICustomComponents
|
|
108
110
|
|
|
109
111
|
export interface IFormContext {
|
|
110
112
|
formDataId?: string
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { memo, ReactElement } from 'react'
|
|
2
2
|
import { IFormContext, LayoutRendererRow } from '../1-row'
|
|
3
3
|
import { DndLayoutNodeEnum } from '../../../../enums'
|
|
4
|
-
import LayoutRendererElementWrapper from '../3-element'
|
|
4
|
+
import LayoutRendererElementWrapper, { ICustomComponents } from '../3-element'
|
|
5
5
|
import { useIsNodeHidden } from '../../../common/custom-hooks/use-node-condition.hook/use-node-condition.hook'
|
|
6
6
|
import { IDndLayoutCol, IDndLayoutElement, IDndLayoutRow } from '../../../../types'
|
|
7
7
|
import { IDynamicButton_DisplayStateProps } from '../3-element/1-dynamic-button'
|
|
@@ -14,6 +14,7 @@ function LayoutRendererCol({
|
|
|
14
14
|
elements,
|
|
15
15
|
formContext,
|
|
16
16
|
colStyle,
|
|
17
|
+
customComponents,
|
|
17
18
|
renderButton,
|
|
18
19
|
}: ILayoutRendererCol) {
|
|
19
20
|
const isHidden = useIsNodeHidden(colData.id)
|
|
@@ -33,6 +34,7 @@ function LayoutRendererCol({
|
|
|
33
34
|
formContext={formContext}
|
|
34
35
|
dataCount={dataCount}
|
|
35
36
|
elements={elements}
|
|
37
|
+
customComponents={customComponents}
|
|
36
38
|
renderButton={renderButton}
|
|
37
39
|
/>
|
|
38
40
|
) : (
|
|
@@ -43,6 +45,7 @@ function LayoutRendererCol({
|
|
|
43
45
|
formContext={formContext}
|
|
44
46
|
dataCount={dataCount}
|
|
45
47
|
renderButton={renderButton}
|
|
48
|
+
customComponents={customComponents}
|
|
46
49
|
/>
|
|
47
50
|
),
|
|
48
51
|
)}
|
|
@@ -51,7 +54,7 @@ function LayoutRendererCol({
|
|
|
51
54
|
}
|
|
52
55
|
|
|
53
56
|
export default memo(LayoutRendererCol)
|
|
54
|
-
|
|
57
|
+
type ILayoutRendererCol = {
|
|
55
58
|
basePath: (string | number)[]
|
|
56
59
|
colData: IDndLayoutCol
|
|
57
60
|
dataCount?: 'pending' | number
|
|
@@ -59,4 +62,4 @@ interface ILayoutRendererCol {
|
|
|
59
62
|
formContext: IFormContext
|
|
60
63
|
colStyle: { [key: string]: number | string }
|
|
61
64
|
renderButton?: (btnProps: IDynamicButton_DisplayStateProps) => ReactElement
|
|
62
|
-
}
|
|
65
|
+
} & ICustomComponents
|
package/src/components/form/layout-renderer/3-element/1-dynamic-button/use-create-data.hook.ts
CHANGED
|
@@ -1,5 +1,4 @@
|
|
|
1
1
|
import { useCallback } from 'react'
|
|
2
|
-
import { FormLoadingModalTypeEnum, TranslationTextTypeEnum } from '../../../../../enums'
|
|
3
2
|
import { useFormPreservedItemValues } from '../../../../common/custom-hooks/use-preserved-form-items.hook'
|
|
4
3
|
import { toMongoDbExtendedJSON } from '../../../../../functions/forms/extended-json-handlers'
|
|
5
4
|
import client from '../../../../../api/client'
|
|
@@ -8,9 +7,15 @@ import { useNotification, useTranslation } from '../../../../common/custom-hooks
|
|
|
8
7
|
import { useNavigate } from 'react-router-dom'
|
|
9
8
|
import { IFormContext } from '../../1-row'
|
|
10
9
|
import { IOnSuccessFunctions } from '.'
|
|
11
|
-
import { IFormTemplateReport } from '../../../../../types'
|
|
10
|
+
import { IFormTemplateReport, ISystemRole, SystemRolePermissionEnum } from '../../../../../types'
|
|
12
11
|
import { useBreadcrumb } from '../../../../common/custom-hooks/use-breadcrumb.hook'
|
|
13
12
|
import { FORM_SUBMISSION_PDF_KEY } from '../../../../../constants'
|
|
13
|
+
import {
|
|
14
|
+
FormLoadingModalTypeEnum,
|
|
15
|
+
LOCAL_STORAGE_KEYS_ENUM,
|
|
16
|
+
TranslationTextTypeEnum,
|
|
17
|
+
UserFormPlaceholders,
|
|
18
|
+
} from '../../../../../enums'
|
|
14
19
|
|
|
15
20
|
/* --------------------------------------------------------------------------
|
|
16
21
|
- Creates new form data.
|
|
@@ -33,83 +38,132 @@ export const useCreateDataWithPdfActions = ({
|
|
|
33
38
|
const { t } = useTranslation(formId)
|
|
34
39
|
const navigate = useNavigate()
|
|
35
40
|
const { errorModal, warning } = useNotification()
|
|
36
|
-
const { baseServerUrl, submissionTemplateReportConfig, formTemplateReports, isPublic } =
|
|
41
|
+
const { baseServerUrl, submissionTemplateReportConfig, formTemplateReports, isPublic, isUserForm } =
|
|
37
42
|
useFormPreservedItemValues(formRef)
|
|
38
43
|
const { breadcrumbs } = useBreadcrumb()
|
|
39
44
|
|
|
40
45
|
const onCreateNewData = useCallback(
|
|
41
|
-
(generatedPdfBlobName?: string) => {
|
|
46
|
+
async (generatedPdfBlobName?: string) => {
|
|
42
47
|
try {
|
|
43
|
-
formRef
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
}
|
|
48
|
+
if (!formRef) return
|
|
49
|
+
|
|
50
|
+
const values = await formRef.validateFields()
|
|
51
|
+
let submissionValues = { ...values }
|
|
52
|
+
|
|
53
|
+
if (isUserForm) {
|
|
54
|
+
setDataLoadingType(FormLoadingModalTypeEnum.CreatingUserAccount)
|
|
55
|
+
|
|
56
|
+
const systemRoles = JSON.parse(localStorage.getItem(LOCAL_STORAGE_KEYS_ENUM.UserRoles) || '[]') || []
|
|
57
|
+
const selectedRoles = values?.[UserFormPlaceholders.ROLES_FIELD_KEY] || []
|
|
58
|
+
const selectedPermissions = systemRoles.reduce(
|
|
59
|
+
(accR: SystemRolePermissionEnum[], r: ISystemRole) =>
|
|
60
|
+
selectedRoles.includes(r.name) ? [...accR, ...r.permissions] : accR,
|
|
61
|
+
[],
|
|
62
|
+
)
|
|
63
|
+
const userPayload = {
|
|
64
|
+
email: values?.[UserFormPlaceholders.EMAIL_FIELD_KEY],
|
|
65
|
+
phone: values?.[UserFormPlaceholders.PHONE_NUMBER_FIELD_KEY],
|
|
66
|
+
enable2FA: values?.[UserFormPlaceholders.ENABLE_2FA] || false,
|
|
67
|
+
password: values?.[UserFormPlaceholders.PASSWORD_FIELD_KEY],
|
|
68
|
+
roles: [...new Set(selectedPermissions)],
|
|
69
|
+
notification: null,
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
try {
|
|
73
|
+
const userResponse = await client.post('/api/user', userPayload)
|
|
74
|
+
|
|
75
|
+
if (userResponse.status >= 300 || !userResponse.data) {
|
|
76
|
+
setDataLoadingType(FormLoadingModalTypeEnum.ErrorOccured)
|
|
77
|
+
errorModal({
|
|
78
|
+
title: 'User account creation failed',
|
|
79
|
+
content: 'An error occurred while creating the user account.',
|
|
80
|
+
})
|
|
81
|
+
onCreateError()
|
|
82
|
+
return
|
|
76
83
|
}
|
|
77
84
|
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
...(isPublic ? { private: false } : { version: 1 }),
|
|
85
|
+
submissionValues = {
|
|
86
|
+
...submissionValues,
|
|
87
|
+
[UserFormPlaceholders.AspUserId]: userResponse.data,
|
|
82
88
|
}
|
|
89
|
+
} catch (_error) {
|
|
90
|
+
setDataLoadingType(FormLoadingModalTypeEnum.ErrorOccured)
|
|
91
|
+
errorModal({
|
|
92
|
+
title: 'User account creation failed',
|
|
93
|
+
content: 'An error occurred while creating the user account.',
|
|
94
|
+
})
|
|
95
|
+
onCreateError()
|
|
96
|
+
return
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
setDataLoadingType(FormLoadingModalTypeEnum.SavingChanges)
|
|
101
|
+
|
|
102
|
+
const shouldGenerateSubmissionPdf =
|
|
103
|
+
isNewFormDataPage(formDataId) &&
|
|
104
|
+
submissionTemplateReportConfig?.enabled &&
|
|
105
|
+
submissionTemplateReportConfig.template &&
|
|
106
|
+
formId &&
|
|
107
|
+
(isPublic ? !!companyKey : true)
|
|
108
|
+
|
|
109
|
+
if (shouldGenerateSubmissionPdf) {
|
|
110
|
+
const templateReportsForForm = formTemplateReports?.[formId]
|
|
111
|
+
const templateInfo = Array.isArray(templateReportsForForm?.templates)
|
|
112
|
+
? (templateReportsForForm?.templates.find(
|
|
113
|
+
(report: IFormTemplateReport) => report.fileBlobName === submissionTemplateReportConfig.template,
|
|
114
|
+
) as IFormTemplateReport | undefined)
|
|
115
|
+
: undefined
|
|
116
|
+
|
|
117
|
+
if (templateInfo) {
|
|
118
|
+
const generatedTemplate = await replaceTemplateReportAndUpload({
|
|
119
|
+
templateInfo,
|
|
120
|
+
formData: { Data: submissionValues },
|
|
121
|
+
companyKey,
|
|
122
|
+
formId,
|
|
123
|
+
isPublic,
|
|
124
|
+
fileNameHint: submissionTemplateReportConfig.fileName || 'SubmissionProofPdf.pdf',
|
|
125
|
+
})
|
|
83
126
|
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
127
|
+
if (generatedTemplate?.blobName) submissionValues[FORM_SUBMISSION_PDF_KEY] = generatedTemplate.blobName
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
const reqData = {
|
|
132
|
+
name: '', // TODO: maybe later, make it dynamic
|
|
133
|
+
data: JSON.stringify(toMongoDbExtendedJSON({ ...submissionValues, generatedPdfBlobName })),
|
|
134
|
+
...(isPublic ? { private: false } : { version: 1 }),
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
const endpoint = isPublic ? `/api/site/${formKey}` : `/api/formdata/${formId}`
|
|
138
|
+
const res = await client.post(endpoint, reqData)
|
|
139
|
+
|
|
140
|
+
if (res.status === 200) onCreateSuccess(res.data)
|
|
141
|
+
} catch (err: any) {
|
|
142
|
+
if (err?.errorFields) {
|
|
143
|
+
const firstErrorField = Array.isArray(err.errorFields) && err.errorFields.length > 0 && err.errorFields[0]
|
|
144
|
+
const labels = firstErrorField
|
|
145
|
+
? (t(
|
|
146
|
+
err.errorFields
|
|
147
|
+
.filter((f: { name?: string[] }) => Array.isArray(f.name) && f.name.length === 1)
|
|
148
|
+
.map((f: { name: string[] }) => ({ key: f.name[0], type: TranslationTextTypeEnum.Label })),
|
|
149
|
+
) as string[])
|
|
150
|
+
: []
|
|
151
|
+
|
|
152
|
+
warning({
|
|
153
|
+
message: 'Please fill out all the required fields!',
|
|
154
|
+
description: labels.length > 5 ? `${labels.length} required fields are missing.` : labels.join(', '),
|
|
88
155
|
})
|
|
89
|
-
.catch((err) => {
|
|
90
|
-
const firstErrorField = Array.isArray(err?.errorFields) && err.errorFields.length > 0 && err.errorFields[0]
|
|
91
|
-
const labels = firstErrorField
|
|
92
|
-
? (t(
|
|
93
|
-
err.errorFields
|
|
94
|
-
.filter((f: { name?: string[] }) => Array.isArray(f.name) && f.name.length === 1)
|
|
95
|
-
.map((f: { name: string[] }) => ({ key: f.name[0], type: TranslationTextTypeEnum.Label })),
|
|
96
|
-
) as string[])
|
|
97
|
-
: []
|
|
98
|
-
|
|
99
|
-
warning({
|
|
100
|
-
message: 'Please fill out all the required fields!',
|
|
101
|
-
description: labels.length > 5 ? `${labels.length} required fields are missing.` : labels.join(', '),
|
|
102
|
-
})
|
|
103
156
|
|
|
104
|
-
|
|
105
|
-
|
|
157
|
+
const firstFieldName =
|
|
158
|
+
Array.isArray(firstErrorField?.name) && firstErrorField.name.length > 0 && firstErrorField?.name[0]
|
|
106
159
|
|
|
107
|
-
|
|
160
|
+
if (!firstFieldName) return
|
|
161
|
+
|
|
162
|
+
const element = document.getElementById(firstFieldName)
|
|
163
|
+
if (element) element.scrollIntoView({ behavior: 'smooth', block: 'start' })
|
|
164
|
+
return
|
|
165
|
+
}
|
|
108
166
|
|
|
109
|
-
const element = document.getElementById(firstFieldName)
|
|
110
|
-
if (element) element.scrollIntoView({ behavior: 'smooth', block: 'start' })
|
|
111
|
-
})
|
|
112
|
-
} catch (err) {
|
|
113
167
|
onCreateError()
|
|
114
168
|
} finally {
|
|
115
169
|
onCreateFinal()
|
|
@@ -125,10 +179,17 @@ export const useCreateDataWithPdfActions = ({
|
|
|
125
179
|
formTemplateReports,
|
|
126
180
|
submissionTemplateReportConfig,
|
|
127
181
|
isPublic,
|
|
182
|
+
isUserForm,
|
|
128
183
|
baseServerUrl,
|
|
129
184
|
companyKey,
|
|
130
185
|
navigate,
|
|
131
186
|
errorModal,
|
|
187
|
+
setDataLoadingType,
|
|
188
|
+
onCreateSuccess,
|
|
189
|
+
onCreateError,
|
|
190
|
+
onCreateFinal,
|
|
191
|
+
t,
|
|
192
|
+
warning,
|
|
132
193
|
],
|
|
133
194
|
)
|
|
134
195
|
|
|
@@ -44,7 +44,7 @@ function LayoutRenderer_CurrencyField({
|
|
|
44
44
|
)
|
|
45
45
|
|
|
46
46
|
return (
|
|
47
|
-
<LayoutRenderer_FieldElement formRef={formRef} elementKey={elementData.key} formId={formContext.formId}>
|
|
47
|
+
<LayoutRenderer_FieldElement formRef={formRef} elementKey={elementData.key} formId={formContext.formId} formContext={formContext}>
|
|
48
48
|
{() => ({
|
|
49
49
|
...props,
|
|
50
50
|
label,
|
|
@@ -9,6 +9,7 @@ import { findMaxAndMinValues } from '../../../../functions'
|
|
|
9
9
|
import { VALIDATION_CURRENT_DATE_IDENTIFIER } from '../../../../constants'
|
|
10
10
|
import dayjs from 'dayjs'
|
|
11
11
|
import { useTranslation } from '../../../common/custom-hooks/use-translation.hook/hook'
|
|
12
|
+
import useGetCurrentBreakpoint from '../../../common/custom-hooks/use-window-width.hook'
|
|
12
13
|
|
|
13
14
|
function LayoutRenderer_PickerField({ formContext, formItem, elementData, isDisabled }: ILayoutRenderer_PickerField) {
|
|
14
15
|
const { t } = useTranslation(formContext.formId)
|
|
@@ -32,10 +33,15 @@ function LayoutRenderer_PickerField({ formContext, formItem, elementData, isDisa
|
|
|
32
33
|
? [placeholder1, placeholder2]
|
|
33
34
|
: placeholder1
|
|
34
35
|
|
|
35
|
-
|
|
36
|
+
const currentBreakpoint = useGetCurrentBreakpoint()
|
|
37
|
+
let { maxValue, minValue } = findMaxAndMinValues(elementData.validations, {
|
|
38
|
+
fieldValues: (formContext.formRef?.getFieldsValue(true) ?? {}) as Record<string, any>,
|
|
39
|
+
formDataId: formContext.formDataId,
|
|
40
|
+
currentBreakpoint,
|
|
41
|
+
})
|
|
36
42
|
|
|
37
43
|
return (
|
|
38
|
-
<LayoutRenderer_FieldElement formRef={formContext.formRef} elementKey={elementData.key} formId={formContext.formId}>
|
|
44
|
+
<LayoutRenderer_FieldElement formRef={formContext.formRef} elementKey={elementData.key} formId={formContext.formId} formContext={formContext}>
|
|
39
45
|
{() => ({
|
|
40
46
|
...props,
|
|
41
47
|
label,
|
|
@@ -65,3 +71,4 @@ type ILayoutRenderer_PickerField = {
|
|
|
65
71
|
elementData: IPickerElement
|
|
66
72
|
} & IElementBaseProps
|
|
67
73
|
|
|
74
|
+
|