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.
Files changed (54) hide show
  1. package/package.json +3 -2
  2. package/src/api/client.ts +1 -1
  3. package/src/api/user.ts +7 -8
  4. package/src/components/common/custom-hooks/use-breadcrumb.hook.ts +90 -0
  5. package/src/components/common/custom-hooks/use-check-element-conditions.hook.ts +3 -3
  6. package/src/components/common/custom-hooks/use-find-dynamic-form.hook.ts +30 -11
  7. package/src/components/common/custom-hooks/use-login-handler.ts +81 -4
  8. package/src/components/common/custom-hooks/use-many-to-many-connector.hook.ts +30 -0
  9. package/src/components/common/not-found.tsx +21 -0
  10. package/src/components/common/section-panel.tsx +42 -0
  11. package/src/components/companies/1-authenticated/change-password.tsx +110 -0
  12. package/src/components/companies/2-unauthenticated/reset-password.tsx +128 -0
  13. package/src/components/companies/index.tsx +2 -0
  14. package/src/components/form/1-list/index.tsx +37 -38
  15. package/src/components/form/1-list/table-header.tsx +6 -6
  16. package/src/components/form/1-list/table.tsx +10 -12
  17. package/src/components/form/2-details/index.tsx +63 -41
  18. package/src/components/form/layout-renderer/1-row/index.tsx +12 -5
  19. package/src/components/form/layout-renderer/3-element/1-dynamic-button/index.tsx +59 -89
  20. package/src/components/form/layout-renderer/3-element/1-dynamic-button/use-button-navigate.hook.tsx +88 -0
  21. package/src/components/form/layout-renderer/3-element/1-dynamic-button/use-create-data.hook.ts +22 -23
  22. package/src/components/form/layout-renderer/3-element/1-dynamic-button/use-generate-report.hook.tsx +3 -4
  23. package/src/components/form/layout-renderer/3-element/1-dynamic-button/use-save-data.hook.ts +2 -2
  24. package/src/components/form/layout-renderer/3-element/11-breadcrumb.tsx +30 -0
  25. package/src/components/form/layout-renderer/3-element/2-field-element.tsx +4 -1
  26. package/src/components/form/layout-renderer/3-element/4-rich-text-editor.tsx +27 -2
  27. package/src/components/form/layout-renderer/3-element/6-signature.tsx +5 -1
  28. package/src/components/form/layout-renderer/3-element/8-fields-with-options.tsx +115 -74
  29. package/src/components/form/layout-renderer/3-element/9-form-data-render.tsx +50 -110
  30. package/src/components/form/layout-renderer/3-element/index.tsx +27 -9
  31. package/src/components/modals/report-filters.modal/helper-functions.ts +3 -6
  32. package/src/constants.ts +62 -45
  33. package/src/enums/form.enum.ts +5 -3
  34. package/src/enums/index.ts +9 -3
  35. package/src/{global-helpers → functions}/cookie-handler.ts +9 -10
  36. package/src/functions/forms/breadcrumb-handlers.ts +21 -0
  37. package/src/functions/forms/create-form-rules.ts +4 -1
  38. package/src/functions/forms/data-render-functions.tsx +5 -4
  39. package/src/functions/forms/extended-json-handlers.ts +56 -0
  40. package/src/functions/forms/index.ts +17 -11
  41. package/src/functions/index.ts +2 -1
  42. package/src/functions/reports/index.tsx +2 -1
  43. package/src/types/companies/site-layout/authenticated/index.tsx +23 -14
  44. package/src/types/forms/data-list/index.ts +0 -7
  45. package/src/types/forms/index.ts +1 -0
  46. package/src/types/forms/layout-elements/button.ts +11 -3
  47. package/src/types/forms/layout-elements/data-render-config.ts +1 -0
  48. package/src/types/forms/layout-elements/index.ts +12 -2
  49. package/src/types/forms/layout-elements/sanitization.ts +6 -1
  50. package/src/types/forms/relationship/index.ts +12 -1
  51. package/src/types/index.ts +2 -0
  52. package/src/functions/forms/json-handlers.ts +0 -19
  53. package/src/global-helpers/constants.ts +0 -2
  54. 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, useMemo, useState } from 'react'
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
- btnProps,
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 baseDynamicUrl = useMemo(() => constructDynamicFormHref(formName) ?? '', [formName])
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: () => displayResultMessage(),
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: () => displayResultMessage(),
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
- switch (btnProps.navigateType) {
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: CUSTOM_ERROR_MESSAGES.DataIdNotFound })
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: CUSTOM_ERROR_MESSAGES.DataIdNotFound })
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: CUSTOM_ERROR_MESSAGES.DataIdNotFound })
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
- btnProps.category !== ButtonActionCategoryEnum.SaveDataChanges
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={CUSTOM_ERROR_MESSAGES.UnavailableForPublic} />
245
+ <WarningIcon tooltip={BUTTON_CUSTOM_ERROR_MESSAGES.UnavailableForPublic} />
275
246
  )}
276
- {inPreviewMode && <WarningIcon tooltip={CUSTOM_ERROR_MESSAGES.InPreviewMode} />}
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 CUSTOM_ERROR_MESSAGES = {
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!',
@@ -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
+ }
@@ -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 { stringifyJSON } from '../../../../../functions/forms/json-handlers'
4
+ import { toMongoDbExtendedJSON } from '../../../../../functions/forms/extended-json-handlers'
5
5
  import client from '../../../../../api/client'
6
- import { constructDynamicFormHref, objectToQueryParams } from '../../../../../functions/forms'
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: stringifyJSON({ ...values, generatedPdfBlobName }),
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
- setTimeout(() => {
52
- onCreateSuccess()
53
- if (!isPublic && formName) navigate(`${constructDynamicFormHref(formName)}/${res.data}`)
54
- else if (submissionPdfConfig?.showDownloadModal) {
55
- confirmModal({
56
- title: submissionPdfConfig.successTitle ?? 'Successfully generated the PDF!',
57
- content: submissionPdfConfig.successContent ?? 'Would you like to download the PDF?',
58
- centered: true,
59
- okText: 'Download',
60
- onOk: () => {
61
- const href = `${baseServerUrl}/api/attachment/${companyKey}/${generatedPdfBlobName}`
62
- window.open(href, '_blank')
63
- window.location.reload()
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,
@@ -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 = parseJSON(res.data.data)
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({ $expr: { $eq: [`$_id`, { $toObjectId: formDataId }] } }),
53
+ match: JSON.stringify(getIdEqualsQuery('', formDataId)),
55
54
  })
56
55
  .then((res) => {
57
56
  if (res.status === 200) {
@@ -1,6 +1,6 @@
1
1
  import { useCallback } from 'react'
2
2
  import { FormLoadingModalTypeEnum } from '../../../../../enums'
3
- import { stringifyJSON } from '../../../../../functions/forms/json-handlers'
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: stringifyJSON(values),
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='off'/>
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 name={formItem.name} label={elementProps.hasNoLabel ? '' : elementProps.label} labelAlign="left">
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