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.
Files changed (60) hide show
  1. package/.env.development +4 -0
  2. package/.env.production +5 -0
  3. package/.env.staging +4 -0
  4. package/package.json +4 -1
  5. package/src/{functions/axios-handler.ts → api/client.ts} +75 -7
  6. package/src/api/user.ts +50 -0
  7. package/src/components/common/currency-field.tsx +8 -0
  8. package/src/components/common/custom-hooks/use-login-handler.ts +110 -0
  9. package/src/components/common/custom-hooks/use-site-meta-data.ts +24 -0
  10. package/src/components/common/custom-hooks/use-theme-config.ts +51 -0
  11. package/src/components/companies/1-authenticated/index.tsx +22 -0
  12. package/src/components/companies/2-unauthenticated/forgot-password.tsx +46 -0
  13. package/src/components/companies/2-unauthenticated/index.tsx +267 -0
  14. package/src/components/companies/3-config-provider/index.tsx +16 -0
  15. package/src/components/companies/context/index.tsx +12 -0
  16. package/src/components/companies/index.tsx +4 -0
  17. package/src/components/form/1-list/index.tsx +103 -58
  18. package/src/components/form/1-list/table-header.tsx +42 -57
  19. package/src/components/form/1-list/table.tsx +101 -157
  20. package/src/components/form/2-details/index.tsx +1 -1
  21. package/src/components/form/layout-renderer/1-row/index.tsx +39 -46
  22. package/src/components/form/layout-renderer/3-element/1-dynamic-button/index.tsx +1 -1
  23. package/src/components/form/layout-renderer/3-element/1-dynamic-button/use-create-data.hook.ts +1 -1
  24. package/src/components/form/layout-renderer/3-element/1-dynamic-button/use-delete-data.hook.ts +1 -1
  25. package/src/components/form/layout-renderer/3-element/1-dynamic-button/use-generate-report.hook.tsx +2 -2
  26. package/src/components/form/layout-renderer/3-element/1-dynamic-button/use-publish-data.hook.ts +1 -1
  27. package/src/components/form/layout-renderer/3-element/1-dynamic-button/use-save-data.hook.ts +1 -1
  28. package/src/components/form/layout-renderer/3-element/3-read-field-data.tsx +7 -6
  29. package/src/components/form/layout-renderer/3-element/5-re-captcha.tsx +11 -3
  30. package/src/components/form/layout-renderer/3-element/7-file-upload.tsx +1 -1
  31. package/src/components/form/layout-renderer/3-element/8-fields-with-options.tsx +62 -10
  32. package/src/components/form/layout-renderer/3-element/9-form-data-render.tsx +173 -171
  33. package/src/components/index.tsx +1 -0
  34. package/src/components/modals/report-filters.modal/index.tsx +1 -1
  35. package/src/constants.ts +8 -0
  36. package/src/enums/form.enum.ts +5 -5
  37. package/src/enums/index.ts +6 -0
  38. package/src/functions/companies/index.tsx +2 -0
  39. package/src/functions/companies/use-company-config.tsx +82 -0
  40. package/src/functions/forms/convert-number-into-words.ts +47 -0
  41. package/src/functions/forms/data-render-functions.tsx +11 -9
  42. package/src/functions/forms/form.ts +1 -1
  43. package/src/functions/forms/get-data-list-option-value.ts +69 -0
  44. package/src/functions/forms/index.ts +24 -87
  45. package/src/functions/index.ts +1 -0
  46. package/src/global-helpers/constants.ts +2 -0
  47. package/src/global-helpers/cookie-handler.ts +44 -0
  48. package/src/global-helpers/enums.ts +12 -0
  49. package/src/types/companies/index.ts +4 -4
  50. package/src/types/companies/site-layout/authenticated/index.tsx +71 -38
  51. package/src/types/companies/site-layout/unauthenticated/index.tsx +1 -12
  52. package/src/types/forms/data-list/index.ts +2 -13
  53. package/src/types/forms/index.ts +10 -0
  54. package/src/types/forms/layout-elements/button.ts +5 -3
  55. package/src/types/forms/layout-elements/data-render-config.ts +5 -0
  56. package/src/types/forms/layout-elements/field-option-source.ts +2 -1
  57. package/src/types/forms/layout-elements/index.ts +2 -7
  58. package/src/types/forms/layout-elements/read-field-data-props.ts +21 -0
  59. package/tsconfig.json +1 -0
  60. /package/src/functions/companies/{FontSelector.tsx → font-selector.tsx} +0 -0
@@ -0,0 +1,4 @@
1
+ VITE_API_BASE_URL=https://bark-backend-form.azurewebsites.net
2
+ VITE_LOG_LEVEL=debug
3
+ VITE_COOKIE_DOMAIN=localhost
4
+ VITE_ENV=development
@@ -0,0 +1,5 @@
1
+ # .env
2
+ VITE_API_BASE_URL=https://bark-backend-form.azurewebsites.net
3
+ VITE_LOG_LEVEL=error
4
+ VITE_COOKIE_DOMAIN=localhost
5
+ VITE_ENV=production
package/.env.staging ADDED
@@ -0,0 +1,4 @@
1
+ VITE_API_BASE_URL=https://bark-backend-form.azurewebsites.net
2
+ VITE_LOG_LEVEL=debug
3
+ VITE_COOKIE_DOMAIN=localhost
4
+ VITE_ENV=staging
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "form-craft-package",
3
- "version": "1.7.3",
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
- let baseURL = 'https://bark-backend-form.azurewebsites.net'
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
- axios
57
- .post(`${baseURL}/connect/token/refresh`, { refreshToken })
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 }
@@ -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