form-craft-package 1.7.3 → 1.7.5
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/.env.development +4 -0
- package/.env.production +5 -0
- package/.env.staging +4 -0
- package/package.json +4 -1
- package/src/{functions/axios-handler.ts → api/client.ts} +75 -7
- package/src/api/user.ts +50 -0
- package/src/components/common/currency-field.tsx +8 -0
- package/src/components/common/custom-hooks/use-login-handler.ts +110 -0
- package/src/components/common/custom-hooks/use-site-meta-data.ts +24 -0
- package/src/components/common/custom-hooks/use-theme-config.ts +51 -0
- package/src/components/companies/1-authenticated/index.tsx +22 -0
- package/src/components/companies/2-unauthenticated/forgot-password.tsx +46 -0
- package/src/components/companies/2-unauthenticated/index.tsx +267 -0
- package/src/components/companies/3-config-provider/index.tsx +16 -0
- package/src/components/companies/context/index.tsx +12 -0
- package/src/components/companies/index.tsx +4 -0
- package/src/components/form/1-list/index.tsx +103 -58
- package/src/components/form/1-list/table-header.tsx +42 -57
- package/src/components/form/1-list/table.tsx +101 -157
- package/src/components/form/2-details/index.tsx +1 -1
- package/src/components/form/layout-renderer/1-row/index.tsx +39 -46
- package/src/components/form/layout-renderer/3-element/1-dynamic-button/index.tsx +1 -1
- package/src/components/form/layout-renderer/3-element/1-dynamic-button/use-create-data.hook.ts +1 -1
- package/src/components/form/layout-renderer/3-element/1-dynamic-button/use-delete-data.hook.ts +1 -1
- package/src/components/form/layout-renderer/3-element/1-dynamic-button/use-generate-report.hook.tsx +2 -2
- package/src/components/form/layout-renderer/3-element/1-dynamic-button/use-publish-data.hook.ts +1 -1
- package/src/components/form/layout-renderer/3-element/1-dynamic-button/use-save-data.hook.ts +1 -1
- package/src/components/form/layout-renderer/3-element/3-read-field-data.tsx +7 -6
- package/src/components/form/layout-renderer/3-element/5-re-captcha.tsx +11 -3
- package/src/components/form/layout-renderer/3-element/7-file-upload.tsx +1 -1
- package/src/components/form/layout-renderer/3-element/8-fields-with-options.tsx +62 -10
- package/src/components/form/layout-renderer/3-element/9-form-data-render.tsx +173 -171
- package/src/components/index.tsx +1 -0
- package/src/components/modals/report-filters.modal/index.tsx +1 -1
- package/src/constants.ts +8 -0
- package/src/enums/form.enum.ts +5 -5
- package/src/enums/index.ts +6 -0
- package/src/functions/companies/index.tsx +2 -0
- package/src/functions/companies/use-company-config.tsx +82 -0
- package/src/functions/forms/convert-number-into-words.ts +47 -0
- package/src/functions/forms/data-render-functions.tsx +11 -9
- package/src/functions/forms/form.ts +1 -1
- package/src/functions/forms/get-data-list-option-value.ts +69 -0
- package/src/functions/forms/index.ts +24 -87
- package/src/functions/index.ts +1 -0
- package/src/global-helpers/constants.ts +2 -0
- package/src/global-helpers/cookie-handler.ts +44 -0
- package/src/global-helpers/enums.ts +12 -0
- package/src/types/companies/index.ts +4 -4
- package/src/types/companies/site-layout/authenticated/index.tsx +71 -38
- package/src/types/companies/site-layout/unauthenticated/index.tsx +1 -12
- package/src/types/forms/data-list/index.ts +2 -13
- package/src/types/forms/index.ts +10 -0
- package/src/types/forms/layout-elements/button.ts +5 -3
- package/src/types/forms/layout-elements/data-render-config.ts +5 -0
- package/src/types/forms/layout-elements/field-option-source.ts +2 -1
- package/src/types/forms/layout-elements/index.ts +2 -7
- package/src/types/forms/layout-elements/read-field-data-props.ts +21 -0
- package/tsconfig.json +1 -0
- /package/src/functions/companies/{FontSelector.tsx → font-selector.tsx} +0 -0
package/.env.development
ADDED
package/.env.production
ADDED
package/.env.staging
ADDED
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "form-craft-package",
|
|
3
|
-
"version": "1.7.
|
|
3
|
+
"version": "1.7.5",
|
|
4
4
|
"main": "index.ts",
|
|
5
5
|
"scripts": {
|
|
6
6
|
"test": "echo \"Error: no test specified\" && exit 1",
|
|
@@ -15,6 +15,7 @@
|
|
|
15
15
|
"@types/react-signature-canvas": "^1.0.6",
|
|
16
16
|
"ajv": "^8.17.1",
|
|
17
17
|
"pdfmake": "^0.2.18",
|
|
18
|
+
"qs": "^6.14.0",
|
|
18
19
|
"react-google-recaptcha": "^3.1.0",
|
|
19
20
|
"react-signature-canvas": "^1.0.7"
|
|
20
21
|
},
|
|
@@ -31,8 +32,10 @@
|
|
|
31
32
|
"react-router-dom": ">=6.27.0"
|
|
32
33
|
},
|
|
33
34
|
"devDependencies": {
|
|
35
|
+
"@types/crypto-js": "^4.2.2",
|
|
34
36
|
"@types/js-cookie": "^3.0.6",
|
|
35
37
|
"@types/pdfmake": "^0.2.11",
|
|
38
|
+
"@types/qs": "^6.9.18",
|
|
36
39
|
"@types/react": "^18.3.18",
|
|
37
40
|
"@types/react-dom": "^18.3.5",
|
|
38
41
|
"react": "^18.3.1",
|
|
@@ -1,15 +1,20 @@
|
|
|
1
1
|
import Cookies from 'js-cookie'
|
|
2
|
-
import axios from 'axios'
|
|
3
|
-
|
|
2
|
+
import axios, { AxiosRequestConfig, AxiosResponse, CancelTokenSource } from 'axios'
|
|
3
|
+
import qs from 'qs'
|
|
4
4
|
import { notification } from 'antd'
|
|
5
|
-
import { SHARED_COOKIE_KEYS } from '../enums'
|
|
5
|
+
import { LOCAL_STORAGE_KEYS_ENUM, SHARED_COOKIE_KEYS } from '../enums'
|
|
6
|
+
|
|
7
|
+
import { IDynamicForm } from '../types'
|
|
8
|
+
import { constructDynamicFormHref, fetchDynamicForms } from '../functions'
|
|
9
|
+
import { CLIENT_ID, CLIENT_SECRET } from '../global-helpers/constants'
|
|
6
10
|
|
|
7
|
-
|
|
11
|
+
export const baseURL = 'https://formcraftmaster-backend-dev-f8egf2fcfhhahuhr.centralus-01.azurewebsites.net/'
|
|
8
12
|
|
|
9
13
|
const getCookieDeadline = (expiresIn: number): Date => {
|
|
10
14
|
const now = new Date()
|
|
11
15
|
return new Date(now.getTime() + expiresIn * 1000)
|
|
12
16
|
}
|
|
17
|
+
const stringify = (obj: any) => qs.stringify(obj)
|
|
13
18
|
|
|
14
19
|
const authResHandler = (resData: any) => {
|
|
15
20
|
const expires = getCookieDeadline(resData.expires_in)
|
|
@@ -21,6 +26,9 @@ const authResHandler = (resData: any) => {
|
|
|
21
26
|
Cookies.set(SHARED_COOKIE_KEYS.AccessToken, resData.access_token, cookieSettings)
|
|
22
27
|
Cookies.set(SHARED_COOKIE_KEYS.RefreshToken, resData.refresh_token)
|
|
23
28
|
}
|
|
29
|
+
/** --------------- Cancel token source --------------- */
|
|
30
|
+
|
|
31
|
+
const clientCancelToken = axios.CancelToken
|
|
24
32
|
|
|
25
33
|
/** --------------- Access Token Interceptor --------------- */
|
|
26
34
|
|
|
@@ -35,6 +43,48 @@ client.interceptors.request.use((req) => {
|
|
|
35
43
|
if (req.headers) req.headers.Authorization = `Bearer ${accessToken}`
|
|
36
44
|
return req
|
|
37
45
|
})
|
|
46
|
+
/** --------------- Login --------------- */
|
|
47
|
+
|
|
48
|
+
const auth = async (
|
|
49
|
+
email: string,
|
|
50
|
+
password: string,
|
|
51
|
+
domain: string,
|
|
52
|
+
type: string
|
|
53
|
+
): Promise<{ authRes: any; initialPath: string }> => {
|
|
54
|
+
|
|
55
|
+
const reqData = stringify({
|
|
56
|
+
username: email,
|
|
57
|
+
password,
|
|
58
|
+
grant_type: type,
|
|
59
|
+
refresh_token: type === 'refresh_token' ? Cookies.get(SHARED_COOKIE_KEYS.RefreshToken) : undefined,
|
|
60
|
+
client_id: CLIENT_ID,
|
|
61
|
+
client_secret: CLIENT_SECRET,
|
|
62
|
+
scope: 'offline_access',
|
|
63
|
+
domain,
|
|
64
|
+
})
|
|
65
|
+
try {
|
|
66
|
+
|
|
67
|
+
const loginAuthRes = await client.post(`/connect/token`, reqData)
|
|
68
|
+
|
|
69
|
+
let forms: IDynamicForm[] = []
|
|
70
|
+
if (loginAuthRes.status === 200) {
|
|
71
|
+
authResHandler(loginAuthRes.data)
|
|
72
|
+
if (!loginAuthRes.data.required2FA || type === 'refresh_token') {
|
|
73
|
+
Cookies.set(SHARED_COOKIE_KEYS.Authorized, 'true')
|
|
74
|
+
forms = await fetchDynamicForms()
|
|
75
|
+
localStorage.setItem(LOCAL_STORAGE_KEYS_ENUM.DynamicForms, JSON.stringify(forms))
|
|
76
|
+
localStorage.setItem(LOCAL_STORAGE_KEYS_ENUM.Domain, domain)
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
return {
|
|
80
|
+
authRes: loginAuthRes,
|
|
81
|
+
initialPath: forms.length > 0 ? constructDynamicFormHref(forms[0].name) : '/accounts',
|
|
82
|
+
}
|
|
83
|
+
} catch (error) {
|
|
84
|
+
return { authRes: error, initialPath: '' }
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
|
|
38
88
|
|
|
39
89
|
/** --------------- Refresh Token --------------- */
|
|
40
90
|
|
|
@@ -53,8 +103,8 @@ const handleTokenRefresh = () => {
|
|
|
53
103
|
window.location.href = '/login'
|
|
54
104
|
}
|
|
55
105
|
return new Promise((resolve, reject) => {
|
|
56
|
-
|
|
57
|
-
.post(
|
|
106
|
+
client
|
|
107
|
+
.post(`$/connect/token/refresh`, { refreshToken })
|
|
58
108
|
.then(({ data }) => {
|
|
59
109
|
resolve({
|
|
60
110
|
accessToken: data.accessToken,
|
|
@@ -154,5 +204,23 @@ const displayNotification = (error: any) => {
|
|
|
154
204
|
}
|
|
155
205
|
}
|
|
156
206
|
|
|
207
|
+
const sendCancelableRequest = (
|
|
208
|
+
config: AxiosRequestConfig,
|
|
209
|
+
): { request: Promise<AxiosResponse<any>>; cancel: (message?: string) => void } => {
|
|
210
|
+
const source: CancelTokenSource = axios.CancelToken.source()
|
|
211
|
+
config.cancelToken = source.token
|
|
212
|
+
const request: Promise<AxiosResponse<any>> = client.request(config)
|
|
213
|
+
return { request, cancel: source.cancel }
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
export const cancelableClient = {
|
|
217
|
+
get: (url: string, config?: AxiosRequestConfig) => sendCancelableRequest({ method: 'get', url, ...config }),
|
|
218
|
+
post: (url: string, data?: any, config?: AxiosRequestConfig) =>
|
|
219
|
+
sendCancelableRequest({ method: 'post', url, data, ...config }),
|
|
220
|
+
put: (url: string, data?: any, config?: AxiosRequestConfig) =>
|
|
221
|
+
sendCancelableRequest({ method: 'put', url, data, ...config }),
|
|
222
|
+
delete: (url: string, config?: AxiosRequestConfig) => sendCancelableRequest({ method: 'delete', url, ...config }),
|
|
223
|
+
}
|
|
224
|
+
|
|
157
225
|
export default client
|
|
158
|
-
export { authResHandler }
|
|
226
|
+
export { authResHandler, auth, clientCancelToken }
|
package/src/api/user.ts
ADDED
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
import { SHARED_COOKIE_KEYS } from "../enums"
|
|
2
|
+
import { CLIENT_SECRET } from "../global-helpers/constants"
|
|
3
|
+
import { cookieHandler } from "../global-helpers/cookie-handler"
|
|
4
|
+
import crypto from 'crypto-js'
|
|
5
|
+
import { CookieKeysEnum } from "../global-helpers/enums"
|
|
6
|
+
|
|
7
|
+
const endSession = () => {
|
|
8
|
+
cookieHandler.empty()
|
|
9
|
+
localStorage.clear()
|
|
10
|
+
return
|
|
11
|
+
}
|
|
12
|
+
const decrypt = (toDecrypt: CookieKeysEnum) => {
|
|
13
|
+
const cookieValue = cookieHandler.get(toDecrypt)
|
|
14
|
+
if (!cookieValue) return ''
|
|
15
|
+
const bytes = crypto.AES.decrypt(cookieValue, CLIENT_SECRET)
|
|
16
|
+
return JSON.parse(bytes.toString(crypto.enc.Utf8))
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
const UserAuth = {
|
|
20
|
+
hasRefreshToken: () => {
|
|
21
|
+
return !!cookieHandler.get(SHARED_COOKIE_KEYS.RefreshToken)
|
|
22
|
+
},
|
|
23
|
+
isAuthorized: () => {
|
|
24
|
+
return cookieHandler.get(SHARED_COOKIE_KEYS.Authorized)
|
|
25
|
+
},
|
|
26
|
+
getRefreshToken: () => {
|
|
27
|
+
return cookieHandler.get(SHARED_COOKIE_KEYS.RefreshToken)
|
|
28
|
+
},
|
|
29
|
+
getCompanyKey: () => {
|
|
30
|
+
return cookieHandler.get(SHARED_COOKIE_KEYS.CompanyKey)
|
|
31
|
+
},
|
|
32
|
+
getUserEmail: () => {
|
|
33
|
+
try {
|
|
34
|
+
return decrypt(CookieKeysEnum.UserEmail) ? decrypt(CookieKeysEnum.UserEmail) : ''
|
|
35
|
+
} catch {
|
|
36
|
+
endSession()
|
|
37
|
+
}
|
|
38
|
+
},
|
|
39
|
+
getUserFullName: () => {
|
|
40
|
+
try {
|
|
41
|
+
return decrypt(CookieKeysEnum.FullName) ? decrypt(CookieKeysEnum.FullName) : ''
|
|
42
|
+
} catch {
|
|
43
|
+
endSession()
|
|
44
|
+
}
|
|
45
|
+
},
|
|
46
|
+
getUserId: () => {
|
|
47
|
+
return cookieHandler.get(CookieKeysEnum.UserId)
|
|
48
|
+
},
|
|
49
|
+
}
|
|
50
|
+
export default UserAuth
|
|
@@ -6,6 +6,8 @@ import { INTERNATIONALIZATION_DATA } from '../../constants'
|
|
|
6
6
|
export default function CurrencyField({
|
|
7
7
|
country = CountryEnum.US,
|
|
8
8
|
decimalPoint = 0,
|
|
9
|
+
value,
|
|
10
|
+
onChange,
|
|
9
11
|
...restProps
|
|
10
12
|
}: ICurrencyField) {
|
|
11
13
|
const inputRef = useRef<HTMLInputElement>(null)
|
|
@@ -44,6 +46,10 @@ export default function CurrencyField({
|
|
|
44
46
|
ref={inputRef}
|
|
45
47
|
onFocus={handleFocus}
|
|
46
48
|
precision={decimalPoint}
|
|
49
|
+
value={value}
|
|
50
|
+
onChange={(val) => {
|
|
51
|
+
if (typeof val === 'number' && onChange) onChange(val)
|
|
52
|
+
}}
|
|
47
53
|
formatter={(value) => {
|
|
48
54
|
if (!value) return `${currencySymbol}${parseInt('0').toFixed(decimalPoint)}`
|
|
49
55
|
|
|
@@ -65,4 +71,6 @@ interface ICurrencyField {
|
|
|
65
71
|
decimalPoint?: number
|
|
66
72
|
placeholder?: string
|
|
67
73
|
disabled?: boolean
|
|
74
|
+
value?: number
|
|
75
|
+
onChange?: (val: number) => void
|
|
68
76
|
}
|
|
@@ -0,0 +1,110 @@
|
|
|
1
|
+
import { useReducer, useState } from 'react'
|
|
2
|
+
import { useNavigate } from 'react-router-dom'
|
|
3
|
+
import client, { auth } from '../../../api/client'
|
|
4
|
+
import { LOCAL_STORAGE_KEYS_ENUM } from '../../../enums'
|
|
5
|
+
import { cookieHandler } from '../../../global-helpers/cookie-handler'
|
|
6
|
+
import { message } from 'antd'
|
|
7
|
+
export function useLoginHandler() {
|
|
8
|
+
const navigate = useNavigate()
|
|
9
|
+
const domain = localStorage.getItem(LOCAL_STORAGE_KEYS_ENUM.Domain) || ''
|
|
10
|
+
const [loading, updateLoading] = useReducer(
|
|
11
|
+
(currState: any, updated: any) => ({ ...currState, ...updated }),
|
|
12
|
+
{ login: false }
|
|
13
|
+
)
|
|
14
|
+
|
|
15
|
+
const [googleAuthSetup, setGoogleAuthSetup] = useState<null | {
|
|
16
|
+
qrCodeImageUrl: string
|
|
17
|
+
manualEntryCode: string
|
|
18
|
+
key: string
|
|
19
|
+
}>(null)
|
|
20
|
+
|
|
21
|
+
const [showVerificationModal, setShowVerificationModal] = useState(false)
|
|
22
|
+
const [latestLoginValues, setLatestLoginValues] = useState<any>(null)
|
|
23
|
+
|
|
24
|
+
const handleLogin = async (values: any) => {
|
|
25
|
+
updateLoading({ login: true })
|
|
26
|
+
setLatestLoginValues(values)
|
|
27
|
+
|
|
28
|
+
const { authRes, initialPath } = await auth(values.email, values.password, domain, values.type || 'password')
|
|
29
|
+
if (authRes.status === 200) {
|
|
30
|
+
const data = authRes.data
|
|
31
|
+
if (!data.required2FA) {
|
|
32
|
+
navigate(initialPath)
|
|
33
|
+
} else if (data.required2FA && !data.configuredGoogleAuth) {
|
|
34
|
+
const googleSetup = await client.get(`api/authorization/authkey`)
|
|
35
|
+
if (googleSetup.status === 200) {
|
|
36
|
+
setGoogleAuthSetup({
|
|
37
|
+
qrCodeImageUrl: googleSetup.data.qrCodeImageUrl,
|
|
38
|
+
manualEntryCode: googleSetup.data.manualEntrySetupCode,
|
|
39
|
+
key: googleSetup.data.key,
|
|
40
|
+
})
|
|
41
|
+
}
|
|
42
|
+
} else if (data.required2FA && data.configuredGoogleAuth) {
|
|
43
|
+
setShowVerificationModal(true)
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
updateLoading({ login: false })
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
const handleSetupDone = async () => {
|
|
51
|
+
if (!googleAuthSetup) return
|
|
52
|
+
await client.put(`api/authorization/authkey?key=${googleAuthSetup.key}`)
|
|
53
|
+
setGoogleAuthSetup(null)
|
|
54
|
+
handleLogin(latestLoginValues)
|
|
55
|
+
}
|
|
56
|
+
const handleSetupCancel = async () => {
|
|
57
|
+
await cookieHandler.empty()
|
|
58
|
+
setGoogleAuthSetup(null)
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
const handleVerifyCode = async (code: any) => {
|
|
62
|
+
await handleLogin({ email: latestLoginValues.email, password: code, type: 'refresh_token' })
|
|
63
|
+
}
|
|
64
|
+
const handleUnlinkAuthenticator = async () => {
|
|
65
|
+
const res = await client.get('api/authorization/authkey')
|
|
66
|
+
if (res.status === 200) {
|
|
67
|
+
setGoogleAuthSetup({
|
|
68
|
+
qrCodeImageUrl: res.data.qrCodeImageUrl,
|
|
69
|
+
manualEntryCode: res.data.manualEntrySetupCode,
|
|
70
|
+
key: res.data.key,
|
|
71
|
+
})
|
|
72
|
+
setShowVerificationModal(false)
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
const handleForgotPassword = async (email?: string) => {
|
|
76
|
+
if (!email) {
|
|
77
|
+
return message.error('Please enter your email or username first.')
|
|
78
|
+
}
|
|
79
|
+
const payload = {
|
|
80
|
+
subject: 'Reset your password',
|
|
81
|
+
body: 'Please click the link to reset your password.',
|
|
82
|
+
from: 'no-reply@icodice.com',
|
|
83
|
+
to: email,
|
|
84
|
+
cc: '',
|
|
85
|
+
bcc: '',
|
|
86
|
+
notificationId: 0,
|
|
87
|
+
attachments: [],
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
try {
|
|
91
|
+
await client.post(`/api/user/${domain}/forgotpassword/${email}`, payload)
|
|
92
|
+
message.success('Reset link sent. Please check your email.')
|
|
93
|
+
} catch (error) {
|
|
94
|
+
console.error('Forgot Password Error:', error)
|
|
95
|
+
message.error('Failed to send reset email.')
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
return {
|
|
99
|
+
handleLogin,
|
|
100
|
+
loading,
|
|
101
|
+
googleAuthSetup,
|
|
102
|
+
showVerificationModal,
|
|
103
|
+
handleSetupDone,
|
|
104
|
+
setShowVerificationModal,
|
|
105
|
+
handleSetupCancel,
|
|
106
|
+
handleVerifyCode,
|
|
107
|
+
handleUnlinkAuthenticator,
|
|
108
|
+
handleForgotPassword
|
|
109
|
+
}
|
|
110
|
+
}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import { useEffect } from 'react';
|
|
2
|
+
import { ISiteIdentity } from '../../../types';
|
|
3
|
+
|
|
4
|
+
|
|
5
|
+
const useSiteMetadata = (siteIdentity?: ISiteIdentity) => {
|
|
6
|
+
|
|
7
|
+
useEffect(() => {
|
|
8
|
+
if (siteIdentity?.title) {
|
|
9
|
+
document.title = siteIdentity.title;
|
|
10
|
+
}
|
|
11
|
+
if (siteIdentity?.iconUrl) {
|
|
12
|
+
const iconUrl = siteIdentity.iconUrl;
|
|
13
|
+
let link = document.querySelector("link[rel~='icon']") as HTMLLinkElement | null;
|
|
14
|
+
if (!link) {
|
|
15
|
+
link = document.createElement('link');
|
|
16
|
+
link.rel = 'icon';
|
|
17
|
+
document.head.appendChild(link);
|
|
18
|
+
}
|
|
19
|
+
link.href = iconUrl;
|
|
20
|
+
}
|
|
21
|
+
}, [siteIdentity]);
|
|
22
|
+
};
|
|
23
|
+
|
|
24
|
+
export default useSiteMetadata;
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
/* eslint-disable @typescript-eslint/no-explicit-any */
|
|
2
|
+
function loadGoogleFont(fontName: any) {
|
|
3
|
+
const id = `google-font-${fontName}`
|
|
4
|
+
if (document.getElementById(id)) return
|
|
5
|
+
|
|
6
|
+
const link = document.createElement('link')
|
|
7
|
+
link.id = id
|
|
8
|
+
link.rel = 'stylesheet'
|
|
9
|
+
link.href = `https://fonts.googleapis.com/css2?family=${fontName.replace(/ /g, '+')}&display=swap`
|
|
10
|
+
document.head.appendChild(link)
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
const transformThemeData = (data: Record<string, any>) => {
|
|
14
|
+
if (!data || typeof data !== 'object') return { token: {}, components: {} }
|
|
15
|
+
|
|
16
|
+
const { colors, fontFamily, ...rest } = data
|
|
17
|
+
|
|
18
|
+
const token: Record<string, string> = {}
|
|
19
|
+
|
|
20
|
+
if (colors && typeof colors === 'object') {
|
|
21
|
+
const root = document.documentElement.style
|
|
22
|
+
Object.entries(colors).forEach(([key, value]) => {
|
|
23
|
+
if (typeof value === 'string') {
|
|
24
|
+
token[`color${key.charAt(0).toUpperCase() + key.slice(1)}`] = value
|
|
25
|
+
root.setProperty(`--color-${key}`, value)
|
|
26
|
+
}
|
|
27
|
+
})
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
if (fontFamily && typeof fontFamily === 'string') {
|
|
31
|
+
token.fontFamily = fontFamily
|
|
32
|
+
loadGoogleFont(fontFamily)
|
|
33
|
+
document.documentElement.style.setProperty('--font-family', fontFamily)
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
const components: Record<string, any> = {}
|
|
37
|
+
Object.entries(rest).forEach(([key, value]) => {
|
|
38
|
+
if (value && typeof value === 'object') {
|
|
39
|
+
const { colors: componentColors, ...otherProps } = value
|
|
40
|
+
components[key] = {
|
|
41
|
+
...otherProps,
|
|
42
|
+
...(componentColors && typeof componentColors === 'object' ? componentColors : {}),
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
})
|
|
46
|
+
|
|
47
|
+
return { token, components }
|
|
48
|
+
}
|
|
49
|
+
export default transformThemeData;
|
|
50
|
+
|
|
51
|
+
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import { Outlet } from 'react-router-dom'
|
|
2
|
+
import { Col, Row, Spin } from 'antd'
|
|
3
|
+
import { useConfigContext } from '../context'
|
|
4
|
+
import { layoutTemplates } from '../../../types'
|
|
5
|
+
import { DEFAULT_CONFIG } from '../../../constants'
|
|
6
|
+
|
|
7
|
+
export default function AuthenticatedLayout() {
|
|
8
|
+
const { config } = useConfigContext()
|
|
9
|
+
const template = layoutTemplates.find((t: any) => t.name === config?.siteLayout?.template)
|
|
10
|
+
if (!template) {
|
|
11
|
+
return (
|
|
12
|
+
<Row className="h-screen flex justify-center items-center">
|
|
13
|
+
<Col>
|
|
14
|
+
<Spin spinning size="default" />
|
|
15
|
+
</Col>
|
|
16
|
+
</Row>
|
|
17
|
+
)
|
|
18
|
+
}
|
|
19
|
+
const LayoutComponent = template.value
|
|
20
|
+
|
|
21
|
+
return <LayoutComponent children={<Outlet />} config={config || DEFAULT_CONFIG} isPreview={false} />
|
|
22
|
+
}
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
import { Row, Col, Form, Button, Space, Input } from 'antd'
|
|
2
|
+
import { useNavigate } from 'react-router-dom'
|
|
3
|
+
import { useLoginHandler } from '../../common/custom-hooks/use-login-handler'
|
|
4
|
+
|
|
5
|
+
const ForgotPassword = () => {
|
|
6
|
+
const navigate = useNavigate()
|
|
7
|
+
const { handleForgotPassword } = useLoginHandler()
|
|
8
|
+
return (
|
|
9
|
+
<Row gutter={[60, 0]} justify="center" className="w-full">
|
|
10
|
+
<Col xs={24} md={24} lg={10} xl={12} xxl={10} className="flex self-center">
|
|
11
|
+
<Space size={15} direction="vertical" className="bg-white shadow py-10 px-8 w-full rounded">
|
|
12
|
+
<div className="text-36 text-primary text-center">Forgot Password?</div>
|
|
13
|
+
<div className="text-info text-18 text-center mx-auto" style={{ maxWidth: 300 }}>
|
|
14
|
+
Please enter your account username to reset your password!
|
|
15
|
+
</div>
|
|
16
|
+
<Form
|
|
17
|
+
name="forgot_password"
|
|
18
|
+
id="forgot_password"
|
|
19
|
+
initialValues={{ remember: false }}
|
|
20
|
+
layout="vertical"
|
|
21
|
+
onFinish={({ email }) => handleForgotPassword(email)}
|
|
22
|
+
>
|
|
23
|
+
<Row gutter={[0, 10]}>
|
|
24
|
+
<Form.Item
|
|
25
|
+
label={'Email'}
|
|
26
|
+
name={'email'}
|
|
27
|
+
rules={[{ required: true, message: `Please enter your email` }]}
|
|
28
|
+
>
|
|
29
|
+
<Input />
|
|
30
|
+
</Form.Item>
|
|
31
|
+
</Row>
|
|
32
|
+
</Form>
|
|
33
|
+
<Button className="btn-success w-full" htmlType="submit" form="forgot_password">
|
|
34
|
+
Submit Request
|
|
35
|
+
</Button>
|
|
36
|
+
<Button className="btn-primary2 w-full" onClick={() => navigate('/login')}>
|
|
37
|
+
Back to Login
|
|
38
|
+
</Button>
|
|
39
|
+
</Space>
|
|
40
|
+
</Col>
|
|
41
|
+
<Col xxl={1}></Col>
|
|
42
|
+
</Row>
|
|
43
|
+
)
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
export default ForgotPassword
|