form-craft-package 1.7.9-dev.0 → 1.7.9-dev.2
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/package.json +3 -2
- package/src/api/client.ts +1 -1
- package/src/api/user.ts +7 -8
- package/src/components/common/custom-hooks/use-breadcrumb.hook.ts +90 -0
- package/src/components/common/custom-hooks/use-check-element-conditions.hook.ts +3 -3
- package/src/components/common/custom-hooks/use-find-dynamic-form.hook.ts +30 -11
- package/src/components/common/custom-hooks/use-login-handler.ts +81 -4
- package/src/components/common/custom-hooks/use-many-to-many-connector.hook.ts +30 -0
- package/src/components/common/not-found.tsx +21 -0
- package/src/components/common/section-panel.tsx +42 -0
- package/src/components/companies/1-authenticated/change-password.tsx +110 -0
- package/src/components/companies/2-unauthenticated/reset-password.tsx +128 -0
- package/src/components/companies/index.tsx +2 -0
- package/src/components/form/1-list/index.tsx +37 -38
- package/src/components/form/1-list/table-header.tsx +6 -6
- package/src/components/form/1-list/table.tsx +10 -12
- package/src/components/form/2-details/index.tsx +63 -41
- package/src/components/form/layout-renderer/1-row/index.tsx +12 -5
- package/src/components/form/layout-renderer/3-element/1-dynamic-button/index.tsx +59 -89
- package/src/components/form/layout-renderer/3-element/1-dynamic-button/use-button-navigate.hook.tsx +88 -0
- package/src/components/form/layout-renderer/3-element/1-dynamic-button/use-create-data.hook.ts +22 -23
- package/src/components/form/layout-renderer/3-element/1-dynamic-button/use-generate-report.hook.tsx +3 -4
- package/src/components/form/layout-renderer/3-element/1-dynamic-button/use-save-data.hook.ts +2 -2
- package/src/components/form/layout-renderer/3-element/11-breadcrumb.tsx +30 -0
- package/src/components/form/layout-renderer/3-element/2-field-element.tsx +4 -1
- package/src/components/form/layout-renderer/3-element/4-rich-text-editor.tsx +27 -2
- package/src/components/form/layout-renderer/3-element/6-signature.tsx +5 -1
- package/src/components/form/layout-renderer/3-element/8-fields-with-options.tsx +115 -74
- package/src/components/form/layout-renderer/3-element/9-form-data-render.tsx +50 -110
- package/src/components/form/layout-renderer/3-element/index.tsx +27 -9
- package/src/components/modals/report-filters.modal/helper-functions.ts +3 -6
- package/src/constants.ts +62 -45
- package/src/enums/form.enum.ts +5 -3
- package/src/enums/index.ts +9 -3
- package/src/{global-helpers → functions}/cookie-handler.ts +9 -10
- package/src/functions/forms/breadcrumb-handlers.ts +21 -0
- package/src/functions/forms/create-form-rules.ts +4 -1
- package/src/functions/forms/data-render-functions.tsx +5 -4
- package/src/functions/forms/extended-json-handlers.ts +56 -0
- package/src/functions/forms/index.ts +17 -11
- package/src/functions/index.ts +2 -1
- package/src/functions/reports/index.tsx +2 -1
- package/src/types/companies/site-layout/authenticated/index.tsx +23 -14
- package/src/types/forms/data-list/index.ts +0 -7
- package/src/types/forms/index.ts +1 -0
- package/src/types/forms/layout-elements/button.ts +11 -3
- package/src/types/forms/layout-elements/data-render-config.ts +1 -0
- package/src/types/forms/layout-elements/index.ts +12 -2
- package/src/types/forms/layout-elements/sanitization.ts +6 -1
- package/src/types/forms/relationship/index.ts +12 -1
- package/src/types/index.ts +2 -0
- package/src/functions/forms/json-handlers.ts +0 -19
- package/src/global-helpers/constants.ts +0 -2
- package/src/global-helpers/enums.ts +0 -12
|
@@ -1,15 +1,8 @@
|
|
|
1
1
|
import { useNavigate } from 'react-router-dom'
|
|
2
|
-
import { useNotification } from '../../../../common/custom-hooks'
|
|
2
|
+
import { useFindDynamiForm, useNotification } from '../../../../common/custom-hooks'
|
|
3
3
|
import { useCheckElementConditions } from '../../../../common/custom-hooks/use-check-element-conditions.hook'
|
|
4
|
-
import { memo, useCallback,
|
|
5
|
-
import {
|
|
6
|
-
ButtonActionCategoryEnum,
|
|
7
|
-
FormLoadingModalTypeEnum,
|
|
8
|
-
NavigateButtonCustomComponentType,
|
|
9
|
-
NavigateButtonTypesEnum,
|
|
10
|
-
} from '../../../../../enums'
|
|
4
|
+
import { memo, useCallback, useState } from 'react'
|
|
11
5
|
import { useFormPreservedItemValues } from '../../../../common/custom-hooks/use-preserved-form-items.hook'
|
|
12
|
-
import { constructDynamicFormHref } from '../../../../../functions/forms'
|
|
13
6
|
import { useDuplicateDataAction } from './use-duplicate-data.hook'
|
|
14
7
|
import { useDeleteDataAction } from './use-delete-data.hook'
|
|
15
8
|
import { usePublishDataAction } from './use-publish-data.hook'
|
|
@@ -21,34 +14,34 @@ import { NEW_FORM_DATA_IDENTIFIER } from '../../../../../constants'
|
|
|
21
14
|
import { Button_FillerPortal } from '../../../../common/button'
|
|
22
15
|
import { getButtonRenderProps } from '../../../../../functions/forms/get-element-props'
|
|
23
16
|
import WarningIcon from '../../../../common/warning-icon'
|
|
17
|
+
import FormDataLoadingIndicatorModal from '../../../../modals/form-data-loading.modal'
|
|
18
|
+
import { IFormContext } from '../../1-row'
|
|
19
|
+
import { ButtonActionCategoryEnum, FormLoadingModalTypeEnum } from '../../../../../enums'
|
|
20
|
+
import { useButtonNavigateAction } from './use-button-navigate.hook'
|
|
21
|
+
import { constructDynamicFormHref } from '../../../../../functions'
|
|
24
22
|
import {
|
|
25
|
-
IButtonNavigateState,
|
|
26
23
|
IButtonElementProps,
|
|
27
24
|
IFormLayoutElementConditions,
|
|
28
25
|
IButtonProps_Other,
|
|
29
26
|
IButtonProps_Navigate,
|
|
30
27
|
} from '../../../../../types'
|
|
31
|
-
import FormDataLoadingIndicatorModal from '../../../../modals/form-data-loading.modal'
|
|
32
|
-
import { IFormContext } from '../../1-row'
|
|
33
28
|
|
|
34
29
|
export const DynamicFormButtonRender = memo((props: IDynamicButton) => {
|
|
35
30
|
const { displayStateProps = {}, formContext = {}, onCustomFunctionCall = () => {} } = props
|
|
36
|
-
const {
|
|
37
|
-
|
|
38
|
-
conditions,
|
|
39
|
-
defaultDisabled = false,
|
|
40
|
-
stateToPass = {},
|
|
41
|
-
} = displayStateProps as IDynamicButton_DisplayStateProps
|
|
42
|
-
const { formDataId, formRef, formName = '' } = formContext as IFormContext
|
|
31
|
+
const { btnProps, conditions, defaultDisabled = false } = displayStateProps as IDynamicButton_DisplayStateProps
|
|
32
|
+
const { formDataId, formRef } = formContext as IFormContext
|
|
43
33
|
|
|
34
|
+
const { getFormById } = useFindDynamiForm()
|
|
44
35
|
const navigate = useNavigate()
|
|
45
36
|
const { success, warning, error, confirmModal } = useNotification()
|
|
46
37
|
const { isElementDisabled, isElementHidden } = useCheckElementConditions({ formRef, conditions, defaultDisabled })
|
|
47
38
|
const [loading, setLoading] = useState(false)
|
|
48
39
|
const [dataLoadingType, setDataLoadingType] = useState<FormLoadingModalTypeEnum | undefined>()
|
|
49
|
-
const { inPreviewMode, isPublic = false } = useFormPreservedItemValues(formRef)
|
|
40
|
+
const { inPreviewMode, isPublic = false, submissionPdfConfig } = useFormPreservedItemValues(formRef)
|
|
50
41
|
|
|
51
|
-
const
|
|
42
|
+
const formInfo = btnProps.formId ? getFormById(btnProps.formId) : undefined
|
|
43
|
+
formContext.formId = btnProps.formId
|
|
44
|
+
formContext.formName = formInfo?.name
|
|
52
45
|
|
|
53
46
|
const displayResultMessage = useCallback(
|
|
54
47
|
(isSuccess: boolean = true) => {
|
|
@@ -58,24 +51,49 @@ export const DynamicFormButtonRender = memo((props: IDynamicButton) => {
|
|
|
58
51
|
[btnProps.messages, success, error],
|
|
59
52
|
)
|
|
60
53
|
|
|
54
|
+
const onButtonNavigate = useButtonNavigateAction({
|
|
55
|
+
...formContext,
|
|
56
|
+
onSuccess: () => {},
|
|
57
|
+
onError: () => {},
|
|
58
|
+
onFinal: () => {},
|
|
59
|
+
})
|
|
60
|
+
|
|
61
|
+
const handleSecondaryAction = useCallback(
|
|
62
|
+
(formDataId?: string) => {
|
|
63
|
+
if (!btnProps.secondaryAction) {
|
|
64
|
+
if (formInfo?.name) navigate(`${constructDynamicFormHref(formInfo.name)}/${formDataId}`, { replace: true })
|
|
65
|
+
setTimeout(() => {
|
|
66
|
+
setDataLoadingType(undefined)
|
|
67
|
+
displayResultMessage()
|
|
68
|
+
}, 500)
|
|
69
|
+
return
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
const { category } = btnProps.secondaryAction
|
|
73
|
+
if (category === ButtonActionCategoryEnum.Navigate) onButtonNavigate(btnProps.secondaryAction)
|
|
74
|
+
else if (category === ButtonActionCategoryEnum.CustomFunction) {
|
|
75
|
+
onCustomFunctionCall(btnProps.secondaryAction.functionName, btnProps.messages ?? defaultMessage)
|
|
76
|
+
setTimeout(() => setLoading(false), 500)
|
|
77
|
+
}
|
|
78
|
+
},
|
|
79
|
+
[btnProps, onButtonNavigate, formInfo],
|
|
80
|
+
)
|
|
81
|
+
|
|
61
82
|
const onDuplicateData = useDuplicateDataAction({
|
|
62
83
|
...formContext,
|
|
63
|
-
onSuccess: () =>
|
|
84
|
+
onSuccess: () => handleSecondaryAction(),
|
|
64
85
|
onError: () => displayResultMessage(false),
|
|
65
86
|
onFinal: () => setLoading(false),
|
|
66
87
|
})
|
|
67
88
|
const onDeleteData = useDeleteDataAction({
|
|
68
89
|
...formContext,
|
|
69
|
-
onSuccess: () =>
|
|
70
|
-
displayResultMessage(true)
|
|
71
|
-
navigate(baseDynamicUrl)
|
|
72
|
-
},
|
|
90
|
+
onSuccess: () => handleSecondaryAction(),
|
|
73
91
|
onError: () => displayResultMessage(false),
|
|
74
92
|
onFinal: () => setLoading(false),
|
|
75
93
|
})
|
|
76
94
|
const onPublishData = usePublishDataAction({
|
|
77
95
|
...formContext,
|
|
78
|
-
onSuccess: () =>
|
|
96
|
+
onSuccess: () => handleSecondaryAction(),
|
|
79
97
|
onError: () => displayResultMessage(false),
|
|
80
98
|
onFinal: () => setLoading(false),
|
|
81
99
|
})
|
|
@@ -86,22 +104,14 @@ export const DynamicFormButtonRender = memo((props: IDynamicButton) => {
|
|
|
86
104
|
setDataLoadingType(undefined)
|
|
87
105
|
setLoading(false)
|
|
88
106
|
},
|
|
89
|
-
onSuccess: () =>
|
|
90
|
-
setDataLoadingType(undefined)
|
|
91
|
-
displayResultMessage()
|
|
92
|
-
},
|
|
107
|
+
onSuccess: (formDataId?: string) => handleSecondaryAction(formDataId),
|
|
93
108
|
onError: () => displayResultMessage(false),
|
|
94
109
|
onFinal: () => setLoading(false),
|
|
95
110
|
})
|
|
96
111
|
const onSaveExistingData = useSaveExistingDataAction({
|
|
97
112
|
...formContext,
|
|
98
113
|
setDataLoadingType,
|
|
99
|
-
onSuccess: () =>
|
|
100
|
-
setTimeout(() => {
|
|
101
|
-
setDataLoadingType(undefined)
|
|
102
|
-
displayResultMessage()
|
|
103
|
-
}, 500)
|
|
104
|
-
},
|
|
114
|
+
onSuccess: () => handleSecondaryAction(),
|
|
105
115
|
onError: () => displayResultMessage(false),
|
|
106
116
|
onFinal: () => setLoading(false),
|
|
107
117
|
})
|
|
@@ -116,54 +126,14 @@ export const DynamicFormButtonRender = memo((props: IDynamicButton) => {
|
|
|
116
126
|
const handleButtonClick = useCallback(async () => {
|
|
117
127
|
switch (btnProps.category) {
|
|
118
128
|
case ButtonActionCategoryEnum.Navigate:
|
|
119
|
-
|
|
120
|
-
case NavigateButtonTypesEnum.NewDataPage:
|
|
121
|
-
navigate(`${baseDynamicUrl}/${NEW_FORM_DATA_IDENTIFIER}`, { state: stateToPass })
|
|
122
|
-
break
|
|
123
|
-
|
|
124
|
-
case NavigateButtonTypesEnum.ViewDataDetails:
|
|
125
|
-
if (formDataId) navigate(`${baseDynamicUrl}/${formDataId}`)
|
|
126
|
-
else error({ message: CUSTOM_ERROR_MESSAGES.DataIdNotFound })
|
|
127
|
-
break
|
|
128
|
-
|
|
129
|
-
case NavigateButtonTypesEnum.ReturnToList:
|
|
130
|
-
navigate(baseDynamicUrl)
|
|
131
|
-
break
|
|
132
|
-
|
|
133
|
-
case NavigateButtonTypesEnum.Custom:
|
|
134
|
-
if (Array.isArray(btnProps.urlComponents)) {
|
|
135
|
-
const validComponents = btnProps.urlComponents.filter((c) => !!c.type && !!c.value)
|
|
136
|
-
const formData = formRef?.getFieldsValue() ?? {}
|
|
137
|
-
|
|
138
|
-
const finalUrl = validComponents
|
|
139
|
-
.map((c) => {
|
|
140
|
-
if (c.type === NavigateButtonCustomComponentType.DynamicPath) {
|
|
141
|
-
const data = formData[c.value] ?? ''
|
|
142
|
-
return data.replaceAll(' ', '')
|
|
143
|
-
}
|
|
144
|
-
|
|
145
|
-
return c.value
|
|
146
|
-
})
|
|
147
|
-
.join('/')
|
|
148
|
-
|
|
149
|
-
window.open(finalUrl, '_blank')
|
|
150
|
-
}
|
|
151
|
-
break
|
|
152
|
-
|
|
153
|
-
case NavigateButtonTypesEnum.ReturnOneBack:
|
|
154
|
-
default:
|
|
155
|
-
navigate(-1)
|
|
156
|
-
break
|
|
157
|
-
}
|
|
158
|
-
// window.location.href = `${baseDynamicUrl}/${NEW_FORM_DATA_IDENTIFIER}`
|
|
159
|
-
|
|
129
|
+
onButtonNavigate(btnProps)
|
|
160
130
|
setLoading(false)
|
|
161
131
|
break
|
|
162
132
|
|
|
163
133
|
case ButtonActionCategoryEnum.DuplicateData:
|
|
164
134
|
if (formDataId) await onDuplicateData()
|
|
165
135
|
else {
|
|
166
|
-
error({ message:
|
|
136
|
+
error({ message: BUTTON_CUSTOM_ERROR_MESSAGES.DataIdNotFound })
|
|
167
137
|
setLoading(false)
|
|
168
138
|
}
|
|
169
139
|
break
|
|
@@ -171,7 +141,7 @@ export const DynamicFormButtonRender = memo((props: IDynamicButton) => {
|
|
|
171
141
|
case ButtonActionCategoryEnum.DeleteData:
|
|
172
142
|
if (formDataId) await onDeleteData()
|
|
173
143
|
else {
|
|
174
|
-
error({ message:
|
|
144
|
+
error({ message: BUTTON_CUSTOM_ERROR_MESSAGES.DataIdNotFound })
|
|
175
145
|
setLoading(false)
|
|
176
146
|
}
|
|
177
147
|
break
|
|
@@ -179,7 +149,7 @@ export const DynamicFormButtonRender = memo((props: IDynamicButton) => {
|
|
|
179
149
|
case ButtonActionCategoryEnum.PublishDataChanges:
|
|
180
150
|
if (formDataId) await onPublishData()
|
|
181
151
|
else {
|
|
182
|
-
error({ message:
|
|
152
|
+
error({ message: BUTTON_CUSTOM_ERROR_MESSAGES.DataIdNotFound })
|
|
183
153
|
setLoading(false)
|
|
184
154
|
}
|
|
185
155
|
break
|
|
@@ -188,7 +158,7 @@ export const DynamicFormButtonRender = memo((props: IDynamicButton) => {
|
|
|
188
158
|
await onSaveSignature()
|
|
189
159
|
|
|
190
160
|
if (formDataId === NEW_FORM_DATA_IDENTIFIER) {
|
|
191
|
-
if (isPublic) onGeneratePdfProof()
|
|
161
|
+
if (isPublic && submissionPdfConfig) onGeneratePdfProof()
|
|
192
162
|
else onCreateNewData()
|
|
193
163
|
} else onSaveExistingData()
|
|
194
164
|
|
|
@@ -224,9 +194,9 @@ export const DynamicFormButtonRender = memo((props: IDynamicButton) => {
|
|
|
224
194
|
}
|
|
225
195
|
}, [
|
|
226
196
|
btnProps,
|
|
227
|
-
baseDynamicUrl,
|
|
228
197
|
formDataId,
|
|
229
198
|
isPublic,
|
|
199
|
+
submissionPdfConfig,
|
|
230
200
|
onCustomFunctionCall,
|
|
231
201
|
onDuplicateData,
|
|
232
202
|
onDeleteData,
|
|
@@ -235,6 +205,7 @@ export const DynamicFormButtonRender = memo((props: IDynamicButton) => {
|
|
|
235
205
|
onGeneratePdfProof,
|
|
236
206
|
onSaveExistingData,
|
|
237
207
|
onGenerateReport,
|
|
208
|
+
onButtonNavigate,
|
|
238
209
|
setDataLoadingType,
|
|
239
210
|
])
|
|
240
211
|
|
|
@@ -252,7 +223,7 @@ export const DynamicFormButtonRender = memo((props: IDynamicButton) => {
|
|
|
252
223
|
if (
|
|
253
224
|
isPublic &&
|
|
254
225
|
formDataId === NEW_FORM_DATA_IDENTIFIER &&
|
|
255
|
-
|
|
226
|
+
[ButtonActionCategoryEnum.SaveDataChanges, ButtonActionCategoryEnum.Navigate].includes(btnProps.category)
|
|
256
227
|
)
|
|
257
228
|
return
|
|
258
229
|
setLoading(true)
|
|
@@ -271,9 +242,9 @@ export const DynamicFormButtonRender = memo((props: IDynamicButton) => {
|
|
|
271
242
|
{isPublic &&
|
|
272
243
|
formDataId === NEW_FORM_DATA_IDENTIFIER &&
|
|
273
244
|
btnProps.category !== ButtonActionCategoryEnum.SaveDataChanges && (
|
|
274
|
-
<WarningIcon tooltip={
|
|
245
|
+
<WarningIcon tooltip={BUTTON_CUSTOM_ERROR_MESSAGES.UnavailableForPublic} />
|
|
275
246
|
)}
|
|
276
|
-
{inPreviewMode && <WarningIcon tooltip={
|
|
247
|
+
{inPreviewMode && <WarningIcon tooltip={BUTTON_CUSTOM_ERROR_MESSAGES.InPreviewMode} />}
|
|
277
248
|
{btnProps.label}
|
|
278
249
|
</Button_FillerPortal>
|
|
279
250
|
{!!dataLoadingType && (
|
|
@@ -296,7 +267,6 @@ interface IDynamicButton_DisplayStateProps {
|
|
|
296
267
|
btnProps: IButtonElementProps
|
|
297
268
|
conditions?: IFormLayoutElementConditions
|
|
298
269
|
defaultDisabled?: boolean
|
|
299
|
-
stateToPass?: IButtonNavigateState
|
|
300
270
|
}
|
|
301
271
|
|
|
302
272
|
export interface ICustomFunctionCall {
|
|
@@ -309,14 +279,14 @@ export interface ICustomFunctionCall {
|
|
|
309
279
|
) => void
|
|
310
280
|
}
|
|
311
281
|
export interface IOnSuccessFunctions {
|
|
312
|
-
onSuccess: () => void
|
|
282
|
+
onSuccess: (formDataId?: string) => void
|
|
313
283
|
onError: () => void
|
|
314
284
|
onFinal: () => void
|
|
315
285
|
}
|
|
316
286
|
|
|
317
287
|
const defaultMessage = { success: 'Successfully completed!', error: 'Error occured' }
|
|
318
288
|
|
|
319
|
-
const
|
|
289
|
+
export const BUTTON_CUSTOM_ERROR_MESSAGES = {
|
|
320
290
|
DataIdNotFound: 'Data id was not found!',
|
|
321
291
|
FormInstanceNotFound: 'Form ref was not found!',
|
|
322
292
|
UnavailableForPublic: 'Button action is NOT allowed for public use!',
|
package/src/components/form/layout-renderer/3-element/1-dynamic-button/use-button-navigate.hook.tsx
ADDED
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
import { useCallback } from 'react'
|
|
2
|
+
import { IButtonProps_Navigate } from '../../../../../types'
|
|
3
|
+
import { useFindDynamiForm, useNotification } from '../../../../common/custom-hooks'
|
|
4
|
+
import { constructDynamicFormHref } from '../../../../../functions/forms'
|
|
5
|
+
import { NavigateButtonCustomComponentType, NavigateButtonTypesEnum } from '../../../../../enums'
|
|
6
|
+
import { IFormContext } from '../../1-row'
|
|
7
|
+
import { BUTTON_CUSTOM_ERROR_MESSAGES, IOnSuccessFunctions } from '.'
|
|
8
|
+
import { useNavigate } from 'react-router-dom'
|
|
9
|
+
import { NEW_FORM_DATA_IDENTIFIER } from '../../../../../constants'
|
|
10
|
+
import { useBreadcrumb } from '../../../../common/custom-hooks/use-breadcrumb.hook'
|
|
11
|
+
|
|
12
|
+
/* --------------------------------------------------------------------------
|
|
13
|
+
Navigation button action handler
|
|
14
|
+
-------------------------------------------------------------------------- */
|
|
15
|
+
export const useButtonNavigateAction = ({
|
|
16
|
+
onSuccess,
|
|
17
|
+
onError,
|
|
18
|
+
onFinal,
|
|
19
|
+
...formContext
|
|
20
|
+
}: IOnSuccessFunctions & IFormContext) => {
|
|
21
|
+
const { formRef, formDataId, parentFormJoins, manyToManyRelInfo } = formContext
|
|
22
|
+
const { getFormById } = useFindDynamiForm()
|
|
23
|
+
const { error } = useNotification()
|
|
24
|
+
const { updateCrumb } = useBreadcrumb()
|
|
25
|
+
const navigate = useNavigate()
|
|
26
|
+
|
|
27
|
+
const onButtonNavigate = useCallback(
|
|
28
|
+
async (btnProps: IButtonProps_Navigate) => {
|
|
29
|
+
const formInfo = btnProps.formId ? getFormById(btnProps.formId) : undefined
|
|
30
|
+
const baseDynamicUrl = formInfo ? constructDynamicFormHref(formInfo.name) : ''
|
|
31
|
+
|
|
32
|
+
switch (btnProps.navigateType) {
|
|
33
|
+
case NavigateButtonTypesEnum.NewDataPage:
|
|
34
|
+
let newDatapageNavigateUrl = `${baseDynamicUrl}/${NEW_FORM_DATA_IDENTIFIER}`
|
|
35
|
+
if (manyToManyRelInfo && parentFormJoins && formDataId) {
|
|
36
|
+
const middleFormInfo = getFormById(manyToManyRelInfo.middleFormId)
|
|
37
|
+
|
|
38
|
+
if (middleFormInfo)
|
|
39
|
+
newDatapageNavigateUrl = `${constructDynamicFormHref(middleFormInfo.name)}/${NEW_FORM_DATA_IDENTIFIER}`
|
|
40
|
+
}
|
|
41
|
+
if (formDataId !== NEW_FORM_DATA_IDENTIFIER)
|
|
42
|
+
updateCrumb(location.pathname, { formJoins: parentFormJoins, formDataId, manyToManyRelInfo })
|
|
43
|
+
|
|
44
|
+
navigate(newDatapageNavigateUrl)
|
|
45
|
+
break
|
|
46
|
+
|
|
47
|
+
case NavigateButtonTypesEnum.ViewDataDetails:
|
|
48
|
+
if (formDataId) navigate(`${baseDynamicUrl}/${formDataId}`)
|
|
49
|
+
else error({ message: BUTTON_CUSTOM_ERROR_MESSAGES.DataIdNotFound })
|
|
50
|
+
break
|
|
51
|
+
|
|
52
|
+
case NavigateButtonTypesEnum.GoToDataList:
|
|
53
|
+
navigate(baseDynamicUrl)
|
|
54
|
+
break
|
|
55
|
+
|
|
56
|
+
case NavigateButtonTypesEnum.Custom:
|
|
57
|
+
if (btnProps.customUrl) {
|
|
58
|
+
navigate(btnProps.customUrl)
|
|
59
|
+
} else if (Array.isArray(btnProps.urlComponents)) {
|
|
60
|
+
const validComponents = btnProps.urlComponents.filter((c) => !!c.type && !!c.value)
|
|
61
|
+
const formData = formRef?.getFieldsValue() ?? {}
|
|
62
|
+
|
|
63
|
+
const finalUrl = validComponents
|
|
64
|
+
.map((c) => {
|
|
65
|
+
if (c.type === NavigateButtonCustomComponentType.DynamicPath) {
|
|
66
|
+
const data = formData[c.value] ?? ''
|
|
67
|
+
return data.replaceAll(' ', '')
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
return c.value
|
|
71
|
+
})
|
|
72
|
+
.join('/')
|
|
73
|
+
|
|
74
|
+
window.open(finalUrl, '_blank')
|
|
75
|
+
}
|
|
76
|
+
break
|
|
77
|
+
|
|
78
|
+
case NavigateButtonTypesEnum.GoBack:
|
|
79
|
+
default:
|
|
80
|
+
navigate(-1 * (btnProps.navigateBackCount ?? 1))
|
|
81
|
+
break
|
|
82
|
+
}
|
|
83
|
+
},
|
|
84
|
+
[parentFormJoins, manyToManyRelInfo, formRef],
|
|
85
|
+
)
|
|
86
|
+
|
|
87
|
+
return onButtonNavigate
|
|
88
|
+
}
|
package/src/components/form/layout-renderer/3-element/1-dynamic-button/use-create-data.hook.ts
CHANGED
|
@@ -1,13 +1,14 @@
|
|
|
1
1
|
import { useCallback } from 'react'
|
|
2
2
|
import { FormLoadingModalTypeEnum } from '../../../../../enums'
|
|
3
3
|
import { useFormPreservedItemValues } from '../../../../common/custom-hooks/use-preserved-form-items.hook'
|
|
4
|
-
import {
|
|
4
|
+
import { toMongoDbExtendedJSON } from '../../../../../functions/forms/extended-json-handlers'
|
|
5
5
|
import client from '../../../../../api/client'
|
|
6
|
-
import {
|
|
6
|
+
import { objectToQueryParams } from '../../../../../functions/forms'
|
|
7
7
|
import { useNotification } from '../../../../common/custom-hooks'
|
|
8
8
|
import { useNavigate } from 'react-router-dom'
|
|
9
9
|
import { IFormContext } from '../../1-row'
|
|
10
10
|
import { IOnSuccessFunctions } from '.'
|
|
11
|
+
import { useBreadcrumb } from '../../../../common/custom-hooks/use-breadcrumb.hook'
|
|
11
12
|
|
|
12
13
|
/* --------------------------------------------------------------------------
|
|
13
14
|
- Creates new form data.
|
|
@@ -28,43 +29,40 @@ export const useCreateDataWithPdfActions = ({
|
|
|
28
29
|
const { formRef, formKey, formId, formName, companyKey } = formContext
|
|
29
30
|
|
|
30
31
|
const navigate = useNavigate()
|
|
31
|
-
// const { isModalOpen: isSuccessModalOpen, isPendingTransition: isSuccessModalPending, openModal: openSuccessModal, closeModal: closeSuccessModal } = useLazyModalOpener()
|
|
32
32
|
const { errorModal, confirmModal } = useNotification()
|
|
33
33
|
const { baseServerUrl, submissionPdfConfig, isPublic } = useFormPreservedItemValues(formRef)
|
|
34
|
+
const { breadcrumbs } = useBreadcrumb()
|
|
34
35
|
|
|
35
36
|
const onCreateNewData = useCallback(
|
|
36
37
|
(generatedPdfBlobName?: string) => {
|
|
37
38
|
try {
|
|
38
39
|
formRef?.validateFields().then(async (values) => {
|
|
39
40
|
setDataLoadingType(FormLoadingModalTypeEnum.SavingChanges)
|
|
40
|
-
|
|
41
41
|
const reqData = {
|
|
42
42
|
name: '', // TODO: maybe later, make it dynamic
|
|
43
|
-
data:
|
|
43
|
+
data: JSON.stringify(toMongoDbExtendedJSON({ ...values, generatedPdfBlobName })),
|
|
44
44
|
...(isPublic ? { private: false } : { version: 1 }),
|
|
45
45
|
}
|
|
46
46
|
|
|
47
47
|
const endpoint = isPublic ? `/api/site/${formKey}` : `/api/formdata/${formId}`
|
|
48
48
|
const res = await client.post(endpoint, reqData)
|
|
49
49
|
|
|
50
|
-
if (res.status === 200)
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
}
|
|
67
|
-
}, 500)
|
|
50
|
+
if (res.status === 200) {
|
|
51
|
+
onCreateSuccess(res.data)
|
|
52
|
+
if (submissionPdfConfig?.showDownloadModal) {
|
|
53
|
+
confirmModal({
|
|
54
|
+
title: submissionPdfConfig.successTitle ?? 'Successfully generated the PDF!',
|
|
55
|
+
content: submissionPdfConfig.successContent ?? 'Would you like to download the PDF?',
|
|
56
|
+
centered: true,
|
|
57
|
+
okText: 'Download',
|
|
58
|
+
onOk: () => {
|
|
59
|
+
const href = `${baseServerUrl}/api/attachment/${companyKey}/${generatedPdfBlobName}`
|
|
60
|
+
window.open(href, '_blank')
|
|
61
|
+
window.location.reload()
|
|
62
|
+
},
|
|
63
|
+
})
|
|
64
|
+
}
|
|
65
|
+
}
|
|
68
66
|
})
|
|
69
67
|
} catch (err) {
|
|
70
68
|
if (submissionPdfConfig?.errorMsg) errorModal({ title: submissionPdfConfig.errorMsg })
|
|
@@ -74,6 +72,7 @@ export const useCreateDataWithPdfActions = ({
|
|
|
74
72
|
}
|
|
75
73
|
},
|
|
76
74
|
[
|
|
75
|
+
breadcrumbs,
|
|
77
76
|
formRef,
|
|
78
77
|
formId,
|
|
79
78
|
formKey,
|
package/src/components/form/layout-renderer/3-element/1-dynamic-button/use-generate-report.hook.tsx
CHANGED
|
@@ -4,9 +4,8 @@ import { FaCaretDown } from 'react-icons/fa6'
|
|
|
4
4
|
import { IDynamicForm, IFormSchema, IFormTemplateReport } from '../../../../../types'
|
|
5
5
|
import { Button_FillerPortal } from '../../../../common/button'
|
|
6
6
|
import { useNotification } from '../../../../common/custom-hooks'
|
|
7
|
-
import { fetchFormDataAsLookup, renderData } from '../../../../../functions/forms'
|
|
7
|
+
import { fetchFormDataAsLookup, getIdEqualsQuery, renderData } from '../../../../../functions/forms'
|
|
8
8
|
import client from '../../../../../api/client'
|
|
9
|
-
import { parseJSON } from '../../../../../functions/forms/json-handlers'
|
|
10
9
|
import { FieldElementOptionSourceEnum, LOCAL_STORAGE_KEYS_ENUM } from '../../../../../enums'
|
|
11
10
|
import { IFormContext } from '../../1-row'
|
|
12
11
|
import { IOnSuccessFunctions } from '.'
|
|
@@ -44,14 +43,14 @@ export const useGenerateReportAction = ({
|
|
|
44
43
|
.get(`/api/form/${form.id}`)
|
|
45
44
|
.then((res) => {
|
|
46
45
|
if (res.status === 200) {
|
|
47
|
-
const parsedFormData: IFormSchema | null =
|
|
46
|
+
const parsedFormData: IFormSchema | null = JSON.parse(res.data.data)
|
|
48
47
|
if (parsedFormData) {
|
|
49
48
|
const reports = parsedFormData.generateConfig.templateReports ?? []
|
|
50
49
|
if (reports.length)
|
|
51
50
|
client
|
|
52
51
|
.post(`/api/report/data/${form.id}`, {
|
|
53
52
|
joins: parsedFormData.generateConfig.formJoins,
|
|
54
|
-
match: JSON.stringify(
|
|
53
|
+
match: JSON.stringify(getIdEqualsQuery('', formDataId)),
|
|
55
54
|
})
|
|
56
55
|
.then((res) => {
|
|
57
56
|
if (res.status === 200) {
|
package/src/components/form/layout-renderer/3-element/1-dynamic-button/use-save-data.hook.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { useCallback } from 'react'
|
|
2
2
|
import { FormLoadingModalTypeEnum } from '../../../../../enums'
|
|
3
|
-
import {
|
|
3
|
+
import { toMongoDbExtendedJSON } from '../../../../../functions/forms/extended-json-handlers'
|
|
4
4
|
import client from '../../../../../api/client'
|
|
5
5
|
import { IFormContext } from '../../1-row'
|
|
6
6
|
import { IOnSuccessFunctions } from '.'
|
|
@@ -27,7 +27,7 @@ export const useSaveExistingDataAction = ({
|
|
|
27
27
|
setDataLoadingType(FormLoadingModalTypeEnum.SavingChanges)
|
|
28
28
|
const reqData = {
|
|
29
29
|
name: 'Dynamic form data', // TODO: maybe later, make it dynamic
|
|
30
|
-
data:
|
|
30
|
+
data: JSON.stringify(toMongoDbExtendedJSON(values)),
|
|
31
31
|
version: 1, // FIXME: increment
|
|
32
32
|
private: false, // not used anymore, sending it as false by default
|
|
33
33
|
}
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import { useBreadcrumb } from '../../../common/custom-hooks/use-breadcrumb.hook'
|
|
2
|
+
import { Button_FillerPortal } from '../../../common/button'
|
|
3
|
+
import { useNavigate } from 'react-router-dom'
|
|
4
|
+
import { FaChevronRight } from 'react-icons/fa6'
|
|
5
|
+
import React from 'react'
|
|
6
|
+
|
|
7
|
+
export default function LayoutRenderer_Breadcrumb() {
|
|
8
|
+
const navigate = useNavigate()
|
|
9
|
+
const { breadcrumbs, sliceAt } = useBreadcrumb()
|
|
10
|
+
|
|
11
|
+
return (
|
|
12
|
+
<div className="flex items-center text-12">
|
|
13
|
+
{breadcrumbs.map((bc, bcIdx) => (
|
|
14
|
+
<React.Fragment key={bcIdx}>
|
|
15
|
+
<Button_FillerPortal
|
|
16
|
+
link
|
|
17
|
+
disabled={breadcrumbs.length - 1 === bcIdx}
|
|
18
|
+
onClick={() => {
|
|
19
|
+
sliceAt(bcIdx)
|
|
20
|
+
navigate(bc.href)
|
|
21
|
+
}}
|
|
22
|
+
>
|
|
23
|
+
<span className="font-normal italic">{bc.label}</span>
|
|
24
|
+
</Button_FillerPortal>
|
|
25
|
+
{breadcrumbs.length - 1 !== bcIdx && <FaChevronRight className="text-primary" />}
|
|
26
|
+
</React.Fragment>
|
|
27
|
+
))}
|
|
28
|
+
</div>
|
|
29
|
+
)
|
|
30
|
+
}
|
|
@@ -50,6 +50,9 @@ export const LayoutRenderer_FieldElement = ({
|
|
|
50
50
|
const sanitizationRule = field.sanitization
|
|
51
51
|
if (!sanitizationRule || sanitizationRule.type === DataSanitizationTypeEnum.Default) return value
|
|
52
52
|
|
|
53
|
+
if (sanitizationRule.type === DataSanitizationTypeEnum.Predefined && sanitizationRule.pattern)
|
|
54
|
+
return formatByPattern(value, sanitizationRule.pattern)
|
|
55
|
+
|
|
53
56
|
if (sanitizationRule.type === DataSanitizationTypeEnum.Custom)
|
|
54
57
|
return formatByPattern(value, sanitizationRule.pattern)
|
|
55
58
|
}}
|
|
@@ -193,7 +196,7 @@ const getField = ({
|
|
|
193
196
|
)
|
|
194
197
|
case ElementTypeEnum.ShortInput:
|
|
195
198
|
default:
|
|
196
|
-
return <Input placeholder={placeholder} disabled={disabled} autoComplete=
|
|
199
|
+
return <Input placeholder={placeholder} disabled={disabled} autoComplete="off" />
|
|
197
200
|
}
|
|
198
201
|
}
|
|
199
202
|
|
|
@@ -2,20 +2,44 @@ import { Form } from 'antd'
|
|
|
2
2
|
import ReactQuill from 'react-quill'
|
|
3
3
|
import 'react-quill/dist/quill.snow.css'
|
|
4
4
|
import { IElementBaseProps } from '.'
|
|
5
|
-
import { IInputElementProps } from '../../../../types'
|
|
5
|
+
import { IInputElementProps, IValidationRule } from '../../../../types'
|
|
6
6
|
import { IFormContext } from '../1-row'
|
|
7
|
+
import { mapToFormItemRules } from '../../../../functions'
|
|
8
|
+
import { FieldValidationEnum } from '../../../../enums'
|
|
9
|
+
import { REGEX_PATTERNS } from '../../../../constants'
|
|
7
10
|
|
|
8
11
|
export default function LayoutRenderer_RichEditor({
|
|
9
12
|
formContext,
|
|
10
13
|
elementProps,
|
|
11
14
|
formItem,
|
|
15
|
+
validations,
|
|
12
16
|
isDisabled,
|
|
13
17
|
}: ILayoutRenderer_RichEditor) {
|
|
14
18
|
const { formRef } = formContext
|
|
15
19
|
const fieldValue = Form.useWatch(formItem.path, { form: formRef, preserve: true })
|
|
20
|
+
const requiredValidation = validations?.find((v) => v.rule === FieldValidationEnum.Required)
|
|
16
21
|
|
|
17
22
|
return (
|
|
18
|
-
<Form.Item
|
|
23
|
+
<Form.Item
|
|
24
|
+
name={formItem.name}
|
|
25
|
+
rules={
|
|
26
|
+
requiredValidation
|
|
27
|
+
? mapToFormItemRules(
|
|
28
|
+
[
|
|
29
|
+
requiredValidation,
|
|
30
|
+
{
|
|
31
|
+
rule: FieldValidationEnum.Regex,
|
|
32
|
+
value: REGEX_PATTERNS.NonEmptyHtmlString.toString(),
|
|
33
|
+
message: requiredValidation.message,
|
|
34
|
+
},
|
|
35
|
+
],
|
|
36
|
+
formItem.name,
|
|
37
|
+
)
|
|
38
|
+
: []
|
|
39
|
+
}
|
|
40
|
+
label={elementProps.hasNoLabel ? '' : elementProps.label}
|
|
41
|
+
labelAlign="left"
|
|
42
|
+
>
|
|
19
43
|
<ReactQuill
|
|
20
44
|
theme="snow"
|
|
21
45
|
value={fieldValue}
|
|
@@ -43,4 +67,5 @@ const modules = {
|
|
|
43
67
|
type ILayoutRenderer_RichEditor = {
|
|
44
68
|
elementProps: IInputElementProps
|
|
45
69
|
formContext: IFormContext
|
|
70
|
+
validations?: IValidationRule[]
|
|
46
71
|
} & IElementBaseProps
|
|
@@ -6,14 +6,16 @@ import { FormPreservedItemKeys } from '../../../../enums'
|
|
|
6
6
|
import { Button_FillerPortal } from '../../../common/button'
|
|
7
7
|
import { useFormPreservedItemValues } from '../../../common/custom-hooks/use-preserved-form-items.hook'
|
|
8
8
|
import { IElementBaseProps } from '.'
|
|
9
|
-
import { ISignatureElementProps } from '../../../../types'
|
|
9
|
+
import { ISignatureElementProps, IValidationRule } from '../../../../types'
|
|
10
10
|
import { IFormContext } from '../1-row'
|
|
11
|
+
import { mapToFormItemRules } from '../../../../functions'
|
|
11
12
|
|
|
12
13
|
export default function LayoutRenderer_Signature({
|
|
13
14
|
formItem,
|
|
14
15
|
elementProps,
|
|
15
16
|
isDisabled,
|
|
16
17
|
formContext,
|
|
18
|
+
validations,
|
|
17
19
|
}: ILayoutRenderer_Signature) {
|
|
18
20
|
const { formRef, formDataId, companyKey } = formContext
|
|
19
21
|
const sigCanvasRef = useRef<SignatureCanvas>(null)
|
|
@@ -33,6 +35,7 @@ export default function LayoutRenderer_Signature({
|
|
|
33
35
|
return (
|
|
34
36
|
<Form.Item
|
|
35
37
|
name={formItem.name}
|
|
38
|
+
rules={Array.isArray(validations) ? mapToFormItemRules(validations, formItem.name) : []}
|
|
36
39
|
label={
|
|
37
40
|
!elementProps.hasNoLabel && (
|
|
38
41
|
<div className="flex items-center gap-1">
|
|
@@ -81,4 +84,5 @@ export default function LayoutRenderer_Signature({
|
|
|
81
84
|
type ILayoutRenderer_Signature = {
|
|
82
85
|
elementProps: ISignatureElementProps
|
|
83
86
|
formContext: IFormContext
|
|
87
|
+
validations?: IValidationRule[]
|
|
84
88
|
} & IElementBaseProps
|