form-craft-package 1.7.9-dev.1 → 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 (33) hide show
  1. package/package.json +2 -1
  2. package/src/components/common/custom-hooks/use-find-dynamic-form.hook.ts +30 -11
  3. package/src/components/common/custom-hooks/use-many-to-many-connector.hook.ts +30 -0
  4. package/src/components/common/not-found.tsx +21 -0
  5. package/src/components/companies/1-authenticated/change-password.tsx +1 -1
  6. package/src/components/form/1-list/index.tsx +4 -5
  7. package/src/components/form/1-list/table-header.tsx +5 -5
  8. package/src/components/form/1-list/table.tsx +10 -12
  9. package/src/components/form/2-details/index.tsx +53 -40
  10. package/src/components/form/layout-renderer/1-row/index.tsx +2 -1
  11. package/src/components/form/layout-renderer/3-element/1-dynamic-button/index.tsx +57 -87
  12. package/src/components/form/layout-renderer/3-element/1-dynamic-button/use-button-navigate.hook.tsx +88 -0
  13. package/src/components/form/layout-renderer/3-element/1-dynamic-button/use-create-data.hook.ts +22 -23
  14. package/src/components/form/layout-renderer/3-element/1-dynamic-button/use-generate-report.hook.tsx +3 -4
  15. package/src/components/form/layout-renderer/3-element/1-dynamic-button/use-save-data.hook.ts +2 -2
  16. package/src/components/form/layout-renderer/3-element/11-breadcrumb.tsx +3 -2
  17. package/src/components/form/layout-renderer/3-element/2-field-element.tsx +4 -1
  18. package/src/components/form/layout-renderer/3-element/8-fields-with-options.tsx +9 -12
  19. package/src/components/form/layout-renderer/3-element/9-form-data-render.tsx +48 -79
  20. package/src/components/modals/report-filters.modal/helper-functions.ts +3 -6
  21. package/src/constants.ts +7 -1
  22. package/src/enums/form.enum.ts +5 -3
  23. package/src/functions/forms/breadcrumb-handlers.ts +21 -0
  24. package/src/functions/forms/data-render-functions.tsx +1 -0
  25. package/src/functions/forms/extended-json-handlers.ts +56 -0
  26. package/src/functions/forms/index.ts +17 -11
  27. package/src/functions/reports/index.tsx +2 -1
  28. package/src/types/forms/index.ts +1 -0
  29. package/src/types/forms/layout-elements/button.ts +11 -3
  30. package/src/types/forms/layout-elements/index.ts +6 -2
  31. package/src/types/forms/layout-elements/sanitization.ts +6 -1
  32. package/src/types/forms/relationship/index.ts +12 -1
  33. package/src/functions/forms/json-handlers.ts +0 -19
@@ -1,9 +1,8 @@
1
- import { useLocation, useNavigate } from 'react-router-dom'
2
- import { useNotification } from '../../../../common/custom-hooks'
1
+ import { useNavigate } from 'react-router-dom'
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'
4
+ import { memo, useCallback, useState } from 'react'
5
5
  import { useFormPreservedItemValues } from '../../../../common/custom-hooks/use-preserved-form-items.hook'
6
- import { constructDynamicFormHref } from '../../../../../functions/forms'
7
6
  import { useDuplicateDataAction } from './use-duplicate-data.hook'
8
7
  import { useDeleteDataAction } from './use-delete-data.hook'
9
8
  import { usePublishDataAction } from './use-publish-data.hook'
@@ -16,36 +15,33 @@ import { Button_FillerPortal } from '../../../../common/button'
16
15
  import { getButtonRenderProps } from '../../../../../functions/forms/get-element-props'
17
16
  import WarningIcon from '../../../../common/warning-icon'
18
17
  import FormDataLoadingIndicatorModal from '../../../../modals/form-data-loading.modal'
19
- import { useBreadcrumb } from '../../../../common/custom-hooks/use-breadcrumb.hook'
20
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'
21
22
  import {
22
23
  IButtonElementProps,
23
24
  IFormLayoutElementConditions,
24
25
  IButtonProps_Other,
25
26
  IButtonProps_Navigate,
26
27
  } from '../../../../../types'
27
- import {
28
- ButtonActionCategoryEnum,
29
- FormLoadingModalTypeEnum,
30
- NavigateButtonCustomComponentType,
31
- NavigateButtonTypesEnum,
32
- } from '../../../../../enums'
33
28
 
34
29
  export const DynamicFormButtonRender = memo((props: IDynamicButton) => {
35
30
  const { displayStateProps = {}, formContext = {}, onCustomFunctionCall = () => {} } = props
36
31
  const { btnProps, conditions, defaultDisabled = false } = displayStateProps as IDynamicButton_DisplayStateProps
37
- const { formDataId, formRef, formName = '', parentFormJoins } = formContext as IFormContext
32
+ const { formDataId, formRef } = formContext as IFormContext
38
33
 
39
- const location = useLocation()
34
+ const { getFormById } = useFindDynamiForm()
40
35
  const navigate = useNavigate()
41
- const { updateCrumb } = useBreadcrumb()
42
36
  const { success, warning, error, confirmModal } = useNotification()
43
37
  const { isElementDisabled, isElementHidden } = useCheckElementConditions({ formRef, conditions, defaultDisabled })
44
38
  const [loading, setLoading] = useState(false)
45
39
  const [dataLoadingType, setDataLoadingType] = useState<FormLoadingModalTypeEnum | undefined>()
46
- const { inPreviewMode, isPublic = false } = useFormPreservedItemValues(formRef)
40
+ const { inPreviewMode, isPublic = false, submissionPdfConfig } = useFormPreservedItemValues(formRef)
47
41
 
48
- 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
49
45
 
50
46
  const displayResultMessage = useCallback(
51
47
  (isSuccess: boolean = true) => {
@@ -55,24 +51,49 @@ export const DynamicFormButtonRender = memo((props: IDynamicButton) => {
55
51
  [btnProps.messages, success, error],
56
52
  )
57
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
+
58
82
  const onDuplicateData = useDuplicateDataAction({
59
83
  ...formContext,
60
- onSuccess: () => displayResultMessage(),
84
+ onSuccess: () => handleSecondaryAction(),
61
85
  onError: () => displayResultMessage(false),
62
86
  onFinal: () => setLoading(false),
63
87
  })
64
88
  const onDeleteData = useDeleteDataAction({
65
89
  ...formContext,
66
- onSuccess: () => {
67
- displayResultMessage(true)
68
- navigate(baseDynamicUrl)
69
- },
90
+ onSuccess: () => handleSecondaryAction(),
70
91
  onError: () => displayResultMessage(false),
71
92
  onFinal: () => setLoading(false),
72
93
  })
73
94
  const onPublishData = usePublishDataAction({
74
95
  ...formContext,
75
- onSuccess: () => displayResultMessage(),
96
+ onSuccess: () => handleSecondaryAction(),
76
97
  onError: () => displayResultMessage(false),
77
98
  onFinal: () => setLoading(false),
78
99
  })
@@ -83,22 +104,14 @@ export const DynamicFormButtonRender = memo((props: IDynamicButton) => {
83
104
  setDataLoadingType(undefined)
84
105
  setLoading(false)
85
106
  },
86
- onSuccess: () => {
87
- setDataLoadingType(undefined)
88
- displayResultMessage()
89
- },
107
+ onSuccess: (formDataId?: string) => handleSecondaryAction(formDataId),
90
108
  onError: () => displayResultMessage(false),
91
109
  onFinal: () => setLoading(false),
92
110
  })
93
111
  const onSaveExistingData = useSaveExistingDataAction({
94
112
  ...formContext,
95
113
  setDataLoadingType,
96
- onSuccess: () => {
97
- setTimeout(() => {
98
- setDataLoadingType(undefined)
99
- displayResultMessage()
100
- }, 500)
101
- },
114
+ onSuccess: () => handleSecondaryAction(),
102
115
  onError: () => displayResultMessage(false),
103
116
  onFinal: () => setLoading(false),
104
117
  })
@@ -113,56 +126,14 @@ export const DynamicFormButtonRender = memo((props: IDynamicButton) => {
113
126
  const handleButtonClick = useCallback(async () => {
114
127
  switch (btnProps.category) {
115
128
  case ButtonActionCategoryEnum.Navigate:
116
- switch (btnProps.navigateType) {
117
- case NavigateButtonTypesEnum.NewDataPage:
118
- if (formDataId !== NEW_FORM_DATA_IDENTIFIER)
119
- updateCrumb(location.pathname, { formJoins: parentFormJoins, formDataId })
120
- navigate(`${baseDynamicUrl}/${NEW_FORM_DATA_IDENTIFIER}`)
121
- break
122
-
123
- case NavigateButtonTypesEnum.ViewDataDetails:
124
- if (formDataId) navigate(`${baseDynamicUrl}/${formDataId}`)
125
- else error({ message: CUSTOM_ERROR_MESSAGES.DataIdNotFound })
126
- break
127
-
128
- case NavigateButtonTypesEnum.ReturnToList:
129
- navigate(baseDynamicUrl)
130
- break
131
-
132
- case NavigateButtonTypesEnum.Custom:
133
- if (Array.isArray(btnProps.urlComponents)) {
134
- const validComponents = btnProps.urlComponents.filter((c) => !!c.type && !!c.value)
135
- const formData = formRef?.getFieldsValue() ?? {}
136
-
137
- const finalUrl = validComponents
138
- .map((c) => {
139
- if (c.type === NavigateButtonCustomComponentType.DynamicPath) {
140
- const data = formData[c.value] ?? ''
141
- return data.replaceAll(' ', '')
142
- }
143
-
144
- return c.value
145
- })
146
- .join('/')
147
-
148
- window.open(finalUrl, '_blank')
149
- }
150
- break
151
-
152
- case NavigateButtonTypesEnum.ReturnOneBack:
153
- default:
154
- navigate(-1)
155
- break
156
- }
157
- // window.location.href = `${baseDynamicUrl}/${NEW_FORM_DATA_IDENTIFIER}`
158
-
129
+ onButtonNavigate(btnProps)
159
130
  setLoading(false)
160
131
  break
161
132
 
162
133
  case ButtonActionCategoryEnum.DuplicateData:
163
134
  if (formDataId) await onDuplicateData()
164
135
  else {
165
- error({ message: CUSTOM_ERROR_MESSAGES.DataIdNotFound })
136
+ error({ message: BUTTON_CUSTOM_ERROR_MESSAGES.DataIdNotFound })
166
137
  setLoading(false)
167
138
  }
168
139
  break
@@ -170,7 +141,7 @@ export const DynamicFormButtonRender = memo((props: IDynamicButton) => {
170
141
  case ButtonActionCategoryEnum.DeleteData:
171
142
  if (formDataId) await onDeleteData()
172
143
  else {
173
- error({ message: CUSTOM_ERROR_MESSAGES.DataIdNotFound })
144
+ error({ message: BUTTON_CUSTOM_ERROR_MESSAGES.DataIdNotFound })
174
145
  setLoading(false)
175
146
  }
176
147
  break
@@ -178,7 +149,7 @@ export const DynamicFormButtonRender = memo((props: IDynamicButton) => {
178
149
  case ButtonActionCategoryEnum.PublishDataChanges:
179
150
  if (formDataId) await onPublishData()
180
151
  else {
181
- error({ message: CUSTOM_ERROR_MESSAGES.DataIdNotFound })
152
+ error({ message: BUTTON_CUSTOM_ERROR_MESSAGES.DataIdNotFound })
182
153
  setLoading(false)
183
154
  }
184
155
  break
@@ -187,7 +158,7 @@ export const DynamicFormButtonRender = memo((props: IDynamicButton) => {
187
158
  await onSaveSignature()
188
159
 
189
160
  if (formDataId === NEW_FORM_DATA_IDENTIFIER) {
190
- if (isPublic) onGeneratePdfProof()
161
+ if (isPublic && submissionPdfConfig) onGeneratePdfProof()
191
162
  else onCreateNewData()
192
163
  } else onSaveExistingData()
193
164
 
@@ -222,12 +193,10 @@ export const DynamicFormButtonRender = memo((props: IDynamicButton) => {
222
193
  break
223
194
  }
224
195
  }, [
225
- location.pathname,
226
- parentFormJoins,
227
196
  btnProps,
228
- baseDynamicUrl,
229
197
  formDataId,
230
198
  isPublic,
199
+ submissionPdfConfig,
231
200
  onCustomFunctionCall,
232
201
  onDuplicateData,
233
202
  onDeleteData,
@@ -236,6 +205,7 @@ export const DynamicFormButtonRender = memo((props: IDynamicButton) => {
236
205
  onGeneratePdfProof,
237
206
  onSaveExistingData,
238
207
  onGenerateReport,
208
+ onButtonNavigate,
239
209
  setDataLoadingType,
240
210
  ])
241
211
 
@@ -253,7 +223,7 @@ export const DynamicFormButtonRender = memo((props: IDynamicButton) => {
253
223
  if (
254
224
  isPublic &&
255
225
  formDataId === NEW_FORM_DATA_IDENTIFIER &&
256
- btnProps.category !== ButtonActionCategoryEnum.SaveDataChanges
226
+ [ButtonActionCategoryEnum.SaveDataChanges, ButtonActionCategoryEnum.Navigate].includes(btnProps.category)
257
227
  )
258
228
  return
259
229
  setLoading(true)
@@ -272,9 +242,9 @@ export const DynamicFormButtonRender = memo((props: IDynamicButton) => {
272
242
  {isPublic &&
273
243
  formDataId === NEW_FORM_DATA_IDENTIFIER &&
274
244
  btnProps.category !== ButtonActionCategoryEnum.SaveDataChanges && (
275
- <WarningIcon tooltip={CUSTOM_ERROR_MESSAGES.UnavailableForPublic} />
245
+ <WarningIcon tooltip={BUTTON_CUSTOM_ERROR_MESSAGES.UnavailableForPublic} />
276
246
  )}
277
- {inPreviewMode && <WarningIcon tooltip={CUSTOM_ERROR_MESSAGES.InPreviewMode} />}
247
+ {inPreviewMode && <WarningIcon tooltip={BUTTON_CUSTOM_ERROR_MESSAGES.InPreviewMode} />}
278
248
  {btnProps.label}
279
249
  </Button_FillerPortal>
280
250
  {!!dataLoadingType && (
@@ -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
  }
@@ -2,6 +2,7 @@ import { useBreadcrumb } from '../../../common/custom-hooks/use-breadcrumb.hook'
2
2
  import { Button_FillerPortal } from '../../../common/button'
3
3
  import { useNavigate } from 'react-router-dom'
4
4
  import { FaChevronRight } from 'react-icons/fa6'
5
+ import React from 'react'
5
6
 
6
7
  export default function LayoutRenderer_Breadcrumb() {
7
8
  const navigate = useNavigate()
@@ -10,7 +11,7 @@ export default function LayoutRenderer_Breadcrumb() {
10
11
  return (
11
12
  <div className="flex items-center text-12">
12
13
  {breadcrumbs.map((bc, bcIdx) => (
13
- <>
14
+ <React.Fragment key={bcIdx}>
14
15
  <Button_FillerPortal
15
16
  link
16
17
  disabled={breadcrumbs.length - 1 === bcIdx}
@@ -22,7 +23,7 @@ export default function LayoutRenderer_Breadcrumb() {
22
23
  <span className="font-normal italic">{bc.label}</span>
23
24
  </Button_FillerPortal>
24
25
  {breadcrumbs.length - 1 !== bcIdx && <FaChevronRight className="text-primary" />}
25
- </>
26
+ </React.Fragment>
26
27
  ))}
27
28
  </div>
28
29
  )
@@ -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
 
@@ -18,6 +18,7 @@ import {
18
18
  IRadioElement,
19
19
  ISelectElement,
20
20
  } from '../../../../types'
21
+ import { getIdEqualsQuery } from '../../../../functions'
21
22
 
22
23
  export default function LayoutRenderer_FieldsWithOptions({
23
24
  formContext,
@@ -31,6 +32,7 @@ export default function LayoutRenderer_FieldsWithOptions({
31
32
  const cancelTokenRef = useRef<CancelToken | undefined>(undefined)
32
33
  const { breadcrumbs } = useBreadcrumb()
33
34
  const [loading, setLoading] = useState(true)
35
+ const isManyToManyPageRef = useRef(false)
34
36
 
35
37
  const { inPreviewMode } = useFormPreservedItemValues(formRef)
36
38
 
@@ -38,6 +40,9 @@ export default function LayoutRenderer_FieldsWithOptions({
38
40
  if (breadcrumbs.length < 2) return { formJoins: [], formDataId: '' }
39
41
 
40
42
  const breadcrumb = breadcrumbs[breadcrumbs.length - 2]
43
+
44
+ if (!!breadcrumb.manyToManyRelInfo) isManyToManyPageRef.current = true
45
+
41
46
  return { formJoins: breadcrumb.formJoins ?? [], formDataId: breadcrumb.formDataId ?? '' }
42
47
  }, [breadcrumbs])
43
48
 
@@ -52,16 +57,8 @@ export default function LayoutRenderer_FieldsWithOptions({
52
57
  .post(`/api/report/data/${selectedFormId}`, {
53
58
  joins: filteredJoins,
54
59
  match: JSON.stringify(
55
- parentRelInfo.formDataId
56
- ? {
57
- $expr: {
58
- $eq: [
59
- lastJoin?.alias ? `$${lastJoin.alias}._id` : '$_id',
60
- { $toObjectId: parentRelInfo.formDataId },
61
- ],
62
- },
63
- DeletedDate: null,
64
- }
60
+ parentRelInfo.formDataId && !isManyToManyPageRef.current
61
+ ? { DeletedDate: null, ...getIdEqualsQuery(lastJoin?.alias, parentRelInfo.formDataId) }
65
62
  : { DeletedDate: null },
66
63
  ),
67
64
  project: JSON.stringify({ value: '$_id', label: `$Data.${selectedFormField}` }),
@@ -102,9 +99,9 @@ export default function LayoutRenderer_FieldsWithOptions({
102
99
  .post(`/api/report/data/${lastPath.formId}`, {
103
100
  joins: slicedJoinsForLastPath,
104
101
  match: JSON.stringify(
105
- formDataId && lastJoin
102
+ formDataId && lastJoin && !isManyToManyPageRef.current
106
103
  ? {
107
- $expr: { $eq: [`$${lastJoin.alias}._id`, { $toObjectId: formDataId }] },
104
+ ...getIdEqualsQuery(lastJoin.alias, formDataId),
108
105
  DeletedDate: null,
109
106
  }
110
107
  : { DeletedDate: null },