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.
- package/package.json +1 -1
- package/src/components/common/company-logo.tsx +40 -0
- package/src/components/common/custom-hooks/index.ts +1 -0
- package/src/components/common/custom-hooks/use-login-handler.ts +3 -3
- package/src/components/common/custom-hooks/use-node-condition.hook/use-disabled-elements.hook.ts +62 -22
- package/src/components/common/custom-hooks/use-node-condition.hook/use-hidden-elements.hook.ts +54 -42
- package/src/components/common/custom-hooks/use-set-persistent-domain.hook.ts +20 -0
- package/src/components/companies/2-unauthenticated/forgot-password.tsx +4 -6
- package/src/components/companies/2-unauthenticated/index.tsx +8 -5
- package/src/components/companies/2-unauthenticated/reset-password.tsx +4 -6
- package/src/components/companies/3-config-provider/index.tsx +27 -2
- package/src/components/form/2-details/index.tsx +14 -5
- package/src/components/form/layout-renderer/1-row/header-render.tsx +23 -14
- package/src/components/form/layout-renderer/1-row/index.tsx +5 -1
- package/src/components/form/layout-renderer/1-row/repeatable-render.tsx +69 -28
- package/src/components/form/layout-renderer/3-element/10-currency.tsx +4 -2
- package/src/components/form/layout-renderer/3-element/11-breadcrumb.tsx +4 -2
- package/src/components/form/layout-renderer/3-element/12-picker-field.tsx +4 -7
- package/src/components/form/layout-renderer/3-element/2-field-element.tsx +203 -225
- package/src/components/form/layout-renderer/3-element/3-read-field-data.tsx +4 -6
- package/src/components/form/layout-renderer/3-element/4-rich-text-editor.tsx +4 -1
- package/src/components/form/layout-renderer/3-element/5-re-captcha.tsx +4 -7
- package/src/components/form/layout-renderer/3-element/6-signature.tsx +4 -2
- package/src/components/form/layout-renderer/3-element/7-file-upload.tsx +96 -44
- package/src/components/form/layout-renderer/3-element/8-fields-with-options.tsx +5 -3
- package/src/components/form/layout-renderer/3-element/9-form-data-render.tsx +4 -2
- package/src/components/form/layout-renderer/3-element/index.tsx +125 -86
- package/src/enums/form.enum.ts +4 -0
- package/src/enums/index.ts +1 -0
- package/src/functions/companies/index.tsx +12 -0
- package/src/functions/companies/use-company-config.tsx +14 -10
- package/src/functions/forms/data-render-functions.tsx +5 -3
- package/src/functions/forms/get-element-props.ts +2 -2
- package/src/functions/forms/index.ts +3 -1
- package/src/types/companies/site-layout/authenticated/index.tsx +11 -34
- package/src/types/forms/index.ts +10 -4
- package/src/types/forms/layout-elements/button.ts +1 -1
package/package.json
CHANGED
|
@@ -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
|
+
}
|
|
@@ -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 =
|
|
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
|
-
|
|
30
|
+
getLocalStorageDomain(),
|
|
31
31
|
values.type || 'password',
|
|
32
32
|
)
|
|
33
33
|
if (authRes.status === 200) {
|
package/src/components/common/custom-hooks/use-node-condition.hook/use-disabled-elements.hook.ts
CHANGED
|
@@ -1,8 +1,9 @@
|
|
|
1
|
-
import { useEffect
|
|
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
|
-
|
|
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(
|
|
88
|
+
Object.values(allValues).length > 0 &&
|
|
40
89
|
!!formDataId
|
|
41
90
|
|
|
42
|
-
const
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
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
|
-
|
|
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
|
}
|
package/src/components/common/custom-hooks/use-node-condition.hook/use-hidden-elements.hook.ts
CHANGED
|
@@ -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
|
-
|
|
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
|
-
):
|
|
62
|
-
const
|
|
63
|
-
const layout: IDndLayoutRow[] =
|
|
95
|
+
): string[] {
|
|
96
|
+
const layout =
|
|
64
97
|
layoutConfig?.layouts?.[currentBreakpoint] || layoutConfig?.layouts?.[DeviceBreakpointEnum.Default] || []
|
|
65
|
-
const elementsMap
|
|
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
|
|
79
|
-
|
|
80
|
-
|
|
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
|
|
88
|
-
|
|
89
|
-
|
|
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
|
-
|
|
100
|
-
}
|
|
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
|
-
|
|
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
|
-
|
|
30
|
-
<
|
|
31
|
-
|
|
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
|
-
<
|
|
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 ${
|
|
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=
|
|
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=
|
|
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
|
-
|
|
68
|
-
<
|
|
69
|
-
|
|
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
|
|
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}>
|
|
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 {
|
|
16
|
-
|
|
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,
|
|
56
|
-
useSetDisabledElements(cachedConfig?.detailsConfig,
|
|
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
|
-
<
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
{
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
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
|
|
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(
|