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.
Files changed (36) hide show
  1. package/package.json +1 -1
  2. package/src/components/common/custom-hooks/index.ts +1 -0
  3. package/src/components/common/custom-hooks/use-translation.hook/hook.ts +28 -0
  4. package/src/components/common/custom-hooks/use-translation.hook/store.ts +130 -0
  5. package/src/components/form/1-list/index.tsx +7 -0
  6. package/src/components/form/2-details/index.tsx +7 -0
  7. package/src/components/form/layout-renderer/1-row/header-render.tsx +12 -2
  8. package/src/components/form/layout-renderer/1-row/header.tsx +1 -0
  9. package/src/components/form/layout-renderer/1-row/index.tsx +7 -1
  10. package/src/components/form/layout-renderer/1-row/repeatable-render.tsx +17 -3
  11. package/src/components/form/layout-renderer/3-element/1-dynamic-button/index.tsx +32 -14
  12. package/src/components/form/layout-renderer/3-element/10-currency.tsx +12 -1
  13. package/src/components/form/layout-renderer/3-element/11-breadcrumb.tsx +14 -6
  14. package/src/components/form/layout-renderer/3-element/12-picker-field.tsx +10 -6
  15. package/src/components/form/layout-renderer/3-element/13-language-selector.tsx +42 -0
  16. package/src/components/form/layout-renderer/3-element/2-field-element.tsx +31 -17
  17. package/src/components/form/layout-renderer/3-element/3-read-field-data.tsx +13 -4
  18. package/src/components/form/layout-renderer/3-element/4-rich-text-editor.tsx +10 -9
  19. package/src/components/form/layout-renderer/3-element/6-signature.tsx +7 -4
  20. package/src/components/form/layout-renderer/3-element/7-file-upload.tsx +44 -10
  21. package/src/components/form/layout-renderer/3-element/8-fields-with-options.tsx +25 -6
  22. package/src/components/form/layout-renderer/3-element/9-form-data-render.tsx +7 -0
  23. package/src/components/form/layout-renderer/3-element/index.tsx +35 -15
  24. package/src/constants.ts +2 -1
  25. package/src/enums/index.ts +28 -0
  26. package/src/functions/companies/use-company-config.tsx +13 -2
  27. package/src/functions/forms/create-form-rules.ts +18 -6
  28. package/src/functions/forms/data-render-functions.tsx +44 -23
  29. package/src/functions/forms/get-element-props.ts +1 -1
  30. package/src/types/forms/data-list/index.ts +1 -0
  31. package/src/types/forms/index.ts +10 -3
  32. package/src/types/forms/layout-elements/button.ts +2 -11
  33. package/src/types/forms/layout-elements/index.ts +10 -42
  34. package/src/types/forms/layout-elements/read-field-data-props.ts +0 -1
  35. package/src/types/forms/layout-elements/validation.ts +0 -1
  36. package/src/types/index.ts +2 -0
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "form-craft-package",
3
- "version": "1.8.2-dev.2",
3
+ "version": "1.8.2-dev.3",
4
4
  "main": "index.ts",
5
5
  "scripts": {
6
6
  "test": "echo \"Error: no test specified\" && exit 1",
@@ -4,3 +4,4 @@ export * from './use-lazy-modal-opener.hook'
4
4
  export * from './use-breadcrumb.hook'
5
5
  export * from './use-dayjs-extender.hook'
6
6
  export * from './use-set-persistent-domain.hook'
7
+ export * from './use-translation.hook/hook'
@@ -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 (header?.name)
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
  )
@@ -35,6 +35,7 @@ export default function RowHeader({
35
35
  }
36
36
 
37
37
  interface IRowHeader extends IFormDndLayoutRowHeader {
38
+ name: string
38
39
  isCollapsed?: boolean
39
40
  setIsCollapsed?: () => void
40
41
  }
@@ -27,8 +27,14 @@ export const LayoutRendererRow = memo(
27
27
  id={rowData.id}
28
28
  className={ELEMENTS_DEFAULT_CLASS.LayoutRowContainer}
29
29
  >
30
- <LayoutRowConditionalHeaderRenderer header={rowData.props?.header}>
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 { RepeatingSectionTabTitleTypeEnum } from '../../../../enums'
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
- {repeatingSection.addButton?.label ?? <FaPlus className="text-primary" />}
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
- {repeatingSection.removeButton?.label || <FaTimes className="text-danger" />}
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 { ButtonActionCategoryEnum, FormLoadingModalTypeEnum } from '../../../../../enums'
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
- if (!btnProps.messages) return
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
- [btnProps.messages, success, error],
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, btnProps.messages ?? defaultMessage)
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, btnProps.messages)
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.confirmation?.enabled) {
257
+ if (btnProps.confirmationEnabled) {
240
258
  confirmModal({
241
- content: btnProps.confirmation.message,
242
- okText: btnProps.confirmation.okLabel,
243
- cancelText: btnProps.confirmation.cancelLabel,
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
- {btnProps.label}
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={btnProps.messages?.error}
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({ elementProps }: { elementProps: IBreadcrumbElementProps }) {
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 ? `${elementProps.newText} ` : ''}
35
+ {bc.type === PageViewTypEnum.New ? `${newText} ` : ''}
28
36
  {bc.label}
29
37
  {bc.type === PageViewTypEnum.Details
30
- ? ` ${elementProps.detailText}`
38
+ ? ` ${detailText}`
31
39
  : bc.type === PageViewTypEnum.List
32
- ? ` ${elementProps.listText}`
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 placeholder =
16
- props.elementType === ElementTypeEnum.RangePicker && props.placeholder2
17
- ? [props.placeholder, props.placeholder2]
18
- : props.placeholder
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