form-craft-package 1.5.1 → 1.5.3
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 +1 -1
- package/src/components/common/custom-hooks/use-preserved-form-items.hook.ts +0 -2
- package/src/components/form/1-list/table.tsx +5 -4
- package/src/components/form/2-details/index.tsx +3 -4
- package/src/components/form/layout-renderer/1-row/index.tsx +37 -18
- package/src/components/form/layout-renderer/2-col/index.tsx +18 -4
- package/src/components/form/layout-renderer/3-element/1-dynamic-button/index.tsx +8 -2
- package/src/components/form/layout-renderer/3-element/1-dynamic-button/use-create-data.hook.ts +6 -6
- package/src/components/form/layout-renderer/3-element/1-dynamic-button/use-delete-data.hook.ts +2 -4
- package/src/components/form/layout-renderer/3-element/1-dynamic-button/use-generate-report.hook.tsx +114 -42
- package/src/components/form/layout-renderer/3-element/3-read-field-data.tsx +120 -3
- package/src/components/form/layout-renderer/3-element/9-form-data-render.tsx +13 -5
- package/src/components/form/layout-renderer/3-element/index.tsx +7 -3
- package/src/enums.ts +20 -1
- package/src/functions/data-render-functions.tsx +11 -5
- package/src/types/companies/site-layout/authenticated/index.tsx +1 -5
- package/src/types/generate/index.ts +2 -1
- package/src/types/layout-elements/evaluation-config.ts +86 -0
- package/src/types/layout-elements/index.ts +4 -4
package/package.json
CHANGED
|
@@ -10,7 +10,6 @@ export const useFormPreservedItemValues = (formRef?: FormInstance): IFormValues
|
|
|
10
10
|
const isGeneratingPdf = Form.useWatch(FormPreservedItemKeys.IsGeneratingPDF, { form: formRef, preserve: true })
|
|
11
11
|
const hasSignature = Form.useWatch(FormPreservedItemKeys.HasSignature, { form: formRef, preserve: true })
|
|
12
12
|
const inPreviewMode = Form.useWatch(FormPreservedItemKeys.InPreviewMode, { form: formRef, preserve: true })
|
|
13
|
-
const templateReports = Form.useWatch(FormPreservedItemKeys.TemplateReports, { form: formRef, preserve: true })
|
|
14
13
|
const isPublic = Form.useWatch(FormPreservedItemKeys.IsPublic, { form: formRef, preserve: true })
|
|
15
14
|
const submissionPdfConfig = Form.useWatch(FormPreservedItemKeys.SubmissionPdfConfig, {
|
|
16
15
|
form: formRef,
|
|
@@ -27,7 +26,6 @@ export const useFormPreservedItemValues = (formRef?: FormInstance): IFormValues
|
|
|
27
26
|
[FormPreservedItemKeys.IsGeneratingPDF]: isGeneratingPdf,
|
|
28
27
|
[FormPreservedItemKeys.HasSignature]: hasSignature,
|
|
29
28
|
[FormPreservedItemKeys.InPreviewMode]: inPreviewMode,
|
|
30
|
-
[FormPreservedItemKeys.TemplateReports]: templateReports,
|
|
31
29
|
[FormPreservedItemKeys.IsPublic]: isPublic,
|
|
32
30
|
}
|
|
33
31
|
}
|
|
@@ -2,7 +2,7 @@ import { useCallback, useEffect, useRef, useState } from 'react'
|
|
|
2
2
|
import { IDataListParentInfo, IFormData, IFormDataListConfig, IFormLayoutRow, ITableColumn } from '../../../types'
|
|
3
3
|
import FormDataListHeaderComponent from './table-header'
|
|
4
4
|
import useDebounced from '../../common/custom-hooks/use-debounce.hook'
|
|
5
|
-
import { Table } from 'antd'
|
|
5
|
+
import { FormInstance, Table } from 'antd'
|
|
6
6
|
import { FilterConfigTypeEnum, FormDataListViewTypeEnum, MongoDbSortOrderEnum } from '../../../enums'
|
|
7
7
|
import { ICustomFunctionCall } from '../layout-renderer/3-element/1-dynamic-button'
|
|
8
8
|
import { SorterResult } from 'antd/es/table/interface'
|
|
@@ -39,7 +39,7 @@ export default function FormDataListTableComponent({
|
|
|
39
39
|
pagination: DEFAULT_PAGINATION,
|
|
40
40
|
})
|
|
41
41
|
|
|
42
|
-
const { attachmentBaseUrl, userId, formId, formName } = constantValues
|
|
42
|
+
const { attachmentBaseUrl, userId, formId, formName, formRef } = constantValues
|
|
43
43
|
|
|
44
44
|
useEffect(() => {
|
|
45
45
|
setIsHandlingSchema(true)
|
|
@@ -68,7 +68,7 @@ export default function FormDataListTableComponent({
|
|
|
68
68
|
if (elementsWithOptions_key.length > 0)
|
|
69
69
|
optionKeyValuePair = await extractElementsOptions(layout, elementsWithOptions_key)
|
|
70
70
|
|
|
71
|
-
setTableColumns(generateTableColumns(elements, optionKeyValuePair, { attachmentBaseUrl, formName }))
|
|
71
|
+
setTableColumns(generateTableColumns(elements, optionKeyValuePair, { attachmentBaseUrl, formName, formRef }))
|
|
72
72
|
setHeaderLayout(header.layout)
|
|
73
73
|
setOtherConfigs({ ...restConfigs, hasNoPagination: pagination?.hasNoPagination ?? false })
|
|
74
74
|
|
|
@@ -95,7 +95,7 @@ export default function FormDataListTableComponent({
|
|
|
95
95
|
setIsHandlingSchema(false)
|
|
96
96
|
isFinishedHandlingRef.current = true
|
|
97
97
|
},
|
|
98
|
-
[attachmentBaseUrl, formName, userId, formId],
|
|
98
|
+
[attachmentBaseUrl, formName, userId, formId, formRef],
|
|
99
99
|
)
|
|
100
100
|
|
|
101
101
|
useEffect(() => {
|
|
@@ -207,4 +207,5 @@ export interface IConstantValues {
|
|
|
207
207
|
formDataId?: string
|
|
208
208
|
formName?: string
|
|
209
209
|
parentInfo?: IDataListParentInfo
|
|
210
|
+
formRef?: FormInstance
|
|
210
211
|
}
|
|
@@ -17,6 +17,7 @@ export default function FormDataDetailsComponent({
|
|
|
17
17
|
formId,
|
|
18
18
|
formKey,
|
|
19
19
|
formDataId,
|
|
20
|
+
formName,
|
|
20
21
|
companyKey,
|
|
21
22
|
baseServerUrl,
|
|
22
23
|
onCustomFunctionCall,
|
|
@@ -98,10 +99,6 @@ export default function FormDataDetailsComponent({
|
|
|
98
99
|
)
|
|
99
100
|
setLoadings((c) => ({ ...c, data: false }))
|
|
100
101
|
} else {
|
|
101
|
-
formDataRef.setFieldValue(
|
|
102
|
-
FormPreservedItemKeys.TemplateReports,
|
|
103
|
-
parsedData.generateConfig.templateReports,
|
|
104
|
-
)
|
|
105
102
|
const dateFields = extractDateFields(layout)
|
|
106
103
|
|
|
107
104
|
fetchFormData(formId, dateFields)
|
|
@@ -129,6 +126,7 @@ export default function FormDataDetailsComponent({
|
|
|
129
126
|
conditions={conditions}
|
|
130
127
|
formDataId={formDataId}
|
|
131
128
|
formRef={formDataRef}
|
|
129
|
+
formName={formName}
|
|
132
130
|
defaulDisabled
|
|
133
131
|
onCustomFunctionCall={onCustomFunctionCall}
|
|
134
132
|
/>
|
|
@@ -143,6 +141,7 @@ type IFormDataDetailsComponent = {
|
|
|
143
141
|
baseServerUrl?: string
|
|
144
142
|
companyKey?: string
|
|
145
143
|
formDataId: string
|
|
144
|
+
formName?: string
|
|
146
145
|
} & (IDataDetailsPublicProps | IDataDetailsPrivateProps) &
|
|
147
146
|
ICustomFunctionCall
|
|
148
147
|
|
|
@@ -3,7 +3,7 @@ import { ResponsivenessDeviceEnum } from '../../../../enums'
|
|
|
3
3
|
import { getFlexContainerStyle, getFlexItemStyle, kebabCaseToCamelCase } from '../../../../functions'
|
|
4
4
|
import { IDataRender_ButtonProps, IFormLayoutElementConditions, IFormLayoutRow } from '../../../../types'
|
|
5
5
|
import LayoutRendererCol from '../2-col'
|
|
6
|
-
import { memo, ReactElement, ReactNode, useMemo } from 'react'
|
|
6
|
+
import { memo, ReactElement, ReactNode, useMemo, useState } from 'react'
|
|
7
7
|
import { LayoutRowConditionalHeaderRenderer } from './header-render'
|
|
8
8
|
import { LayoutRowRepeatableRenderer } from './repeatable-render'
|
|
9
9
|
|
|
@@ -16,6 +16,8 @@ export const LayoutRendererRow = memo(
|
|
|
16
16
|
breakpointDevice = ResponsivenessDeviceEnum.Default,
|
|
17
17
|
renderButton,
|
|
18
18
|
}: ILayoutRendererRow) => {
|
|
19
|
+
const [hiddenElementCount, setHiddenElementCount] = useState(0)
|
|
20
|
+
|
|
19
21
|
const styleConfig = useMemo(() => {
|
|
20
22
|
if (!rowData.style || !rowData.style[breakpointDevice]) return {}
|
|
21
23
|
return kebabCaseToCamelCase(rowData.style[breakpointDevice])
|
|
@@ -25,23 +27,40 @@ export const LayoutRendererRow = memo(
|
|
|
25
27
|
<>
|
|
26
28
|
<LayoutRowConditionalHeaderRenderer header={rowData.props?.header}>
|
|
27
29
|
<LayoutRowRepeatableRenderer basePath={basePath} repeatingSection={rowData.props?.repeatingSection}>
|
|
28
|
-
{(formListItemProps) =>
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
30
|
+
{(formListItemProps) => {
|
|
31
|
+
const style: { [key: string]: any } = {
|
|
32
|
+
...styleConfig,
|
|
33
|
+
...getFlexContainerStyle(breakpointDevice, rowData.responsiveness),
|
|
34
|
+
}
|
|
35
|
+
if (hiddenElementCount === rowData.children.length) style.display = 'none'
|
|
36
|
+
|
|
37
|
+
return (
|
|
38
|
+
<div style={style}>
|
|
39
|
+
{rowData.children.map((col, colIdx) => (
|
|
40
|
+
<LayoutRendererCol
|
|
41
|
+
key={colIdx}
|
|
42
|
+
basePath={formListItemProps ? formListItemProps.updatedBasePath : basePath}
|
|
43
|
+
colData={col}
|
|
44
|
+
formRef={formRef}
|
|
45
|
+
titleComponent={titleComponent}
|
|
46
|
+
breakpointDevice={breakpointDevice}
|
|
47
|
+
renderButton={renderButton}
|
|
48
|
+
hideRow={(isHidden) =>
|
|
49
|
+
setHiddenElementCount((c) => {
|
|
50
|
+
if (isHidden) return c + 1
|
|
51
|
+
else {
|
|
52
|
+
if (c - 1 < 0) return 0
|
|
53
|
+
return c - 1
|
|
54
|
+
}
|
|
55
|
+
})
|
|
56
|
+
}
|
|
57
|
+
colStyle={getFlexItemStyle(breakpointDevice, colIdx, rowData.responsiveness)}
|
|
58
|
+
/>
|
|
59
|
+
))}
|
|
60
|
+
{formListItemProps?.removeButton}
|
|
61
|
+
</div>
|
|
62
|
+
)
|
|
63
|
+
}}
|
|
45
64
|
</LayoutRowRepeatableRenderer>
|
|
46
65
|
</LayoutRowConditionalHeaderRenderer>
|
|
47
66
|
</>
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { memo, ReactElement, ReactNode, useState } from 'react'
|
|
1
|
+
import { memo, ReactElement, ReactNode, useEffect, useMemo, useState } from 'react'
|
|
2
2
|
import { LayoutRendererRow } from '../1-row'
|
|
3
3
|
import {
|
|
4
4
|
IDataRender_ButtonProps,
|
|
@@ -18,15 +18,26 @@ function LayoutRendererCol({
|
|
|
18
18
|
titleComponent,
|
|
19
19
|
breakpointDevice,
|
|
20
20
|
colStyle,
|
|
21
|
+
hideRow,
|
|
21
22
|
renderButton,
|
|
22
23
|
}: ILayoutRendererCol) {
|
|
23
24
|
const [hiddenElementCount, setHiddenElementCount] = useState(0)
|
|
24
25
|
|
|
26
|
+
const isAllChildrenHidden = useMemo(
|
|
27
|
+
() => colData.children.length === hiddenElementCount,
|
|
28
|
+
[colData.children, hiddenElementCount],
|
|
29
|
+
)
|
|
30
|
+
|
|
31
|
+
useEffect(() => {
|
|
32
|
+
console.log(isAllChildrenHidden)
|
|
33
|
+
hideRow(isAllChildrenHidden)
|
|
34
|
+
}, [isAllChildrenHidden])
|
|
35
|
+
|
|
25
36
|
return (
|
|
26
37
|
<div
|
|
27
38
|
style={{
|
|
28
39
|
...colStyle,
|
|
29
|
-
display:
|
|
40
|
+
display: isAllChildrenHidden ? 'none' : 'flex',
|
|
30
41
|
flexDirection: FlexDirection.Col,
|
|
31
42
|
}}
|
|
32
43
|
>
|
|
@@ -53,8 +64,10 @@ function LayoutRendererCol({
|
|
|
53
64
|
hideElement={(isHidden) =>
|
|
54
65
|
setHiddenElementCount((c) => {
|
|
55
66
|
if (isHidden) return c + 1
|
|
56
|
-
|
|
57
|
-
|
|
67
|
+
else {
|
|
68
|
+
if (c - 1 < 0) return 0
|
|
69
|
+
return c - 1
|
|
70
|
+
}
|
|
58
71
|
})
|
|
59
72
|
}
|
|
60
73
|
/>
|
|
@@ -72,5 +85,6 @@ interface ILayoutRendererCol {
|
|
|
72
85
|
titleComponent?: ReactNode
|
|
73
86
|
breakpointDevice: ResponsivenessDeviceEnum
|
|
74
87
|
colStyle: { [key: string]: number | string }
|
|
88
|
+
hideRow: (bool: boolean) => void
|
|
75
89
|
renderButton?: (props: IDataRender_ButtonProps, conditions?: IFormLayoutElementConditions) => ReactElement
|
|
76
90
|
}
|
|
@@ -39,7 +39,7 @@ export const DynamicFormButtonRender = memo((props: IDynamicButton) => {
|
|
|
39
39
|
const [dataLoadingType, setDataLoadingType] = useState<FormLoadingModalTypeEnum | undefined>()
|
|
40
40
|
const { inPreviewMode, isPublic = false } = useFormPreservedItemValues(formRef)
|
|
41
41
|
|
|
42
|
-
const baseDynamicUrl = useMemo(() => constructDynamicFormHref(formName), [formName])
|
|
42
|
+
const baseDynamicUrl = useMemo(() => constructDynamicFormHref(formName) ?? '', [formName])
|
|
43
43
|
|
|
44
44
|
useEffect(() => {
|
|
45
45
|
formRef?.setFieldValue(FormPreservedItemKeys.FormDataId, formDataId)
|
|
@@ -76,6 +76,7 @@ export const DynamicFormButtonRender = memo((props: IDynamicButton) => {
|
|
|
76
76
|
})
|
|
77
77
|
const { onCreateNewData, onGeneratePdfProof } = useCreateDataWithPdfActions({
|
|
78
78
|
formRef,
|
|
79
|
+
baseDynamicUrl,
|
|
79
80
|
setDataLoadingType,
|
|
80
81
|
onGenerateError: () => {
|
|
81
82
|
setDataLoadingType(undefined)
|
|
@@ -168,7 +169,11 @@ export const DynamicFormButtonRender = memo((props: IDynamicButton) => {
|
|
|
168
169
|
break
|
|
169
170
|
|
|
170
171
|
case ButtonActionCategoryEnum.GenerateReport:
|
|
171
|
-
|
|
172
|
+
if (!formDataId) {
|
|
173
|
+
warning({ message: 'Data id was not found!' })
|
|
174
|
+
break
|
|
175
|
+
}
|
|
176
|
+
onGenerateReport(formName, formDataId)
|
|
172
177
|
setTimeout(() => setLoading(false), 500)
|
|
173
178
|
break
|
|
174
179
|
|
|
@@ -189,6 +194,7 @@ export const DynamicFormButtonRender = memo((props: IDynamicButton) => {
|
|
|
189
194
|
baseDynamicUrl,
|
|
190
195
|
formDataId,
|
|
191
196
|
isPublic,
|
|
197
|
+
formName,
|
|
192
198
|
onCustomFunctionCall,
|
|
193
199
|
onDuplicateData,
|
|
194
200
|
onDeleteData,
|
package/src/components/form/layout-renderer/3-element/1-dynamic-button/use-create-data.hook.ts
CHANGED
|
@@ -1,11 +1,11 @@
|
|
|
1
|
-
import { useCallback
|
|
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
4
|
import { stringifyJSON } from '../../../../../functions/json-handlers'
|
|
5
5
|
import client from '../../../../../functions/axios-handler'
|
|
6
|
-
import {
|
|
6
|
+
import { objectToQueryParams } from '../../../../../functions'
|
|
7
7
|
import { useNotification } from '../../../../common/custom-hooks'
|
|
8
|
-
import {
|
|
8
|
+
import { useNavigate } from 'react-router-dom'
|
|
9
9
|
import { FormInstance } from 'antd'
|
|
10
10
|
|
|
11
11
|
/* --------------------------------------------------------------------------
|
|
@@ -14,6 +14,7 @@ import { FormInstance } from 'antd'
|
|
|
14
14
|
-------------------------------------------------------------------------- */
|
|
15
15
|
export const useCreateDataWithPdfActions = ({
|
|
16
16
|
formRef,
|
|
17
|
+
baseDynamicUrl,
|
|
17
18
|
setDataLoadingType,
|
|
18
19
|
onCreateSuccess,
|
|
19
20
|
onCreateError,
|
|
@@ -21,6 +22,7 @@ export const useCreateDataWithPdfActions = ({
|
|
|
21
22
|
onGenerateError,
|
|
22
23
|
}: {
|
|
23
24
|
formRef: FormInstance | undefined
|
|
25
|
+
baseDynamicUrl: string
|
|
24
26
|
setDataLoadingType: (type: FormLoadingModalTypeEnum) => void
|
|
25
27
|
onCreateSuccess: () => void
|
|
26
28
|
onCreateError: () => void
|
|
@@ -28,13 +30,11 @@ export const useCreateDataWithPdfActions = ({
|
|
|
28
30
|
onGenerateError: () => void
|
|
29
31
|
}) => {
|
|
30
32
|
const navigate = useNavigate()
|
|
31
|
-
const
|
|
33
|
+
// const { isModalOpen: isSuccessModalOpen, isPendingTransition: isSuccessModalPending, openModal: openSuccessModal, closeModal: closeSuccessModal } = useLazyModalOpener()
|
|
32
34
|
const { errorModal, confirmModal } = useNotification()
|
|
33
35
|
const { formId, formKey, baseServerUrl, companyKey, submissionPdfConfig, isPublic } =
|
|
34
36
|
useFormPreservedItemValues(formRef)
|
|
35
37
|
|
|
36
|
-
const baseDynamicUrl = useMemo(() => extractDynamicFormHref(location.pathname), [location.pathname])
|
|
37
|
-
|
|
38
38
|
const onCreateNewData = useCallback(
|
|
39
39
|
(generatedPdfBlobName?: string) => {
|
|
40
40
|
try {
|
package/src/components/form/layout-renderer/3-element/1-dynamic-button/use-delete-data.hook.ts
CHANGED
|
@@ -1,5 +1,4 @@
|
|
|
1
|
-
import { useCallback
|
|
2
|
-
import { extractDynamicFormHref } from '../../../../../functions'
|
|
1
|
+
import { useCallback } from 'react'
|
|
3
2
|
import { useFormPreservedItemValues } from '../../../../common/custom-hooks/use-preserved-form-items.hook'
|
|
4
3
|
import client from '../../../../../functions/axios-handler'
|
|
5
4
|
import { FormInstance } from 'antd'
|
|
@@ -18,7 +17,6 @@ export const useDeleteDataAction = ({
|
|
|
18
17
|
onError: () => void
|
|
19
18
|
onFinal: () => void
|
|
20
19
|
}) => {
|
|
21
|
-
const baseDynamicUrl = useMemo(() => extractDynamicFormHref(location.pathname), [location.pathname])
|
|
22
20
|
const { formId, formDataId } = useFormPreservedItemValues(formRef)
|
|
23
21
|
|
|
24
22
|
const onDeleteData = useCallback(async () => {
|
|
@@ -31,7 +29,7 @@ export const useDeleteDataAction = ({
|
|
|
31
29
|
} finally {
|
|
32
30
|
onFinal()
|
|
33
31
|
}
|
|
34
|
-
}, [formId, formDataId
|
|
32
|
+
}, [formId, formDataId])
|
|
35
33
|
|
|
36
34
|
return onDeleteData
|
|
37
35
|
}
|
package/src/components/form/layout-renderer/3-element/1-dynamic-button/use-generate-report.hook.tsx
CHANGED
|
@@ -1,12 +1,13 @@
|
|
|
1
|
-
import { useCallback, useState } from 'react'
|
|
2
|
-
import {
|
|
3
|
-
import { FormInstance, Modal, Select } from 'antd'
|
|
1
|
+
import { useCallback, useRef, useState } from 'react'
|
|
2
|
+
import { FormInstance, Modal, Select, Spin } from 'antd'
|
|
4
3
|
import { FaCaretDown } from 'react-icons/fa6'
|
|
5
|
-
import { IFormTemplateReport } from '../../../../../types'
|
|
4
|
+
import { IDynamicForm, IFormSchema, IFormTemplateReport } from '../../../../../types'
|
|
6
5
|
import { Button_FillerPortal } from '../../../../common/button'
|
|
7
6
|
import { useNotification } from '../../../../common/custom-hooks'
|
|
8
7
|
import { fetchFormDataAsLookup, renderData } from '../../../../../functions'
|
|
9
|
-
import
|
|
8
|
+
import client from '../../../../../functions/axios-handler'
|
|
9
|
+
import { parseJSON } from '../../../../../functions/json-handlers'
|
|
10
|
+
import { FieldElementOptionSourceEnum, FormPreservedItemKeys, LOCAL_STORAGE_KEYS_ENUM } from '../../../../../enums'
|
|
10
11
|
|
|
11
12
|
/* --------------------------------------------------------------------------
|
|
12
13
|
Generates a report by logging current form values.
|
|
@@ -17,30 +18,76 @@ export const useGenerateReportAction = ({
|
|
|
17
18
|
onError,
|
|
18
19
|
onFinal,
|
|
19
20
|
}: {
|
|
20
|
-
formRef
|
|
21
|
+
formRef?: FormInstance
|
|
21
22
|
onSuccess: () => void
|
|
22
23
|
onError: () => void
|
|
23
24
|
onFinal: () => void
|
|
24
25
|
}) => {
|
|
25
26
|
const { warning } = useNotification()
|
|
26
|
-
const { templateReports } = useFormPreservedItemValues(formRef)
|
|
27
27
|
const [isModalOpen, setIsModalOpen] = useState(false)
|
|
28
28
|
const [selectedTemplate, setSelectedTemplate] = useState(undefined)
|
|
29
|
+
const [templateReports, setTemplateReports] = useState<IFormTemplateReport[]>([])
|
|
29
30
|
const [loading, setLoading] = useState(false)
|
|
31
|
+
const [formData, setFormData] = useState<{ [key: string]: string | number }>({})
|
|
32
|
+
const companyKeyRef = useRef('')
|
|
30
33
|
|
|
31
|
-
const onGenerateReport = useCallback(
|
|
32
|
-
|
|
34
|
+
const onGenerateReport = useCallback(
|
|
35
|
+
async (formName: string, formDataId: string) => {
|
|
36
|
+
setLoading(true)
|
|
37
|
+
setIsModalOpen(true)
|
|
38
|
+
try {
|
|
39
|
+
const storedData = localStorage.getItem(LOCAL_STORAGE_KEYS_ENUM.DynamicForms)
|
|
40
|
+
if (storedData) {
|
|
41
|
+
const parsedData: IDynamicForm[] = JSON.parse(storedData)
|
|
42
|
+
const form = parsedData.find((entry) => entry.name === formName)
|
|
33
43
|
|
|
34
|
-
|
|
35
|
-
|
|
44
|
+
if (form) {
|
|
45
|
+
client
|
|
46
|
+
.get(`/api/form/${form.id}`)
|
|
47
|
+
.then((res) => {
|
|
48
|
+
if (res.status === 200) {
|
|
49
|
+
const parsedFormData: IFormSchema | null = parseJSON(res.data.data)
|
|
50
|
+
if (parsedFormData) {
|
|
51
|
+
const preservedFormValues = formRef?.getFieldsValue(true) ?? {}
|
|
52
|
+
companyKeyRef.current = preservedFormValues.companyKey ?? ''
|
|
53
|
+
|
|
54
|
+
const formDataListFormKey =
|
|
55
|
+
preservedFormValues[`${FormPreservedItemKeys.FormDataListElementKey}_${form.id}`]
|
|
56
|
+
|
|
57
|
+
if (formDataListFormKey) {
|
|
58
|
+
const formDataList = preservedFormValues[formDataListFormKey] ?? []
|
|
59
|
+
const formDataRowFormData =
|
|
60
|
+
formDataList.find((d: { [key: string]: string | number }) => d.id === formDataId) ?? {}
|
|
61
|
+
|
|
62
|
+
setFormData(formDataRowFormData)
|
|
63
|
+
} else {
|
|
64
|
+
const mainFormData = formRef?.getFieldsValue() ?? {}
|
|
65
|
+
setFormData(mainFormData)
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
const reports = parsedFormData.generateConfig.templateReports ?? []
|
|
69
|
+
setTemplateReports(reports)
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
})
|
|
73
|
+
.finally(() => setLoading(false))
|
|
74
|
+
} else setLoading(false)
|
|
75
|
+
} else setLoading(false)
|
|
76
|
+
} catch (error) {
|
|
77
|
+
console.error('Error reading or parsing localStorage data', error)
|
|
78
|
+
setLoading(false)
|
|
79
|
+
}
|
|
80
|
+
},
|
|
81
|
+
[templateReports, formRef],
|
|
82
|
+
)
|
|
36
83
|
|
|
37
84
|
const ChooseTemplateReportModal = isModalOpen ? (
|
|
38
85
|
<Modal
|
|
39
86
|
open
|
|
40
|
-
closable={false}
|
|
41
87
|
maskClosable={false}
|
|
42
88
|
width={400}
|
|
43
89
|
title="Please select a template!"
|
|
90
|
+
onCancel={() => setIsModalOpen(false)}
|
|
44
91
|
footer={
|
|
45
92
|
<div className="flex justify-between">
|
|
46
93
|
<Button_FillerPortal outline onClick={() => setIsModalOpen(false)}>
|
|
@@ -52,14 +99,16 @@ export const useGenerateReportAction = ({
|
|
|
52
99
|
title={selectedTemplate ? '' : 'Please select a template to continue!'}
|
|
53
100
|
loading={loading}
|
|
54
101
|
onClick={async () => {
|
|
102
|
+
if (!companyKeyRef.current) {
|
|
103
|
+
warning({ message: 'Company was not found!' })
|
|
104
|
+
return
|
|
105
|
+
}
|
|
106
|
+
|
|
55
107
|
setLoading(true)
|
|
56
108
|
const selectedTemplateInfo: IFormTemplateReport = templateReports.find(
|
|
57
109
|
(t: IFormTemplateReport) => t.fileBlobName === selectedTemplate,
|
|
58
|
-
)
|
|
59
|
-
const formValues = formRef?.getFieldsValue()
|
|
60
|
-
console.log('Form Values:', formValues)
|
|
110
|
+
)!
|
|
61
111
|
|
|
62
|
-
console.log(selectedTemplateInfo)
|
|
63
112
|
const replacements = await Promise.all(
|
|
64
113
|
selectedTemplateInfo.replacements.map(async (rep) => {
|
|
65
114
|
if (!!rep.optionSource) {
|
|
@@ -67,7 +116,7 @@ export const useGenerateReportAction = ({
|
|
|
67
116
|
return {
|
|
68
117
|
placeholder: rep.placeholder,
|
|
69
118
|
value: renderData(
|
|
70
|
-
rep.optionSource.options.find((op) => op.id === rep.
|
|
119
|
+
rep.optionSource.options.find((op) => op.id === formData[rep.field])?.value,
|
|
71
120
|
rep.renderConfig,
|
|
72
121
|
),
|
|
73
122
|
}
|
|
@@ -76,7 +125,7 @@ export const useGenerateReportAction = ({
|
|
|
76
125
|
return {
|
|
77
126
|
placeholder: rep.placeholder,
|
|
78
127
|
value: renderData(
|
|
79
|
-
formDataResData.find((data) => data.id ===
|
|
128
|
+
formDataResData.find((data) => data.id === formData[rep.field])?.[
|
|
80
129
|
rep.optionSource.form.field
|
|
81
130
|
],
|
|
82
131
|
rep.renderConfig,
|
|
@@ -86,18 +135,39 @@ export const useGenerateReportAction = ({
|
|
|
86
135
|
}
|
|
87
136
|
return {
|
|
88
137
|
placeholder: rep.placeholder,
|
|
89
|
-
value: renderData(
|
|
138
|
+
value: renderData(formData[rep.field], rep.renderConfig),
|
|
139
|
+
type: rep.type,
|
|
90
140
|
}
|
|
91
141
|
}),
|
|
92
142
|
)
|
|
93
143
|
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
144
|
+
client
|
|
145
|
+
.post(
|
|
146
|
+
`/api/attachment/template/${companyKeyRef.current}/${selectedTemplateInfo.templateName}`,
|
|
147
|
+
replacements.map((r) => ({
|
|
148
|
+
key: r.placeholder,
|
|
149
|
+
value: r.value,
|
|
150
|
+
type: r.type,
|
|
151
|
+
})),
|
|
152
|
+
{ responseType: 'blob' },
|
|
153
|
+
)
|
|
154
|
+
.then((res) => {
|
|
155
|
+
if (res.status === 200) {
|
|
156
|
+
const url = window.URL.createObjectURL(new Blob([res.data]))
|
|
157
|
+
const link = document.createElement('a')
|
|
158
|
+
link.href = url
|
|
159
|
+
link.setAttribute('download', `${selectedTemplateInfo.templateName}.pdf`) //or any other extension
|
|
160
|
+
document.body.appendChild(link)
|
|
161
|
+
link.click()
|
|
162
|
+
onSuccess()
|
|
163
|
+
}
|
|
164
|
+
})
|
|
165
|
+
.catch(() => onError())
|
|
166
|
+
.finally(() => {
|
|
167
|
+
onFinal()
|
|
168
|
+
setLoading(false)
|
|
169
|
+
setIsModalOpen(false)
|
|
170
|
+
})
|
|
101
171
|
}}
|
|
102
172
|
>
|
|
103
173
|
Continue
|
|
@@ -106,22 +176,24 @@ export const useGenerateReportAction = ({
|
|
|
106
176
|
}
|
|
107
177
|
>
|
|
108
178
|
<span>Template</span>
|
|
109
|
-
<
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
179
|
+
<Spin spinning={loading}>
|
|
180
|
+
<Select
|
|
181
|
+
placeholder="Please select a template"
|
|
182
|
+
suffixIcon={<FaCaretDown />}
|
|
183
|
+
options={templateReports.map((t: IFormTemplateReport) => ({ value: t.fileBlobName, label: t.templateName }))}
|
|
184
|
+
value={selectedTemplate}
|
|
185
|
+
onChange={setSelectedTemplate}
|
|
186
|
+
showSearch
|
|
187
|
+
optionFilterProp="label"
|
|
188
|
+
filterOption={(input, option) =>
|
|
189
|
+
option && typeof option.label === 'string'
|
|
190
|
+
? option.label.toLowerCase().indexOf(input.toLowerCase()) >= 0
|
|
191
|
+
: false
|
|
192
|
+
}
|
|
193
|
+
allowClear={false}
|
|
194
|
+
className="w-full"
|
|
195
|
+
/>
|
|
196
|
+
</Spin>
|
|
125
197
|
</Modal>
|
|
126
198
|
) : (
|
|
127
199
|
<></>
|
|
@@ -1,17 +1,25 @@
|
|
|
1
1
|
import { Form, FormInstance } from 'antd'
|
|
2
|
-
import { IReadFieldDataElementProps } from '../../../../types'
|
|
3
2
|
import { renderData } from '../../../../functions'
|
|
4
3
|
import { useFormPreservedItemValues } from '../../../common/custom-hooks/use-preserved-form-items.hook'
|
|
5
4
|
import { useMemo } from 'react'
|
|
6
|
-
import { DataRenderTypeEnum } from '../../../../enums'
|
|
5
|
+
import { DataRenderTypeEnum, EvaluationOperatorsEnum, EvaluationValueTypeEnum } from '../../../../enums'
|
|
7
6
|
import { IFormItemPath } from '.'
|
|
7
|
+
import {
|
|
8
|
+
IEvaluationCondition,
|
|
9
|
+
IEvaluationConfig,
|
|
10
|
+
IEvaluationOnField,
|
|
11
|
+
IEvaluationOnList,
|
|
12
|
+
IReadFieldDataElementProps,
|
|
13
|
+
} from '../../../../types'
|
|
8
14
|
|
|
9
15
|
export default function LayoutRenderer_ReadFieldData({
|
|
10
16
|
formRef,
|
|
11
17
|
elementProps,
|
|
12
18
|
style = {},
|
|
13
19
|
}: ILayoutRenderer_ReadFieldData) {
|
|
14
|
-
const fieldValue =
|
|
20
|
+
const fieldValue = elementProps.isValueEvaluated
|
|
21
|
+
? undefined
|
|
22
|
+
: Form.useWatch(elementProps.field, { form: formRef, preserve: true })
|
|
15
23
|
const { baseServerUrl, companyKey } = useFormPreservedItemValues(formRef)
|
|
16
24
|
|
|
17
25
|
const formValues = Form.useWatch([], { form: formRef, preserve: true })
|
|
@@ -21,6 +29,14 @@ export default function LayoutRenderer_ReadFieldData({
|
|
|
21
29
|
[baseServerUrl, companyKey, fieldValue],
|
|
22
30
|
)
|
|
23
31
|
|
|
32
|
+
const evaluatedValue = useMemo(() => {
|
|
33
|
+
if (elementProps.isValueEvaluated && formValues) {
|
|
34
|
+
const config = elementProps.evaluationConfig
|
|
35
|
+
return evaluateConfig(config, formValues)
|
|
36
|
+
}
|
|
37
|
+
return 0
|
|
38
|
+
}, [formValues, elementProps])
|
|
39
|
+
|
|
24
40
|
return (
|
|
25
41
|
<div className="flex items-center gap-2">
|
|
26
42
|
{elementProps.label && <span>{elementProps.label}: </span>}
|
|
@@ -30,6 +46,8 @@ export default function LayoutRenderer_ReadFieldData({
|
|
|
30
46
|
? attachmentUrl
|
|
31
47
|
: elementProps.renderConfig?.type === DataRenderTypeEnum.Conditional
|
|
32
48
|
? formValues
|
|
49
|
+
: elementProps.isValueEvaluated
|
|
50
|
+
? evaluatedValue
|
|
33
51
|
: fieldValue,
|
|
34
52
|
elementProps.renderConfig ?? { type: DataRenderTypeEnum.Default },
|
|
35
53
|
) ?? 'N/A'}
|
|
@@ -43,3 +61,102 @@ type ILayoutRenderer_ReadFieldData = {
|
|
|
43
61
|
elementProps: IReadFieldDataElementProps
|
|
44
62
|
style?: { [key: string]: any }
|
|
45
63
|
} & IFormItemPath
|
|
64
|
+
|
|
65
|
+
const evaluateConfig = (config: IEvaluationConfig, data: { [key: string]: any }): number => {
|
|
66
|
+
// Helper to check an evaluation’s condition (if any)
|
|
67
|
+
function checkConditions(
|
|
68
|
+
dataToCheckConditions: { [key: string]: any },
|
|
69
|
+
conditions?: IEvaluationCondition[],
|
|
70
|
+
): boolean {
|
|
71
|
+
if (!conditions || conditions.length === 0) return true
|
|
72
|
+
// As stated, at most one condition is supported.
|
|
73
|
+
const condition = conditions[0]
|
|
74
|
+
|
|
75
|
+
// Currently only the Equal operator is supported.
|
|
76
|
+
return dataToCheckConditions[condition.field] === condition.value
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
// Helper to combine values using the operator.
|
|
80
|
+
function applyOperator(operator: EvaluationOperatorsEnum, values: number[]): number {
|
|
81
|
+
if (values.length === 0) return 0
|
|
82
|
+
let result = values[0]
|
|
83
|
+
for (let i = 1; i < values.length; i++) {
|
|
84
|
+
const value = values[i]
|
|
85
|
+
switch (operator) {
|
|
86
|
+
case EvaluationOperatorsEnum.Add:
|
|
87
|
+
result += value
|
|
88
|
+
break
|
|
89
|
+
case EvaluationOperatorsEnum.Subtract:
|
|
90
|
+
result -= value
|
|
91
|
+
break
|
|
92
|
+
case EvaluationOperatorsEnum.Multiply:
|
|
93
|
+
result *= value
|
|
94
|
+
break
|
|
95
|
+
case EvaluationOperatorsEnum.Divide:
|
|
96
|
+
result /= value
|
|
97
|
+
break
|
|
98
|
+
default:
|
|
99
|
+
throw new Error(`Unsupported operator: ${operator}`)
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
return result
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
// Object to store results from each evaluation by its id.
|
|
106
|
+
const evaluationResults: { [id: string]: number } = {}
|
|
107
|
+
|
|
108
|
+
// Process each evaluation in the config.
|
|
109
|
+
for (const evaluation of config.evaluations) {
|
|
110
|
+
// Evaluation based on fields (IEvaluationOnField)
|
|
111
|
+
if (evaluation.type === EvaluationValueTypeEnum.Fields) {
|
|
112
|
+
// If a condition exists and fails, treat the result as 0.
|
|
113
|
+
if (!checkConditions(data, evaluation.conditions)) {
|
|
114
|
+
evaluationResults[evaluation.id] = 0
|
|
115
|
+
continue
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
const evalFields = (evaluation as IEvaluationOnField).fields
|
|
119
|
+
// For each field, take the value from data (using default if missing)
|
|
120
|
+
const values = evalFields.map((fieldConfig) => {
|
|
121
|
+
const value = data[fieldConfig.field]
|
|
122
|
+
return value !== undefined && value !== null ? value : fieldConfig.defaultValue ?? 0
|
|
123
|
+
})
|
|
124
|
+
evaluationResults[evaluation.id] = applyOperator(evaluation.operator, values)
|
|
125
|
+
}
|
|
126
|
+
// Evaluation based on a list (IEvaluationOnList)
|
|
127
|
+
else if (evaluation.type === EvaluationValueTypeEnum.List) {
|
|
128
|
+
|
|
129
|
+
const listData = data[(evaluation as IEvaluationOnList).list]
|
|
130
|
+
if (Array.isArray(listData)) {
|
|
131
|
+
const values = listData.map((item, itemIdx) => {
|
|
132
|
+
const listItemData = listData[itemIdx]
|
|
133
|
+
if (!checkConditions(listItemData, evaluation.conditions)) return 0
|
|
134
|
+
|
|
135
|
+
const fieldValue = item[(evaluation as IEvaluationOnList).listField.field]
|
|
136
|
+
|
|
137
|
+
return fieldValue !== undefined && fieldValue !== null
|
|
138
|
+
? fieldValue
|
|
139
|
+
: (evaluation as IEvaluationOnList).listField.defaultValue ?? 0
|
|
140
|
+
})
|
|
141
|
+
evaluationResults[evaluation.id] = applyOperator(evaluation.operator, values)
|
|
142
|
+
} else {
|
|
143
|
+
evaluationResults[evaluation.id] = 0
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
// Determine and return the final result.
|
|
149
|
+
// If evaluationGroups is provided, use the last one as the final evaluation.
|
|
150
|
+
if (config.evaluationGroups && config.evaluationGroups.length > 0) {
|
|
151
|
+
const finalGroup = config.evaluationGroups[config.evaluationGroups.length - 1]
|
|
152
|
+
// Retrieve results for the evaluation IDs in the group (defaulting to 0 if missing)
|
|
153
|
+
const groupValues = finalGroup.evaluationIds.map((id) => evaluationResults[id] ?? 0)
|
|
154
|
+
return applyOperator(finalGroup.operator, groupValues)
|
|
155
|
+
}
|
|
156
|
+
// Otherwise, return the result from the last evaluation.
|
|
157
|
+
else if (config.evaluations.length > 0) {
|
|
158
|
+
const lastEvaluation = config.evaluations[config.evaluations.length - 1]
|
|
159
|
+
return evaluationResults[lastEvaluation.id]
|
|
160
|
+
}
|
|
161
|
+
return 0
|
|
162
|
+
}
|
|
@@ -17,8 +17,9 @@ import {
|
|
|
17
17
|
IFormLayoutRow,
|
|
18
18
|
IFormRelationshipConfig,
|
|
19
19
|
} from '../../../../types'
|
|
20
|
+
import { FormPreservedItemKeys } from '../../../../enums'
|
|
20
21
|
|
|
21
|
-
export default function LayoutRenderer_LoadFormData({ formRef, elementProps }: ILayoutRenderer_LoadFormData) {
|
|
22
|
+
export default function LayoutRenderer_LoadFormData({ fullPath, formRef, elementProps }: ILayoutRenderer_LoadFormData) {
|
|
22
23
|
const { form, filterConfig, sorterConfig } = elementProps
|
|
23
24
|
const { formDataId } = useFormPreservedItemValues(formRef)
|
|
24
25
|
const recursiveCallCount = useRef(0)
|
|
@@ -52,6 +53,7 @@ export default function LayoutRenderer_LoadFormData({ formRef, elementProps }: I
|
|
|
52
53
|
layout: IFormLayoutRow[]
|
|
53
54
|
formName: string
|
|
54
55
|
parentInfo: { foreignKey: string; dataIds: string[] }
|
|
56
|
+
rootFormId: number
|
|
55
57
|
data: { data: IFormData[]; total: number }
|
|
56
58
|
}
|
|
57
59
|
| undefined
|
|
@@ -125,6 +127,7 @@ export default function LayoutRenderer_LoadFormData({ formRef, elementProps }: I
|
|
|
125
127
|
layout: detailsConfig.layout,
|
|
126
128
|
formName: formDetailsRes.data.name,
|
|
127
129
|
parentInfo: { foreignKey: relationship.foreignKey, dataIds: foreignKeyIds },
|
|
130
|
+
rootFormId: childFormId,
|
|
128
131
|
data: {
|
|
129
132
|
...formData,
|
|
130
133
|
data: formData.data.map((d: { [key: string]: any }) => ({
|
|
@@ -178,19 +181,24 @@ export default function LayoutRenderer_LoadFormData({ formRef, elementProps }: I
|
|
|
178
181
|
(filterConfig as IFilterByAuthUser | IFilterCustom)?.config,
|
|
179
182
|
sorterConfig,
|
|
180
183
|
).then((resData) => {
|
|
181
|
-
const { config, data, layout = [], formName = '', parentInfo } = resData ?? {}
|
|
184
|
+
const { config, data, layout = [], formName = '', parentInfo, rootFormId } = resData ?? {}
|
|
182
185
|
|
|
183
186
|
if (config) setDisplayConfig({ dataListConfig: config, detailsLayout: layout })
|
|
184
|
-
if (data)
|
|
187
|
+
if (data) {
|
|
188
|
+
setDataList(data)
|
|
189
|
+
if (rootFormId)
|
|
190
|
+
formRef.setFieldValue(`${FormPreservedItemKeys.FormDataListElementKey}_${rootFormId}`, fullPath)
|
|
191
|
+
formRef.setFieldValue(fullPath, data.data)
|
|
192
|
+
}
|
|
185
193
|
setCurrentFormInfo({ formName, parentInfo })
|
|
186
194
|
})
|
|
187
195
|
hasInitialFetched.current = true
|
|
188
196
|
}
|
|
189
|
-
}, [form, filterConfig, sorterConfig, fetchFormDetails, formDataId, formValues])
|
|
197
|
+
}, [form, filterConfig, sorterConfig, fetchFormDetails, formDataId, formValues, fullPath])
|
|
190
198
|
|
|
191
199
|
if (!form || !form.relationshipConfig?.formId) return <></>
|
|
192
200
|
|
|
193
|
-
const constantValues = { formDataId, ...currentFormInfo }
|
|
201
|
+
const constantValues = { formDataId, formRef, ...currentFormInfo }
|
|
194
202
|
|
|
195
203
|
return (
|
|
196
204
|
<FormDataListTableComponent
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { Divider, FormInstance } from 'antd'
|
|
2
2
|
import { memo, ReactElement, ReactNode, useEffect, useMemo } from 'react'
|
|
3
|
-
import { IDataRender_ButtonProps, IFormLayoutElement } from '../../../../types'
|
|
3
|
+
import { IDataRender_ButtonProps, IFormLayoutElement, IReadFieldDataElementProps } from '../../../../types'
|
|
4
4
|
import { getElementGeneralizedProps } from '../../../../functions/get-element-props'
|
|
5
5
|
import { ElementTypeEnum, ResponsivenessDeviceEnum, TextElementTypeEnum } from '../../../../enums'
|
|
6
6
|
import { LayoutRenderer_FieldElement } from './2-field-element'
|
|
@@ -119,7 +119,11 @@ function LayoutRendererElement({
|
|
|
119
119
|
|
|
120
120
|
case ElementTypeEnum.ReadFieldData:
|
|
121
121
|
return (
|
|
122
|
-
<LayoutRenderer_ReadFieldData
|
|
122
|
+
<LayoutRenderer_ReadFieldData
|
|
123
|
+
formRef={formRef}
|
|
124
|
+
elementProps={props as IReadFieldDataElementProps}
|
|
125
|
+
style={elementData.style?.[breakpointDevice]}
|
|
126
|
+
/>
|
|
123
127
|
)
|
|
124
128
|
|
|
125
129
|
case ElementTypeEnum.Divider:
|
|
@@ -159,7 +163,7 @@ function LayoutRendererElement({
|
|
|
159
163
|
)
|
|
160
164
|
|
|
161
165
|
case ElementTypeEnum.LoadFormData:
|
|
162
|
-
return <LayoutRenderer_LoadFormData elementProps={props} formRef={formRef} />
|
|
166
|
+
return <LayoutRenderer_LoadFormData fullPath={fullPath} elementProps={props} formRef={formRef} />
|
|
163
167
|
|
|
164
168
|
default:
|
|
165
169
|
return (
|
package/src/enums.ts
CHANGED
|
@@ -221,8 +221,8 @@ export enum FormPreservedItemKeys {
|
|
|
221
221
|
IsGeneratingPDF = 'isGeneratingPdf',
|
|
222
222
|
HasSignature = 'hasSignature',
|
|
223
223
|
InPreviewMode = 'inPreviewMode',
|
|
224
|
-
TemplateReports = 'templateReports',
|
|
225
224
|
IsPublic = 'isPublic',
|
|
225
|
+
FormDataListElementKey = 'formDataListElementKey',
|
|
226
226
|
}
|
|
227
227
|
export enum MongoDbSortOrderEnum {
|
|
228
228
|
Ascending = 1,
|
|
@@ -234,6 +234,7 @@ export enum DataCategoryEnum {
|
|
|
234
234
|
Boolean = 'boolean',
|
|
235
235
|
Null = 'null',
|
|
236
236
|
Undefined = 'undefined',
|
|
237
|
+
Option = 'option',
|
|
237
238
|
Array = 'array',
|
|
238
239
|
Function = 'function',
|
|
239
240
|
UserInput = 'user-input',
|
|
@@ -254,6 +255,17 @@ export enum RelationalOperatorsEnum {
|
|
|
254
255
|
In = '@in',
|
|
255
256
|
NotIn = '@nin',
|
|
256
257
|
}
|
|
258
|
+
export enum EvaluationOperatorsEnum {
|
|
259
|
+
Add = 'add',
|
|
260
|
+
Subtract = 'subtract',
|
|
261
|
+
Multiply = 'multiply',
|
|
262
|
+
Divide = 'divide',
|
|
263
|
+
}
|
|
264
|
+
export enum EvaluationValueTypeEnum {
|
|
265
|
+
Fields = 'fields',
|
|
266
|
+
List = 'list',
|
|
267
|
+
Group = 'group', // for nested evaluation
|
|
268
|
+
}
|
|
257
269
|
export enum FormRelationshipEnum {
|
|
258
270
|
ManyToOne = 'many-to-one',
|
|
259
271
|
OneToOne = 'one-to-one',
|
|
@@ -279,3 +291,10 @@ export enum PatternPlacholdersEnum {
|
|
|
279
291
|
CyrillicLowercase = 'c',
|
|
280
292
|
Digit = 'D',
|
|
281
293
|
}
|
|
294
|
+
export enum TemplateDataReplacementFieldTypesEnum {
|
|
295
|
+
Text = 1,
|
|
296
|
+
Html,
|
|
297
|
+
Image,
|
|
298
|
+
Table,
|
|
299
|
+
TableRow,
|
|
300
|
+
}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import dayjs from 'dayjs'
|
|
2
2
|
import { AlignTypeEnum, CountryEnum, DataRenderTypeEnum, PatternPlacholdersEnum } from '../enums'
|
|
3
3
|
import { DynamicFormButtonRender } from '../components/form/layout-renderer/3-element/1-dynamic-button'
|
|
4
|
-
import { Dropdown, Image } from 'antd'
|
|
4
|
+
import { Dropdown, FormInstance, Image } from 'antd'
|
|
5
5
|
import { FaCaretDown } from 'react-icons/fa6'
|
|
6
6
|
import { INTERNATIONALIZATION_DATA } from '../constants'
|
|
7
7
|
import { evaluateCondition } from './evaluate-condition'
|
|
@@ -22,9 +22,9 @@ import {
|
|
|
22
22
|
export const generateTableColumns = (
|
|
23
23
|
elements: IFormDataListElement[],
|
|
24
24
|
optionKeyValuePair: { [key: string]: string },
|
|
25
|
-
contextValues: { attachmentBaseUrl?: string; formName?: string } = {},
|
|
25
|
+
contextValues: { attachmentBaseUrl?: string; formName?: string; formRef?: FormInstance } = {},
|
|
26
26
|
) => {
|
|
27
|
-
const { attachmentBaseUrl, formName } = contextValues
|
|
27
|
+
const { attachmentBaseUrl, formName, formRef } = contextValues
|
|
28
28
|
|
|
29
29
|
return elements.map((el) => {
|
|
30
30
|
const col: ITableColumn = {
|
|
@@ -62,7 +62,12 @@ export const generateTableColumns = (
|
|
|
62
62
|
.map((btnProps, btnKey) => ({
|
|
63
63
|
key: btnKey,
|
|
64
64
|
label: (
|
|
65
|
-
<DynamicFormButtonRender
|
|
65
|
+
<DynamicFormButtonRender
|
|
66
|
+
formRef={formRef}
|
|
67
|
+
btnProps={btnProps}
|
|
68
|
+
formDataId={rowData?.id}
|
|
69
|
+
formName={formName}
|
|
70
|
+
/>
|
|
66
71
|
),
|
|
67
72
|
})),
|
|
68
73
|
}}
|
|
@@ -76,6 +81,7 @@ export const generateTableColumns = (
|
|
|
76
81
|
</Dropdown>
|
|
77
82
|
) : (
|
|
78
83
|
<DynamicFormButtonRender
|
|
84
|
+
formRef={formRef}
|
|
79
85
|
btnProps={(el.renderConfig as IDataRender_Buttons).buttons[0]}
|
|
80
86
|
formDataId={rowData?.id}
|
|
81
87
|
formName={formName}
|
|
@@ -92,7 +98,7 @@ export const generateTableColumns = (
|
|
|
92
98
|
}
|
|
93
99
|
|
|
94
100
|
export const renderData = (data?: any, renderConfig?: IDataRenderConfig): ReactNode => {
|
|
95
|
-
if (
|
|
101
|
+
if ([null, undefined, ''].includes(data) || typeof data === 'object' || !renderConfig) return 'N/A'
|
|
96
102
|
|
|
97
103
|
switch (renderConfig.type) {
|
|
98
104
|
case DataRenderTypeEnum.Date:
|
|
@@ -3,7 +3,7 @@ import { HiMenu } from 'react-icons/hi'
|
|
|
3
3
|
import { ILayoutTemplateProps } from '../..'
|
|
4
4
|
import { Link } from 'react-router-dom'
|
|
5
5
|
import { IDynamicForm } from '../../../data-list'
|
|
6
|
-
import { isEncodedURI } from '../../../../functions'
|
|
6
|
+
import { constructDynamicFormHref, isEncodedURI } from '../../../../functions'
|
|
7
7
|
import { useState } from 'react'
|
|
8
8
|
import { Button, Drawer, Dropdown, Menu } from 'antd'
|
|
9
9
|
|
|
@@ -473,7 +473,3 @@ const isActive = (path: string, href: string) => {
|
|
|
473
473
|
|
|
474
474
|
return path.startsWith(href)
|
|
475
475
|
}
|
|
476
|
-
|
|
477
|
-
const constructDynamicFormHref = (formName: string): string => {
|
|
478
|
-
return `/forms/${formName.toLowerCase().replace(/ /g, '-')}`
|
|
479
|
-
}
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { TemplateDataReplacementFieldTypesEnum } from '../../enums'
|
|
1
2
|
import { IDataRenderConfig, IFieldElementOptionSource } from '../layout-elements'
|
|
2
3
|
|
|
3
4
|
export interface IFormSubmissionPdf {
|
|
@@ -19,5 +20,5 @@ interface IFormTemplateReportsReplacement {
|
|
|
19
20
|
field: string
|
|
20
21
|
renderConfig: IDataRenderConfig
|
|
21
22
|
optionSource?: IFieldElementOptionSource
|
|
22
|
-
|
|
23
|
+
type: TemplateDataReplacementFieldTypesEnum
|
|
23
24
|
}
|
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
import {
|
|
2
|
+
DataCategoryEnum,
|
|
3
|
+
EvaluationOperatorsEnum,
|
|
4
|
+
EvaluationValueTypeEnum,
|
|
5
|
+
RelationalOperatorsEnum,
|
|
6
|
+
} from '../../enums'
|
|
7
|
+
|
|
8
|
+
export interface IEvaluationConfig {
|
|
9
|
+
evaluations: (IEvaluationOnField | IEvaluationOnList)[]
|
|
10
|
+
evaluationGroups?: IEvaluationGroup[]
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
interface IEvaluationConfigBase {
|
|
14
|
+
id: string
|
|
15
|
+
operator: EvaluationOperatorsEnum
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
export interface IEvaluationGroup extends IEvaluationConfigBase {
|
|
19
|
+
type: EvaluationValueTypeEnum.Group
|
|
20
|
+
evaluationIds: string[] // ids
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
export interface IEvaluationOnField extends IEvaluationConfigBase {
|
|
24
|
+
type: EvaluationValueTypeEnum.Fields
|
|
25
|
+
fields: IEvaluationField[]
|
|
26
|
+
conditions?: IEvaluationCondition[]
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
export interface IEvaluationOnList extends IEvaluationConfigBase {
|
|
30
|
+
type: EvaluationValueTypeEnum.List
|
|
31
|
+
list: string
|
|
32
|
+
listField: IEvaluationField
|
|
33
|
+
conditions?: IEvaluationCondition[]
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
interface IEvaluationField {
|
|
37
|
+
field: string
|
|
38
|
+
defaultValue: number
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
export interface IEvaluationCondition {
|
|
42
|
+
id: string
|
|
43
|
+
field: string
|
|
44
|
+
operator: RelationalOperatorsEnum
|
|
45
|
+
valueType: DataCategoryEnum
|
|
46
|
+
value: string | number | boolean
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
// {
|
|
50
|
+
// id: string
|
|
51
|
+
// type: 'evaluation',
|
|
52
|
+
// operator: 'plus',
|
|
53
|
+
// evaluations: [
|
|
54
|
+
// {
|
|
55
|
+
// id: string,
|
|
56
|
+
// type: 'list',
|
|
57
|
+
// list: 'dDataList',
|
|
58
|
+
// listField: 'dDataAmount',
|
|
59
|
+
// operator: 'plus',
|
|
60
|
+
// },
|
|
61
|
+
// {
|
|
62
|
+
// id: string,
|
|
63
|
+
// type: 'fields',
|
|
64
|
+
// fields: ['amount1', 'amount2'],
|
|
65
|
+
// },
|
|
66
|
+
// {
|
|
67
|
+
// id: string,
|
|
68
|
+
// type: 'evaluation',
|
|
69
|
+
// operator: 'plus',
|
|
70
|
+
// evaluations: [
|
|
71
|
+
// {
|
|
72
|
+
// id: string
|
|
73
|
+
// type: 'list',
|
|
74
|
+
// list: 'dDataList',
|
|
75
|
+
// listField: 'dDataAmount',
|
|
76
|
+
// operator: 'plus',
|
|
77
|
+
// },
|
|
78
|
+
// {
|
|
79
|
+
// id: string
|
|
80
|
+
// type: 'fields',
|
|
81
|
+
// fields: ['amount1', 'amount2'],
|
|
82
|
+
// }
|
|
83
|
+
// ]
|
|
84
|
+
// }
|
|
85
|
+
// ]
|
|
86
|
+
// }
|
|
@@ -3,7 +3,8 @@ export * from './field-option-source'
|
|
|
3
3
|
export * from './style'
|
|
4
4
|
export * from './conditions'
|
|
5
5
|
export * from './sanitization'
|
|
6
|
-
|
|
6
|
+
export * from './evaluation-config'
|
|
7
|
+
import { ICssStyle, IDataListSorter, IEvaluationConfig, IFilterConfig, IRelationshipForm } from '..'
|
|
7
8
|
import { IFieldElementOptionSource } from './field-option-source'
|
|
8
9
|
import { IDataRenderConfig } from './data-render-config'
|
|
9
10
|
import {
|
|
@@ -243,12 +244,11 @@ export interface IReadFieldDataElement extends BaseFormLayoutElement {
|
|
|
243
244
|
style?: ICssStyle
|
|
244
245
|
props: IReadFieldDataElementProps
|
|
245
246
|
}
|
|
246
|
-
export
|
|
247
|
+
export type IReadFieldDataElementProps = {
|
|
247
248
|
label?: string
|
|
248
249
|
hasNoLabel?: boolean
|
|
249
|
-
field?: string
|
|
250
250
|
renderConfig?: IDataRenderConfig
|
|
251
|
-
}
|
|
251
|
+
} & ({ isValueEvaluated: true; evaluationConfig: IEvaluationConfig } | { isValueEvaluated: false; field: string })
|
|
252
252
|
|
|
253
253
|
/** -------------------------------------------------------------------------------------------- */
|
|
254
254
|
|