form-craft-package 1.8.2-dev.0 → 1.8.2-dev.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (37) hide show
  1. package/package.json +1 -1
  2. package/src/components/common/company-logo.tsx +40 -0
  3. package/src/components/common/custom-hooks/index.ts +1 -0
  4. package/src/components/common/custom-hooks/use-login-handler.ts +3 -3
  5. package/src/components/common/custom-hooks/use-node-condition.hook/use-disabled-elements.hook.ts +62 -22
  6. package/src/components/common/custom-hooks/use-node-condition.hook/use-hidden-elements.hook.ts +54 -42
  7. package/src/components/common/custom-hooks/use-set-persistent-domain.hook.ts +20 -0
  8. package/src/components/companies/2-unauthenticated/forgot-password.tsx +4 -6
  9. package/src/components/companies/2-unauthenticated/index.tsx +8 -5
  10. package/src/components/companies/2-unauthenticated/reset-password.tsx +4 -6
  11. package/src/components/companies/3-config-provider/index.tsx +27 -2
  12. package/src/components/form/2-details/index.tsx +14 -5
  13. package/src/components/form/layout-renderer/1-row/header-render.tsx +23 -14
  14. package/src/components/form/layout-renderer/1-row/index.tsx +5 -1
  15. package/src/components/form/layout-renderer/1-row/repeatable-render.tsx +69 -28
  16. package/src/components/form/layout-renderer/3-element/10-currency.tsx +4 -2
  17. package/src/components/form/layout-renderer/3-element/11-breadcrumb.tsx +4 -2
  18. package/src/components/form/layout-renderer/3-element/12-picker-field.tsx +4 -7
  19. package/src/components/form/layout-renderer/3-element/2-field-element.tsx +203 -225
  20. package/src/components/form/layout-renderer/3-element/3-read-field-data.tsx +4 -6
  21. package/src/components/form/layout-renderer/3-element/4-rich-text-editor.tsx +4 -1
  22. package/src/components/form/layout-renderer/3-element/5-re-captcha.tsx +4 -7
  23. package/src/components/form/layout-renderer/3-element/6-signature.tsx +4 -2
  24. package/src/components/form/layout-renderer/3-element/7-file-upload.tsx +96 -44
  25. package/src/components/form/layout-renderer/3-element/8-fields-with-options.tsx +5 -3
  26. package/src/components/form/layout-renderer/3-element/9-form-data-render.tsx +4 -2
  27. package/src/components/form/layout-renderer/3-element/index.tsx +125 -86
  28. package/src/enums/form.enum.ts +4 -0
  29. package/src/enums/index.ts +1 -0
  30. package/src/functions/companies/index.tsx +12 -0
  31. package/src/functions/companies/use-company-config.tsx +14 -10
  32. package/src/functions/forms/data-render-functions.tsx +5 -3
  33. package/src/functions/forms/get-element-props.ts +2 -2
  34. package/src/functions/forms/index.ts +3 -1
  35. package/src/types/companies/site-layout/authenticated/index.tsx +11 -34
  36. package/src/types/forms/index.ts +10 -4
  37. package/src/types/forms/layout-elements/button.ts +1 -1
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "form-craft-package",
3
- "version": "1.8.2-dev.0",
3
+ "version": "1.8.2-dev.2",
4
4
  "main": "index.ts",
5
5
  "scripts": {
6
6
  "test": "echo \"Error: no test specified\" && exit 1",
@@ -0,0 +1,40 @@
1
+ import { useState } from 'react'
2
+
3
+ export default function CompanyLogoSection({
4
+ location = LogoLocationEnum.Authenticated,
5
+ logoUrl,
6
+ isPreview,
7
+ }: {
8
+ location?: LogoLocationEnum
9
+ logoUrl?: string
10
+ isPreview?: boolean
11
+ }) {
12
+ const [logoFailed, setLogoFailed] = useState(false)
13
+
14
+ if (!logoUrl || logoFailed)
15
+ return (
16
+ <div className="w-full py-1.5 rounded-md flex items-center justify-center text-danger italic text-lg font-bold">
17
+ [Company Logo]
18
+ </div>
19
+ )
20
+
21
+ const imgClassName =
22
+ location === LogoLocationEnum.Authenticated
23
+ ? 'w-auto h-auto max-w-[140px] sm:max-w-[160px] md:max-w-[180px] lg:max-w-[200px] xl:max-w-[220px]'
24
+ : 'h-10 object-contain'
25
+
26
+ return (
27
+ <img
28
+ alt="Logo"
29
+ src={logoUrl}
30
+ style={{ maxWidth: isPreview ? '200px' : undefined }}
31
+ className={imgClassName}
32
+ onError={() => setLogoFailed(true)}
33
+ />
34
+ )
35
+ }
36
+
37
+ export enum LogoLocationEnum {
38
+ Authenticated,
39
+ Unauthenticated,
40
+ }
@@ -3,3 +3,4 @@ export * from './use-notification.hook'
3
3
  export * from './use-lazy-modal-opener.hook'
4
4
  export * from './use-breadcrumb.hook'
5
5
  export * from './use-dayjs-extender.hook'
6
+ export * from './use-set-persistent-domain.hook'
@@ -1,12 +1,12 @@
1
1
  import { useReducer, useState } from 'react'
2
2
  import { useNavigate } from 'react-router-dom'
3
3
  import client, { auth } from '../../../api/client'
4
- import { LOCAL_STORAGE_KEYS_ENUM } from '../../../enums'
5
4
  import { cookieHandler } from '../../../functions/cookie-handler'
6
5
  import { message } from 'antd'
6
+ import { getLocalStorageDomain } from '../../../functions'
7
7
  export function useLoginHandler() {
8
8
  const navigate = useNavigate()
9
- const domain = localStorage.getItem(LOCAL_STORAGE_KEYS_ENUM.Domain) || ''
9
+ const domain = getLocalStorageDomain()
10
10
  const [loading, updateLoading] = useReducer((currState: any, updated: any) => ({ ...currState, ...updated }), {
11
11
  login: false,
12
12
  })
@@ -27,7 +27,7 @@ export function useLoginHandler() {
27
27
  const { authRes, initialPath } = await auth(
28
28
  values.email,
29
29
  values.password,
30
- localStorage.getItem(LOCAL_STORAGE_KEYS_ENUM.Domain) ?? '',
30
+ getLocalStorageDomain(),
31
31
  values.type || 'password',
32
32
  )
33
33
  if (authRes.status === 200) {
@@ -1,8 +1,9 @@
1
- import { useEffect, useCallback } from 'react'
1
+ import { useEffect } from 'react'
2
2
  import useGetCurrentBreakpoint from '../use-window-width.hook'
3
3
  import { DeviceBreakpointEnum, FormElementConditionalKeyEnum } from '../../../../enums'
4
4
  import { disabledStore } from './visibility-store'
5
5
  import { isValidationsMet } from './visibility-utils'
6
+ import { FormInstance } from 'antd'
6
7
  import {
7
8
  IDndLayoutElement,
8
9
  IDndLayoutRow,
@@ -12,16 +13,64 @@ import {
12
13
 
13
14
  export function useSetDisabledElements(
14
15
  layoutConfig?: IDndLayoutStructure_Responsive,
15
- formValues?: { [key: string]: any },
16
+ formDataRef?: FormInstance,
16
17
  formDataId?: string,
17
18
  ): void {
18
19
  const currentBreakpoint = useGetCurrentBreakpoint()
20
+
21
+ useEffect(() => {
22
+ // during the initial renders, handle the initially disabled fields
23
+ if (!formDataRef) return
24
+
25
+ const allValues = formDataRef.getFieldsValue()
26
+
27
+ const layout: IDndLayoutRow[] =
28
+ layoutConfig?.layouts?.[currentBreakpoint] || layoutConfig?.layouts?.[DeviceBreakpointEnum.Default] || []
29
+ const elementsMap: Record<string, IDndLayoutElement> = layoutConfig?.elements || {}
30
+ const configsPerElement: Record<string, IFormLayoutElementConditions> = Object.values(elementsMap).reduce(
31
+ (acc, el) => {
32
+ const elConditions = el.conditions?.[FormElementConditionalKeyEnum.EnableIf]
33
+ if (elConditions && Array.isArray(elConditions) && elConditions.length > 0) acc[el.key] = el.conditions!
34
+
35
+ return acc
36
+ },
37
+ {} as Record<string, IFormLayoutElementConditions>,
38
+ )
39
+
40
+ const allInputsLoaded =
41
+ layout.length > 0 &&
42
+ Object.keys(elementsMap).length > 0 &&
43
+ Object.keys(configsPerElement).length > 0 &&
44
+ Object.values(allValues).filter(Boolean).length > 0
45
+
46
+ if (!allInputsLoaded) return
47
+
48
+ handleDisabledSet(allValues, currentBreakpoint, layoutConfig, formDataId)
49
+ }, [formDataRef, layoutConfig, currentBreakpoint, formDataId])
50
+ }
51
+
52
+ export function handleDisabledSet(
53
+ allValues: Record<string, any>,
54
+ currentBreakpoint: DeviceBreakpointEnum,
55
+ layoutConfig?: IDndLayoutStructure_Responsive,
56
+ formDataId?: string,
57
+ ) {
58
+ const disabledSet = computeDisabledSet(allValues, currentBreakpoint, layoutConfig, formDataId)
59
+ disabledStore.setIds([...disabledSet])
60
+ }
61
+
62
+ function computeDisabledSet(
63
+ allValues: Record<string, any>,
64
+ currentBreakpoint: DeviceBreakpointEnum,
65
+ layoutConfig?: IDndLayoutStructure_Responsive,
66
+ formDataId?: string,
67
+ ): Set<string> {
19
68
  const layout: IDndLayoutRow[] =
20
69
  layoutConfig?.layouts?.[currentBreakpoint] || layoutConfig?.layouts?.[DeviceBreakpointEnum.Default] || []
21
70
 
22
71
  const elementsMap: Record<string, IDndLayoutElement> = layoutConfig?.elements || {}
23
- const dataValues: { [key: string]: any } = formValues && typeof formValues === 'object' ? formValues : {}
24
72
 
73
+ // Pull out only those elements which have EnableIf conditions
25
74
  const configsPerElement: Record<string, IFormLayoutElementConditions> = Object.values(elementsMap).reduce(
26
75
  (acc, el) => {
27
76
  const elConditions = el.conditions?.[FormElementConditionalKeyEnum.EnableIf]
@@ -36,29 +85,20 @@ export function useSetDisabledElements(
36
85
  layout.length > 0 &&
37
86
  Object.keys(elementsMap).length > 0 &&
38
87
  Object.keys(configsPerElement).length > 0 &&
39
- Object.values(dataValues).length > 0 &&
88
+ Object.values(allValues).length > 0 &&
40
89
  !!formDataId
41
90
 
42
- const computeIndividuallyDisabled = useCallback((): Set<string> => {
43
- if (!allInputsLoaded) return new Set()
44
-
45
- const disabledSet = new Set<string>()
46
- Object.values(elementsMap).forEach(({ key }) => {
47
- const elementConfigs = configsPerElement[key]
48
- const shouldEnable = isValidationsMet(FormElementConditionalKeyEnum.EnableIf, dataValues, elementConfigs, {
91
+ const disabled = new Set<string>()
92
+ if (allInputsLoaded) {
93
+ for (const el of Object.values(elementsMap)) {
94
+ const conds = configsPerElement[el.key]
95
+ const shouldEnable = isValidationsMet(FormElementConditionalKeyEnum.EnableIf, allValues, conds, {
49
96
  currentBreakpoint,
50
97
  formDataId,
51
98
  })
99
+ if (!shouldEnable) disabled.add(el.key)
100
+ }
101
+ }
52
102
 
53
- if (!shouldEnable) disabledSet.add(key)
54
- })
55
-
56
- return disabledSet
57
- }, [elementsMap, dataValues, configsPerElement, currentBreakpoint, formDataId, allInputsLoaded])
58
-
59
- const individuallyDisabled = computeIndividuallyDisabled()
60
-
61
- useEffect(() => {
62
- if (allInputsLoaded) disabledStore.setIds([...individuallyDisabled])
63
- }, [allInputsLoaded, individuallyDisabled])
103
+ return disabled
64
104
  }
@@ -1,16 +1,49 @@
1
- import { useEffect, useMemo, useCallback } from 'react'
2
- import useGetCurrentBreakpoint from '../use-window-width.hook'
3
1
  import { DeviceBreakpointEnum, FormElementConditionalKeyEnum } from '../../../../enums'
4
- import { hiddenStore } from './visibility-store'
5
2
  import { isValidationsMet } from './visibility-utils'
6
- import {
3
+ import { useEffect } from 'react'
4
+ import useGetCurrentBreakpoint from '../use-window-width.hook'
5
+ import { FormInstance } from 'antd'
6
+ import { hiddenStore } from './visibility-store'
7
+ import type {
7
8
  IDndLayoutCol,
8
- IDndLayoutElement,
9
9
  IDndLayoutRow,
10
10
  IDndLayoutStructure_Responsive,
11
11
  IFormLayoutElementConditions,
12
12
  } from '../../../../types'
13
13
 
14
+ export function useSetHiddenNodes(
15
+ layoutConfig?: IDndLayoutStructure_Responsive,
16
+ formDataRef?: FormInstance,
17
+ formDataId?: string,
18
+ ): void {
19
+ const currentBreakpoint = useGetCurrentBreakpoint()
20
+
21
+ useEffect(() => {
22
+ // during the initial renders, handle the initially hidden nodes
23
+ if (!formDataRef) return
24
+
25
+ const allValues = formDataRef.getFieldsValue() || {}
26
+ const layout =
27
+ layoutConfig?.layouts?.[currentBreakpoint] || layoutConfig?.layouts?.[DeviceBreakpointEnum.Default] || []
28
+
29
+ const allInputsLoaded = layout.length > 0 && Object.values(allValues).filter(Boolean).length > 0 && !!formDataId
30
+
31
+ if (!allInputsLoaded) return
32
+
33
+ handleHiddenSet(allValues, currentBreakpoint, layoutConfig, formDataId)
34
+ }, [formDataRef, layoutConfig, currentBreakpoint, formDataId])
35
+ }
36
+
37
+ export function handleHiddenSet(
38
+ allValues: Record<string, any>,
39
+ currentBreakpoint: DeviceBreakpointEnum,
40
+ layoutConfig?: IDndLayoutStructure_Responsive,
41
+ formDataId?: string,
42
+ ) {
43
+ const hiddenIds = computeHiddenIds(allValues, currentBreakpoint, layoutConfig, formDataId)
44
+ hiddenStore.setIds(hiddenIds)
45
+ }
46
+
14
47
  function computeHiddenIdsRecursive(layout: IDndLayoutRow[], individuallyHidden: Set<string>): string[] {
15
48
  const hiddenCols = new Set<string>()
16
49
  const hiddenRows = new Set<string>()
@@ -54,16 +87,15 @@ function computeHiddenIdsRecursive(layout: IDndLayoutRow[], individuallyHidden:
54
87
  return [...individuallyHidden, ...Array.from(hiddenCols), ...Array.from(hiddenRows)]
55
88
  }
56
89
 
57
- export function useSetHiddenNodes(
90
+ function computeHiddenIds(
91
+ allValues: Record<string, any>,
92
+ currentBreakpoint: DeviceBreakpointEnum,
58
93
  layoutConfig?: IDndLayoutStructure_Responsive,
59
- formValues?: { [key: string]: any },
60
94
  formDataId?: string,
61
- ): void {
62
- const currentBreakpoint = useGetCurrentBreakpoint() || DeviceBreakpointEnum.Default
63
- const layout: IDndLayoutRow[] =
95
+ ): string[] {
96
+ const layout =
64
97
  layoutConfig?.layouts?.[currentBreakpoint] || layoutConfig?.layouts?.[DeviceBreakpointEnum.Default] || []
65
- const elementsMap: Record<string, IDndLayoutElement> = layoutConfig?.elements || {}
66
- const dataValues: { [key: string]: any } = formValues && typeof formValues === 'object' ? formValues : {}
98
+ const elementsMap = layoutConfig?.elements || {}
67
99
  const configsPerElement: Record<string, IFormLayoutElementConditions> = Object.values(elementsMap).reduce(
68
100
  (curr, el) => {
69
101
  const elConditions = el.conditions?.[FormElementConditionalKeyEnum.ShowIf]
@@ -72,40 +104,20 @@ export function useSetHiddenNodes(
72
104
 
73
105
  return curr
74
106
  },
75
- {},
107
+ {} as any,
76
108
  )
77
109
 
78
- const allInputsLoaded =
79
- layout.length > 0 &&
80
- Object.keys(elementsMap).length > 0 &&
81
- Object.keys(configsPerElement).length > 0 &&
82
- !!formDataId
83
-
84
- const computeIndividuallyHidden = useCallback((): Set<string> => {
85
- if (!allInputsLoaded) return new Set()
110
+ const individuallyHidden = new Set<string>()
111
+ for (const el of Object.values(elementsMap)) {
112
+ const elementConfigs = configsPerElement[el.key]
86
113
 
87
- const hiddenSet = new Set<string>()
88
-
89
- Object.values(elementsMap).forEach(({ key }) => {
90
- const elementConfigs = configsPerElement[key]
91
-
92
- const shouldShow = isValidationsMet(FormElementConditionalKeyEnum.ShowIf, dataValues, elementConfigs, {
93
- currentBreakpoint,
94
- formDataId,
95
- })
96
- if (!shouldShow) hiddenSet.add(key)
114
+ const shouldShow = isValidationsMet(FormElementConditionalKeyEnum.ShowIf, allValues, elementConfigs, {
115
+ currentBreakpoint,
116
+ formDataId,
97
117
  })
98
118
 
99
- return hiddenSet
100
- }, [elementsMap, dataValues, configsPerElement, currentBreakpoint, formDataId, allInputsLoaded])
101
-
102
- const allHiddenArray = useMemo(() => {
103
- const individuallyHidden = computeIndividuallyHidden()
104
-
105
- return computeHiddenIdsRecursive(layout, individuallyHidden)
106
- }, [layout, computeIndividuallyHidden])
119
+ if (!shouldShow) individuallyHidden.add(el.key)
120
+ }
107
121
 
108
- useEffect(() => {
109
- if (allInputsLoaded) hiddenStore.setIds(allHiddenArray)
110
- }, [allHiddenArray, allInputsLoaded])
122
+ return computeHiddenIdsRecursive(layout, individuallyHidden)
111
123
  }
@@ -0,0 +1,20 @@
1
+ import { useEffect } from 'react'
2
+ import { useLocation } from 'react-router-dom'
3
+ import { LOCAL_STORAGE_KEYS_ENUM } from '../../../enums'
4
+
5
+ export const useSetPeristentDomain = (domain: string) => {
6
+ const location = useLocation()
7
+ const isLogin = location.pathname === '/login'
8
+
9
+ useEffect(() => {
10
+ if (!isLogin) return
11
+
12
+ if (typeof window === 'undefined') return
13
+
14
+ const current = window.localStorage.getItem(LOCAL_STORAGE_KEYS_ENUM.PersistDomain)
15
+ if (current !== domain) {
16
+ window.localStorage.setItem(LOCAL_STORAGE_KEYS_ENUM.PersistDomain, domain)
17
+ console.info(`[useSetCompanyDomain] "${LOCAL_STORAGE_KEYS_ENUM.PersistDomain}" set to "${domain}"`)
18
+ }
19
+ }, [isLogin])
20
+ }
@@ -2,6 +2,7 @@ import { Button, Col, Form, Input, Row, Spin } from 'antd'
2
2
  import { useNavigate } from 'react-router-dom'
3
3
  import { useLoginHandler } from '../../common/custom-hooks/use-login-handler'
4
4
  import { useConfigContext } from '../context'
5
+ import CompanyLogoSection, { LogoLocationEnum } from '../../common/company-logo'
5
6
 
6
7
  export const ForgotPassword = () => {
7
8
  const { config } = useConfigContext()
@@ -11,7 +12,6 @@ export const ForgotPassword = () => {
11
12
  const formPadding = config?.loginLayout?.form?.padding
12
13
  const titleColor = config?.loginLayout.form.color
13
14
  const background = config?.loginLayout?.background
14
- const logoUrl = config?.siteIdentity?.logoUrl
15
15
  const primaryColor = config?.siteLayout?.siteConfigs?.colors?.primary
16
16
 
17
17
  return (
@@ -26,11 +26,9 @@ export const ForgotPassword = () => {
26
26
  maxWidth: '100%',
27
27
  }}
28
28
  >
29
- {logoUrl && (
30
- <div className="flex justify-center mb-4">
31
- <img src={logoUrl} alt="Logo" className="h-10 object-contain" />
32
- </div>
33
- )}
29
+ <div className="flex justify-center mb-4">
30
+ <CompanyLogoSection logoUrl={config?.siteIdentity?.logoUrl} location={LogoLocationEnum.Unauthenticated} />
31
+ </div>
34
32
 
35
33
  <div className="font-bold mb-2" style={{ color: titleColor }}>
36
34
  Forgot Password?
@@ -3,6 +3,7 @@ import { useConfigContext } from '../context'
3
3
  import { useLoginHandler } from '../../common/custom-hooks/use-login-handler'
4
4
  import { FaPlus } from 'react-icons/fa'
5
5
  import { useNavigate } from 'react-router-dom'
6
+ import CompanyLogoSection, { LogoLocationEnum } from '../../common/company-logo'
6
7
 
7
8
  export function UnauthenticatedLayout() {
8
9
  const { config } = useConfigContext()
@@ -23,7 +24,7 @@ export function UnauthenticatedLayout() {
23
24
 
24
25
  const logo = (
25
26
  <div className="flex justify-center">
26
- <img src={siteIdentity?.logoUrl} alt="Logo" className="h-10 object-contain" />
27
+ <CompanyLogoSection logoUrl={siteIdentity?.logoUrl} location={LogoLocationEnum.Unauthenticated} />
27
28
  </div>
28
29
  )
29
30
 
@@ -43,7 +44,9 @@ export function UnauthenticatedLayout() {
43
44
 
44
45
  const formSection = (
45
46
  <div
46
- className={`fc-login-container ${loginLayout?.layout === 'center' ? 'w-full' : 'w-1/2'} flex items-center justify-center h-screen`}
47
+ className={`fc-login-container ${
48
+ loginLayout?.layout === 'center' ? 'w-full' : 'w-1/2'
49
+ } flex items-center justify-center h-screen`}
47
50
  >
48
51
  <div
49
52
  style={{
@@ -57,7 +60,7 @@ export function UnauthenticatedLayout() {
57
60
  padding: `${loginLayout?.form?.padding || 16}px`,
58
61
  background: loginLayout?.form?.background,
59
62
  }}
60
- className={`fc-login-panel max-h-screen overflow-auto shadow content-center text-center ${
63
+ className={`fc-login-panel max-h-screen overflow-auto shadow content-center text-center space-y-4 ${
61
64
  loginLayout?.formStyle === 'full' ? 'h-screen' : ''
62
65
  }`}
63
66
  >
@@ -75,7 +78,7 @@ export function UnauthenticatedLayout() {
75
78
  {loginLayout?.form?.title}
76
79
  </div>
77
80
  )}
78
- <Form layout="vertical" onFinish={handleLogin} className='fc-login-form'>
81
+ <Form layout="vertical" onFinish={handleLogin} className="fc-login-form">
79
82
  <div>
80
83
  {loginLayout?.form?.fields?.map((field, index) => {
81
84
  const isLast = index === loginLayout?.form.fields.length - 1
@@ -140,7 +143,7 @@ export function UnauthenticatedLayout() {
140
143
  </div>
141
144
  </Form>
142
145
 
143
- {loginLayout?.logoPosition === 'bottom' && <div className='fc-login-logo'>{logo}</div>}
146
+ {loginLayout?.logoPosition === 'bottom' && <div className="fc-login-logo">{logo}</div>}
144
147
  </div>
145
148
  </div>
146
149
  )
@@ -4,6 +4,7 @@ import { useNavigate, useLocation } from 'react-router-dom'
4
4
  import { useLoginHandler } from '../../common/custom-hooks/use-login-handler'
5
5
  import { useConfigContext } from '../context'
6
6
  import { useRef } from 'react'
7
+ import CompanyLogoSection, { LogoLocationEnum } from '../../common/company-logo'
7
8
 
8
9
  export const ResetPassword = () => {
9
10
  const { config } = useConfigContext()
@@ -21,7 +22,6 @@ export const ResetPassword = () => {
21
22
  const formPadding = config?.loginLayout?.form?.padding
22
23
  const titleColor = config?.loginLayout.form.color
23
24
  const background = config?.loginLayout?.background
24
- const logoUrl = config?.siteIdentity?.logoUrl
25
25
  const primaryColor = config?.siteLayout?.siteConfigs?.colors?.primary
26
26
  const isFirstLoad = useRef(true)
27
27
  useEffect(() => {
@@ -64,11 +64,9 @@ export const ResetPassword = () => {
64
64
  maxWidth: '100%',
65
65
  }}
66
66
  >
67
- {logoUrl && (
68
- <div className="flex justify-center mb-4">
69
- <img src={logoUrl} alt="Logo" className="h-10 object-contain" />
70
- </div>
71
- )}
67
+ <div className="flex justify-center mb-4">
68
+ <CompanyLogoSection logoUrl={config?.siteIdentity?.logoUrl} location={LogoLocationEnum.Unauthenticated} />
69
+ </div>
72
70
 
73
71
  <div className="font-bold mb-2" style={{ color: titleColor }}>
74
72
  Reset Password
@@ -3,14 +3,39 @@ import { ConfigProvider } from 'antd'
3
3
  import { ConfigContext } from '../context'
4
4
  import useSiteMetadata from '../../common/custom-hooks/use-site-meta-data'
5
5
  import { ReactNode } from 'react'
6
+ import { Button_FillerPortal } from '../../common/button'
7
+ import { LOCAL_STORAGE_KEYS_ENUM } from '../../../enums'
8
+ import { useLocation, useNavigate } from 'react-router-dom'
6
9
 
7
10
  export function ConfigProviderLayout({ children }: { children: ReactNode }): JSX.Element {
8
- const { config, theme } = useCompanyConfig()
11
+ const navigate = useNavigate()
12
+ const location = useLocation()
13
+ const { config, theme, canSwitchDomain } = useCompanyConfig()
9
14
  useSiteMetadata(config?.siteIdentity)
10
15
 
16
+ const showSwitchCompany =
17
+ canSwitchDomain &&
18
+ !localStorage.getItem(LOCAL_STORAGE_KEYS_ENUM.PersistDomain) &&
19
+ location.pathname.startsWith('/login')
20
+
11
21
  return (
12
22
  <ConfigContext.Provider value={{ config }}>
13
- <ConfigProvider theme={theme}>{children}</ConfigProvider>
23
+ <ConfigProvider theme={theme}>
24
+ {showSwitchCompany && (
25
+ <div className="absolute top-2 right-2 z-20">
26
+ <Button_FillerPortal
27
+ outline
28
+ onClick={() => {
29
+ localStorage.removeItem(LOCAL_STORAGE_KEYS_ENUM.Domain)
30
+ navigate(0)
31
+ }}
32
+ >
33
+ Switch Project
34
+ </Button_FillerPortal>
35
+ </div>
36
+ )}
37
+ {children}
38
+ </ConfigProvider>
14
39
  </ConfigContext.Provider>
15
40
  )
16
41
  }
@@ -12,8 +12,14 @@ import { useManyToManyConnector } from '../../common/custom-hooks/use-many-to-ma
12
12
  import { PageViewTypEnum, DeviceBreakpointEnum, FormPreservedItemKeys, LOCAL_STORAGE_KEYS_ENUM } from '../../../enums'
13
13
  import { UserAuth } from '../../../api/user'
14
14
  import { useCacheFormLayoutConfig } from '../../common/custom-hooks/use-cache-form-layout-config.hook'
15
- import { useSetHiddenNodes } from '../../common/custom-hooks/use-node-condition.hook/use-hidden-elements.hook'
16
- import { useSetDisabledElements } from '../../common/custom-hooks/use-node-condition.hook/use-disabled-elements.hook'
15
+ import {
16
+ handleHiddenSet,
17
+ useSetHiddenNodes,
18
+ } from '../../common/custom-hooks/use-node-condition.hook/use-hidden-elements.hook'
19
+ import {
20
+ handleDisabledSet,
21
+ useSetDisabledElements,
22
+ } from '../../common/custom-hooks/use-node-condition.hook/use-disabled-elements.hook'
17
23
  import { ELEMENTS_DEFAULT_CLASS } from '../../../constants'
18
24
  import {
19
25
  DynamicFormButtonRender,
@@ -50,10 +56,9 @@ export default function FormDataDetailsComponent({
50
56
  const currentBreakpoint = useGetCurrentBreakpoint()
51
57
 
52
58
  const { cachedConfig, isConfigLoading } = useCacheFormLayoutConfig(formId, formKey)
53
- const detailsData = Form.useWatch([], formDataRef)
54
59
 
55
- useSetHiddenNodes(cachedConfig?.detailsConfig, detailsData, formDataId)
56
- useSetDisabledElements(cachedConfig?.detailsConfig, detailsData, formDataId)
60
+ useSetHiddenNodes(cachedConfig?.detailsConfig, formDataRef, formDataId)
61
+ useSetDisabledElements(cachedConfig?.detailsConfig, formDataRef, formDataId)
57
62
 
58
63
  useEffect(() => {
59
64
  // for public forms, setting the details form into localStorage, so that buttons work correctly
@@ -176,6 +181,10 @@ export default function FormDataDetailsComponent({
176
181
  name="dynamic_form_data_form"
177
182
  form={formDataRef}
178
183
  className={ELEMENTS_DEFAULT_CLASS.DataDetailsForm}
184
+ onValuesChange={(_changed, allValues) => {
185
+ handleDisabledSet(allValues, currentBreakpoint, cachedConfig?.detailsConfig, formDataId)
186
+ handleHiddenSet(allValues, currentBreakpoint, cachedConfig?.detailsConfig, formDataId)
187
+ }}
179
188
  >
180
189
  {layout.map((row, rowIdx) => (
181
190
  <LayoutRendererRow
@@ -1,6 +1,6 @@
1
- import { ReactNode, useState } from 'react'
1
+ import { ReactNode, useState, useTransition } from 'react'
2
2
  import { IFormDndLayoutRowHeader } from '../../../../types'
3
- import { Collapse } from 'antd'
3
+ import { Collapse, Spin } from 'antd'
4
4
  import RowHeader from './header'
5
5
 
6
6
  export const LayoutRowConditionalHeaderRenderer = ({
@@ -11,21 +11,30 @@ export const LayoutRowConditionalHeaderRenderer = ({
11
11
  header: IFormDndLayoutRowHeader | undefined
12
12
  }) => {
13
13
  const [isCollapsed, setIsCollapsed] = useState(false)
14
+ const [isPendingTransition, startTransition] = useTransition()
14
15
 
15
16
  if (header?.isCollapsible)
16
17
  return (
17
- <Collapse
18
- className="fc-collapsible-row bg-transparent"
19
- bordered={false}
20
- defaultActiveKey={[]}
21
- items={[
22
- {
23
- key: '1',
24
- label: <RowHeader {...header} isCollapsed={isCollapsed} setIsCollapsed={() => setIsCollapsed((c) => !c)} />,
25
- children,
26
- },
27
- ]}
28
- />
18
+ <Spin spinning={isPendingTransition}>
19
+ <Collapse
20
+ className="fc-collapsible-row bg-transparent"
21
+ bordered={false}
22
+ defaultActiveKey={header.defaultCollapsed ? [] : ['1']}
23
+ items={[
24
+ {
25
+ key: '1',
26
+ label: (
27
+ <RowHeader
28
+ {...header}
29
+ isCollapsed={isCollapsed}
30
+ setIsCollapsed={() => startTransition(() => setIsCollapsed((c) => !c))}
31
+ />
32
+ ),
33
+ children,
34
+ },
35
+ ]}
36
+ />
37
+ </Spin>
29
38
  )
30
39
 
31
40
  if (header?.name)
@@ -28,7 +28,11 @@ export const LayoutRendererRow = memo(
28
28
  className={ELEMENTS_DEFAULT_CLASS.LayoutRowContainer}
29
29
  >
30
30
  <LayoutRowConditionalHeaderRenderer header={rowData.props?.header}>
31
- <LayoutRowRepeatableRenderer basePath={basePath} repeatingSection={rowData.props?.repeatingSection}>
31
+ <LayoutRowRepeatableRenderer
32
+ basePath={basePath}
33
+ formRef={formContext.formRef}
34
+ repeatingSection={rowData.props?.repeatingSection}
35
+ >
32
36
  {(formListItemProps) => {
33
37
  const style: { [key: string]: any } = { ...styleConfig, ...getGridContainerStyle(rowData.display) }
34
38
  const hiddenColsIndices = rowData.children.reduce(