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
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "form-craft-package",
3
- "version": "1.7.9-dev.1",
3
+ "version": "1.7.9-dev.2",
4
4
  "main": "index.ts",
5
5
  "scripts": {
6
6
  "test": "echo \"Error: no test specified\" && exit 1",
@@ -14,6 +14,7 @@
14
14
  "@types/react-google-recaptcha": "^2.1.9",
15
15
  "@types/react-signature-canvas": "^1.0.7",
16
16
  "ajv": "^8.17.1",
17
+ "bson": "^4.7.2",
17
18
  "pdfmake": "^0.2.18",
18
19
  "qs": "^6.14.0",
19
20
  "react-google-recaptcha": "^3.1.0",
@@ -1,27 +1,46 @@
1
- import { useState, useEffect } from 'react'
1
+ import { useState, useEffect, useCallback } from 'react'
2
2
  import { useParams } from 'react-router-dom'
3
3
  import { IDynamicForm } from '../../../types'
4
4
  import { LOCAL_STORAGE_KEYS_ENUM } from '../../../enums'
5
5
 
6
6
  export const useFindDynamiForm = () => {
7
7
  const { formName } = useParams<{ formName: string }>()
8
- const [foundItem, setFoundItem] = useState<IDynamicForm | undefined | null>(undefined)
8
+ const [foundItem, setFoundItem] = useState<IDynamicForm | null | undefined>(undefined)
9
9
 
10
10
  useEffect(() => {
11
11
  if (!formName) return
12
12
 
13
13
  try {
14
- const storedData = localStorage.getItem(LOCAL_STORAGE_KEYS_ENUM.DynamicForms)
15
- if (storedData) {
16
- const parsedData: IDynamicForm[] = JSON.parse(storedData)
17
- const item = parsedData.find((entry) => entry.name.split(' ').join('-').toLocaleLowerCase() === formName)
18
-
19
- setFoundItem(item ?? null)
14
+ const stored = localStorage.getItem(LOCAL_STORAGE_KEYS_ENUM.DynamicForms)
15
+ if (stored) {
16
+ const list: IDynamicForm[] = JSON.parse(stored)
17
+ const match = list.find((entry) => entry.name.split(' ').join('-').toLowerCase() === formName)
18
+ setFoundItem(match ?? null)
19
+ } else {
20
+ setFoundItem(null)
20
21
  }
21
- } catch (error) {
22
- console.error('Error reading or parsing localStorage data', error)
22
+ } catch (err) {
23
+ console.error('Error reading or parsing DynamicForms:', err)
24
+ setFoundItem(null)
23
25
  }
24
26
  }, [formName])
25
27
 
26
- return foundItem?.name?.split(' ').join('-').toLowerCase() === formName ? foundItem : undefined
28
+ const getFormById = useCallback((id: number): IDynamicForm | undefined => {
29
+ if (!id) return
30
+
31
+ try {
32
+ const stored = localStorage.getItem(LOCAL_STORAGE_KEYS_ENUM.DynamicForms)
33
+ if (!stored) return undefined
34
+
35
+ const list: IDynamicForm[] = JSON.parse(stored)
36
+ return list.find((entry) => entry.id === id)
37
+ } catch (err) {
38
+ console.error('Error in getFormById reading DynamicForms:', err)
39
+ return
40
+ }
41
+ }, [])
42
+
43
+ const formInfo = foundItem && foundItem.name.split(' ').join('-').toLowerCase() === formName ? foundItem : undefined
44
+
45
+ return { formInfo, getFormById }
27
46
  }
@@ -0,0 +1,30 @@
1
+ import { useEffect, useMemo } from 'react'
2
+ import { useBreadcrumb } from './use-breadcrumb.hook'
3
+ import { FormInstance } from 'antd'
4
+ import { getForeignKeysFromBreadcrumb } from '../../../functions/forms/breadcrumb-handlers'
5
+
6
+ export const useManyToManyConnector = (formRef: FormInstance) => {
7
+ const { breadcrumbs } = useBreadcrumb()
8
+
9
+ const prevPageBreadcrumb = useMemo(() => {
10
+ if (breadcrumbs.length > 2) {
11
+ const prevPageBreadcrumb = breadcrumbs[breadcrumbs.length - 2]
12
+ const { formDataId, formJoins, manyToManyRelInfo } = prevPageBreadcrumb
13
+ if (!formDataId || !formJoins || !Array.isArray(formJoins) || !manyToManyRelInfo) return
14
+
15
+ return prevPageBreadcrumb
16
+ }
17
+
18
+ return
19
+ }, [breadcrumbs])
20
+
21
+ useEffect(() => {
22
+ if (!prevPageBreadcrumb) return
23
+
24
+ const { foreignKey, foreignKey2 } = getForeignKeysFromBreadcrumb(prevPageBreadcrumb)
25
+
26
+ if (foreignKey && foreignKey2) formRef.setFieldValue(foreignKey, prevPageBreadcrumb.formDataId)
27
+ }, [prevPageBreadcrumb, formRef])
28
+
29
+ return
30
+ }
@@ -0,0 +1,21 @@
1
+ import { Result } from 'antd'
2
+ import { Button_FillerPortal } from './button'
3
+ import { useNavigate } from 'react-router-dom'
4
+
5
+ export default function NotFound() {
6
+ const navigate = useNavigate()
7
+ return (
8
+ <Result
9
+ status="404"
10
+ title="404"
11
+ subTitle="Sorry, the page you visited does not exist."
12
+ extra={
13
+ <div className="grid justify-center grid-cols-[200px]">
14
+ <Button_FillerPortal primary onClick={() => navigate('/d')}>
15
+ Go Back
16
+ </Button_FillerPortal>
17
+ </div>
18
+ }
19
+ />
20
+ )
21
+ }
@@ -43,7 +43,7 @@ export const ChangePassword = () => {
43
43
  }
44
44
 
45
45
  // Validate that the confirm password matches the new password
46
- const validateConfirmPassword = (_, value: string) => {
46
+ const validateConfirmPassword = (_: any, value: string) => {
47
47
  if (!value || value === '') {
48
48
  return Promise.resolve()
49
49
  }
@@ -4,9 +4,9 @@ import FormDataListSkeleton_Table from '../../common/loading-skeletons/table'
4
4
  import { ICustomFunctionCall } from '../layout-renderer/3-element/1-dynamic-button'
5
5
  import FormDataListTableComponent, { IDataListLayoutConfig } from './table'
6
6
  import { IFormJoin, IFormDataListData, IFormDataListConfig } from '../../../types'
7
+ import { getProjectionKey } from '../../../functions'
7
8
 
8
9
  function FormDataListComponent({
9
- formName,
10
10
  formId,
11
11
  userId,
12
12
  companyKey,
@@ -23,7 +23,7 @@ function FormDataListComponent({
23
23
  const defaultFilterReqDataRef = useRef<IDataListReqData | undefined>(undefined)
24
24
 
25
25
  const attachmentBaseUrl = useMemo(() => `${baseServerUrl}/api/attachment/${companyKey}`, [baseServerUrl, companyKey])
26
- const headerLayoutContext = { formId, userId, attachmentBaseUrl, formName, companyKey, onCustomFunctionCall }
26
+ const headerLayoutContext = { formId, userId, attachmentBaseUrl, companyKey, onCustomFunctionCall }
27
27
 
28
28
  useEffect(() => {
29
29
  setLoadings({ initial: true, data: true })
@@ -45,7 +45,7 @@ function FormDataListComponent({
45
45
 
46
46
  if (Array.isArray(columns))
47
47
  dataProjectRef.current = columns.reduce(
48
- (curr, c) => (c.key ? { ...curr, [c.key.replace(/\./g, '_')]: `$${c.key}` } : curr),
48
+ (curr, c) => (c.key ? { ...curr, [getProjectionKey(c.key)]: `$${c.key}` } : curr),
49
49
  {},
50
50
  )
51
51
  if (Array.isArray(formJoins)) formJoinsRef.current = formJoins
@@ -69,7 +69,7 @@ function FormDataListComponent({
69
69
  configForFormId: formId,
70
70
  dataListConfig: {
71
71
  ...dataListConfig,
72
- columns: columns.map((c) => (c.key ? { ...c, key: c.key.replace(/\./g, '_') } : c)),
72
+ columns: columns.map((c) => (c.key ? { ...c, key: getProjectionKey(c.key) } : c)),
73
73
  },
74
74
  })
75
75
  }
@@ -127,7 +127,6 @@ function FormDataListComponent({
127
127
  export { FormDataListComponent }
128
128
 
129
129
  type IFormDataListComponent = {
130
- formName?: string
131
130
  baseServerUrl?: string
132
131
  companyKey?: string
133
132
  formId?: number
@@ -1,7 +1,7 @@
1
1
  import { Form } from 'antd'
2
2
  import { ReactNode, useCallback, useEffect, useMemo, useRef, useState } from 'react'
3
3
  import dayjs from 'dayjs'
4
- import { extractFiltersFromLayout } from '../../../functions/forms'
4
+ import { extractFiltersFromLayout, getIdEqualsQuery } from '../../../functions/forms'
5
5
  import { DeviceBreakpointEnum, FilterConfigTypeEnum, FormPreservedItemKeys } from '../../../enums'
6
6
  import { LayoutRendererRow } from '../layout-renderer/1-row'
7
7
  import { VALUE_REPLACEMENT_PLACEHOLDER } from '../../../constants'
@@ -26,7 +26,7 @@ export default function FormDataListHeaderComponent({
26
26
  }: IFormDataListHeaderComponent) {
27
27
  const [filtersFormRef] = Form.useForm()
28
28
  const [filterConfigs, setFilterConfigs] = useState<IFilterNested>({})
29
- const { userId, formId, formName, parentFormJoins, formDataId, onCustomFunctionCall } = headerLayoutContext
29
+ const { userId, formId, parentFormJoins, formDataId, manyToManyRelInfo, onCustomFunctionCall } = headerLayoutContext
30
30
  const isInitialFetchRef = useRef(true)
31
31
 
32
32
  const currentBreakpoint = useGetCurrentBreakpoint()
@@ -108,8 +108,8 @@ export default function FormDataListHeaderComponent({
108
108
  }, [filterValues])
109
109
 
110
110
  const formContext = useMemo(
111
- () => ({ formRef: filtersFormRef, formName, formId, formDataId, parentFormJoins }),
112
- [filtersFormRef, formName, formId, formDataId, parentFormJoins],
111
+ () => ({ formRef: filtersFormRef, formId, formDataId, parentFormJoins, manyToManyRelInfo }),
112
+ [filtersFormRef, formId, formDataId, parentFormJoins, manyToManyRelInfo],
113
113
  )
114
114
 
115
115
  return (
@@ -172,7 +172,7 @@ const handleFilterValues = async (
172
172
 
173
173
  return {
174
174
  customFilter: {},
175
- dynamicFilter: JSON.stringify({ $expr: { $eq: [`$${lastRel.formId}._id`, { $toObjectId: value }] } }),
175
+ dynamicFilter: JSON.stringify(getIdEqualsQuery(lastRel.formId, value)),
176
176
  }
177
177
  }
178
178
  }
@@ -1,7 +1,7 @@
1
1
  import { useCallback, useEffect, useMemo, useState } from 'react'
2
2
  import FormDataListHeaderComponent from './table-header'
3
3
  import useDebounced from '../../common/custom-hooks/use-debounce.hook'
4
- import { FormInstance, Table } from 'antd'
4
+ import { Table } from 'antd'
5
5
  import { ICustomFunctionCall } from '../layout-renderer/3-element/1-dynamic-button'
6
6
  import { SorterResult } from 'antd/es/table/interface'
7
7
  import { generateTableColumns } from '../../../functions/forms'
@@ -9,8 +9,9 @@ import { useLocation } from 'react-router-dom'
9
9
  import useGetCurrentBreakpoint from '../../common/custom-hooks/use-window-width.hook'
10
10
  import { DeviceBreakpointEnum, FormDataListViewTypeEnum, MongoDbSortOrderEnum } from '../../../enums'
11
11
  import { IDataListReqData } from '.'
12
- import { IFormDataListConfig, IFormDataListData, IFormDataTableColumn, IFormJoin } from '../../../types'
12
+ import { IFormDataListConfig, IFormDataListData, IFormDataTableColumn } from '../../../types'
13
13
  import { processOptions } from '../../../functions/forms/get-data-list-option-value'
14
+ import { IFormContext } from '../layout-renderer/1-row'
14
15
 
15
16
  export default function FormDataListTableComponent({
16
17
  layoutsConfigs,
@@ -33,7 +34,9 @@ export default function FormDataListTableComponent({
33
34
  )
34
35
  const [otherConfigs, setOtherConfigs] = useState<Partial<IFormDataListConfig> | undefined>(undefined)
35
36
 
36
- const { formId } = headerLayoutContext
37
+ useEffect(() => {
38
+ setFilterReqData(undefined)
39
+ }, [location.pathname])
37
40
 
38
41
  useEffect(() => {
39
42
  if (Array.isArray(layoutsConfigs?.dataListConfig.columns)) {
@@ -64,7 +67,7 @@ export default function FormDataListTableComponent({
64
67
  async (layoutsConfigs: IDataListLayoutConfig) => {
65
68
  const { configForFormId, dataListConfig } = layoutsConfigs!
66
69
 
67
- if (configForFormId !== formId) {
70
+ if (configForFormId !== headerLayoutContext.formId) {
68
71
  setLoading(false)
69
72
  return
70
73
  }
@@ -82,7 +85,7 @@ export default function FormDataListTableComponent({
82
85
 
83
86
  setLoading(false)
84
87
  },
85
- [formId, headerLayoutContext, layoutsConfigs],
88
+ [headerLayoutContext, layoutsConfigs],
86
89
  )
87
90
 
88
91
  useEffect(() => {
@@ -170,14 +173,9 @@ type IFormDataListTableComponent = {
170
173
 
171
174
  export type IDataListHeaderLayoutContext = {
172
175
  userId?: string | number
173
- formId?: number
174
176
  attachmentBaseUrl?: string
175
- formDataId?: string
176
- formName?: string
177
- parentFormJoins?: IFormJoin[]
178
- formRef?: FormInstance
179
- companyKey?: string
180
- } & ICustomFunctionCall
177
+ } & ICustomFunctionCall &
178
+ IFormContext
181
179
 
182
180
  export type IDataListLayoutConfig =
183
181
  | {
@@ -2,17 +2,17 @@ import { Form } from 'antd'
2
2
  import { useCallback, useEffect, useMemo, useState } from 'react'
3
3
  import { IDndLayoutStructure_Responsive, IFormSchema } from '../../../types'
4
4
  import { NEW_FORM_DATA_IDENTIFIER } from '../../../constants'
5
- import { parseJSON } from '../../../functions/forms/json-handlers'
6
5
  import { LayoutRendererRow } from '../layout-renderer/1-row'
7
6
  import { DynamicFormButtonRender, ICustomFunctionCall } from '../layout-renderer/3-element/1-dynamic-button'
8
7
  import FormDataListSkeleton_Details from '../../common/loading-skeletons/details'
9
8
  import { useLocation } from 'react-router-dom'
10
- import { extractDateFields, queryParamsToObject } from '../../../functions/forms'
11
- import { DeviceBreakpointEnum, FormPreservedItemKeys } from '../../../enums'
12
- import dayjs from 'dayjs'
9
+ import { fromMongoDbExtendedJSON, isValidMongoDbId, queryParamsToObject } from '../../../functions/forms'
10
+ import { DeviceBreakpointEnum, FormPreservedItemKeys, LOCAL_STORAGE_KEYS_ENUM } from '../../../enums'
13
11
  import useGetCurrentBreakpoint from '../../common/custom-hooks/use-window-width.hook'
14
12
  import client from '../../../api/client'
15
13
  import { useBreadcrumb } from '../../common/custom-hooks/use-breadcrumb.hook'
14
+ import NotFound from '../../common/not-found'
15
+ import { useManyToManyConnector } from '../../common/custom-hooks/use-many-to-many-connector.hook'
16
16
 
17
17
  export default function FormDataDetailsComponent({
18
18
  isPublic,
@@ -30,13 +30,28 @@ export default function FormDataDetailsComponent({
30
30
  const [formDataRef] = Form.useForm()
31
31
  const [loadings, setLoadings] = useState({ layout: true, data: true })
32
32
  const [layoutConfig, setLayoutConfig] = useState<IDndLayoutStructure_Responsive>({ elements: {}, layouts: {} })
33
+ const [isNotFound, setIsNotFound] = useState(false)
34
+ useManyToManyConnector(formDataRef)
33
35
 
34
36
  const currentBreakpoint = useGetCurrentBreakpoint()
35
37
 
38
+ useEffect(() => {
39
+ // for public forms, setting the details form into localStorage, so that buttons work correctly
40
+ if (isPublic)
41
+ localStorage.setItem(LOCAL_STORAGE_KEYS_ENUM.DynamicForms, JSON.stringify([{ id: formId, name: formName }]))
42
+ }, [isPublic, formId, formName])
43
+
44
+ useEffect(() => {
45
+ if (!formDataId || !isValidMongoDbId(formDataId)) setIsNotFound(true)
46
+ }, [formDataId])
47
+
36
48
  useEffect(() => {
37
49
  const isNewDataPage = formDataId === NEW_FORM_DATA_IDENTIFIER
38
50
  const splittedFormName = formName?.split(' ')?.[0] ?? ''
39
- push({ label: isNewDataPage ? `New ${splittedFormName.toLowerCase()}` : `${splittedFormName} details`, href: location.pathname })
51
+ push({
52
+ label: isNewDataPage ? `New ${splittedFormName.toLowerCase()}` : `${splittedFormName} details`,
53
+ href: location.pathname,
54
+ })
40
55
  }, [location.pathname, formName, formDataId])
41
56
 
42
57
  const layout = useMemo(
@@ -69,8 +84,9 @@ export default function FormDataDetailsComponent({
69
84
  }, [formDataRef, baseServerUrl, isPublic])
70
85
 
71
86
  const fetchFormData = useCallback(
72
- (dFormId?: number, dateFields: string[] = []) => {
73
- if (formDataId === NEW_FORM_DATA_IDENTIFIER) setLoadings((c) => ({ ...c, data: false }))
87
+ (dFormId?: number) => {
88
+ if (formDataId === NEW_FORM_DATA_IDENTIFIER || !isValidMongoDbId(formDataId))
89
+ setLoadings((c) => ({ ...c, data: false }))
74
90
  else {
75
91
  if (!dFormId) {
76
92
  console.error('Form ID is required to fetch form data')
@@ -81,18 +97,14 @@ export default function FormDataDetailsComponent({
81
97
  .then((res) => {
82
98
  if (res.status === 200) {
83
99
  const { data: jsonData, ...restFormData } = res.data
84
- let parsedFormData = parseJSON(jsonData)
100
+ let parsedFormData = JSON.parse(jsonData)
85
101
  if (parsedFormData) {
86
- const fieldsValue: { [key: string]: any } = parsedFormData
87
- if (dateFields.length > 0)
88
- Object.entries(parsedFormData).map(([field, value]) => {
89
- if (dateFields.includes(field) && value) fieldsValue[field] = dayjs(value)
90
- else fieldsValue[field] = value
91
- })
102
+ const fieldsValue: { [key: string]: any } = fromMongoDbExtendedJSON(parsedFormData)
92
103
  formDataRef.setFieldsValue({ ...restFormData, ...fieldsValue })
104
+
105
+ console.log('FORM DATA FETCH (PARSED)', fieldsValue)
93
106
  }
94
- console.log('FORM DATA FETCH (PARSED)', parsedFormData)
95
- }
107
+ } else setIsNotFound(true)
96
108
  })
97
109
  .finally(() => setLoadings((c) => ({ ...c, data: false })))
98
110
  }
@@ -107,7 +119,7 @@ export default function FormDataDetailsComponent({
107
119
  .get(endpoint)
108
120
  .then((res) => {
109
121
  if (res.status === 200) {
110
- const parsedData: IFormSchema | null = parseJSON(res.data.data)
122
+ const parsedData: IFormSchema | null = JSON.parse(res.data.data)
111
123
  if (parsedData) {
112
124
  console.log('FORM LAYOUT FETCH (PARSED)', parsedData)
113
125
  const { layouts: rawLayouts, elements } = parsedData.detailsConfig
@@ -119,11 +131,8 @@ export default function FormDataDetailsComponent({
119
131
  parsedData.generateConfig.submissionPdf,
120
132
  )
121
133
  setLoadings((c) => ({ ...c, data: false }))
122
- } else {
123
- const dateFields = extractDateFields(elements)
134
+ } else fetchFormData(formId)
124
135
 
125
- fetchFormData(formId, dateFields)
126
- }
127
136
  setLayoutConfig({ elements, layouts: rawLayouts })
128
137
  }
129
138
  }
@@ -133,30 +142,34 @@ export default function FormDataDetailsComponent({
133
142
  }, [formId, formKey, isPublic, formDataRef, fetchFormData])
134
143
 
135
144
  const formContext = useMemo(
136
- () => ({ formId, formKey, formDataId, formRef: formDataRef, formName, companyKey }),
137
- [formDataId, formDataRef, formName, formId, formKey, companyKey],
145
+ () => ({ formId, formKey, formDataId, formRef: formDataRef, companyKey }),
146
+ [formDataId, formDataRef, formId, formKey, companyKey],
138
147
  )
139
148
 
140
149
  if (loadings.layout || loadings.data) return <FormDataListSkeleton_Details />
141
150
 
151
+ if (isNotFound) return <NotFound />
152
+
142
153
  return (
143
- <Form layout="vertical" name="dynamic_form_data_form" form={formDataRef}>
144
- {layout.map((row, rowIdx) => (
145
- <LayoutRendererRow
146
- key={rowIdx}
147
- rowData={row}
148
- formContext={formContext}
149
- elements={layoutConfig.elements}
150
- renderButton={(btnProps, conditions) => (
151
- <DynamicFormButtonRender
152
- displayStateProps={{ btnProps, conditions, defaultDisabled: true }}
153
- formContext={formContext}
154
- onCustomFunctionCall={onCustomFunctionCall}
155
- />
156
- )}
157
- />
158
- ))}
159
- </Form>
154
+ <>
155
+ <Form layout="vertical" name="dynamic_form_data_form" form={formDataRef}>
156
+ {layout.map((row, rowIdx) => (
157
+ <LayoutRendererRow
158
+ key={rowIdx}
159
+ rowData={row}
160
+ formContext={formContext}
161
+ elements={layoutConfig.elements}
162
+ renderButton={(btnProps, conditions) => (
163
+ <DynamicFormButtonRender
164
+ displayStateProps={{ btnProps, conditions, defaultDisabled: true }}
165
+ formContext={formContext}
166
+ onCustomFunctionCall={onCustomFunctionCall}
167
+ />
168
+ )}
169
+ />
170
+ ))}
171
+ </Form>
172
+ </>
160
173
  )
161
174
  }
162
175
 
@@ -99,8 +99,9 @@ export interface IFormContext {
99
99
  formDataId?: string
100
100
  formRef?: FormInstance
101
101
  companyKey?: string
102
- formName?: string
102
+ formName?: string // only passed in DynamicButton component
103
103
  formKey?: string
104
104
  formId?: number
105
105
  parentFormJoins?: IFormJoin[]
106
+ manyToManyRelInfo?: { middleFormId: number; currentFormId: number; otherFormId: number } | null
106
107
  }