form-craft-package 1.8.2-dev.2 → 1.8.2-dev.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/index.ts +1 -0
- package/src/components/common/custom-hooks/use-translation.hook/hook.ts +28 -0
- package/src/components/common/custom-hooks/use-translation.hook/store.ts +130 -0
- package/src/components/form/1-list/index.tsx +7 -0
- package/src/components/form/2-details/index.tsx +7 -0
- package/src/components/form/layout-renderer/1-row/header-render.tsx +12 -2
- package/src/components/form/layout-renderer/1-row/header.tsx +1 -0
- package/src/components/form/layout-renderer/1-row/index.tsx +7 -1
- package/src/components/form/layout-renderer/1-row/repeatable-render.tsx +17 -3
- package/src/components/form/layout-renderer/3-element/1-dynamic-button/index.tsx +32 -14
- package/src/components/form/layout-renderer/3-element/10-currency.tsx +12 -1
- package/src/components/form/layout-renderer/3-element/11-breadcrumb.tsx +14 -6
- package/src/components/form/layout-renderer/3-element/12-picker-field.tsx +10 -6
- package/src/components/form/layout-renderer/3-element/13-language-selector.tsx +42 -0
- package/src/components/form/layout-renderer/3-element/2-field-element.tsx +31 -17
- package/src/components/form/layout-renderer/3-element/3-read-field-data.tsx +13 -4
- package/src/components/form/layout-renderer/3-element/4-rich-text-editor.tsx +10 -9
- package/src/components/form/layout-renderer/3-element/6-signature.tsx +7 -4
- package/src/components/form/layout-renderer/3-element/7-file-upload.tsx +44 -10
- package/src/components/form/layout-renderer/3-element/8-fields-with-options.tsx +25 -6
- package/src/components/form/layout-renderer/3-element/9-form-data-render.tsx +7 -0
- package/src/components/form/layout-renderer/3-element/index.tsx +35 -15
- package/src/constants.ts +2 -1
- package/src/enums/index.ts +28 -0
- package/src/functions/companies/use-company-config.tsx +13 -2
- package/src/functions/forms/create-form-rules.ts +18 -6
- package/src/functions/forms/data-render-functions.tsx +44 -23
- package/src/functions/forms/get-element-props.ts +1 -1
- package/src/types/forms/data-list/index.ts +1 -0
- package/src/types/forms/index.ts +10 -3
- package/src/types/forms/layout-elements/button.ts +2 -11
- package/src/types/forms/layout-elements/index.ts +10 -42
- package/src/types/forms/layout-elements/read-field-data-props.ts +0 -1
- package/src/types/forms/layout-elements/validation.ts +0 -1
- package/src/types/index.ts +2 -0
|
@@ -13,35 +13,49 @@ import { Checkbox, Form, Input, Select, Radio, InputNumber, DatePicker, Modal, S
|
|
|
13
13
|
interface ILayoutRenderer_FieldElement {
|
|
14
14
|
children: () => IMapperFieldObj | IMapperFieldObj[]
|
|
15
15
|
formRef?: FormInstance
|
|
16
|
+
elementKey?: string
|
|
17
|
+
formId?: number
|
|
16
18
|
}
|
|
17
19
|
|
|
18
|
-
export const LayoutRenderer_FieldElement = memo(
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
20
|
+
export const LayoutRenderer_FieldElement = memo(
|
|
21
|
+
({ children, formRef, elementKey, formId }: ILayoutRenderer_FieldElement): JSX.Element => {
|
|
22
|
+
const elems = children()
|
|
23
|
+
if (Array.isArray(elems))
|
|
24
|
+
return (
|
|
25
|
+
<>
|
|
26
|
+
{elems
|
|
27
|
+
.filter(({ isHidden = false }) => !isHidden)
|
|
28
|
+
.map((field: IMapperFieldObj, idx: Key) => (
|
|
29
|
+
<FieldFormItemWrapper
|
|
30
|
+
key={idx}
|
|
31
|
+
field={field}
|
|
32
|
+
fieldIdx={idx}
|
|
33
|
+
formRef={formRef}
|
|
34
|
+
elementKey={elementKey}
|
|
35
|
+
formId={formId}
|
|
36
|
+
/>
|
|
37
|
+
))}
|
|
38
|
+
</>
|
|
39
|
+
)
|
|
31
40
|
|
|
32
|
-
|
|
41
|
+
if (elems.isHidden) return <></>
|
|
33
42
|
|
|
34
|
-
|
|
35
|
-
}
|
|
43
|
+
return <FieldFormItemWrapper field={elems} fieldIdx={1} formRef={formRef} elementKey={elementKey} formId={formId} />
|
|
44
|
+
},
|
|
45
|
+
)
|
|
36
46
|
|
|
37
47
|
const FieldFormItemWrapper = ({
|
|
38
48
|
field,
|
|
39
49
|
fieldIdx,
|
|
40
50
|
formRef,
|
|
51
|
+
elementKey,
|
|
52
|
+
formId,
|
|
41
53
|
}: {
|
|
42
54
|
field: IMapperFieldObj
|
|
43
55
|
fieldIdx: Key
|
|
44
56
|
formRef?: FormInstance
|
|
57
|
+
elementKey?: string
|
|
58
|
+
formId?: number
|
|
45
59
|
}) => {
|
|
46
60
|
const { name, nameFullPath, label, hasNoLabel, type, validations, sanitization, disabled } = field
|
|
47
61
|
|
|
@@ -53,7 +67,7 @@ const FieldFormItemWrapper = ({
|
|
|
53
67
|
labelAlign="left"
|
|
54
68
|
messageVariables={{ [String(name)]: formRef?.getFieldValue(name) }}
|
|
55
69
|
valuePropName={type && [ElementTypeEnum.Checkbox, ElementTypeEnum.Switch].includes(type) ? 'checked' : 'value'}
|
|
56
|
-
rules={mapToFormItemRules(validations ?? [],
|
|
70
|
+
rules={mapToFormItemRules(elementKey, validations ?? [], formId)}
|
|
57
71
|
normalize={(value) => {
|
|
58
72
|
if (!sanitization || sanitization.type === DataSanitizationTypeEnum.Default) return value
|
|
59
73
|
|
|
@@ -2,13 +2,21 @@ import { Form } from 'antd'
|
|
|
2
2
|
import { renderData } from '../../../../functions/forms'
|
|
3
3
|
import { useFormPreservedItemValues } from '../../../common/custom-hooks/use-preserved-form-items.hook'
|
|
4
4
|
import { memo, useMemo } from 'react'
|
|
5
|
-
import { DataRenderTypeEnum, ReadFieldDataValueTypeEnum } from '../../../../enums'
|
|
5
|
+
import { DataRenderTypeEnum, ReadFieldDataValueTypeEnum, TranslationTextTypeEnum } from '../../../../enums'
|
|
6
6
|
import { IReadFieldDataElementProps } from '../../../../types'
|
|
7
7
|
import { IFormContext } from '../1-row'
|
|
8
8
|
import { evaluateValue } from '../../../../functions/forms/evaluate-value'
|
|
9
9
|
import { ELEMENTS_DEFAULT_CLASS } from '../../../../constants'
|
|
10
|
+
import { IElementBaseProps } from '.'
|
|
11
|
+
import { useTranslation } from '../../../common/custom-hooks/use-translation.hook/hook'
|
|
10
12
|
|
|
11
|
-
function LayoutRenderer_ReadFieldData({
|
|
13
|
+
function LayoutRenderer_ReadFieldData({
|
|
14
|
+
formContext,
|
|
15
|
+
elementProps,
|
|
16
|
+
style = {},
|
|
17
|
+
elementKey,
|
|
18
|
+
}: ILayoutRenderer_ReadFieldData) {
|
|
19
|
+
const { t } = useTranslation(formContext.formId)
|
|
12
20
|
const { formRef, companyKey } = formContext
|
|
13
21
|
const fieldValue =
|
|
14
22
|
elementProps.valueType === ReadFieldDataValueTypeEnum.Evaluated
|
|
@@ -31,9 +39,10 @@ function LayoutRenderer_ReadFieldData({ formContext, elementProps, style = {} }:
|
|
|
31
39
|
return 0
|
|
32
40
|
}, [formValues, elementProps])
|
|
33
41
|
|
|
42
|
+
const label = t({ key: elementKey, type: TranslationTextTypeEnum.Label })
|
|
34
43
|
return (
|
|
35
44
|
<div className={`${ELEMENTS_DEFAULT_CLASS.ReadFieldData} flex items-center gap-2`}>
|
|
36
|
-
{
|
|
45
|
+
{label && <span>{label}: </span>}
|
|
37
46
|
<span style={style}>
|
|
38
47
|
{renderData(
|
|
39
48
|
elementProps.renderConfig?.type === DataRenderTypeEnum.Image
|
|
@@ -56,4 +65,4 @@ type ILayoutRenderer_ReadFieldData = {
|
|
|
56
65
|
formContext: IFormContext
|
|
57
66
|
elementProps: IReadFieldDataElementProps
|
|
58
67
|
style?: { [key: string]: any }
|
|
59
|
-
}
|
|
68
|
+
} & IElementBaseProps
|
|
@@ -5,9 +5,10 @@ import { IElementBaseProps } from '.'
|
|
|
5
5
|
import { IInputElementProps, IValidationRule } from '../../../../types'
|
|
6
6
|
import { IFormContext } from '../1-row'
|
|
7
7
|
import { mapToFormItemRules } from '../../../../functions'
|
|
8
|
-
import { FieldValidationEnum } from '../../../../enums'
|
|
8
|
+
import { FieldValidationEnum, TranslationTextTypeEnum } from '../../../../enums'
|
|
9
9
|
import { ELEMENTS_DEFAULT_CLASS, REGEX_PATTERNS } from '../../../../constants'
|
|
10
10
|
import { memo } from 'react'
|
|
11
|
+
import { useTranslation } from '../../../common/custom-hooks/use-translation.hook/hook'
|
|
11
12
|
|
|
12
13
|
function LayoutRenderer_RichEditor({
|
|
13
14
|
formContext,
|
|
@@ -15,30 +16,30 @@ function LayoutRenderer_RichEditor({
|
|
|
15
16
|
formItem,
|
|
16
17
|
validations,
|
|
17
18
|
isDisabled,
|
|
19
|
+
elementKey,
|
|
18
20
|
}: ILayoutRenderer_RichEditor) {
|
|
19
|
-
const { formRef } = formContext
|
|
21
|
+
const { formRef, formId } = formContext
|
|
22
|
+
const { t } = useTranslation(formId)
|
|
20
23
|
const fieldValue = Form.useWatch(formItem.path, { form: formRef, preserve: true })
|
|
21
24
|
const requiredValidation = validations?.find((v) => v.rule === FieldValidationEnum.Required)
|
|
22
25
|
|
|
26
|
+
const label = t({ key: elementKey, type: TranslationTextTypeEnum.Label })
|
|
23
27
|
return (
|
|
24
28
|
<Form.Item
|
|
25
29
|
name={formItem.name}
|
|
26
30
|
rules={
|
|
27
31
|
requiredValidation
|
|
28
32
|
? mapToFormItemRules(
|
|
33
|
+
elementKey,
|
|
29
34
|
[
|
|
30
35
|
requiredValidation,
|
|
31
|
-
{
|
|
32
|
-
rule: FieldValidationEnum.Regex,
|
|
33
|
-
value: REGEX_PATTERNS.NonEmptyHtmlString.toString(),
|
|
34
|
-
message: requiredValidation.message,
|
|
35
|
-
},
|
|
36
|
+
{ rule: FieldValidationEnum.Regex, value: REGEX_PATTERNS.NonEmptyHtmlString.toString() },
|
|
36
37
|
],
|
|
37
|
-
|
|
38
|
+
formId,
|
|
38
39
|
)
|
|
39
40
|
: []
|
|
40
41
|
}
|
|
41
|
-
label={elementProps.hasNoLabel ? '' :
|
|
42
|
+
label={elementProps.hasNoLabel ? '' : label}
|
|
42
43
|
labelAlign="left"
|
|
43
44
|
>
|
|
44
45
|
<ReactQuill
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { Form } from 'antd'
|
|
2
2
|
import { memo, useEffect, useMemo, useRef } from 'react'
|
|
3
3
|
import SignatureCanvas from 'react-signature-canvas'
|
|
4
|
-
import { FormPreservedItemKeys } from '../../../../enums'
|
|
4
|
+
import { FormPreservedItemKeys, TranslationTextTypeEnum } from '../../../../enums'
|
|
5
5
|
import { Button_FillerPortal } from '../../../common/button'
|
|
6
6
|
import { useFormPreservedItemValues } from '../../../common/custom-hooks/use-preserved-form-items.hook'
|
|
7
7
|
import { IElementBaseProps } from '.'
|
|
@@ -9,6 +9,7 @@ import { ISignatureElementProps, IValidationRule } from '../../../../types'
|
|
|
9
9
|
import { IFormContext } from '../1-row'
|
|
10
10
|
import { isNewFormDataPage, mapToFormItemRules } from '../../../../functions'
|
|
11
11
|
import { ELEMENTS_DEFAULT_CLASS } from '../../../../constants'
|
|
12
|
+
import { useTranslation } from '../../../common/custom-hooks/use-translation.hook/hook'
|
|
12
13
|
|
|
13
14
|
function LayoutRenderer_Signature({
|
|
14
15
|
formItem,
|
|
@@ -16,8 +17,10 @@ function LayoutRenderer_Signature({
|
|
|
16
17
|
isDisabled,
|
|
17
18
|
formContext,
|
|
18
19
|
validations,
|
|
20
|
+
elementKey,
|
|
19
21
|
}: ILayoutRenderer_Signature) {
|
|
20
|
-
const { formRef, formDataId, companyKey } = formContext
|
|
22
|
+
const { formRef, formDataId, companyKey, formId } = formContext
|
|
23
|
+
const { t } = useTranslation(formId)
|
|
21
24
|
const sigCanvasRef = useRef<SignatureCanvas>(null)
|
|
22
25
|
const savedSignatureBlobName = Form.useWatch(formItem.path, formRef)
|
|
23
26
|
const { isGeneratingPdf, baseServerUrl } = useFormPreservedItemValues(formRef)
|
|
@@ -35,11 +38,11 @@ function LayoutRenderer_Signature({
|
|
|
35
38
|
return (
|
|
36
39
|
<Form.Item
|
|
37
40
|
name={formItem.name}
|
|
38
|
-
rules={Array.isArray(validations) ? mapToFormItemRules(validations,
|
|
41
|
+
rules={Array.isArray(validations) ? mapToFormItemRules(elementKey, validations, formId) : []}
|
|
39
42
|
label={
|
|
40
43
|
!elementProps.hasNoLabel && (
|
|
41
44
|
<div className="flex items-center gap-1">
|
|
42
|
-
<span>{
|
|
45
|
+
<span>{t({ key: elementKey, type: TranslationTextTypeEnum.Label })}</span>
|
|
43
46
|
<Button_FillerPortal
|
|
44
47
|
link
|
|
45
48
|
onClick={() => {
|
|
@@ -10,6 +10,9 @@ import { saveFile } from '../../../../functions/forms/form'
|
|
|
10
10
|
import { IElementBaseProps } from '.'
|
|
11
11
|
import { IFormContext } from '../1-row'
|
|
12
12
|
import { ELEMENTS_DEFAULT_CLASS } from '../../../../constants'
|
|
13
|
+
import { TranslationTextSubTypeEnum, TranslationTextTypeEnum } from '../../../../enums'
|
|
14
|
+
import { useTranslation } from '../../../common/custom-hooks/use-translation.hook/hook'
|
|
15
|
+
import { translationStore } from '../../../common/custom-hooks/use-translation.hook/store'
|
|
13
16
|
|
|
14
17
|
const { VITE_API_BASE_URL } = import.meta.env
|
|
15
18
|
|
|
@@ -19,7 +22,9 @@ function LayoutRenderer_FileUpload({
|
|
|
19
22
|
elementProps,
|
|
20
23
|
uploadRules,
|
|
21
24
|
isDisabled,
|
|
25
|
+
elementKey,
|
|
22
26
|
}: ILayoutRenderer_FileUpload) {
|
|
27
|
+
const { t } = useTranslation(formContext.formId)
|
|
23
28
|
const { formRef, companyKey } = formContext
|
|
24
29
|
const { warningModal, confirmModal } = useNotification()
|
|
25
30
|
const [loading, setLoading] = useState(false)
|
|
@@ -56,7 +61,7 @@ function LayoutRenderer_FileUpload({
|
|
|
56
61
|
}
|
|
57
62
|
|
|
58
63
|
const beforeUpload = useCallback((file: any) => {
|
|
59
|
-
const ruleMsg = getUploadRuleErrorMsg(file, uploadRules)
|
|
64
|
+
const ruleMsg = getUploadRuleErrorMsg(elementKey, file, uploadRules, formContext.formId)
|
|
60
65
|
if (ruleMsg) {
|
|
61
66
|
warningModal({ title: '', content: ruleMsg, okText: 'Okay' })
|
|
62
67
|
return false
|
|
@@ -65,17 +70,23 @@ function LayoutRenderer_FileUpload({
|
|
|
65
70
|
return true
|
|
66
71
|
}, [])
|
|
67
72
|
|
|
73
|
+
const [label, placeholder, hint] = t([
|
|
74
|
+
{ key: elementKey, type: TranslationTextTypeEnum.Label },
|
|
75
|
+
{ key: elementKey, type: TranslationTextTypeEnum.Placeholder },
|
|
76
|
+
{ key: elementKey, type: TranslationTextTypeEnum.Description, subType: TranslationTextSubTypeEnum.Secondary },
|
|
77
|
+
])
|
|
78
|
+
|
|
68
79
|
return (
|
|
69
80
|
<>
|
|
70
81
|
<Form.Item name={formItem.name} labelAlign="left" className="hidden">
|
|
71
82
|
<Input />
|
|
72
83
|
</Form.Item>
|
|
73
|
-
{!elementProps.hasNoLabel &&
|
|
84
|
+
{!elementProps.hasNoLabel && label && <span>{label}</span>}
|
|
74
85
|
{savedSignatureBlobName ? (
|
|
75
86
|
elementProps.isDragger ? (
|
|
76
87
|
<div className="flex flex-col items-center p-4 bg-background rounded-md">
|
|
77
88
|
<FaFileCircleCheck size={24} className="text-success" />
|
|
78
|
-
<span className="text-secondary">{
|
|
89
|
+
<span className="text-secondary">{placeholder ?? 'Uploaded'}:</span>
|
|
79
90
|
<div className="flex gap-2 items-center">
|
|
80
91
|
<u className="text-neutral">{savedSignatureBlobName}</u>
|
|
81
92
|
<Button_FillerPortal
|
|
@@ -109,7 +120,7 @@ function LayoutRenderer_FileUpload({
|
|
|
109
120
|
) : (
|
|
110
121
|
<div className="flex items-center gap-2 px-4 py-1 bg-background rounded-md">
|
|
111
122
|
<FaFileCircleCheck size={16} className="text-success" />
|
|
112
|
-
<span className="text-secondary">{
|
|
123
|
+
<span className="text-secondary">{placeholder ?? 'Uploaded'}:</span>
|
|
113
124
|
<u className="text-neutral">{savedSignatureBlobName}</u>
|
|
114
125
|
<div className="flex gap-2 items-center ml-auto">
|
|
115
126
|
<Button_FillerPortal
|
|
@@ -146,8 +157,8 @@ function LayoutRenderer_FileUpload({
|
|
|
146
157
|
<Upload.Dragger {...uploadProps} disabled={isDisabled} beforeUpload={(file) => beforeUpload(file)}>
|
|
147
158
|
<div className="flex flex-col items-center">
|
|
148
159
|
<FaUpload size={24} className="text-primary" />
|
|
149
|
-
<span className="font-semibold mt-2 text-secondary">{
|
|
150
|
-
<span className="text-opacity-50 text-neutral text-12">{
|
|
160
|
+
<span className="font-semibold mt-2 text-secondary">{placeholder}</span>
|
|
161
|
+
<span className="text-opacity-50 text-neutral text-12">{hint}</span>
|
|
151
162
|
</div>
|
|
152
163
|
</Upload.Dragger>
|
|
153
164
|
</Spin>
|
|
@@ -156,7 +167,7 @@ function LayoutRenderer_FileUpload({
|
|
|
156
167
|
<Upload {...uploadProps} disabled={isDisabled} beforeUpload={(file) => beforeUpload(file)}>
|
|
157
168
|
<Button_FillerPortal disabled={isDisabled} outline onClick={() => {}} className="w-full">
|
|
158
169
|
<FaUpload />
|
|
159
|
-
{
|
|
170
|
+
{placeholder}
|
|
160
171
|
</Button_FillerPortal>
|
|
161
172
|
</Upload>
|
|
162
173
|
</Spin>
|
|
@@ -173,7 +184,12 @@ type ILayoutRenderer_FileUpload = {
|
|
|
173
184
|
elementProps: IFileUploadElementProps
|
|
174
185
|
} & IElementBaseProps
|
|
175
186
|
|
|
176
|
-
const getUploadRuleErrorMsg = (
|
|
187
|
+
const getUploadRuleErrorMsg = (
|
|
188
|
+
elementKey: string = '',
|
|
189
|
+
file: any,
|
|
190
|
+
uploadRules?: IFileUploadElementRules,
|
|
191
|
+
formId?: number,
|
|
192
|
+
): null | string => {
|
|
177
193
|
if (!uploadRules) return null
|
|
178
194
|
|
|
179
195
|
if (
|
|
@@ -181,12 +197,30 @@ const getUploadRuleErrorMsg = (file: any, uploadRules?: IFileUploadElementRules)
|
|
|
181
197
|
uploadRules.allowedTypes.types.length > 0 &&
|
|
182
198
|
!uploadRules.allowedTypes.types.includes(file.type)
|
|
183
199
|
) {
|
|
184
|
-
|
|
200
|
+
const errorMsg = translationStore.translate(
|
|
201
|
+
{
|
|
202
|
+
key: elementKey,
|
|
203
|
+
type: TranslationTextTypeEnum.ValidationError,
|
|
204
|
+
subType: TranslationTextSubTypeEnum.UploadFileType,
|
|
205
|
+
},
|
|
206
|
+
formId,
|
|
207
|
+
)
|
|
208
|
+
return errorMsg ?? 'Invalid file type!'
|
|
185
209
|
}
|
|
186
210
|
|
|
187
211
|
if (uploadRules.maxSize && uploadRules.maxSize.size > 0) {
|
|
188
212
|
const fileSizeInMb = file.size / 1024 / 1024
|
|
189
|
-
if (fileSizeInMb > uploadRules.maxSize.size)
|
|
213
|
+
if (fileSizeInMb > uploadRules.maxSize.size) {
|
|
214
|
+
const errorMsg = translationStore.translate(
|
|
215
|
+
{
|
|
216
|
+
key: elementKey,
|
|
217
|
+
type: TranslationTextTypeEnum.ValidationError,
|
|
218
|
+
subType: TranslationTextSubTypeEnum.UploadSizeLimit,
|
|
219
|
+
},
|
|
220
|
+
formId,
|
|
221
|
+
)
|
|
222
|
+
return errorMsg ?? 'File is too large!'
|
|
223
|
+
}
|
|
190
224
|
}
|
|
191
225
|
|
|
192
226
|
return null
|
|
@@ -1,5 +1,10 @@
|
|
|
1
1
|
import { memo, ReactNode, useCallback, useEffect, useMemo, useRef, useState } from 'react'
|
|
2
|
-
import {
|
|
2
|
+
import {
|
|
3
|
+
PageViewTypEnum,
|
|
4
|
+
FieldElementOptionSourceEnum,
|
|
5
|
+
FilterConfigTypeEnum,
|
|
6
|
+
TranslationTextTypeEnum,
|
|
7
|
+
} from '../../../../enums'
|
|
3
8
|
import { IElementBaseProps } from '.'
|
|
4
9
|
import { LayoutRenderer_FieldElement } from './2-field-element'
|
|
5
10
|
import { getElementGeneralizedProps } from '../../../../functions/forms/get-element-props'
|
|
@@ -24,13 +29,16 @@ import {
|
|
|
24
29
|
ISelectElement,
|
|
25
30
|
ISelectElementProps,
|
|
26
31
|
} from '../../../../types'
|
|
32
|
+
import { useTranslation } from '../../../common/custom-hooks/use-translation.hook/hook'
|
|
27
33
|
|
|
28
34
|
function LayoutRenderer_FieldsWithOptions({
|
|
29
35
|
formContext,
|
|
30
36
|
formItem,
|
|
31
37
|
elementData,
|
|
32
38
|
isDisabled,
|
|
39
|
+
elementKey,
|
|
33
40
|
}: ILayoutRenderer_FieldsWithOptions) {
|
|
41
|
+
const { t } = useTranslation(formContext.formId)
|
|
34
42
|
const navigate = useNavigate()
|
|
35
43
|
const { getFormById } = useFindDynamiForm()
|
|
36
44
|
|
|
@@ -194,7 +202,12 @@ function LayoutRenderer_FieldsWithOptions({
|
|
|
194
202
|
if (!props.optionSource) return
|
|
195
203
|
|
|
196
204
|
if (props.optionSource.type === FieldElementOptionSourceEnum.Static) {
|
|
197
|
-
setOptions(
|
|
205
|
+
setOptions(
|
|
206
|
+
props.optionSource.options.map((op: IFormLayoutFieldOption) => ({
|
|
207
|
+
value: op.id,
|
|
208
|
+
label: t({ key: elementKey, type: TranslationTextTypeEnum.OptionValue, subType: op.id }),
|
|
209
|
+
})),
|
|
210
|
+
)
|
|
198
211
|
setLoading(false)
|
|
199
212
|
} else {
|
|
200
213
|
switch (props.optionSource.type) {
|
|
@@ -213,7 +226,7 @@ function LayoutRenderer_FieldsWithOptions({
|
|
|
213
226
|
return
|
|
214
227
|
}
|
|
215
228
|
}
|
|
216
|
-
}, [props.optionSource, fetchLinkedFormData, fetchFormData, cachedConfig])
|
|
229
|
+
}, [props.optionSource, fetchLinkedFormData, fetchFormData, cachedConfig, elementKey])
|
|
217
230
|
|
|
218
231
|
useEffect(() => {
|
|
219
232
|
return () => {
|
|
@@ -223,14 +236,20 @@ function LayoutRenderer_FieldsWithOptions({
|
|
|
223
236
|
}
|
|
224
237
|
}, [])
|
|
225
238
|
|
|
239
|
+
const [label, placeholder] = t([
|
|
240
|
+
{ key: elementData.key, type: TranslationTextTypeEnum.Label },
|
|
241
|
+
{ key: elementData.key, type: TranslationTextTypeEnum.Placeholder },
|
|
242
|
+
])
|
|
243
|
+
|
|
226
244
|
return (
|
|
227
245
|
<div className="relative">
|
|
228
|
-
<LayoutRenderer_FieldElement formRef={formRef}>
|
|
246
|
+
<LayoutRenderer_FieldElement formRef={formRef} elementKey={elementData.key} formId={formContext.formId}>
|
|
229
247
|
{() => ({
|
|
230
248
|
...props,
|
|
231
|
-
|
|
249
|
+
placeholder,
|
|
250
|
+
label: label && (
|
|
232
251
|
<div className="flex items-center gap-2 w-full">
|
|
233
|
-
<span>{
|
|
252
|
+
<span>{label}</span>
|
|
234
253
|
{(props as ISelectElementProps).goToDetails?.enabled &&
|
|
235
254
|
(props as ISelectElementProps).goToDetails?.text &&
|
|
236
255
|
selectedValue && (
|
|
@@ -14,6 +14,7 @@ import {
|
|
|
14
14
|
IFormRelationshipConfig,
|
|
15
15
|
IFormRelationshipConfig_OneToMany,
|
|
16
16
|
} from '../../../../types'
|
|
17
|
+
import { translationStore } from '../../../common/custom-hooks/use-translation.hook/store'
|
|
17
18
|
|
|
18
19
|
function LayoutRenderer_LoadFormData({ formContext, elementProps }: ILayoutRenderer_LoadFormData) {
|
|
19
20
|
const { formId, formRef, formDataId } = formContext
|
|
@@ -68,6 +69,12 @@ function LayoutRenderer_LoadFormData({ formContext, elementProps }: ILayoutRende
|
|
|
68
69
|
[joins, baseFormId, initialFilter],
|
|
69
70
|
)
|
|
70
71
|
|
|
72
|
+
useEffect(() => {
|
|
73
|
+
if (!cachedConfig?.id || !cachedConfig?.translations) return
|
|
74
|
+
|
|
75
|
+
translationStore.setTranslations(cachedConfig.id, cachedConfig.translations)
|
|
76
|
+
}, [cachedConfig])
|
|
77
|
+
|
|
71
78
|
useEffect(() => {
|
|
72
79
|
if (isNewFormDataPage(formDataId) || !cachedConfig) return
|
|
73
80
|
|
|
@@ -1,13 +1,14 @@
|
|
|
1
1
|
import { Divider, Spin } from 'antd'
|
|
2
2
|
import { lazy, memo, ReactElement, useMemo } from 'react'
|
|
3
3
|
import { getElementGeneralizedProps } from '../../../../functions/forms/get-element-props'
|
|
4
|
-
import { ElementTypeEnum, TextElementTypeEnum } from '../../../../enums'
|
|
4
|
+
import { ElementTypeEnum, TextElementTypeEnum, TranslationTextTypeEnum } from '../../../../enums'
|
|
5
5
|
import useGetCurrentBreakpoint from '../../../common/custom-hooks/use-window-width.hook'
|
|
6
6
|
import { findMaxAndMinValues, kebabCaseToCamelCase } from '../../../../functions/forms'
|
|
7
7
|
import { IFormContext } from '../1-row'
|
|
8
8
|
import { LayoutRenderer_FieldElement } from './2-field-element'
|
|
9
9
|
import { IDynamicButton_DisplayStateProps } from './1-dynamic-button'
|
|
10
10
|
import { ELEMENTS_DEFAULT_CLASS } from '../../../../constants'
|
|
11
|
+
import { useTranslation } from '../../../common/custom-hooks/use-translation.hook/hook'
|
|
11
12
|
import {
|
|
12
13
|
useIsNodeDisabled,
|
|
13
14
|
useIsNodeHidden,
|
|
@@ -17,7 +18,6 @@ import {
|
|
|
17
18
|
IFormDataLoadElementProps,
|
|
18
19
|
IDndLayoutElement,
|
|
19
20
|
IReadFieldDataElementProps,
|
|
20
|
-
IBreadcrumbElementProps,
|
|
21
21
|
IFileUploadElement,
|
|
22
22
|
IReadFieldDataElement,
|
|
23
23
|
ITextElement,
|
|
@@ -37,6 +37,7 @@ const LayoutRenderer_LoadFormData = lazy(() => import('./9-form-data-render'))
|
|
|
37
37
|
const LayoutRenderer_CurrencyField = lazy(() => import('./10-currency'))
|
|
38
38
|
const LayoutRenderer_Breadcrumb = lazy(() => import('./11-breadcrumb'))
|
|
39
39
|
const LayoutRenderer_PickerField = lazy(() => import('./12-picker-field'))
|
|
40
|
+
const LayoutRenderer_LanguageSelector = lazy(() => import('./13-language-selector'))
|
|
40
41
|
|
|
41
42
|
interface ILayoutRendererElement {
|
|
42
43
|
basePath: (string | number)[]
|
|
@@ -46,18 +47,20 @@ interface ILayoutRendererElement {
|
|
|
46
47
|
renderButton?: (props: IDynamicButton_DisplayStateProps) => ReactElement
|
|
47
48
|
}
|
|
48
49
|
|
|
49
|
-
function LayoutRendererElementWrapper(
|
|
50
|
-
const
|
|
50
|
+
function LayoutRendererElementWrapper(props: ILayoutRendererElement) {
|
|
51
|
+
const { t } = useTranslation(props.formContext.formId)
|
|
52
|
+
const isHidden = useIsNodeHidden(props.elementData.key)
|
|
51
53
|
|
|
54
|
+
const description = t({ key: props.elementData.key, type: TranslationTextTypeEnum.Description })
|
|
52
55
|
return (
|
|
53
56
|
<div
|
|
54
57
|
className={`${ELEMENTS_DEFAULT_CLASS.ElementContainer} flex flex-col ${isHidden ? 'hidden' : ''}`}
|
|
55
|
-
id={
|
|
58
|
+
id={props.elementData.key}
|
|
56
59
|
>
|
|
57
|
-
<LayoutRendererElement {...
|
|
58
|
-
{
|
|
60
|
+
<LayoutRendererElement {...props} />
|
|
61
|
+
{description && (
|
|
59
62
|
<span className={`${ELEMENTS_DEFAULT_CLASS.ElementDescription} text-neutral text-12 italic`}>
|
|
60
|
-
{
|
|
63
|
+
{description}
|
|
61
64
|
</span>
|
|
62
65
|
)}
|
|
63
66
|
</div>
|
|
@@ -76,6 +79,7 @@ function LayoutRendererElement({
|
|
|
76
79
|
const currentBreakpoint = useGetCurrentBreakpoint()
|
|
77
80
|
const props = getElementGeneralizedProps(elementData.props)
|
|
78
81
|
const { elementType, key, validations } = elementData
|
|
82
|
+
const { t } = useTranslation(formContext.formId)
|
|
79
83
|
const { maxValue, minValue } = useMemo(() => findMaxAndMinValues(validations), [validations])
|
|
80
84
|
const isElementDisabled = useIsNodeDisabled(key)
|
|
81
85
|
|
|
@@ -89,9 +93,17 @@ function LayoutRendererElement({
|
|
|
89
93
|
}, [basePath, key])
|
|
90
94
|
|
|
91
95
|
const elementBaseProps = useMemo(
|
|
92
|
-
() => ({
|
|
93
|
-
|
|
96
|
+
() => ({
|
|
97
|
+
formItem: { name: formItemName, path: fullPath },
|
|
98
|
+
isDisabled: isElementDisabled,
|
|
99
|
+
elementKey: key,
|
|
100
|
+
}),
|
|
101
|
+
[formItemName, fullPath, isElementDisabled, key],
|
|
94
102
|
)
|
|
103
|
+
const [label, placeholder] = t([
|
|
104
|
+
{ key, type: TranslationTextTypeEnum.Label },
|
|
105
|
+
{ key, type: TranslationTextTypeEnum.Placeholder },
|
|
106
|
+
])
|
|
95
107
|
|
|
96
108
|
const renderers: Partial<Record<ElementTypeEnum, () => ReactElement>> = {
|
|
97
109
|
[ElementTypeEnum.RichTextEditor]: () => {
|
|
@@ -140,12 +152,13 @@ function LayoutRendererElement({
|
|
|
140
152
|
elementProps={props as IReadFieldDataElementProps}
|
|
141
153
|
style={(elementData as IReadFieldDataElement).style?.[currentBreakpoint]}
|
|
142
154
|
formContext={formContext}
|
|
155
|
+
{...elementBaseProps}
|
|
143
156
|
/>
|
|
144
157
|
),
|
|
145
158
|
|
|
146
159
|
[ElementTypeEnum.Divider]: () => (
|
|
147
160
|
<Divider className="m-0" orientation={props.labelPlacement}>
|
|
148
|
-
{
|
|
161
|
+
{label && <span className="text-12">{label}</span>}
|
|
149
162
|
</Divider>
|
|
150
163
|
),
|
|
151
164
|
|
|
@@ -157,13 +170,13 @@ function LayoutRendererElement({
|
|
|
157
170
|
if (props.type === TextElementTypeEnum.Plain)
|
|
158
171
|
return (
|
|
159
172
|
<div style={textStyle}>
|
|
160
|
-
{
|
|
173
|
+
{label}{' '}
|
|
161
174
|
{(typeof dataCount === 'number' || dataCount === 'pending') && (
|
|
162
175
|
<>{dataCount === 'pending' ? <Spin size="small" /> : `(${dataCount})`}</>
|
|
163
176
|
)}
|
|
164
177
|
</div>
|
|
165
178
|
)
|
|
166
|
-
return <div dangerouslySetInnerHTML={{ __html:
|
|
179
|
+
return <div dangerouslySetInnerHTML={{ __html: label }} style={textStyle} />
|
|
167
180
|
},
|
|
168
181
|
|
|
169
182
|
[ElementTypeEnum.Select]: () => (
|
|
@@ -198,7 +211,7 @@ function LayoutRendererElement({
|
|
|
198
211
|
/>
|
|
199
212
|
),
|
|
200
213
|
|
|
201
|
-
[ElementTypeEnum.Breadcrumb]: () => <LayoutRenderer_Breadcrumb
|
|
214
|
+
[ElementTypeEnum.Breadcrumb]: () => <LayoutRenderer_Breadcrumb formId={formContext.formId} {...elementBaseProps} />,
|
|
202
215
|
|
|
203
216
|
[ElementTypeEnum.Placeholder]: () => <div />,
|
|
204
217
|
|
|
@@ -225,15 +238,21 @@ function LayoutRendererElement({
|
|
|
225
238
|
{...elementBaseProps}
|
|
226
239
|
/>
|
|
227
240
|
),
|
|
241
|
+
|
|
242
|
+
[ElementTypeEnum.LanguageSwitch]: () => (
|
|
243
|
+
<LayoutRenderer_LanguageSelector formId={formContext.formId} {...elementBaseProps} />
|
|
244
|
+
),
|
|
228
245
|
}
|
|
229
246
|
|
|
230
247
|
const renderer = renderers[elementType]
|
|
231
248
|
return renderer ? (
|
|
232
249
|
renderer()
|
|
233
250
|
) : (
|
|
234
|
-
<LayoutRenderer_FieldElement formRef={formContext.formRef}>
|
|
251
|
+
<LayoutRenderer_FieldElement formRef={formContext.formRef} elementKey={key} formId={formContext.formId}>
|
|
235
252
|
{() => ({
|
|
236
253
|
...props,
|
|
254
|
+
label,
|
|
255
|
+
placeholder,
|
|
237
256
|
numberFieldMax: maxValue,
|
|
238
257
|
numberFieldMin: minValue,
|
|
239
258
|
type: elementType,
|
|
@@ -250,4 +269,5 @@ function LayoutRendererElement({
|
|
|
250
269
|
export interface IElementBaseProps {
|
|
251
270
|
formItem: { name: string | (string | number)[]; path: string | (string | number)[] }
|
|
252
271
|
isDisabled: boolean
|
|
272
|
+
elementKey: string
|
|
253
273
|
}
|
package/src/constants.ts
CHANGED
|
@@ -133,7 +133,7 @@ export const DEFAULT_CONFIG = {
|
|
|
133
133
|
},
|
|
134
134
|
siteIdentity: { logoUrl: '/favicon.png', iconUrl: '/favicon.png', title: 'Site Title' },
|
|
135
135
|
siteMenus: [],
|
|
136
|
-
siteLanguages: [{ value:
|
|
136
|
+
siteLanguages: [{ value: CountryEnum.US, label: 'English', defaultSelected: true }],
|
|
137
137
|
loginLayout: {
|
|
138
138
|
layout: 'center',
|
|
139
139
|
backgroundType: 'color',
|
|
@@ -182,6 +182,7 @@ export const DEFAULT_FORM_SCHEMA_DATA: IFormSchema = {
|
|
|
182
182
|
},
|
|
183
183
|
generateConfig: { submissionPdf: { enabled: false } },
|
|
184
184
|
relationships: [],
|
|
185
|
+
translations: {},
|
|
185
186
|
}
|
|
186
187
|
export const REACT_QUERY_CLIENT = new QueryClient({
|
|
187
188
|
defaultOptions: {
|
package/src/enums/index.ts
CHANGED
|
@@ -36,6 +36,7 @@ export enum ElementTypeEnum {
|
|
|
36
36
|
OrderedList = 'OrderedList',
|
|
37
37
|
UnorderedList = 'UnorderedList',
|
|
38
38
|
Image = 'Image',
|
|
39
|
+
LanguageSwitch = 'LanguageSwitch',
|
|
39
40
|
}
|
|
40
41
|
export enum TextElementTypeEnum {
|
|
41
42
|
Plain = 'Plain',
|
|
@@ -63,6 +64,10 @@ export enum LOCAL_STORAGE_KEYS_ENUM {
|
|
|
63
64
|
Breadcrumb = '83dead30755d4b2555a44a62c1de87a7',
|
|
64
65
|
DisabledElements = '46ef33c492a6abccc4c89dcb485ffdf9',
|
|
65
66
|
HiddenElements = 'c268df125295ca301609b3984d370c3e',
|
|
67
|
+
Languages = '63597792947dcaed2025e9b61aa57e88',
|
|
68
|
+
SelectedLanguage = 'b7cce86301b745f05631bf5c24524e23',
|
|
69
|
+
DefaultLanguage = '848ec029fe071913b5cfa0231493eff3',
|
|
70
|
+
FormTranslations = 'adeac05cba725ab79bf7075a66db6c05',
|
|
66
71
|
}
|
|
67
72
|
export enum CountryEnum {
|
|
68
73
|
US = 'us',
|
|
@@ -109,3 +114,26 @@ export enum RelationalOperatorsEnum {
|
|
|
109
114
|
In = '@in',
|
|
110
115
|
NotIn = '@nin',
|
|
111
116
|
}
|
|
117
|
+
export enum TranslationTextTypeEnum {
|
|
118
|
+
Label = 'L',
|
|
119
|
+
Placeholder = 'P',
|
|
120
|
+
Description = 'D',
|
|
121
|
+
ValidationError = 'VE',
|
|
122
|
+
OptionValue = 'OP',
|
|
123
|
+
}
|
|
124
|
+
export enum TranslationTextSubTypeEnum {
|
|
125
|
+
RepeatButtonAdd = 'RepeatAdd',
|
|
126
|
+
RepeatButtonRemove = 'RepeatRemove',
|
|
127
|
+
Secondary = '2',
|
|
128
|
+
UploadFileType = 'FileType',
|
|
129
|
+
UploadSizeLimit = 'SizeLimit',
|
|
130
|
+
BreadcrumbDetail = 'Detail',
|
|
131
|
+
BreadcrumbNew = 'New',
|
|
132
|
+
BreadcrumbList = 'List',
|
|
133
|
+
ConfirmationMsg = 'BtnConfMsg',
|
|
134
|
+
ConfirmationOk = 'BtnConfOk',
|
|
135
|
+
ConfirmationCancel = 'BtnConfCcl',
|
|
136
|
+
SuccessMsg = 'BtnSucMsg',
|
|
137
|
+
ErrorMsg = 'BtnErrMsg',
|
|
138
|
+
ButtonsGroup = 'BtnsGroup',
|
|
139
|
+
}
|