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
package/package.json
CHANGED
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import { useCallback } from 'react'
|
|
2
|
+
import { useSyncExternalStore } from 'react'
|
|
3
|
+
import { translationStore } from './store'
|
|
4
|
+
import { TranslationTextTypeEnum } from '../../../../enums'
|
|
5
|
+
|
|
6
|
+
export interface ITranslateFuncParam {
|
|
7
|
+
key?: string
|
|
8
|
+
type: TranslationTextTypeEnum
|
|
9
|
+
subType?: string
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
export function useTranslation(formId?: number) {
|
|
13
|
+
const { selectedLanguage, translations } = useSyncExternalStore(
|
|
14
|
+
translationStore.subscribe.bind(translationStore),
|
|
15
|
+
translationStore.getSnapshot.bind(translationStore),
|
|
16
|
+
)
|
|
17
|
+
|
|
18
|
+
const t = useCallback(
|
|
19
|
+
(params: ITranslateFuncParam | ITranslateFuncParam[]) => {
|
|
20
|
+
if (Array.isArray(params)) return params.map((p) => (p.key ? translationStore.translate(p, formId) : ''))
|
|
21
|
+
|
|
22
|
+
return params.key ? translationStore.translate(params, formId) : ''
|
|
23
|
+
},
|
|
24
|
+
[selectedLanguage, translations, formId],
|
|
25
|
+
)
|
|
26
|
+
|
|
27
|
+
return { t, selectedLanguage }
|
|
28
|
+
}
|
|
@@ -0,0 +1,130 @@
|
|
|
1
|
+
import { CountryEnum, LOCAL_STORAGE_KEYS_ENUM } from '../../../../enums'
|
|
2
|
+
import { ITranslations } from '../../../../types'
|
|
3
|
+
import { ITranslateFuncParam } from './hook'
|
|
4
|
+
|
|
5
|
+
interface Snapshot {
|
|
6
|
+
selectedLanguage: string
|
|
7
|
+
translations: ITranslations
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
type Listener = () => void
|
|
11
|
+
const listeners = new Set<Listener>()
|
|
12
|
+
|
|
13
|
+
// —— Monkey-patch localStorage.setItem to fire translationUpdate & storage events ——
|
|
14
|
+
;(function () {
|
|
15
|
+
const originalSetItem = localStorage.setItem
|
|
16
|
+
localStorage.setItem = function (key: string, value: string) {
|
|
17
|
+
const oldValue = localStorage.getItem(key)
|
|
18
|
+
originalSetItem.call(this, key, value)
|
|
19
|
+
if (key === LOCAL_STORAGE_KEYS_ENUM.SelectedLanguage || key === LOCAL_STORAGE_KEYS_ENUM.FormTranslations) {
|
|
20
|
+
// fire native StorageEvent for useSyncExternalStore cross-tab compatibility
|
|
21
|
+
window.dispatchEvent(
|
|
22
|
+
new StorageEvent('storage', {
|
|
23
|
+
key,
|
|
24
|
+
oldValue,
|
|
25
|
+
newValue: value,
|
|
26
|
+
storageArea: localStorage,
|
|
27
|
+
url: window.location.href,
|
|
28
|
+
}),
|
|
29
|
+
)
|
|
30
|
+
// fire custom update for same-tab subscribers
|
|
31
|
+
window.dispatchEvent(new Event('translationUpdate'))
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
})()
|
|
35
|
+
|
|
36
|
+
// —— Internal cache for memoized snapshots ——
|
|
37
|
+
let lastLanguage = ''
|
|
38
|
+
let lastRaw = ''
|
|
39
|
+
let lastTranslations: ITranslations = {}
|
|
40
|
+
let lastSnapshot: Snapshot = { selectedLanguage: '', translations: {} }
|
|
41
|
+
|
|
42
|
+
function readLanguage(): string {
|
|
43
|
+
return localStorage.getItem(LOCAL_STORAGE_KEYS_ENUM.SelectedLanguage) ?? ''
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
function readRawTranslations(): string {
|
|
47
|
+
return localStorage.getItem(LOCAL_STORAGE_KEYS_ENUM.FormTranslations) ?? ''
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
function parseTranslations(raw: string): ITranslations {
|
|
51
|
+
try {
|
|
52
|
+
return raw ? JSON.parse(raw) : {}
|
|
53
|
+
} catch {
|
|
54
|
+
console.error('[translationStore] invalid JSON in FormTranslations')
|
|
55
|
+
return {}
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
function notifyAll() {
|
|
60
|
+
listeners.forEach((fn) => fn())
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
// Cross-tab storage events
|
|
64
|
+
window.addEventListener('storage', (e) => {
|
|
65
|
+
if (
|
|
66
|
+
e.storageArea === localStorage &&
|
|
67
|
+
(e.key === LOCAL_STORAGE_KEYS_ENUM.SelectedLanguage || e.key === LOCAL_STORAGE_KEYS_ENUM.FormTranslations)
|
|
68
|
+
) {
|
|
69
|
+
notifyAll()
|
|
70
|
+
}
|
|
71
|
+
})
|
|
72
|
+
// Same-tab custom events
|
|
73
|
+
window.addEventListener('translationUpdate', notifyAll)
|
|
74
|
+
|
|
75
|
+
export const translationStore = {
|
|
76
|
+
getSnapshot(): Snapshot {
|
|
77
|
+
const currentLanguage = readLanguage()
|
|
78
|
+
const currentRaw = readRawTranslations()
|
|
79
|
+
|
|
80
|
+
if (currentLanguage !== lastLanguage || currentRaw !== lastRaw) {
|
|
81
|
+
lastLanguage = currentLanguage
|
|
82
|
+
lastRaw = currentRaw
|
|
83
|
+
lastTranslations = parseTranslations(currentRaw)
|
|
84
|
+
lastSnapshot = { selectedLanguage: lastLanguage, translations: lastTranslations }
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
return lastSnapshot
|
|
88
|
+
},
|
|
89
|
+
|
|
90
|
+
subscribe(fn: Listener): () => void {
|
|
91
|
+
listeners.add(fn)
|
|
92
|
+
return () => {
|
|
93
|
+
listeners.delete(fn)
|
|
94
|
+
}
|
|
95
|
+
},
|
|
96
|
+
|
|
97
|
+
translate({ key = '', type, subType }: ITranslateFuncParam, formId?: number): string {
|
|
98
|
+
const { selectedLanguage, translations } = this.getSnapshot()
|
|
99
|
+
const formTranslations = formId ? (translations[formId] as ITranslations) : undefined
|
|
100
|
+
const lookup = `${key}__${type}${subType ? `__${subType}` : ''}`
|
|
101
|
+
|
|
102
|
+
if (formTranslations && selectedLanguage) {
|
|
103
|
+
const lookupTranslation = formTranslations[lookup]
|
|
104
|
+
const inSelected = lookupTranslation?.[selectedLanguage as CountryEnum]
|
|
105
|
+
if (inSelected) return inSelected
|
|
106
|
+
|
|
107
|
+
// fallback to default language
|
|
108
|
+
const defaultLang = localStorage.getItem(LOCAL_STORAGE_KEYS_ENUM.DefaultLanguage)
|
|
109
|
+
return lookupTranslation?.[defaultLang as CountryEnum] ?? ''
|
|
110
|
+
}
|
|
111
|
+
return ''
|
|
112
|
+
},
|
|
113
|
+
|
|
114
|
+
getLanguages() {
|
|
115
|
+
const languages = localStorage.getItem(LOCAL_STORAGE_KEYS_ENUM.Languages)
|
|
116
|
+
|
|
117
|
+
if (languages) return JSON.parse(languages)
|
|
118
|
+
return []
|
|
119
|
+
},
|
|
120
|
+
|
|
121
|
+
setTranslations(formId: number, obj: ITranslations) {
|
|
122
|
+
const raw = localStorage.getItem(LOCAL_STORAGE_KEYS_ENUM.FormTranslations) || ''
|
|
123
|
+
const parsed = raw ? JSON.parse(raw) : {}
|
|
124
|
+
localStorage.setItem(LOCAL_STORAGE_KEYS_ENUM.FormTranslations, JSON.stringify({ ...parsed, [formId]: obj }))
|
|
125
|
+
},
|
|
126
|
+
|
|
127
|
+
setLanguage(lang: CountryEnum) {
|
|
128
|
+
localStorage.setItem(LOCAL_STORAGE_KEYS_ENUM.SelectedLanguage, lang)
|
|
129
|
+
},
|
|
130
|
+
}
|
|
@@ -7,6 +7,7 @@ import { IFormJoin, IFormDataListData, IFormDataListConfig } from '../../../type
|
|
|
7
7
|
import { getProjectionKey, mergeJoins } from '../../../functions'
|
|
8
8
|
import { useCacheFormLayoutConfig } from '../../common/custom-hooks/use-cache-form-layout-config.hook'
|
|
9
9
|
import { useNotification } from '../../common/custom-hooks'
|
|
10
|
+
import { translationStore } from '../../common/custom-hooks/use-translation.hook/store'
|
|
10
11
|
|
|
11
12
|
export function FormDataListComponent({
|
|
12
13
|
formId,
|
|
@@ -41,6 +42,12 @@ export function FormDataListComponent({
|
|
|
41
42
|
defaultFilterReqDataRef.current = undefined
|
|
42
43
|
}, [location.pathname])
|
|
43
44
|
|
|
45
|
+
useEffect(() => {
|
|
46
|
+
if (!cachedConfig?.id || !cachedConfig?.translations) return
|
|
47
|
+
|
|
48
|
+
translationStore.setTranslations(cachedConfig.id, cachedConfig.translations)
|
|
49
|
+
}, [cachedConfig])
|
|
50
|
+
|
|
44
51
|
useEffect(() => {
|
|
45
52
|
if (!formId || !cachedConfig) return
|
|
46
53
|
|
|
@@ -33,6 +33,7 @@ import {
|
|
|
33
33
|
isValidMongoDbId,
|
|
34
34
|
queryParamsToObject,
|
|
35
35
|
} from '../../../functions/forms'
|
|
36
|
+
import { translationStore } from '../../common/custom-hooks/use-translation.hook/store'
|
|
36
37
|
|
|
37
38
|
export default function FormDataDetailsComponent({
|
|
38
39
|
isPublic,
|
|
@@ -133,6 +134,12 @@ export default function FormDataDetailsComponent({
|
|
|
133
134
|
[formDataId, formDataRef],
|
|
134
135
|
)
|
|
135
136
|
|
|
137
|
+
useEffect(() => {
|
|
138
|
+
if (!cachedConfig?.id || !cachedConfig?.translations) return
|
|
139
|
+
|
|
140
|
+
translationStore.setTranslations(cachedConfig.id, cachedConfig.translations)
|
|
141
|
+
}, [cachedConfig])
|
|
142
|
+
|
|
136
143
|
useEffect(() => {
|
|
137
144
|
if (!cachedConfig?.detailsConfig) return
|
|
138
145
|
|
|
@@ -2,17 +2,26 @@ import { ReactNode, useState, useTransition } from 'react'
|
|
|
2
2
|
import { IFormDndLayoutRowHeader } from '../../../../types'
|
|
3
3
|
import { Collapse, Spin } from 'antd'
|
|
4
4
|
import RowHeader from './header'
|
|
5
|
+
import { useTranslation } from '../../../common/custom-hooks/use-translation.hook/hook'
|
|
6
|
+
import { TranslationTextTypeEnum } from '../../../../enums'
|
|
5
7
|
|
|
6
8
|
export const LayoutRowConditionalHeaderRenderer = ({
|
|
9
|
+
formId,
|
|
7
10
|
children,
|
|
8
11
|
header,
|
|
12
|
+
rowId,
|
|
9
13
|
}: {
|
|
14
|
+
formId?: number
|
|
10
15
|
children: ReactNode
|
|
11
16
|
header: IFormDndLayoutRowHeader | undefined
|
|
17
|
+
rowId: string
|
|
12
18
|
}) => {
|
|
19
|
+
const { t } = useTranslation(formId)
|
|
13
20
|
const [isCollapsed, setIsCollapsed] = useState(false)
|
|
14
21
|
const [isPendingTransition, startTransition] = useTransition()
|
|
15
22
|
|
|
23
|
+
const headerTitle = t({ key: rowId, type: TranslationTextTypeEnum.Label })
|
|
24
|
+
|
|
16
25
|
if (header?.isCollapsible)
|
|
17
26
|
return (
|
|
18
27
|
<Spin spinning={isPendingTransition}>
|
|
@@ -26,6 +35,7 @@ export const LayoutRowConditionalHeaderRenderer = ({
|
|
|
26
35
|
label: (
|
|
27
36
|
<RowHeader
|
|
28
37
|
{...header}
|
|
38
|
+
name={headerTitle as string}
|
|
29
39
|
isCollapsed={isCollapsed}
|
|
30
40
|
setIsCollapsed={() => startTransition(() => setIsCollapsed((c) => !c))}
|
|
31
41
|
/>
|
|
@@ -37,10 +47,10 @@ export const LayoutRowConditionalHeaderRenderer = ({
|
|
|
37
47
|
</Spin>
|
|
38
48
|
)
|
|
39
49
|
|
|
40
|
-
if (
|
|
50
|
+
if (headerTitle)
|
|
41
51
|
return (
|
|
42
52
|
<>
|
|
43
|
-
<RowHeader {...header} />
|
|
53
|
+
<RowHeader {...header} name={headerTitle as string} />
|
|
44
54
|
{children}
|
|
45
55
|
</>
|
|
46
56
|
)
|
|
@@ -27,8 +27,14 @@ export const LayoutRendererRow = memo(
|
|
|
27
27
|
id={rowData.id}
|
|
28
28
|
className={ELEMENTS_DEFAULT_CLASS.LayoutRowContainer}
|
|
29
29
|
>
|
|
30
|
-
<LayoutRowConditionalHeaderRenderer
|
|
30
|
+
<LayoutRowConditionalHeaderRenderer
|
|
31
|
+
formId={formContext.formId}
|
|
32
|
+
rowId={rowData.id}
|
|
33
|
+
header={rowData.props?.header}
|
|
34
|
+
>
|
|
31
35
|
<LayoutRowRepeatableRenderer
|
|
36
|
+
formId={formContext.formId}
|
|
37
|
+
rowId={rowData.id}
|
|
32
38
|
basePath={basePath}
|
|
33
39
|
formRef={formContext.formRef}
|
|
34
40
|
repeatingSection={rowData.props?.repeatingSection}
|
|
@@ -5,23 +5,37 @@ import { FaTimes } from 'react-icons/fa'
|
|
|
5
5
|
import { FaPlus } from 'react-icons/fa6'
|
|
6
6
|
import { getButtonRenderProps } from '../../../../functions/forms/get-element-props'
|
|
7
7
|
import { useNotification } from '../../../common/custom-hooks'
|
|
8
|
-
import {
|
|
8
|
+
import { useTranslation } from '../../../common/custom-hooks/use-translation.hook/hook'
|
|
9
|
+
import {
|
|
10
|
+
RepeatingSectionTabTitleTypeEnum,
|
|
11
|
+
TranslationTextSubTypeEnum,
|
|
12
|
+
TranslationTextTypeEnum,
|
|
13
|
+
} from '../../../../enums'
|
|
9
14
|
|
|
10
15
|
/** This component return its children as function to feed the children with dynamic base path */
|
|
11
16
|
export const LayoutRowRepeatableRenderer = ({
|
|
12
17
|
children,
|
|
18
|
+
formId,
|
|
19
|
+
rowId,
|
|
13
20
|
formRef,
|
|
14
21
|
basePath,
|
|
15
22
|
repeatingSection,
|
|
16
23
|
}: {
|
|
17
24
|
children: (props?: { updatedBasePath: (string | number)[]; removeButton: JSX.Element }) => JSX.Element
|
|
25
|
+
formId?: number
|
|
26
|
+
rowId: string
|
|
18
27
|
formRef?: FormInstance
|
|
19
28
|
basePath: (string | number)[]
|
|
20
29
|
repeatingSection: IRepeatingSectionConfig | undefined
|
|
21
30
|
}) => {
|
|
31
|
+
const { t } = useTranslation(formId)
|
|
22
32
|
const { confirmModal } = useNotification()
|
|
23
33
|
|
|
24
34
|
if (!!repeatingSection?.name) {
|
|
35
|
+
const [addButtonLabel, removeButtonLabel] = t([
|
|
36
|
+
{ key: rowId, type: TranslationTextTypeEnum.Label, subType: TranslationTextSubTypeEnum.RepeatButtonAdd },
|
|
37
|
+
{ key: rowId, type: TranslationTextTypeEnum.Label, subType: TranslationTextSubTypeEnum.RepeatButtonRemove },
|
|
38
|
+
])
|
|
25
39
|
return (
|
|
26
40
|
<Form.List name={[...basePath, repeatingSection.name]} initialValue={[{}]}>
|
|
27
41
|
{(repeatItems, { add, remove }) => (
|
|
@@ -36,7 +50,7 @@ export const LayoutRowRepeatableRenderer = ({
|
|
|
36
50
|
{...getButtonRenderProps(repeatingSection.addButton ?? ({} as IButtonPropsBase))}
|
|
37
51
|
onClick={() => add()}
|
|
38
52
|
>
|
|
39
|
-
{
|
|
53
|
+
{addButtonLabel ?? <FaPlus className="text-primary" />}
|
|
40
54
|
</Button_FillerPortal>
|
|
41
55
|
),
|
|
42
56
|
}}
|
|
@@ -73,7 +87,7 @@ export const LayoutRowRepeatableRenderer = ({
|
|
|
73
87
|
})
|
|
74
88
|
}
|
|
75
89
|
>
|
|
76
|
-
{
|
|
90
|
+
{removeButtonLabel || <FaTimes className="text-danger" />}
|
|
77
91
|
</Button_FillerPortal>
|
|
78
92
|
</div>
|
|
79
93
|
) : (
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { useFindDynamiForm, useNotification } from '../../../../common/custom-hooks'
|
|
2
|
-
import { memo, useCallback, useState } from 'react'
|
|
2
|
+
import { memo, useCallback, useMemo, useState } from 'react'
|
|
3
3
|
import { useFormPreservedItemValues } from '../../../../common/custom-hooks/use-preserved-form-items.hook'
|
|
4
4
|
import { useDuplicateDataAction } from './use-duplicate-data.hook'
|
|
5
5
|
import { useDeleteDataAction } from './use-delete-data.hook'
|
|
@@ -13,12 +13,18 @@ import { getButtonRenderProps } from '../../../../../functions/forms/get-element
|
|
|
13
13
|
import WarningIcon from '../../../../common/warning-icon'
|
|
14
14
|
import FormDataLoadingIndicatorModal from '../../../../modals/form-data-loading.modal'
|
|
15
15
|
import { IFormContext } from '../../1-row'
|
|
16
|
-
import {
|
|
16
|
+
import {
|
|
17
|
+
ButtonActionCategoryEnum,
|
|
18
|
+
FormLoadingModalTypeEnum,
|
|
19
|
+
TranslationTextSubTypeEnum,
|
|
20
|
+
TranslationTextTypeEnum,
|
|
21
|
+
} from '../../../../../enums'
|
|
17
22
|
import { useButtonNavigateAction } from './use-button-navigate.hook'
|
|
18
23
|
import { constructDynamicFormHref, isNewFormDataPage } from '../../../../../functions'
|
|
19
24
|
import { useCustomFunctionCallAction } from './use-custom-function-call.hook'
|
|
20
25
|
import { IButtonElementProps, IButtonProps_Other, IButtonProps_Navigate } from '../../../../../types'
|
|
21
26
|
import { ELEMENTS_DEFAULT_CLASS } from '../../../../../constants'
|
|
27
|
+
import { useTranslation } from '../../../../common/custom-hooks/use-translation.hook/hook'
|
|
22
28
|
import {
|
|
23
29
|
useIsNodeDisabled,
|
|
24
30
|
useIsNodeHidden,
|
|
@@ -35,6 +41,7 @@ export const DynamicFormButtonRender = memo((props: IDynamicButton) => {
|
|
|
35
41
|
const [dataLoadingType, setDataLoadingType] = useState<FormLoadingModalTypeEnum | undefined>()
|
|
36
42
|
const { isPublic = false, submissionPdfConfig } = useFormPreservedItemValues(formRef)
|
|
37
43
|
|
|
44
|
+
const { t } = useTranslation(formContext.formId)
|
|
38
45
|
const isElementHidden = useIsNodeHidden(btnKey)
|
|
39
46
|
const isElementDisabled = useIsNodeDisabled(btnKey)
|
|
40
47
|
|
|
@@ -42,12 +49,23 @@ export const DynamicFormButtonRender = memo((props: IDynamicButton) => {
|
|
|
42
49
|
formContext.formId = btnProps.formId
|
|
43
50
|
formContext.formName = formInfo?.name
|
|
44
51
|
|
|
52
|
+
const [content, okText, cancelText, successMsg, errorMsg] = useMemo(
|
|
53
|
+
() =>
|
|
54
|
+
t([
|
|
55
|
+
{ key: btnKey, type: TranslationTextTypeEnum.Label, subType: TranslationTextSubTypeEnum.ConfirmationMsg },
|
|
56
|
+
{ key: btnKey, type: TranslationTextTypeEnum.Label, subType: TranslationTextSubTypeEnum.ConfirmationOk },
|
|
57
|
+
{ key: btnKey, type: TranslationTextTypeEnum.Label, subType: TranslationTextSubTypeEnum.ConfirmationCancel },
|
|
58
|
+
{ key: btnKey, type: TranslationTextTypeEnum.Label, subType: TranslationTextSubTypeEnum.SuccessMsg },
|
|
59
|
+
{ key: btnKey, type: TranslationTextTypeEnum.Label, subType: TranslationTextSubTypeEnum.ErrorMsg },
|
|
60
|
+
]),
|
|
61
|
+
[btnKey],
|
|
62
|
+
)
|
|
63
|
+
|
|
45
64
|
const displayResultMessage = useCallback(
|
|
46
65
|
(isSuccess: boolean = true) => {
|
|
47
|
-
|
|
48
|
-
isSuccess ? success({ message: btnProps.messages.success }) : error({ message: btnProps.messages.error })
|
|
66
|
+
isSuccess ? successMsg && success({ message: successMsg }) : errorMsg && error({ message: errorMsg })
|
|
49
67
|
},
|
|
50
|
-
[
|
|
68
|
+
[successMsg, errorMsg, success, error],
|
|
51
69
|
)
|
|
52
70
|
|
|
53
71
|
const onButtonNavigate = useButtonNavigateAction({
|
|
@@ -75,7 +93,7 @@ export const DynamicFormButtonRender = memo((props: IDynamicButton) => {
|
|
|
75
93
|
const { category } = btnProps.secondaryAction
|
|
76
94
|
if (category === ButtonActionCategoryEnum.Navigate) onButtonNavigate(btnProps.secondaryAction)
|
|
77
95
|
else if (category === ButtonActionCategoryEnum.CustomFunction) {
|
|
78
|
-
onFunctionCall(btnProps.secondaryAction.functionName
|
|
96
|
+
onFunctionCall(btnProps.secondaryAction.functionName)
|
|
79
97
|
setTimeout(() => setLoading(false), 500)
|
|
80
98
|
}
|
|
81
99
|
},
|
|
@@ -189,7 +207,7 @@ export const DynamicFormButtonRender = memo((props: IDynamicButton) => {
|
|
|
189
207
|
break
|
|
190
208
|
|
|
191
209
|
case ButtonActionCategoryEnum.CustomFunction:
|
|
192
|
-
onFunctionCall(btnProps.functionName
|
|
210
|
+
onFunctionCall(btnProps.functionName)
|
|
193
211
|
setTimeout(() => setLoading(false), 500)
|
|
194
212
|
break
|
|
195
213
|
|
|
@@ -236,11 +254,11 @@ export const DynamicFormButtonRender = memo((props: IDynamicButton) => {
|
|
|
236
254
|
e.stopPropagation()
|
|
237
255
|
setLoading(true)
|
|
238
256
|
|
|
239
|
-
if (btnProps.
|
|
257
|
+
if (btnProps.confirmationEnabled) {
|
|
240
258
|
confirmModal({
|
|
241
|
-
content
|
|
242
|
-
okText
|
|
243
|
-
cancelText
|
|
259
|
+
content,
|
|
260
|
+
okText,
|
|
261
|
+
cancelText,
|
|
244
262
|
onOk: handleButtonClick,
|
|
245
263
|
})
|
|
246
264
|
} else handleButtonClick()
|
|
@@ -251,13 +269,13 @@ export const DynamicFormButtonRender = memo((props: IDynamicButton) => {
|
|
|
251
269
|
btnProps.category !== ButtonActionCategoryEnum.SaveDataChanges && (
|
|
252
270
|
<WarningIcon tooltip={BUTTON_CUSTOM_ERROR_MESSAGES.UnavailableForPublic} />
|
|
253
271
|
)}
|
|
254
|
-
{
|
|
272
|
+
{t({ key: btnKey, type: TranslationTextTypeEnum.Label })}
|
|
255
273
|
</Button_FillerPortal>
|
|
256
274
|
{!!dataLoadingType && (
|
|
257
275
|
<FormDataLoadingIndicatorModal
|
|
258
276
|
currentType={dataLoadingType}
|
|
259
277
|
closeModal={() => setDataLoadingType(undefined)}
|
|
260
|
-
errorMessage={
|
|
278
|
+
errorMessage={errorMsg}
|
|
261
279
|
/>
|
|
262
280
|
)}
|
|
263
281
|
{ChooseTemplateReportModal}
|
|
@@ -288,7 +306,7 @@ export interface IOnSuccessFunctions {
|
|
|
288
306
|
onFinal: () => void
|
|
289
307
|
}
|
|
290
308
|
|
|
291
|
-
const defaultMessage = { success: 'Successfully completed!', error: 'Error occured' }
|
|
309
|
+
// const defaultMessage = { success: 'Successfully completed!', error: 'Error occured' }
|
|
292
310
|
|
|
293
311
|
export const BUTTON_CUSTOM_ERROR_MESSAGES = {
|
|
294
312
|
DataIdNotFound: 'Data id was not found!',
|
|
@@ -6,13 +6,17 @@ import { getElementGeneralizedProps } from '../../../../functions/forms/get-elem
|
|
|
6
6
|
import { Form } from 'antd'
|
|
7
7
|
import { memo, useEffect, useMemo } from 'react'
|
|
8
8
|
import { evaluateValue } from '../../../../functions/forms/evaluate-value'
|
|
9
|
+
import { useTranslation } from '../../../common/custom-hooks/use-translation.hook/hook'
|
|
10
|
+
import { TranslationTextTypeEnum } from '../../../../enums'
|
|
9
11
|
|
|
10
12
|
function LayoutRenderer_CurrencyField({
|
|
11
13
|
formContext,
|
|
12
14
|
formItem,
|
|
13
15
|
elementData,
|
|
14
16
|
isDisabled,
|
|
17
|
+
elementKey,
|
|
15
18
|
}: ILayoutRenderer_CurrencyField) {
|
|
19
|
+
const { t } = useTranslation(formContext.formId)
|
|
16
20
|
const { formRef } = formContext
|
|
17
21
|
const props = getElementGeneralizedProps(elementData.props)
|
|
18
22
|
|
|
@@ -30,10 +34,17 @@ function LayoutRenderer_CurrencyField({
|
|
|
30
34
|
if (evaluatedValue > -1 && isDisabled) formRef?.setFieldValue(formItem.path, evaluatedValue)
|
|
31
35
|
}, [evaluatedValue, isDisabled, formItem.path])
|
|
32
36
|
|
|
37
|
+
const [label, placeholder] = t([
|
|
38
|
+
{ key: elementKey, type: TranslationTextTypeEnum.Label },
|
|
39
|
+
{ key: elementKey, type: TranslationTextTypeEnum.Placeholder },
|
|
40
|
+
])
|
|
41
|
+
|
|
33
42
|
return (
|
|
34
|
-
<LayoutRenderer_FieldElement formRef={formRef}>
|
|
43
|
+
<LayoutRenderer_FieldElement formRef={formRef} elementKey={elementData.key} formId={formContext.formId}>
|
|
35
44
|
{() => ({
|
|
36
45
|
...props,
|
|
46
|
+
label,
|
|
47
|
+
placeholder,
|
|
37
48
|
type: elementData.elementType,
|
|
38
49
|
name: formItem.name,
|
|
39
50
|
validations: elementData.validations,
|
|
@@ -3,14 +3,22 @@ import { Button_FillerPortal } from '../../../common/button'
|
|
|
3
3
|
import { useNavigate } from 'react-router-dom'
|
|
4
4
|
import { FaChevronRight } from 'react-icons/fa6'
|
|
5
5
|
import React, { memo } from 'react'
|
|
6
|
-
import { PageViewTypEnum } from '../../../../enums'
|
|
7
|
-
import { IBreadcrumbElementProps } from '../../../../types'
|
|
6
|
+
import { PageViewTypEnum, TranslationTextSubTypeEnum, TranslationTextTypeEnum } from '../../../../enums'
|
|
8
7
|
import { ELEMENTS_DEFAULT_CLASS } from '../../../../constants'
|
|
8
|
+
import { IElementBaseProps } from '.'
|
|
9
|
+
import { useTranslation } from '../../../common/custom-hooks/use-translation.hook/hook'
|
|
9
10
|
|
|
10
|
-
function LayoutRenderer_Breadcrumb({
|
|
11
|
+
function LayoutRenderer_Breadcrumb({ formId, elementKey }: { formId?: number } & IElementBaseProps) {
|
|
12
|
+
const { t } = useTranslation(formId)
|
|
11
13
|
const navigate = useNavigate()
|
|
12
14
|
const { breadcrumbs, sliceAt } = useBreadcrumb()
|
|
13
15
|
|
|
16
|
+
const [detailText, newText, listText] = t([
|
|
17
|
+
{ key: elementKey, type: TranslationTextTypeEnum.Label, subType: TranslationTextSubTypeEnum.BreadcrumbDetail },
|
|
18
|
+
{ key: elementKey, type: TranslationTextTypeEnum.Label, subType: TranslationTextSubTypeEnum.BreadcrumbNew },
|
|
19
|
+
{ key: elementKey, type: TranslationTextTypeEnum.Label, subType: TranslationTextSubTypeEnum.BreadcrumbList },
|
|
20
|
+
])
|
|
21
|
+
|
|
14
22
|
return (
|
|
15
23
|
<div className={`${ELEMENTS_DEFAULT_CLASS.Breadcrumb} flex items-center text-12`}>
|
|
16
24
|
{breadcrumbs.map((bc, bcIdx) => (
|
|
@@ -24,12 +32,12 @@ function LayoutRenderer_Breadcrumb({ elementProps }: { elementProps: IBreadcrumb
|
|
|
24
32
|
}}
|
|
25
33
|
>
|
|
26
34
|
<span className="font-normal italic">
|
|
27
|
-
{bc.type === PageViewTypEnum.New ? `${
|
|
35
|
+
{bc.type === PageViewTypEnum.New ? `${newText} ` : ''}
|
|
28
36
|
{bc.label}
|
|
29
37
|
{bc.type === PageViewTypEnum.Details
|
|
30
|
-
? ` ${
|
|
38
|
+
? ` ${detailText}`
|
|
31
39
|
: bc.type === PageViewTypEnum.List
|
|
32
|
-
? ` ${
|
|
40
|
+
? ` ${listText}`
|
|
33
41
|
: ''}
|
|
34
42
|
</span>
|
|
35
43
|
</Button_FillerPortal>
|
|
@@ -3,24 +3,28 @@ import { IFormContext } from '../1-row'
|
|
|
3
3
|
import { IPickerElement } from '../../../../types'
|
|
4
4
|
import { IElementBaseProps } from '.'
|
|
5
5
|
import { getElementGeneralizedProps } from '../../../../functions/forms/get-element-props'
|
|
6
|
-
import { ElementTypeEnum } from '../../../../enums'
|
|
6
|
+
import { ElementTypeEnum, TranslationTextSubTypeEnum, TranslationTextTypeEnum } from '../../../../enums'
|
|
7
7
|
import { memo, useMemo } from 'react'
|
|
8
8
|
import { findMaxAndMinValues } from '../../../../functions'
|
|
9
9
|
import { VALIDATION_CURRENT_DATE_IDENTIFIER } from '../../../../constants'
|
|
10
10
|
import dayjs from 'dayjs'
|
|
11
|
+
import { useTranslation } from '../../../common/custom-hooks/use-translation.hook/hook'
|
|
11
12
|
|
|
12
13
|
function LayoutRenderer_PickerField({ formContext, formItem, elementData, isDisabled }: ILayoutRenderer_PickerField) {
|
|
14
|
+
const { t } = useTranslation(formContext.formId)
|
|
13
15
|
const props = getElementGeneralizedProps(elementData.props)
|
|
14
16
|
|
|
15
|
-
const
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
17
|
+
const [placeholder1, placeholder2] = t([
|
|
18
|
+
{ key: elementData.key, type: TranslationTextTypeEnum.Placeholder },
|
|
19
|
+
{ key: elementData.key, type: TranslationTextTypeEnum.Placeholder, subType: TranslationTextSubTypeEnum.Secondary },
|
|
20
|
+
])
|
|
21
|
+
const placeholder: string | [string, string] =
|
|
22
|
+
props.elementType === ElementTypeEnum.RangePicker && placeholder2 ? [placeholder1, placeholder2] : placeholder1
|
|
19
23
|
|
|
20
24
|
let { maxValue, minValue } = useMemo(() => findMaxAndMinValues(elementData.validations), [elementData.validations])
|
|
21
25
|
|
|
22
26
|
return (
|
|
23
|
-
<LayoutRenderer_FieldElement formRef={formContext.formRef}>
|
|
27
|
+
<LayoutRenderer_FieldElement formRef={formContext.formRef} elementKey={elementData.key} formId={formContext.formId}>
|
|
24
28
|
{() => ({
|
|
25
29
|
...props,
|
|
26
30
|
placeholder,
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
import { IElementBaseProps } from '.'
|
|
2
|
+
import { CountryEnum, LOCAL_STORAGE_KEYS_ENUM, TranslationTextTypeEnum } from '../../../../enums'
|
|
3
|
+
import { memo, useEffect, useState } from 'react'
|
|
4
|
+
import { ELEMENTS_DEFAULT_CLASS } from '../../../../constants'
|
|
5
|
+
import { useTranslation } from '../../../common/custom-hooks/use-translation.hook/hook'
|
|
6
|
+
import { translationStore } from '../../../common/custom-hooks/use-translation.hook/store'
|
|
7
|
+
import { Select } from 'antd'
|
|
8
|
+
import { FaCaretDown } from 'react-icons/fa6'
|
|
9
|
+
|
|
10
|
+
function LayoutRenderer_LanguageSelector({ formId, isDisabled, elementKey }: ILayoutRenderer_LanguageSelector) {
|
|
11
|
+
const { t } = useTranslation(formId)
|
|
12
|
+
const [languages, setLanguages] = useState<{ value: CountryEnum; label: string }[]>([])
|
|
13
|
+
|
|
14
|
+
useEffect(() => {
|
|
15
|
+
setLanguages(translationStore.getLanguages())
|
|
16
|
+
}, [])
|
|
17
|
+
|
|
18
|
+
const [label, placeholder] = t([
|
|
19
|
+
{ key: elementKey, type: TranslationTextTypeEnum.Label },
|
|
20
|
+
{ key: elementKey, type: TranslationTextTypeEnum.Placeholder },
|
|
21
|
+
])
|
|
22
|
+
|
|
23
|
+
return (
|
|
24
|
+
<div className="flex flex-col min-w-max">
|
|
25
|
+
{label && <span>{label}</span>}
|
|
26
|
+
<Select
|
|
27
|
+
placeholder={placeholder}
|
|
28
|
+
suffixIcon={<FaCaretDown />}
|
|
29
|
+
options={languages}
|
|
30
|
+
defaultValue={localStorage.getItem(LOCAL_STORAGE_KEYS_ENUM.SelectedLanguage)}
|
|
31
|
+
disabled={isDisabled}
|
|
32
|
+
allowClear={false}
|
|
33
|
+
className={`${ELEMENTS_DEFAULT_CLASS.Select} min-w-max`}
|
|
34
|
+
onChange={(lang) => translationStore.setLanguage(lang as CountryEnum)}
|
|
35
|
+
/>
|
|
36
|
+
</div>
|
|
37
|
+
)
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
export default memo(LayoutRenderer_LanguageSelector)
|
|
41
|
+
|
|
42
|
+
type ILayoutRenderer_LanguageSelector = { formId?: number } & IElementBaseProps
|