Compare commits
137 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 69b0f8a54d | |||
| ceb1658cdc | |||
| 5a7adf4537 | |||
| 45d5bd870f | |||
| 263c6740c5 | |||
| 07c1a8ae06 | |||
| bc2167e67a | |||
| 4d6a7380bb | |||
| b203c59ff7 | |||
| fe2168ce53 | |||
| 1c1af302aa | |||
| 945a5425f0 | |||
| 25545ad47e | |||
| 5efc935f05 | |||
| 69c43e1d88 | |||
| cb23cbd57c | |||
| 9c8e7dada4 | |||
| 22814f7e10 | |||
| bc9e26aad1 | |||
| 5cacd0f073 | |||
| 475e5c46a1 | |||
| 4af2f7ef10 | |||
| c60f49dc90 | |||
| 157151e6d1 | |||
| 2f7f482293 | |||
| ee86d40bcc | |||
| 409acd300d | |||
| fc7edec093 | |||
| 4637944fbd | |||
| cc44af7e55 | |||
| df5b4c89a4 | |||
| a37a5c24f5 | |||
| 890452af63 | |||
| 69b6850002 | |||
| dd8290af9a | |||
| 306ab06142 | |||
| 4d258a965b | |||
| f63171273e | |||
| b4bf96e841 | |||
| e20b7e32f1 | |||
| 6bd533c7ca | |||
| 408777353d | |||
| a2e039eab4 | |||
| 8d6cc5861e | |||
| 18967d720c | |||
| 044b2ef917 | |||
| 4f7fdfb2ba | |||
| ab389d6632 | |||
| 29538e3b6e | |||
| dd2df0d695 | |||
| a97db9a661 | |||
| 135cbce348 | |||
| 814bfe041a | |||
| d90d515f60 | |||
| e93a3dea68 | |||
| a50d5ec82d | |||
| 333ada0a1c | |||
| bb718953ad | |||
| a31b36686d | |||
| 00f4e1b565 | |||
| 6d302def04 | |||
| 82dd11a772 | |||
| 7e9c395f4a | |||
| 2cb5c471f6 | |||
| 4b008f6785 | |||
| f632099128 | |||
| 122eb31732 | |||
| 0207bf631a | |||
| 378ff4a625 | |||
| f88b6df24c | |||
| fe759c6d0a | |||
| 104295bdb2 | |||
| 44933d4362 | |||
| 9ce7110a5d | |||
| 085b2d4aaa | |||
| 747c945659 | |||
| 94f9803ec5 | |||
| 3998596fba | |||
| 886fd64347 | |||
| f95fd66c57 | |||
| 98b5d4bc4f | |||
| fd2b2245f5 | |||
| a7e97e1890 | |||
| 537d609117 | |||
| 5e0d21b5dd | |||
| ea4e6b7a59 | |||
| 07f7a90c0d | |||
| 2a2b8ee85d | |||
| e2b3978ab6 | |||
| b8f9958517 | |||
| 06cadbb742 | |||
| 067e7a395d | |||
| 5b6695c1f6 | |||
| 911c67e874 | |||
| a33784fc9e | |||
| b3efba09d6 | |||
| ff03282310 | |||
| 05dfb50f62 | |||
| 08af493d65 | |||
| e1bd0293f1 | |||
| a5b77abc92 | |||
| dd6d92654c | |||
| dd9652fe58 | |||
| 266627d941 | |||
| c4ffd49509 | |||
| 21b4bf5e8f | |||
| b71490aa8a | |||
| be9672991e | |||
| 97e6951685 | |||
| 1bd523c493 | |||
| 32f2a358b2 | |||
| ee94a59f75 | |||
| 4c24ce5903 | |||
| 86c0a236fe | |||
| 2daae9b0e0 | |||
| 2890677472 | |||
| 4a3f46c19b | |||
| 79f5af7692 | |||
| 2a59165054 | |||
| e3cf881e09 | |||
| dde4072593 | |||
| df6fe828e3 | |||
| db08d1201c | |||
| 32acf978c3 | |||
| f7ef9ff3a0 | |||
| 37c012461f | |||
| 0a295cc81c | |||
| e5c605560b | |||
| 26b95bab3d | |||
| cbbd23fde3 | |||
| 18c1a78d4c | |||
| 979330478c | |||
| f402efeb85 | |||
| 38becd42ac | |||
| bf87780204 | |||
| 81b2f439ce | |||
| 9d86fc51c8 |
@@ -1 +1,17 @@
|
||||
DIGIFI_PORT=5173
|
||||
|
||||
# Social Links
|
||||
FACEBOOK_URL=https://www.facebook.com
|
||||
TWITTER_URL=https://twitter.com
|
||||
INSTAGRAM_URL=https://www.instagram.com
|
||||
|
||||
# BACKEND END POINTS
|
||||
VITE_USERS_ENDPOINT='https://digifi-apidev.chiefsoft.net/digiusers/v1'
|
||||
|
||||
# ENQUIRIES CONTACTS
|
||||
VITE_CALL_ENDPOINT='09099000000'
|
||||
VITE_EMAIL_ENDPOINT='fcmbloan@support.com'
|
||||
|
||||
#BANK NAME
|
||||
VITE_BANK_NAME='First City Monument Bank'
|
||||
VITE_BANK_NAME_SHORT='FCMB'
|
||||
@@ -1 +1,13 @@
|
||||
DIGIFI_PORT=5173
|
||||
|
||||
# Social Links
|
||||
VITE_FACEBOOK_URL=https://www.facebook.com
|
||||
VITE_TWITTER_URL=https://twitter.com
|
||||
VITE_INSTAGRAM_URL=https://www.instagram.com
|
||||
|
||||
# BACKEND END POINTS
|
||||
VITE_USERS_ENDPOINT='https://digifi-apidev.chiefsoft.net/digiusers/v1'
|
||||
|
||||
#BANK NAME
|
||||
VITE_BANK_NAME='First City Monument Bank'
|
||||
VITE_BANK_NAME_SHORT='FCMB'
|
||||
@@ -1 +1,13 @@
|
||||
DIGIFI_PORT=5173
|
||||
|
||||
# Social Links
|
||||
FACEBOOK_URL=https://www.facebook.com
|
||||
TWITTER_URL=https://twitter.com
|
||||
INSTAGRAM_URL=https://www.instagram.com
|
||||
|
||||
# BACKEND END POINTS
|
||||
VITE_USERS_ENDPOINT='https://digifi-apidev.chiefsoft.net/digiusers/v1'
|
||||
|
||||
#BANK NAME
|
||||
VITE_BANK_NAME='First City Monument Bank'
|
||||
VITE_BANK_NAME_SHORT='FCMB'
|
||||
@@ -11,8 +11,13 @@ services:
|
||||
ports:
|
||||
- 6030:5173
|
||||
expose:
|
||||
- "5173"
|
||||
- "5173"
|
||||
extra_hosts:
|
||||
- digifi-apidev.chiefsoft.net:10.10.33.15
|
||||
- backend.wrenchboard.api.test:10.10.33.15
|
||||
environment:
|
||||
- PORT=${DIGIFI_PORT}
|
||||
tty: true
|
||||
stdin_open: true
|
||||
volumes:
|
||||
src:
|
||||
src:
|
||||
|
||||
@@ -9,10 +9,12 @@
|
||||
"version": "0.0.0",
|
||||
"dependencies": {
|
||||
"@reduxjs/toolkit": "^2.2.1",
|
||||
"axios": "^1.6.8",
|
||||
"clsx": "2.1.0",
|
||||
"formik": "2.4.5",
|
||||
"react": "^18.2.0",
|
||||
"react-dom": "^18.2.0",
|
||||
"react-icons": "^5.0.1",
|
||||
"react-redux": "^8.0.5",
|
||||
"react-router-dom": "6.3.0",
|
||||
"react-select": "^5.8.0",
|
||||
@@ -1805,6 +1807,11 @@
|
||||
"node": ">=8"
|
||||
}
|
||||
},
|
||||
"node_modules/asynckit": {
|
||||
"version": "0.4.0",
|
||||
"resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz",
|
||||
"integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q=="
|
||||
},
|
||||
"node_modules/autoprefixer": {
|
||||
"version": "10.4.18",
|
||||
"resolved": "https://registry.npmjs.org/autoprefixer/-/autoprefixer-10.4.18.tgz",
|
||||
@@ -1842,6 +1849,16 @@
|
||||
"postcss": "^8.1.0"
|
||||
}
|
||||
},
|
||||
"node_modules/axios": {
|
||||
"version": "1.6.8",
|
||||
"resolved": "https://registry.npmjs.org/axios/-/axios-1.6.8.tgz",
|
||||
"integrity": "sha512-v/ZHtJDU39mDpyBoFVkETcd/uNdxrWRrg3bKpOKzXFA6Bvqopts6ALSMU3y6ijYxbw2B+wPrIv46egTzJXCLGQ==",
|
||||
"dependencies": {
|
||||
"follow-redirects": "^1.15.6",
|
||||
"form-data": "^4.0.0",
|
||||
"proxy-from-env": "^1.1.0"
|
||||
}
|
||||
},
|
||||
"node_modules/babel-plugin-macros": {
|
||||
"version": "3.1.0",
|
||||
"resolved": "https://registry.npmjs.org/babel-plugin-macros/-/babel-plugin-macros-3.1.0.tgz",
|
||||
@@ -2030,6 +2047,17 @@
|
||||
"resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz",
|
||||
"integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA=="
|
||||
},
|
||||
"node_modules/combined-stream": {
|
||||
"version": "1.0.8",
|
||||
"resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz",
|
||||
"integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==",
|
||||
"dependencies": {
|
||||
"delayed-stream": "~1.0.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 0.8"
|
||||
}
|
||||
},
|
||||
"node_modules/commander": {
|
||||
"version": "4.1.1",
|
||||
"resolved": "https://registry.npmjs.org/commander/-/commander-4.1.1.tgz",
|
||||
@@ -2124,6 +2152,14 @@
|
||||
"node": ">=0.10.0"
|
||||
}
|
||||
},
|
||||
"node_modules/delayed-stream": {
|
||||
"version": "1.0.0",
|
||||
"resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz",
|
||||
"integrity": "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==",
|
||||
"engines": {
|
||||
"node": ">=0.4.0"
|
||||
}
|
||||
},
|
||||
"node_modules/didyoumean": {
|
||||
"version": "1.2.2",
|
||||
"resolved": "https://registry.npmjs.org/didyoumean/-/didyoumean-1.2.2.tgz",
|
||||
@@ -2550,6 +2586,25 @@
|
||||
"integrity": "sha512-X8cqMLLie7KsNUDSdzeN8FYK9rEt4Dt67OsG/DNGnYTSDBG4uFAJFBnUeiV+zCVAvwFy56IjM9sH51jVaEhNxw==",
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/follow-redirects": {
|
||||
"version": "1.15.6",
|
||||
"resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.6.tgz",
|
||||
"integrity": "sha512-wWN62YITEaOpSK584EZXJafH1AGpO8RVgElfkuXbTOrPX4fIfOyEpW/CsiNd8JdYrAoOvafRTOEnvsO++qCqFA==",
|
||||
"funding": [
|
||||
{
|
||||
"type": "individual",
|
||||
"url": "https://github.com/sponsors/RubenVerborgh"
|
||||
}
|
||||
],
|
||||
"engines": {
|
||||
"node": ">=4.0"
|
||||
},
|
||||
"peerDependenciesMeta": {
|
||||
"debug": {
|
||||
"optional": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"node_modules/foreground-child": {
|
||||
"version": "3.1.1",
|
||||
"resolved": "https://registry.npmjs.org/foreground-child/-/foreground-child-3.1.1.tgz",
|
||||
@@ -2565,6 +2620,19 @@
|
||||
"url": "https://github.com/sponsors/isaacs"
|
||||
}
|
||||
},
|
||||
"node_modules/form-data": {
|
||||
"version": "4.0.0",
|
||||
"resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.0.tgz",
|
||||
"integrity": "sha512-ETEklSGi5t0QMZuiXoA/Q6vcnxcLQP5vdugSpuAyi6SVGi2clPPp+xgEhuMaHC+zGgn31Kd235W35f7Hykkaww==",
|
||||
"dependencies": {
|
||||
"asynckit": "^0.4.0",
|
||||
"combined-stream": "^1.0.8",
|
||||
"mime-types": "^2.1.12"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 6"
|
||||
}
|
||||
},
|
||||
"node_modules/formik": {
|
||||
"version": "2.4.5",
|
||||
"resolved": "https://registry.npmjs.org/formik/-/formik-2.4.5.tgz",
|
||||
@@ -3078,6 +3146,25 @@
|
||||
"node": ">=8.6"
|
||||
}
|
||||
},
|
||||
"node_modules/mime-db": {
|
||||
"version": "1.52.0",
|
||||
"resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz",
|
||||
"integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==",
|
||||
"engines": {
|
||||
"node": ">= 0.6"
|
||||
}
|
||||
},
|
||||
"node_modules/mime-types": {
|
||||
"version": "2.1.35",
|
||||
"resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz",
|
||||
"integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==",
|
||||
"dependencies": {
|
||||
"mime-db": "1.52.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 0.6"
|
||||
}
|
||||
},
|
||||
"node_modules/minimatch": {
|
||||
"version": "9.0.3",
|
||||
"resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.3.tgz",
|
||||
@@ -3532,6 +3619,11 @@
|
||||
"resolved": "https://registry.npmjs.org/property-expr/-/property-expr-2.0.6.tgz",
|
||||
"integrity": "sha512-SVtmxhRE/CGkn3eZY1T6pC8Nln6Fr/lu1mKSgRud0eC73whjGfoAogbn78LkD8aFL0zz3bAFerKSnOl7NlErBA=="
|
||||
},
|
||||
"node_modules/proxy-from-env": {
|
||||
"version": "1.1.0",
|
||||
"resolved": "https://registry.npmjs.org/proxy-from-env/-/proxy-from-env-1.1.0.tgz",
|
||||
"integrity": "sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg=="
|
||||
},
|
||||
"node_modules/punycode": {
|
||||
"version": "2.3.1",
|
||||
"resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz",
|
||||
@@ -3588,6 +3680,14 @@
|
||||
"resolved": "https://registry.npmjs.org/react-fast-compare/-/react-fast-compare-2.0.4.tgz",
|
||||
"integrity": "sha512-suNP+J1VU1MWFKcyt7RtjiSWUjvidmQSlqu+eHslq+342xCbGTYmC0mEhPCOHxlW0CywylOC1u2DFAT+bv4dBw=="
|
||||
},
|
||||
"node_modules/react-icons": {
|
||||
"version": "5.2.1",
|
||||
"resolved": "https://registry.npmjs.org/react-icons/-/react-icons-5.2.1.tgz",
|
||||
"integrity": "sha512-zdbW5GstTzXaVKvGSyTaBalt7HSfuK5ovrzlpyiWHAFXndXTdd/1hdDHI4xBM1Mn7YriT6aqESucFl9kEXzrdw==",
|
||||
"peerDependencies": {
|
||||
"react": "*"
|
||||
}
|
||||
},
|
||||
"node_modules/react-is": {
|
||||
"version": "16.13.1",
|
||||
"resolved": "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz",
|
||||
|
||||
@@ -11,10 +11,12 @@
|
||||
},
|
||||
"dependencies": {
|
||||
"@reduxjs/toolkit": "^2.2.1",
|
||||
"axios": "^1.6.8",
|
||||
"clsx": "2.1.0",
|
||||
"formik": "2.4.5",
|
||||
"react": "^18.2.0",
|
||||
"react-dom": "^18.2.0",
|
||||
"react-icons": "^5.0.1",
|
||||
"react-redux": "^8.0.5",
|
||||
"react-router-dom": "6.3.0",
|
||||
"react-select": "^5.8.0",
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
.btn-primary {
|
||||
background: #5A2C82;
|
||||
background: #5A2C82 !important;
|
||||
color: #FFFFFF;
|
||||
display: block;
|
||||
text-decoration: none;
|
||||
@@ -8,7 +8,34 @@
|
||||
}
|
||||
|
||||
.btn-active {
|
||||
background: #D10056;
|
||||
background: #D10056 !important;
|
||||
}
|
||||
|
||||
.btn-R {
|
||||
padding-inline: 3rem !important;
|
||||
font-weight: bold !important;
|
||||
font-size: 16px !important;
|
||||
background-color: #5A2C82;
|
||||
}
|
||||
|
||||
.btn-W {
|
||||
background: white !important;
|
||||
border-radius: 8px;
|
||||
font-size: 18px;
|
||||
font-weight: bold;
|
||||
color: #FBB700;
|
||||
line-height: 25px;
|
||||
align-items: center !important;
|
||||
}
|
||||
|
||||
.btn-Y {
|
||||
background: #FBB700 !important;
|
||||
border-radius: 8px;
|
||||
font-size: 18px;
|
||||
font-weight: bold;
|
||||
/* color: white; */
|
||||
line-height: 25px;
|
||||
align-items: center !important;
|
||||
}
|
||||
|
||||
.sidebar {
|
||||
|
||||
|
After Width: | Height: | Size: 6.7 KiB |
|
After Width: | Height: | Size: 5.6 KiB |
|
After Width: | Height: | Size: 3.2 KiB |
|
After Width: | Height: | Size: 9.1 KiB |
|
After Width: | Height: | Size: 4.3 KiB |
|
After Width: | Height: | Size: 262 KiB |
@@ -0,0 +1 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" viewBox="0,0,256,256" width="50px" height="50px"><g fill="#ffffff" fill-rule="nonzero" stroke="none" stroke-width="1" stroke-linecap="butt" stroke-linejoin="miter" stroke-miterlimit="10" stroke-dasharray="" stroke-dashoffset="0" font-family="none" font-weight="none" font-size="none" text-anchor="none" style="mix-blend-mode: normal"><g transform="scale(5.12,5.12)"><path d="M32,11h5c0.552,0 1,-0.448 1,-1v-6.737c0,-0.524 -0.403,-0.96 -0.925,-0.997c-1.591,-0.113 -4.699,-0.266 -6.934,-0.266c-6.141,0 -10.141,3.68 -10.141,10.368v6.632h-7c-0.552,0 -1,0.448 -1,1v7c0,0.552 0.448,1 1,1h7v19c0,0.552 0.448,1 1,1h7c0.552,0 1,-0.448 1,-1v-19h7.222c0.51,0 0.938,-0.383 0.994,-0.89l0.778,-7c0.066,-0.592 -0.398,-1.11 -0.994,-1.11h-8v-5c0,-1.657 1.343,-3 3,-3z"></path></g></g></svg>
|
||||
|
After Width: | Height: | Size: 856 B |
@@ -0,0 +1 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" viewBox="0,0,256,256" width="64px" height="64px"><g fill="#ffffff" fill-rule="nonzero" stroke="none" stroke-width="1" stroke-linecap="butt" stroke-linejoin="miter" stroke-miterlimit="10" stroke-dasharray="" stroke-dashoffset="0" font-family="none" font-weight="none" font-size="none" text-anchor="none" style="mix-blend-mode: normal"><g transform="scale(4,4)"><path d="M21.58008,7c-8.039,0 -14.58008,6.54494 -14.58008,14.58594v20.83203c0,8.04 6.54494,14.58203 14.58594,14.58203h20.83203c8.04,0 14.58203,-6.54494 14.58203,-14.58594v-20.83398c0,-8.039 -6.54494,-14.58008 -14.58594,-14.58008zM47,15c1.104,0 2,0.896 2,2c0,1.104 -0.896,2 -2,2c-1.104,0 -2,-0.896 -2,-2c0,-1.104 0.896,-2 2,-2zM32,19c7.17,0 13,5.83 13,13c0,7.17 -5.831,13 -13,13c-7.17,0 -13,-5.831 -13,-13c0,-7.169 5.83,-13 13,-13zM32,23c-4.971,0 -9,4.029 -9,9c0,4.971 4.029,9 9,9c4.971,0 9,-4.029 9,-9c0,-4.971 -4.029,-9 -9,-9z"></path></g></g></svg>
|
||||
|
After Width: | Height: | Size: 993 B |
@@ -0,0 +1 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" viewBox="0,0,256,256" width="50px" height="50px"><g fill="#ffffff" fill-rule="nonzero" stroke="none" stroke-width="1" stroke-linecap="butt" stroke-linejoin="miter" stroke-miterlimit="10" stroke-dasharray="" stroke-dashoffset="0" font-family="none" font-weight="none" font-size="none" text-anchor="none" style="mix-blend-mode: normal"><g transform="scale(5.12,5.12)"><path d="M41,4h-32c-2.76,0 -5,2.24 -5,5v32c0,2.76 2.24,5 5,5h32c2.76,0 5,-2.24 5,-5v-32c0,-2.76 -2.24,-5 -5,-5zM17,20v19h-6v-19zM11,14.47c0,-1.4 1.2,-2.47 3,-2.47c1.8,0 2.93,1.07 3,2.47c0,1.4 -1.12,2.53 -3,2.53c-1.8,0 -3,-1.13 -3,-2.53zM39,39h-6c0,0 0,-9.26 0,-10c0,-2 -1,-4 -3.5,-4.04h-0.08c-2.42,0 -3.42,2.06 -3.42,4.04c0,0.91 0,10 0,10h-6v-19h6v2.56c0,0 1.93,-2.56 5.81,-2.56c3.97,0 7.19,2.73 7.19,8.26z"></path></g></g></svg>
|
||||
|
After Width: | Height: | Size: 878 B |
@@ -0,0 +1 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" viewBox="0,0,256,256" width="50px" height="50px"><g fill="#ffffff" fill-rule="nonzero" stroke="none" stroke-width="1" stroke-linecap="butt" stroke-linejoin="miter" stroke-miterlimit="10" stroke-dasharray="" stroke-dashoffset="0" font-family="none" font-weight="none" font-size="none" text-anchor="none" style="mix-blend-mode: normal"><g transform="scale(5.12,5.12)"><path d="M6.91992,6l14.2168,20.72656l-14.9082,17.27344h3.17773l13.13867,-15.22266l10.44141,15.22266h10.01367l-14.87695,-21.6875l14.08008,-16.3125h-3.17578l-12.31055,14.26172l-9.7832,-14.26172z"></path></g></g></svg>
|
||||
|
After Width: | Height: | Size: 664 B |
@@ -0,0 +1 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" viewBox="0,0,256,256" width="50px" height="50px"><g fill="#ffffff" fill-rule="nonzero" stroke="none" stroke-width="1" stroke-linecap="butt" stroke-linejoin="miter" stroke-miterlimit="10" stroke-dasharray="" stroke-dashoffset="0" font-family="none" font-weight="none" font-size="none" text-anchor="none" style="mix-blend-mode: normal"><g transform="scale(5.12,5.12)"><path d="M25,2c-12.682,0 -23,10.318 -23,23c0,3.96 1.023,7.854 2.963,11.29l-2.926,10.44c-0.096,0.343 -0.003,0.711 0.245,0.966c0.191,0.197 0.451,0.304 0.718,0.304c0.08,0 0.161,-0.01 0.24,-0.029l10.896,-2.699c3.327,1.786 7.074,2.728 10.864,2.728c12.682,0 23,-10.318 23,-23c0,-12.682 -10.318,-23 -23,-23zM36.57,33.116c-0.492,1.362 -2.852,2.605 -3.986,2.772c-1.018,0.149 -2.306,0.213 -3.72,-0.231c-0.857,-0.27 -1.957,-0.628 -3.366,-1.229c-5.923,-2.526 -9.791,-8.415 -10.087,-8.804c-0.295,-0.389 -2.411,-3.161 -2.411,-6.03c0,-2.869 1.525,-4.28 2.067,-4.864c0.542,-0.584 1.181,-0.73 1.575,-0.73c0.394,0 0.787,0.005 1.132,0.021c0.363,0.018 0.85,-0.137 1.329,1.001c0.492,1.168 1.673,4.037 1.819,4.33c0.148,0.292 0.246,0.633 0.05,1.022c-0.196,0.389 -0.294,0.632 -0.59,0.973c-0.296,0.341 -0.62,0.76 -0.886,1.022c-0.296,0.291 -0.603,0.606 -0.259,1.19c0.344,0.584 1.529,2.493 3.285,4.039c2.255,1.986 4.158,2.602 4.748,2.894c0.59,0.292 0.935,0.243 1.279,-0.146c0.344,-0.39 1.476,-1.703 1.869,-2.286c0.393,-0.583 0.787,-0.487 1.329,-0.292c0.542,0.194 3.445,1.604 4.035,1.896c0.59,0.292 0.984,0.438 1.132,0.681c0.148,0.242 0.148,1.41 -0.344,2.771z"></path></g></g></svg>
|
||||
|
After Width: | Height: | Size: 1.6 KiB |
@@ -0,0 +1 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" viewBox="0,0,256,256" width="50px" height="50px"><g fill="#ffffff" fill-rule="evenodd" stroke="none" stroke-width="1" stroke-linecap="butt" stroke-linejoin="miter" stroke-miterlimit="10" stroke-dasharray="" stroke-dashoffset="0" font-family="none" font-weight="none" font-size="none" text-anchor="none" style="mix-blend-mode: normal"><g transform="scale(5.12,5.12)"><path d="M13,5l3,9v6h2v-6l3,-9h-2l-2,6l-2,-6zM24,9c-1.06641,0 -1.58984,0.16797 -2.24219,0.70313c-0.625,0.52734 -0.79687,0.93359 -0.75781,2.29688v5c0,0.99609 0.16406,1.65234 0.76563,2.23438c0.625,0.58203 1.21484,0.76563 2.23438,0.76563c1.06641,0 1.64844,-0.18359 2.25,-0.76562c0.625,-0.55859 0.75,-1.23828 0.75,-2.23437v-5c0,-0.88281 -0.15625,-1.71875 -0.76172,-2.27734c-0.625,-0.57422 -1.26953,-0.72266 -2.23828,-0.72266zM29,9v9c0,0.97266 0.98047,2 2,2c1.01953,0 1.55859,-0.51172 2,-1v1h2v-11h-2v8c-0.01172,0.68359 -0.81641,1 -1,1c-0.20703,0 -1,-0.04297 -1,-1v-8zM24,11c0.30078,0 1,-0.00391 1,1v5c0,0.96875 -0.67578,1 -1,1c-0.30078,0 -1,-0.01172 -1,-1v-5c0,-0.81641 0.43359,-1 1,-1zM10,22c-3.59375,0 -6,2.38281 -6,6v9.5c0,3.61719 2.40625,6.5 6,6.5h30c3.59375,0 6,-2.38281 6,-6v-10c0,-3.61719 -2.40625,-6 -6,-6zM12,26h6v2h-2v12h-2v-12h-2zM26,26h2v4c0.23047,-0.35937 0.57422,-0.64453 0.90234,-0.80469c0.32031,-0.16406 0.64453,-0.25781 0.97266,-0.25781c0.64844,0 1.15625,0.23438 1.50391,0.67188c0.34766,0.44141 0.62109,1.02734 0.62109,1.89063v6c0,0.74219 -0.25,1.20313 -0.57812,1.59766c-0.32031,0.39453 -0.80078,0.89453 -1.42187,0.90234c-1.05078,0.01172 -1.61328,-0.55078 -2,-1v1h-2zM18,29h2v8c0,0.23047 0.26953,1.00781 1,1c0.8125,-0.00781 0.82031,-0.76562 1,-1v-8h2v11h-2v-1c-0.37109,0.4375 -0.5625,0.57422 -0.98047,0.78125c-0.41406,0.23438 -0.83594,0.21875 -1.22656,0.21875c-0.48437,0 -1.03516,-0.4375 -1.29297,-0.76562c-0.23047,-0.30078 -0.5,-0.60937 -0.5,-1.23437zM36.19922,29c0.94922,0 1.61719,0.20313 2.12109,0.73438c0.51563,0.53125 0.67969,1.15234 0.67969,2.15234v3.11328h-4v1.54688c0,0.55859 0.07422,0.91406 0.21875,1.125c0.13672,0.23047 0.41406,0.33203 0.78125,0.32813c0.40625,-0.00391 0.66406,-0.08594 0.80078,-0.26953c0.14063,-0.16406 0.19922,-0.62891 0.19922,-1.23047v-0.5h2v0.59375c0,1.08984 -0.08594,1.90234 -0.625,2.43359c-0.50781,0.55859 -1.30078,0.81641 -2.33984,0.81641c-0.94922,0 -1.69141,-0.28125 -2.22266,-0.85937c-0.53125,-0.57812 -0.80859,-1.37109 -0.80859,-2.39062v-4.70703c0,-0.90625 0.31641,-1.57812 0.89844,-2.17578c0.46875,-0.48047 1.34766,-0.71094 2.29688,-0.71094zM29,30.5c-0.55078,0 -0.99219,0.49609 -1,1v6c0.00781,0.28906 0.44922,0.5 1,0.5c0.55078,0 1,-0.42578 1,-0.97656v-5.02344c0,-1 -0.44922,-1.5 -1,-1.5zM36,31c-0.55078,0 -0.99219,0.46484 -1,1v1h2v-1c0,-0.61328 -0.44922,-1 -1,-1z"></path></g></g></svg>
|
||||
|
After Width: | Height: | Size: 2.7 KiB |
|
After Width: | Height: | Size: 432 KiB |
@@ -0,0 +1,42 @@
|
||||
import { Icons } from "../"
|
||||
|
||||
type Props = {
|
||||
title?: string,
|
||||
descText?: string,
|
||||
iconName?: string,
|
||||
iconColor?: string,
|
||||
cardClass?: string,
|
||||
titleClass?: string,
|
||||
descTextClass?: string,
|
||||
onClick?: ()=>any
|
||||
}
|
||||
|
||||
|
||||
export default function DefaultCard({
|
||||
title,
|
||||
descText,
|
||||
iconName,
|
||||
iconColor,
|
||||
cardClass,
|
||||
titleClass,
|
||||
descTextClass,
|
||||
onClick
|
||||
}:Props) {
|
||||
return (
|
||||
<button
|
||||
className={`h-full w-full rounded-lg p-5 shadow-lg hover:shadow-none bg-no-repeat bg-[90%] flex justify-between gap-4 items-center transition-all duration-300 ${cardClass && cardClass}`}
|
||||
onClick={onClick}
|
||||
>
|
||||
<div className='w-3/4'>
|
||||
<h1 className={`mb-1 text-[#FFF] text-lg text-left font-bold ${titleClass && titleClass}`}>{title}</h1>
|
||||
<p className={`text-sm text-left ${descTextClass && descTextClass}`}>{descText}</p>
|
||||
</div>
|
||||
|
||||
{iconName && // DISPLAYS ICON IF THERE IS ICON NAME PRESENT
|
||||
<div className='group-hover:-translate-x-2 transition-all duration-300'>
|
||||
<Icons name={iconName} fillColor={`${iconColor ? iconColor : '#FFF'}`} />
|
||||
</div>
|
||||
}
|
||||
</button>
|
||||
)
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
import DefaultCard from "./DefaultCard";
|
||||
|
||||
export { DefaultCard };
|
||||
@@ -0,0 +1,17 @@
|
||||
type Props = {
|
||||
width?: string
|
||||
height?: string
|
||||
}
|
||||
|
||||
export default function CustomSpinner({width='w-6', height='h-6'}:Props) {
|
||||
|
||||
return (
|
||||
<div role="status">
|
||||
<svg aria-hidden="true" className={`inline ${width} ${height} text-gray-200 animate-spin dark:text-gray-600 fill-blue-600`} viewBox="0 0 100 101" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M100 50.5908C100 78.2051 77.6142 100.591 50 100.591C22.3858 100.591 0 78.2051 0 50.5908C0 22.9766 22.3858 0.59082 50 0.59082C77.6142 0.59082 100 22.9766 100 50.5908ZM9.08144 50.5908C9.08144 73.1895 27.4013 91.5094 50 91.5094C72.5987 91.5094 90.9186 73.1895 90.9186 50.5908C90.9186 27.9921 72.5987 9.67226 50 9.67226C27.4013 9.67226 9.08144 27.9921 9.08144 50.5908Z" fill="currentColor"/>
|
||||
<path d="M93.9676 39.0409C96.393 38.4038 97.8624 35.9116 97.0079 33.5539C95.2932 28.8227 92.871 24.3692 89.8167 20.348C85.8452 15.1192 80.8826 10.7238 75.2124 7.41289C69.5422 4.10194 63.2754 1.94025 56.7698 1.05124C51.7666 0.367541 46.6976 0.446843 41.7345 1.27873C39.2613 1.69328 37.813 4.19778 38.4501 6.62326C39.0873 9.04874 41.5694 10.4717 44.0505 10.1071C47.8511 9.54855 51.7191 9.52689 55.5402 10.0491C60.8642 10.7766 65.9928 12.5457 70.6331 15.2552C75.2735 17.9648 79.3347 21.5619 82.5849 25.841C84.9175 28.9121 86.7997 32.2913 88.1811 35.8758C89.083 38.2158 91.5421 39.6781 93.9676 39.0409Z" fill="currentFill"/>
|
||||
</svg>
|
||||
{/* <span className="sr-only">Loading...</span> */}
|
||||
</div>
|
||||
)
|
||||
}
|
||||
@@ -0,0 +1,116 @@
|
||||
import { Button, InputCompOne, Stepper } from "..";
|
||||
import {Formik, Form} from 'formik'
|
||||
import * as Yup from "yup";
|
||||
|
||||
type Props = {
|
||||
handleNextStep:(value:{})=>any
|
||||
}
|
||||
|
||||
const initialValues = {
|
||||
loan_amount: "",
|
||||
payment_month: "",
|
||||
sales_agent: "",
|
||||
};
|
||||
|
||||
// To get the validation schema
|
||||
const validationSchema = Yup.object().shape({
|
||||
payment_month: Yup.string()
|
||||
.required("Required"),
|
||||
loan_amount: Yup.string()
|
||||
.required("Required")
|
||||
.test("no-e", "Invalid", (value:any) => {
|
||||
if (value && /^[0-9]*$/.test(value) == false) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}),
|
||||
sales_agent: Yup.string()
|
||||
});
|
||||
|
||||
export default function DashboardFormInit({handleNextStep}:Props) {
|
||||
|
||||
//FUNCTION TO HANDLE SUBMIT
|
||||
const handleSubmit = (values:{}) => {
|
||||
handleNextStep(values)
|
||||
};
|
||||
|
||||
return (
|
||||
<div className="w-full">
|
||||
<div className="w-full flex justify-center">
|
||||
<Stepper step={0} />
|
||||
</div>
|
||||
<Formik
|
||||
initialValues={initialValues}
|
||||
validationSchema={validationSchema}
|
||||
onSubmit={handleSubmit}
|
||||
>
|
||||
{(props)=>(
|
||||
<Form>
|
||||
<div className="mt-[3.25rem] flex flex-col gap-9">
|
||||
<InputCompOne
|
||||
parentClass="max-w-[25.875rem] w-full flex flex-col gap-4"
|
||||
name="loan_amount"
|
||||
label="How Much Do You Want To Apply For?"
|
||||
labelClass="font-bold text-[1.125rem]"
|
||||
input
|
||||
inputClass="w-full h-[3.625rem] bg-[#EFEFEF] px-4 rounded-[.375rem] text-right"
|
||||
placeholder="350,000"
|
||||
value={props.values.loan_amount}
|
||||
onChange={props.handleChange}
|
||||
error={(props.errors.loan_amount && props.touched.loan_amount) ? props.errors.loan_amount : ''}
|
||||
/>
|
||||
<InputCompOne
|
||||
parentClass="max-w-[25.875rem] w-full flex flex-col gap-4"
|
||||
name="payment_month"
|
||||
label="For How Many Months?"
|
||||
labelClass="font-bold text-[1.125rem]"
|
||||
select={true}
|
||||
selectClass="w-full h-[3.625rem] bg-[#EFEFEF] px-4 rounded-[.375rem]"
|
||||
selectOptions={paymentMonth}
|
||||
selectValue={props.values.payment_month}
|
||||
onChange={props.handleChange}
|
||||
error={(props.errors.payment_month && props.touched.payment_month) ? props.errors.payment_month : ''}
|
||||
/>
|
||||
<InputCompOne
|
||||
parentClass="max-w-[25.875rem] w-full flex flex-col gap-4"
|
||||
name="sales_agent"
|
||||
label="Direct sales agent ID ( Optional )"
|
||||
labelClass="font-bold text-[1.125rem]"
|
||||
floatLabel='Enter agent ID'
|
||||
input
|
||||
inputClass="w-full h-[3.625rem] bg-[#EFEFEF] px-4 rounded-[.375rem]"
|
||||
placeholder="Agent ID"
|
||||
value={props.values.sales_agent}
|
||||
onChange={props.handleChange}
|
||||
error={(props.errors.sales_agent && props.touched.sales_agent) ? props.errors.sales_agent : ''}
|
||||
/>
|
||||
<Button
|
||||
className="my-8 max-w-[25.875rem] btn-Y text-black w-full h-11"
|
||||
text="Next"
|
||||
type="submit"
|
||||
/>
|
||||
</div>
|
||||
</Form>
|
||||
)}
|
||||
</Formik>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
interface SelectOption {
|
||||
loading: boolean;
|
||||
data: {value: string;
|
||||
label: string}[]
|
||||
}
|
||||
|
||||
|
||||
const paymentMonth: SelectOption = {
|
||||
loading: false,
|
||||
data: [
|
||||
{ value: "", label: "Please Select" },
|
||||
{ value: "6", label: "6 Months" },
|
||||
{ value: "12", label: "12 Months" },
|
||||
{ value: "18", label: "18 Months" },
|
||||
{ value: "24", label: "24 Months" },
|
||||
]
|
||||
}
|
||||
@@ -0,0 +1,36 @@
|
||||
import React, { FC } from "react";
|
||||
import DashboardHomeIntro from "./DashboardHomeIntro";
|
||||
import DashboardFormInit from "./DashboardFormInit";
|
||||
import DashboardHomeDetail from "./home/DashboardHomeDetail";
|
||||
import DashboardHomeEmploymentInfo from "./home/DashboardHomeEmploymentInfo";
|
||||
import DashboardHomeRefereeInfo from "./home/DashboardHomeRefereeInfo";
|
||||
import DashboardHomeAttestation from "./home/DashboardHomeAttestation";
|
||||
|
||||
interface DashboardHomeProps {}
|
||||
|
||||
const DashboardHome: FC<DashboardHomeProps> = () => {
|
||||
const [step, setStep] = React.useState(1);
|
||||
const [applicationDetails, setApplicationDetails] = React.useState({});
|
||||
|
||||
const handleNextStep = (values:{}={}) => {
|
||||
if (step < 7) {
|
||||
setStep(step + 1);
|
||||
}
|
||||
setApplicationDetails((prev:{}) => ({...prev, ...values}))
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="w-full">
|
||||
{step === 1 && <DashboardHomeIntro step={step} handleNextStep={handleNextStep} />}
|
||||
{step === 2 && <DashboardFormInit handleNextStep={handleNextStep} />}
|
||||
{step === 3 && <DashboardHomeDetail handleNextStep={handleNextStep} />}
|
||||
{step === 4 && <DashboardHomeEmploymentInfo handleNextStep={handleNextStep} />}
|
||||
{step === 5 && <DashboardHomeRefereeInfo handleNextStep={handleNextStep} />}
|
||||
{step === 6 && <DashboardHomeAttestation handleNextStep={handleNextStep} applicationDetails={applicationDetails} />}
|
||||
{step === 7 && <DashboardHomeIntro step={step} handleNextStep={handleNextStep} />}
|
||||
{/* <DashboardHomeAttestation handleNextStep={handleNextStep} applicationDetails={applicationDetails} /> */}
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default DashboardHome;
|
||||
@@ -0,0 +1,310 @@
|
||||
import React, { FC, useState, useEffect } from 'react';
|
||||
import NairaBag from '../../assets/images/dashboard/naira-bag.png';
|
||||
import {useNavigate} from 'react-router-dom'
|
||||
import { Button, Icons } from '../';
|
||||
import { useSelector } from 'react-redux';
|
||||
import { PendingTableList } from '../../core/models';
|
||||
import { NewDateTimeFormatter } from '../../lib/NewDateTimeFormatter';
|
||||
import { getUserPendingLoanList } from '../../core/apiRequest';
|
||||
import {FormatAmount} from '../../lib/FormatAmount'
|
||||
import PendingLoanPopout from './PendingLoanPopout';
|
||||
import { RouteHandler } from '../../router/routes';
|
||||
import TableWrapper from '../tableWrapper/TableWrapper';
|
||||
|
||||
export interface DashBoardCardProps {
|
||||
title?: string;
|
||||
desc?: string;
|
||||
descSpan?: string;
|
||||
descSpanClass?: string;
|
||||
onClick?: any;
|
||||
cardClass?: string;
|
||||
titleClass?: string;
|
||||
descClass?: string;
|
||||
btnTitle?: string;
|
||||
btnTextClass?: string;
|
||||
image?: any;
|
||||
imgClass?: string;
|
||||
}
|
||||
|
||||
export const DashBoardCard: React.FC<DashBoardCardProps> = ({
|
||||
title,
|
||||
desc,
|
||||
onClick,
|
||||
cardClass,
|
||||
titleClass,
|
||||
descClass,
|
||||
descSpan,
|
||||
descSpanClass,
|
||||
btnTitle,
|
||||
btnTextClass,
|
||||
image,
|
||||
imgClass,
|
||||
}) => {
|
||||
return (
|
||||
<div
|
||||
className={`h-full w-full rounded-lg p-5 shadow-lg hover:shadow-none bg-no-repeat bg-[90%] flex justify-between gap-4 items-center transition-all duration-300 ${
|
||||
cardClass && cardClass
|
||||
}`}
|
||||
// onClick={onClick}
|
||||
>
|
||||
<div className="w-3/4 flex flex-col gap-[2.3125rem]">
|
||||
{title && (
|
||||
<h1
|
||||
className={`mb-1 text-[#FFF] text-lg text-left font-bold ${
|
||||
titleClass && titleClass
|
||||
}`}
|
||||
>
|
||||
{title}
|
||||
</h1>
|
||||
)}
|
||||
{desc && (
|
||||
<p className={`text-lg text-left ${descClass && descClass}`}>
|
||||
{desc}{' '}
|
||||
{descSpan && (
|
||||
<span className={`${descSpanClass && descSpanClass}`}>
|
||||
{descSpan}
|
||||
</span>
|
||||
)}
|
||||
</p>
|
||||
)}
|
||||
{btnTitle && (
|
||||
<Button className={btnTextClass} text={btnTitle} onClick={onClick} />
|
||||
)}
|
||||
</div>
|
||||
{image && <img className={imgClass} src={image} alt="card-image" />}
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
interface DashboardHomeIntroProps {
|
||||
handleNextStep: (value: {}) => any;
|
||||
step?: number | string;
|
||||
}
|
||||
|
||||
interface PopoutProps<T> {
|
||||
show?: boolean
|
||||
data?: T
|
||||
}
|
||||
|
||||
const DashboardHomeIntro: FC<DashboardHomeIntroProps> = ({
|
||||
handleNextStep,
|
||||
step,
|
||||
}) => {
|
||||
|
||||
const navigate = useNavigate()
|
||||
const { userDetails } = useSelector((state: any) => state?.userDetails); // CHECKS IF USER Details are avaliable
|
||||
|
||||
const [loanPopout, setLoanPopout] = useState<PopoutProps<PendingTableList>>({show:false, data:{}})
|
||||
const closePopout = () => {
|
||||
setLoanPopout({show:false, data:{}})
|
||||
}
|
||||
|
||||
const [userLoanList, setUserLoanList] = useState<{
|
||||
loading: boolean;
|
||||
data: Array<PendingTableList>;
|
||||
}>({ loading: true, data: [] });
|
||||
|
||||
useEffect(() => {
|
||||
let token = localStorage.getItem('token');
|
||||
let uid = localStorage.getItem('uid');
|
||||
if (!token || !uid) {
|
||||
return;
|
||||
}
|
||||
getUserPendingLoanList(uid)
|
||||
.then((res) => {
|
||||
if (!res || !res.data.loans) {
|
||||
setUserLoanList({ loading: false, data: [] });
|
||||
return;
|
||||
}
|
||||
setUserLoanList({ loading: false, data: res?.data?.loans });
|
||||
})
|
||||
.catch((err) => {
|
||||
setUserLoanList({ loading: false, data: [] });
|
||||
console.log(err)
|
||||
});
|
||||
}, []);
|
||||
|
||||
return (
|
||||
<>
|
||||
<div className="w-full">
|
||||
{step == 1 ? (
|
||||
<>
|
||||
<h1 className="font-bold my-5 text-2xl">
|
||||
Hello, {userDetails.firstname}
|
||||
</h1>
|
||||
<div className="group w-full lg:w-[27.8125rem] h-[12.75rem] mt-7 ">
|
||||
<DashBoardCard
|
||||
cardClass="bg-[#5C2684] relative"
|
||||
desc="Begin your application and get up to "
|
||||
descSpan="5 million naira loan."
|
||||
descClass="leading-[1.5625rem] text-lg text-white"
|
||||
descSpanClass="font-bold"
|
||||
btnTitle="Apply here"
|
||||
btnTextClass="w-[11.125rem] h-[2.8125rem] flex justify-center item-center btn-W text-[#FBB700]"
|
||||
image={NairaBag}
|
||||
imgClass="translate-y-4 -rotate-6"
|
||||
onClick={() => handleNextStep({})}
|
||||
/>
|
||||
</div>
|
||||
</>
|
||||
) : (
|
||||
<>
|
||||
<h1 className="font-bold my-5 text-2xl">
|
||||
Welcome Back, {userDetails.firstname}
|
||||
</h1>
|
||||
<div className="group w-full lg:w-[27.8125rem] h-[12.75rem] mt-7 ">
|
||||
<DashBoardCard
|
||||
cardClass="bg-[#5C2684] relative"
|
||||
desc="Your loan application has been reviewed and accepted, please confirm for disbursement."
|
||||
// descSpan="5 million naira loan."
|
||||
descClass="leading-[1.5625rem] text-lg text-white"
|
||||
// descSpanClass="font-bold"
|
||||
btnTitle="View and accept"
|
||||
btnTextClass="w-[11.125rem] h-[2.8125rem] flex justify-center item-center btn-W text-[#FBB700]"
|
||||
image={NairaBag}
|
||||
imgClass="translate-y-4 -rotate-6"
|
||||
// onClick={handleNextStep}
|
||||
/>
|
||||
</div>
|
||||
</>
|
||||
)}
|
||||
{userLoanList.loading ? null : (
|
||||
<div className="mt-5 w-full">
|
||||
<TableWrapper
|
||||
data={userLoanList.data}
|
||||
itemsPerPage={7}
|
||||
>
|
||||
{({ data }:{data:any}) => (
|
||||
<>
|
||||
<div className='w-full h-[420px] overflow-auto'>
|
||||
<table className="py-2 w-full text-sm text-left rtl:text-right text-gray-500 dark:text-gray-400">
|
||||
<thead className="text-xs text-gray-700 uppercase bg-gray-50 dark:bg-gray-700 dark:text-gray-400">
|
||||
<tr>
|
||||
<th scope="col" className="px-4 py-2">
|
||||
Date
|
||||
</th>
|
||||
<th scope="col" className="px-4 py-2">
|
||||
Amount
|
||||
</th>
|
||||
<th scope="col" className="px-4 py-2">
|
||||
Payment Term
|
||||
</th>
|
||||
<th scope="col" className="px-4 py-2">
|
||||
Status
|
||||
</th>
|
||||
<th scope="col" className="px-4 py-2">
|
||||
Action
|
||||
</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{(data && data.length > 0) ? data?.map((item:any) => (
|
||||
<tr key={item?.application_uid} className="bg-white border-b dark:bg-gray-800 dark:border-gray-700 hover:bg-gray-50 dark:hover:bg-gray-600">
|
||||
<td className="px-3 py-2">
|
||||
{NewDateTimeFormatter(item?.added)}
|
||||
</td>
|
||||
<td className="px-3 py-2">
|
||||
{FormatAmount(item?.loan_amount)}
|
||||
</td>
|
||||
<td className="px-3 py-2">
|
||||
{item?.payment_month}
|
||||
</td>
|
||||
<td className="px-3 py-2">
|
||||
<button
|
||||
className={`${!item?.status_text?.button && 'pointer-events-none border-0'} border p-2`}
|
||||
onClick={()=>setLoanPopout({show:true, data:item})}
|
||||
>
|
||||
{item?.status_text?.text || 'Pending'}
|
||||
</button>
|
||||
</td>
|
||||
<td className="px-3 py-2 flex gap-2">
|
||||
<button className="flex flex-nowrap items-center px-2 py-1 border-2 border-black" onClick={()=>navigate(RouteHandler.dashboardReference, {state:{application_uid: item?.application_uid}})}>
|
||||
View
|
||||
<Icons name="arrow-right" />
|
||||
</button>
|
||||
</td>
|
||||
</tr>
|
||||
))
|
||||
:
|
||||
<tr className="w-3 p-3">
|
||||
<td className="px-3 py-2" colSpan={5}>
|
||||
<div className="flex justify-center items-center">
|
||||
No Record Found
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</>
|
||||
)}
|
||||
</TableWrapper>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
|
||||
{loanPopout.show && <PendingLoanPopout data={loanPopout.data} action={closePopout} />}
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
export default DashboardHomeIntro;
|
||||
|
||||
// {/* <div className="group w-full lg:w-96 h-32">
|
||||
// <DefaultCard
|
||||
// descText="You currently do not have any open application. Click on apply for a loan to get started."
|
||||
// iconName="arrow"
|
||||
// iconColor="#FBB700"
|
||||
// cardClass={`p-4 bg-[#FFFAFA] border border-[#EE4040]`}
|
||||
// descTextClass="text-[#423131] leading-5"
|
||||
// onClick={() => {
|
||||
// console.log("working");
|
||||
// }}
|
||||
// />
|
||||
// </div> */}
|
||||
|
||||
// {/* <div className="w-full mt-20 flex gap-16 flex-wrap">
|
||||
// <div className="group h-40 w-full lg:w-80">
|
||||
// <DefaultCard
|
||||
// title="Apply for a loan"
|
||||
// descText="You currently do not have any open application. Click on apply for a loan to get started."
|
||||
// iconName="greater-than"
|
||||
// iconColor="#FFF"
|
||||
// cardClass={`bg-[#5C2684] bg-[url('../../../src/assets/images/dashboard/card_bg.png')]`}
|
||||
// titleClass="text-[#FFF]"
|
||||
// descTextClass="text-[#EFEFEF] leading-5"
|
||||
// onClick={() => {
|
||||
// console.log("working");
|
||||
// }}
|
||||
// />
|
||||
// </div>
|
||||
// <div className="group h-40 w-full lg:w-80">
|
||||
// <DefaultCard
|
||||
// title="Loan history"
|
||||
// descText="You currently do not have any open application. Click on apply for a loan to get started."
|
||||
// iconName="greater-than"
|
||||
// iconColor="#FFF"
|
||||
// cardClass={`bg-[#635D4D] bg-[url('../../../src/assets/images/dashboard/card_bg.png')]`}
|
||||
// titleClass="text-[#FFF]"
|
||||
// descTextClass="text-[#EFEFEF] leading-5"
|
||||
// onClick={() => {
|
||||
// console.log("working");
|
||||
// }}
|
||||
// />
|
||||
// </div>
|
||||
// <div className="group h-40 w-full lg:w-80">
|
||||
// <DefaultCard
|
||||
// title="How it works?"
|
||||
// descText="Steps to follow to complete your loan application successfully."
|
||||
// iconName="greater-than"
|
||||
// iconColor="#FFF"
|
||||
// cardClass={`bg-[#635D4D] bg-[url('../../../src/assets/images/dashboard/card_bg.png')]`}
|
||||
// titleClass="text-[#FFF]"
|
||||
// descTextClass="text-[#EFEFEF] leading-5"
|
||||
// onClick={() => {
|
||||
// console.log("working");
|
||||
// }}
|
||||
// />
|
||||
// </div>
|
||||
// </div> */}
|
||||
@@ -0,0 +1,111 @@
|
||||
import { useNavigate } from "react-router-dom";
|
||||
import { useSelector } from "react-redux";
|
||||
import {Formik, Form} from 'formik'
|
||||
import * as Yup from "yup";
|
||||
|
||||
import { RouteHandler } from "../../router/routes";
|
||||
|
||||
import { Button, InputCompOne } from "..";
|
||||
|
||||
// To get the validation schema
|
||||
const validationSchema = Yup.object().shape({
|
||||
firstname: Yup.string()
|
||||
.required("Required"),
|
||||
lastname: Yup.string()
|
||||
.required("Required"),
|
||||
internal_email : Yup.string().required("Required").email("Invalid"),
|
||||
});
|
||||
|
||||
export default function DashboardProfile() {
|
||||
let navigate = useNavigate();
|
||||
const navigateToHome = () => navigate(RouteHandler.dashboardHome);
|
||||
|
||||
|
||||
const { userDetails } = useSelector((state:any) => state?.userDetails); // GETS USER DETAILS
|
||||
|
||||
const initialValues = {
|
||||
firstname: userDetails.firstname,
|
||||
lastname: userDetails.lastname,
|
||||
internal_email: userDetails.internal_email
|
||||
};
|
||||
|
||||
//FUNCTION TO HANDLE SUBMIT
|
||||
const handleSubmit = (values:any) => {
|
||||
console.log('good', values)
|
||||
};
|
||||
|
||||
return (
|
||||
<div className="w-full">
|
||||
|
||||
<div className='my-[2rem] flex items-center'>
|
||||
<button onClick={navigateToHome} className='py-2 px-4 text-lg text-white flex justify-center items-center bg-[#5C2684]'>< Back</button>
|
||||
</div>
|
||||
|
||||
<Formik
|
||||
initialValues={initialValues}
|
||||
validationSchema={validationSchema}
|
||||
onSubmit={handleSubmit}
|
||||
>
|
||||
{(props)=>(
|
||||
<Form>
|
||||
<div className="flex flex-col lg:flex-row items-start gap-[2rem]">
|
||||
<div className='w-full lg:max-w-[30rem] flex flex-col gap-[2rem]'>
|
||||
<InputCompOne
|
||||
parentClass="w-full"
|
||||
name="firstname"
|
||||
floatLabel="Firstname"
|
||||
// labelClass="font-bold text-[1.125rem]"
|
||||
input
|
||||
inputClass="w-full h-[3.625rem] bg-[#EFEFEF] px-4 rounded-[.375rem]"
|
||||
placeholder="Mr. Mark John"
|
||||
value={props.values.firstname}
|
||||
onChange={props.handleChange}
|
||||
// error={(props.errors.firstname && props.touched.firstname) ? props.errors.firstname : ''}
|
||||
/>
|
||||
<InputCompOne
|
||||
parentClass="w-full"
|
||||
name="internal_email"
|
||||
floatLabel="Email"
|
||||
// labelClass="font-bold text-[1.125rem]"
|
||||
input
|
||||
// disabled={true}
|
||||
inputClass="w-full h-[3.625rem] bg-[#EFEFEF] px-4 rounded-[.375rem]"
|
||||
placeholder="Mr. Mark John"
|
||||
value={props.values.internal_email}
|
||||
onChange={props.handleChange}
|
||||
// error={(props.errors.internal_email && props.touched.internal_email) ? props.errors.internal_email : ''}
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div className='w-full lg:max-w-[30rem] flex flex-col gap-[2rem]'>
|
||||
<InputCompOne
|
||||
parentClass="w-full"
|
||||
name="lastname"
|
||||
floatLabel="Lastname"
|
||||
// labelClass="font-bold text-[1.125rem]"
|
||||
input
|
||||
// disabled={true}
|
||||
inputClass="w-full h-[3.625rem] bg-[#EFEFEF] px-4 rounded-[.375rem]"
|
||||
placeholder="Mr. Mark John"
|
||||
value={props.values.lastname}
|
||||
onChange={props.handleChange}
|
||||
// error={(props.errors.lastname && props.touched.lastname) ? props.errors.lastname : ''}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className='hidden w-full lg:max-w-[416px] flex flex-col gap-[2rem]'>
|
||||
<div className="w-full">
|
||||
<Button
|
||||
className="my-4 btn-Y text-black w-full h-11"
|
||||
text="Update"
|
||||
type="submit"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</Form>
|
||||
)}
|
||||
</Formik>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,109 @@
|
||||
import {useState} from 'react'
|
||||
import ModalWrapper from '../modal/ModalWrapper'
|
||||
import { PendingTableList } from '../../core/models'
|
||||
import { NewDateTimeFormatter } from '../../lib/NewDateTimeFormatter'
|
||||
import { addCard } from '../../core/apiRequest'
|
||||
import CustomSpinner from '../CustomSpinner'
|
||||
|
||||
|
||||
interface Props<T> {
|
||||
action: ()=>void
|
||||
data?: T
|
||||
}
|
||||
|
||||
export default function PendingLoanPopout({data, action}:Props<PendingTableList>) {
|
||||
|
||||
const [addCardStatus, setAddCardStatus] = useState<{
|
||||
loading: boolean;
|
||||
status: boolean
|
||||
msg: string
|
||||
}>({ loading: false, status: false, msg: ''});
|
||||
|
||||
const handleAddCard = (appID:string | undefined) => {
|
||||
let reqData = {
|
||||
application_uid: appID
|
||||
|
||||
}
|
||||
setAddCardStatus({ loading: true, status: false, msg: ''})
|
||||
addCard(reqData).then(res => {
|
||||
if(res?.data?.call_return != '100'){
|
||||
setAddCardStatus({ loading: false, status: false, msg: 'failed to add card'})
|
||||
setTimeout(()=>{
|
||||
setAddCardStatus({ loading: false, status: false, msg: ''})
|
||||
},3000)
|
||||
}
|
||||
window.location.href = res?.data?.redirect_url
|
||||
setAddCardStatus({ loading: false, status: true, msg: 'card added'})
|
||||
action() // TO CLOSE MODAL
|
||||
}).catch(err => {
|
||||
setAddCardStatus({ loading: false, status: false, msg: 'failed to add card'})
|
||||
console.log('ERR', err)
|
||||
setTimeout(()=>{
|
||||
setAddCardStatus({ loading: false, status: false, msg: ''})
|
||||
},3000)
|
||||
})
|
||||
}
|
||||
return (
|
||||
<ModalWrapper>
|
||||
<div className='modal-container'>
|
||||
<div className='modal-header'>
|
||||
<h1 className='modal-title'>Add Card</h1>
|
||||
<button
|
||||
type="button"
|
||||
className="modal-close-btn"
|
||||
name='cancel'
|
||||
onClick={action}
|
||||
>
|
||||
<svg
|
||||
width="36"
|
||||
height="36"
|
||||
viewBox="0 0 36 36"
|
||||
fill="none"
|
||||
className="fill-current"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
>
|
||||
<path
|
||||
d="M36 16.16C36 17.4399 36 18.7199 36 20.0001C35.7911 20.0709 35.8636 20.2554 35.8385 20.4001C34.5321 27.9453 30.246 32.9248 22.9603 35.2822C21.9006 35.6251 20.7753 35.7657 19.6802 35.9997C18.4003 35.9997 17.1204 35.9997 15.8401 35.9997C15.5896 35.7086 15.2189 35.7732 14.9034 35.7093C7.77231 34.2621 3.08728 30.0725 0.769671 23.187C0.435002 22.1926 0.445997 21.1199 0 20.1599C0 18.7198 0 17.2798 0 15.8398C0.291376 15.6195 0.214408 15.2656 0.270759 14.9808C1.71321 7.69774 6.02611 2.99691 13.0428 0.700951C14.0118 0.383805 15.0509 0.386897 15.9999 0C17.2265 0 18.4532 0 19.6799 0C19.7156 0.124041 19.8125 0.136067 19.9225 0.146719C27.3 0.868973 33.5322 6.21922 35.3801 13.427C35.6121 14.3313 35.7945 15.2484 36 16.16ZM33.011 18.0787C33.0433 9.77105 26.3423 3.00309 18.077 2.9945C9.78479 2.98626 3.00344 9.658 2.98523 17.8426C2.96667 26.1633 9.58859 32.9601 17.7602 33.0079C26.197 33.0577 32.9787 26.4186 33.011 18.0787Z"
|
||||
fill=""
|
||||
fillOpacity="0.6"
|
||||
/>
|
||||
<path
|
||||
d="M15.9309 18.023C13.9329 16.037 12.007 14.1207 10.0787 12.2072C9.60071 11.733 9.26398 11.2162 9.51996 10.506C9.945 9.32677 11.1954 9.0811 12.1437 10.0174C13.9067 11.7585 15.6766 13.494 17.385 15.2879C17.9108 15.8401 18.1633 15.7487 18.6375 15.258C20.3586 13.4761 22.1199 11.7327 23.8822 9.99096C24.8175 9.06632 26.1095 9.33639 26.4967 10.517C26.7286 11.2241 26.3919 11.7413 25.9133 12.2178C24.1757 13.9472 22.4477 15.6855 20.7104 17.4148C20.5228 17.6018 20.2964 17.7495 20.0466 17.9485C22.0831 19.974 24.0372 21.8992 25.9689 23.8468C26.9262 24.8119 26.6489 26.1101 25.4336 26.4987C24.712 26.7292 24.2131 26.3441 23.7455 25.8757C21.9945 24.1227 20.2232 22.3892 18.5045 20.6049C18.0698 20.1534 17.8716 20.2269 17.4802 20.6282C15.732 22.4215 13.9493 24.1807 12.1777 25.951C11.7022 26.4262 11.193 26.7471 10.4738 26.4537C9.31345 25.9798 9.06881 24.8398 9.98589 23.8952C11.285 22.5576 12.6138 21.2484 13.9387 19.9355C14.5792 19.3005 15.2399 18.6852 15.9309 18.023Z"
|
||||
fill="#"
|
||||
fillOpacity="0.6"
|
||||
/>
|
||||
</svg>
|
||||
</button>
|
||||
</div>
|
||||
<div className='modal-body'>
|
||||
<div className='w-full flex flex-col gap-2'>
|
||||
<div className='py-2 w-full flex gap-1'>
|
||||
<p className='font-semibold'>ID :</p>
|
||||
<p>{data?.application_uid}</p>
|
||||
</div>
|
||||
<div className='py-2 w-full flex gap-1'>
|
||||
<p className='font-semibold'>Loan Amount :</p>
|
||||
<p>{data?.loan_amount}</p>
|
||||
</div>
|
||||
<div className='py-2 w-full flex gap-1'>
|
||||
<p className='font-semibold'>Payment Month :</p>
|
||||
<p>{data?.payment_month}</p>
|
||||
</div>
|
||||
<div className='py-2 w-full flex gap-1'>
|
||||
<p className='font-semibold'>Added :</p>
|
||||
<p>{NewDateTimeFormatter(data?.added)}</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div className='modal-footer'>
|
||||
<button onClick={action} name='cancel' className='custom-btn text-red-500 border border-red-500 hover:text-red-700'>Cancel</button>
|
||||
{addCardStatus.loading ?
|
||||
<CustomSpinner width='w-6' height='h-6' />
|
||||
:
|
||||
<button onClick={()=>handleAddCard(data?.application_uid)} name='proceed' className={`custom-btn border border-sky-500 text-sky-500 hover:text-sky-700 ${addCardStatus.loading && 'opacity-20 pointer-events-none'}`}>Add Card</button>
|
||||
}
|
||||
</div>
|
||||
</div>
|
||||
</ModalWrapper>
|
||||
)
|
||||
}
|
||||
@@ -0,0 +1,365 @@
|
||||
import {useState, useEffect} from 'react'
|
||||
import { Button, InputCompOne } from '../../shared/index';
|
||||
|
||||
import {Formik, Form} from 'formik'
|
||||
import * as Yup from "yup";
|
||||
|
||||
import { getEmployer } from '../../../core/apiRequest';
|
||||
import CustomSpinner from '../../CustomSpinner';
|
||||
import { FormatAmount } from '../../../lib/FormatAmount';
|
||||
|
||||
// type Props = {
|
||||
// handleNextStep?:(value:{})=>any
|
||||
// }
|
||||
|
||||
// type EmployerProps = {
|
||||
// loading?: boolean,
|
||||
// data?: Array<{[index:string]: string}> | {[index:string]: Array<{[index:string]: string}> }
|
||||
// }
|
||||
|
||||
const initialValues = {
|
||||
job_title: "",
|
||||
name: "",
|
||||
job_sector: "",
|
||||
industry: "",
|
||||
start_date: "",
|
||||
official_email:"",
|
||||
annual_salary: "",
|
||||
net_montlty: "",
|
||||
salary_date: "",
|
||||
employee_id: "",
|
||||
highest_eductaion: "",
|
||||
employer_uid: "",
|
||||
isChecked: true
|
||||
};
|
||||
|
||||
// To get the validation schema
|
||||
const validationSchema = Yup.object().shape({
|
||||
isChecked: Yup.bool(), // use bool instead of boolean
|
||||
// .oneOf([true, false], "You must accept the terms and conditions"),
|
||||
job_title: Yup.string()
|
||||
.required("Required"),
|
||||
name: Yup.string().when('isChecked', {
|
||||
is: true,
|
||||
then: () => Yup.string().required('required'),
|
||||
otherwise: () => Yup.string(),
|
||||
}),
|
||||
job_sector: Yup.string().when('isChecked', {
|
||||
is: true,
|
||||
then: () => Yup.string().required('required'),
|
||||
}),
|
||||
industry: Yup.string().when('isChecked', {
|
||||
is: true,
|
||||
then: () => Yup.string().required('required'),
|
||||
}),
|
||||
start_date: Yup.string()
|
||||
.required("Required"),
|
||||
official_email : Yup.string().when('isChecked', {
|
||||
is: true,
|
||||
then: () => Yup.string().required('required'),
|
||||
})
|
||||
.email("Invalid"),
|
||||
annual_salary: Yup.string()
|
||||
.required("Required")
|
||||
.test("no-e", "Invalid", (value:any) => {
|
||||
if (value && /^[0-9]*$/.test(value) == false) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}),
|
||||
net_montlty: Yup.string()
|
||||
.required("Required")
|
||||
.test("no-e", "Invalid", (value:any) => {
|
||||
if (value && /^[0-9]*$/.test(value) == false) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}),
|
||||
salary_date: Yup.string()
|
||||
.required("Required"),
|
||||
employee_id: Yup.string()
|
||||
.required("Required"),
|
||||
highest_eductaion: Yup.string()
|
||||
.required("Required"),
|
||||
employer_uid: Yup.string().when('isChecked', {
|
||||
is: false,
|
||||
then: () => Yup.string().required('required'),
|
||||
}),
|
||||
});
|
||||
|
||||
export default function EmploymentDetail() {
|
||||
|
||||
const [employerList, setEmployerList] = useState<any>({
|
||||
loading: true,
|
||||
data: {}
|
||||
})
|
||||
|
||||
|
||||
//FUNCTION TO HANDLE SUBMIT
|
||||
const handleSubmit = () => {
|
||||
console.log('good')
|
||||
};
|
||||
|
||||
useEffect(()=>{
|
||||
getEmployer().then(res => {
|
||||
setEmployerList({loading:false, data:res?.data?.employer})
|
||||
}).catch(err => {
|
||||
console.log(err)
|
||||
setEmployerList({loading:false, data:{}})
|
||||
})
|
||||
},[])
|
||||
|
||||
const formInitialValue = (employerList.loading || Object.keys(employerList?.data)?.length < 1) ? initialValues : {...initialValues, ...employerList?.data}
|
||||
|
||||
return (
|
||||
<>
|
||||
{employerList.loading ?
|
||||
<div className='flex flex-col justify-center items-center h-96'>
|
||||
<CustomSpinner width='w-8' height='h-8' />
|
||||
</div>
|
||||
:
|
||||
<div className="w-full">
|
||||
<Formik
|
||||
initialValues={formInitialValue}
|
||||
validationSchema={validationSchema}
|
||||
onSubmit={handleSubmit}
|
||||
>
|
||||
{(props)=>(
|
||||
<Form>
|
||||
<div className="mt-[3.25rem] flex flex-col gap-9">
|
||||
<p className='text-red-500 text-lg md:text-2xl'>Employment Informaton</p>
|
||||
<div className="flex flex-col lg:flex-row items-start gap-[2rem]">
|
||||
<div className='w-full lg:max-w-[30rem] flex flex-col'>
|
||||
{/* <div className='w-full gap-[2rem]'>
|
||||
<InputCompOne
|
||||
parentClass="w-full"
|
||||
name="employer_uid"
|
||||
floatLabel="Employer Name"
|
||||
// labelClass="font-bold text-[1.125rem]"
|
||||
select={true}
|
||||
selectClass="w-full h-[3.625rem] bg-[#EFEFEF] px-4 rounded-[.375rem]"
|
||||
selectOptions={{loading:employersList?.loading, data: employersList?.data?.records}}
|
||||
selectValue={props.values.employer_uid}
|
||||
onChange={props.handleChange}
|
||||
error={(props.errors.employer_uid && props.touched.employer_uid) ? props.errors.employer_uid : ''}
|
||||
disabled={props.values.isChecked}
|
||||
/>
|
||||
<div className='flex gap-4 items-start my-2'>
|
||||
<input
|
||||
type='checkbox'
|
||||
name="isChecked"
|
||||
className='w-4 h-4 p-2 accent-purple-600 text-purple-600 bg-gray-100 border-gray-300 rounded focus:ring-purple-500'
|
||||
onChange={props.handleChange}
|
||||
checked={props.values.isChecked}
|
||||
/>
|
||||
<p className='text-[12px] text-justify'>Check here if employer is not on the list</p>
|
||||
</div>
|
||||
<div className={`hidden p-4 ${props.values.isChecked && 'hidden'}`}>
|
||||
Name: {'Name'}
|
||||
</div>
|
||||
</div> */}
|
||||
<div className={`w-full flex flex-col gap-[2rem] ${!props.values.isChecked && 'hidden'}`}>
|
||||
<InputCompOne
|
||||
parentClass="w-full"
|
||||
name="name"
|
||||
floatLabel="Employer name"
|
||||
// labelClass="font-bold text-[1.125rem]"
|
||||
input
|
||||
disabled={true}
|
||||
inputClass="w-full h-[3.625rem] bg-[#EFEFEF] px-4 rounded-[.375rem]"
|
||||
placeholder="Mr. Mark John"
|
||||
value={props.values.name}
|
||||
onChange={props.handleChange}
|
||||
// error={(props.errors.name && props.touched.name) ? props.errors.name : ''}
|
||||
/>
|
||||
<InputCompOne
|
||||
parentClass="w-full"
|
||||
name="official_email"
|
||||
floatLabel="Employers official email"
|
||||
// labelClass="font-bold text-[1.125rem]"
|
||||
input
|
||||
disabled={true}
|
||||
inputClass="w-full h-[3.625rem] bg-[#EFEFEF] px-4 rounded-[.375rem]"
|
||||
placeholder="example@gmail.com"
|
||||
value={props.values.official_email}
|
||||
onChange={props.handleChange}
|
||||
// error={(props.errors.official_email && props.touched.official_email) ? props.errors.official_email : ''}
|
||||
/>
|
||||
<InputCompOne
|
||||
parentClass="w-full"
|
||||
name="industry"
|
||||
floatLabel="Select your industry"
|
||||
// labelClass="font-bold text-[1.125rem]"
|
||||
select={true}
|
||||
disabled={true}
|
||||
selectClass="w-full h-[3.625rem] bg-[#EFEFEF] px-4 rounded-[.375rem]"
|
||||
selectOptions={props.values.industry}
|
||||
selectValue={props.values.industry}
|
||||
onChange={props.handleChange}
|
||||
// error={(props.errors.industry && props.touched.industry) ? props.errors.industry : ''}
|
||||
/>
|
||||
<InputCompOne
|
||||
parentClass="w-full"
|
||||
name="job_sector"
|
||||
floatLabel="Job Sector"
|
||||
// labelClass="font-bold text-[1.125rem]"
|
||||
select={true}
|
||||
disabled={true}
|
||||
selectClass="w-full h-[3.625rem] bg-[#EFEFEF] px-4 rounded-[.375rem]"
|
||||
selectOptions={props.values.job_sector}
|
||||
selectValue={props.values.job_sector}
|
||||
onChange={props.handleChange}
|
||||
// error={(props.errors.job_sector && props.touched.job_sector) ? props.errors.job_sector : ''}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className='w-full lg:max-w-[30rem] flex flex-col gap-[2rem]'>
|
||||
<InputCompOne
|
||||
parentClass="w-full"
|
||||
name="job_title"
|
||||
floatLabel="Job Title"
|
||||
// labelClass="font-bold text-[1.125rem]"
|
||||
input
|
||||
disabled={true}
|
||||
inputClass="w-full h-[3.625rem] bg-[#EFEFEF] px-4 rounded-[.375rem]"
|
||||
placeholder="Software Engineer"
|
||||
value={props.values.job_title}
|
||||
onChange={props.handleChange}
|
||||
// error={(props.errors.job_title && props.touched.job_title) ? props.errors.job_title : ''}
|
||||
/>
|
||||
<InputCompOne
|
||||
parentClass="w-full"
|
||||
name="highest_eductaion"
|
||||
floatLabel="Highest level of education"
|
||||
// labelClass="font-bold text-[1.125rem]"
|
||||
select={true}
|
||||
disabled={true}
|
||||
selectClass="w-full h-[3.625rem] bg-[#EFEFEF] px-4 rounded-[.375rem]"
|
||||
selectOptions={props.values.highest_eductaion}
|
||||
selectValue={props.values.highest_eductaion}
|
||||
onChange={props.handleChange}
|
||||
// error={(props.errors.highest_eductaion && props.touched.highest_eductaion) ? props.errors.highest_eductaion : ''}
|
||||
/>
|
||||
<div className="w-full flex flex-col sm:flex-row items-center gap-4">
|
||||
<InputCompOne
|
||||
parentClass="w-full"
|
||||
name="start_date"
|
||||
floatLabel="Date of resumption"
|
||||
// labelClass="font-bold text-[1.125rem]"
|
||||
input
|
||||
disabled={true}
|
||||
inputType='text'
|
||||
inputClass="w-full h-[3.625rem] bg-[#EFEFEF] px-4 rounded-[.375rem]"
|
||||
placeholder="12/12/2015"
|
||||
value={props.values.start_date}
|
||||
onChange={props.handleChange}
|
||||
// error={(props.errors.start_date && props.touched.start_date) ? props.errors.start_date : ''}
|
||||
/>
|
||||
<InputCompOne
|
||||
parentClass="w-full"
|
||||
name="salary_date"
|
||||
floatLabel="Salary payment date"
|
||||
// labelClass="font-bold text-[1.125rem]"
|
||||
input
|
||||
disabled={true}
|
||||
inputType='text'
|
||||
inputClass="w-full h-[3.625rem] bg-[#EFEFEF] px-4 rounded-[.375rem]"
|
||||
placeholder="30th of every month"
|
||||
value={(props.values.salary_date)}
|
||||
onChange={props.handleChange}
|
||||
// error={(props.errors.salary_date && props.touched.salary_date) ? props.errors.salary_date : ''}
|
||||
/>
|
||||
</div>
|
||||
<div className="w-full flex flex-col sm:flex-row items-center gap-4">
|
||||
<InputCompOne
|
||||
parentClass="w-full"
|
||||
name="annual_salary"
|
||||
floatLabel="Annual Income"
|
||||
// labelClass="font-bold text-[1.125rem]"
|
||||
input
|
||||
disabled={true}
|
||||
inputClass="w-full h-[3.625rem] bg-[#EFEFEF] px-4 rounded-[.375rem] text-right"
|
||||
placeholder="1,200,000"
|
||||
value={FormatAmount(props.values.annual_salary)}
|
||||
onChange={props.handleChange}
|
||||
// error={(props.errors.annual_salary && props.touched.annual_salary) ? props.errors.annual_salary : ''}
|
||||
/>
|
||||
<InputCompOne
|
||||
parentClass="w-full"
|
||||
name="net_montlty"
|
||||
floatLabel="Net monthly salary"
|
||||
// labelClass="font-bold text-[1.125rem]"
|
||||
input
|
||||
disabled={true}
|
||||
inputClass="w-full h-[3.625rem] bg-[#EFEFEF] px-4 rounded-[.375rem] text-right"
|
||||
placeholder="100,000"
|
||||
value={FormatAmount(props.values.net_montlty)}
|
||||
onChange={props.handleChange}
|
||||
// error={(props.errors.net_montlty && props.touched.net_montlty) ? props.errors.net_montlty : ''}
|
||||
/>
|
||||
</div>
|
||||
<InputCompOne
|
||||
parentClass="w-full"
|
||||
name="employee_id"
|
||||
floatLabel="Employee ID"
|
||||
// labelClass="font-bold text-[1.125rem]"
|
||||
input
|
||||
disabled={true}
|
||||
inputClass="w-full h-[3.625rem] bg-[#EFEFEF] px-4 rounded-[.375rem]"
|
||||
placeholder="LS/001/005"
|
||||
value={props.values.employee_id}
|
||||
onChange={props.handleChange}
|
||||
// error={(props.errors.employee_id && props.touched.employee_id) ? props.errors.employee_id : ''}
|
||||
/>
|
||||
<div className="hidden w-full">
|
||||
<Button
|
||||
className="my-4 btn-Y text-black w-full h-11"
|
||||
text="Next"
|
||||
type="submit"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</Form>
|
||||
)}
|
||||
</Formik>
|
||||
</div>
|
||||
}
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
|
||||
// interface SelectOption {
|
||||
// loading: boolean;
|
||||
// data: {value: string;
|
||||
// label: string}[]
|
||||
// }
|
||||
|
||||
|
||||
// const jobSector: SelectOption = {
|
||||
// loading: false,
|
||||
// data: [
|
||||
// { value: "", label: "Please Select" },
|
||||
// { value: "private (non academic)", label: "Private (non academic)" },
|
||||
// ]
|
||||
// }
|
||||
|
||||
// const industry: SelectOption = {
|
||||
// loading: false,
|
||||
// data: [
|
||||
// { value: "", label: "Please Select" },
|
||||
// { value: "engineering", label: "Engineering" },
|
||||
// ]
|
||||
// }
|
||||
|
||||
// const highestEductaion: SelectOption = {
|
||||
// loading: false,
|
||||
// data: [
|
||||
// { value: "", label: "Please Select" },
|
||||
// { value: "b.sc + professional qualification", label: "B.Sc + Professional Qualification" },
|
||||
// ]
|
||||
// }
|
||||
@@ -0,0 +1,146 @@
|
||||
import { Button, InputCompOne, Stepper } from '../../shared/index';
|
||||
|
||||
import {Formik, Form} from 'formik'
|
||||
import * as Yup from "yup";
|
||||
import { applyForLoan } from '../../../core/apiRequest';
|
||||
|
||||
// import { useNavigate } from "react-router-dom";
|
||||
// import { RouteHandler } from '../../../router/routes';
|
||||
|
||||
type Props = {
|
||||
handleNextStep:(value:{})=>any
|
||||
applicationDetails: {}
|
||||
}
|
||||
|
||||
const initialValues = {
|
||||
account: "",
|
||||
checked: false
|
||||
};
|
||||
|
||||
// To get the validation schema
|
||||
const validationSchema = Yup.object().shape({
|
||||
account: Yup.string()
|
||||
.required("Required"),
|
||||
checked: Yup.bool() // use bool instead of boolean
|
||||
.oneOf([true], "You must accept the terms and conditions")
|
||||
});
|
||||
|
||||
export default function DashboardHomeAttestation({handleNextStep, applicationDetails}:Props) {
|
||||
// let navigate = useNavigate();
|
||||
// const navigateToProfile = () => navigate(RouteHandler.dashboardProfile);
|
||||
|
||||
//FUNCTION TO HANDLE LOAN APPLICATION
|
||||
const handleSubmit = (values:any) => {
|
||||
delete values.checked
|
||||
applyForLoan({...applicationDetails,disbursement_account: values?.account, disbursement: values}).then(res=>{
|
||||
console.log('APPLY FOR LOAN', res)
|
||||
handleNextStep({disbursement: values})
|
||||
}).catch(err=>{
|
||||
console.log(err)
|
||||
})
|
||||
// applyForLoan(payload).then(res=>{
|
||||
// console.log('APPLY FOR LOAN', res)
|
||||
// // handleNextStep({disbursement: values})
|
||||
// }).catch(err=>{
|
||||
// console.log(err)
|
||||
// })
|
||||
};
|
||||
|
||||
return (
|
||||
<div className="w-full">
|
||||
<div className="w-full flex justify-center">
|
||||
<Stepper step={4} />
|
||||
</div>
|
||||
<p className='my-10 text-red-500 text-lg md:text-2xl'>Applicant's Attestation and Debit Instruction</p>
|
||||
<p className='text-red-500 text-base'>NB: Must be your {import.meta.env.VITE_BANK_NAME_SHORT} account number</p>
|
||||
<Formik
|
||||
initialValues={initialValues}
|
||||
validationSchema={validationSchema}
|
||||
onSubmit={handleSubmit}
|
||||
>
|
||||
{(props)=>(
|
||||
<Form>
|
||||
<div className="flex flex-col gap-9">
|
||||
<div className="flex items-center gap-[4.125rem]">
|
||||
<InputCompOne
|
||||
parentClass="max-w-[25.875rem] w-full flex flex-col gap-4"
|
||||
name="account"
|
||||
floatLabel="Disbursement account number"
|
||||
// labelClass="font-bold text-[1.125rem]"
|
||||
input
|
||||
inputClass="w-full h-[3.625rem] bg-[#EFEFEF] px-4 rounded-[.375rem]"
|
||||
placeholder="0102547896"
|
||||
value={props.values.account}
|
||||
onChange={props.handleChange}
|
||||
error={(props.errors.account && props.touched.account) ? props.errors.account : ''}
|
||||
/>
|
||||
</div>
|
||||
<div className='max-w-[25.875rem]'>
|
||||
<div className='flex gap-4 items-start'>
|
||||
<input
|
||||
type='checkbox'
|
||||
name="checked"
|
||||
className='w-4 h-4 p-2 accent-purple-600 text-purple-600 bg-gray-100 border-gray-300 rounded focus:ring-purple-500'
|
||||
onChange={props.handleChange}
|
||||
/>
|
||||
<p className='text-[12px] text-justify'>By pressing, you agree that you have read, understood and accept the <span className='text-blue-600'>applicatant's attestation</span> and <span className='text-blue-600'>terms and conditions</span> for {import.meta.env.VITE_BANK_NAME_SHORT}
|
||||
premium salary loan. You also give us permission to collect financial information and run credit checks on the account provided through our partners
|
||||
</p>
|
||||
</div>
|
||||
{props.errors.checked && props.touched.checked && <span className='text-[10px] text-red-500'>{props.errors.checked}</span>}
|
||||
</div>
|
||||
<Button
|
||||
className="my-8 max-w-[25.875rem] btn-Y text-black w-full h-11"
|
||||
text="Apply"
|
||||
type="submit"
|
||||
/>
|
||||
</div>
|
||||
</Form>
|
||||
)}
|
||||
</Formik>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
// const payload = {
|
||||
// "loan_amount": "100000",
|
||||
// "payment_month": "18",
|
||||
// "sales_agent": "Testing1234",
|
||||
// "gender": "male",
|
||||
// "address": "World bank housing Estate, Umuahia",
|
||||
// "marital_status": "single",
|
||||
// "state": "abia",
|
||||
// "email": "test5070@gmail.com",
|
||||
// "country": "NG",
|
||||
// "employment": {
|
||||
// "job_title": "Information Officer",
|
||||
// "name": "Testing Testing",
|
||||
// "sector": "private (non academic)",
|
||||
// "industry": "engineering",
|
||||
// "resumption_date": "2024-04-05",
|
||||
// "email": "test50700@gmail.com",
|
||||
// "annual_income": "600000",
|
||||
// "monthly_salary": "50000",
|
||||
// "salary_payment_date": "2024-04-19",
|
||||
// "employment_id": "2555566",
|
||||
// "highest_eductaion": "b.sc + professional qualification"
|
||||
// },
|
||||
// "loan_reference": [
|
||||
// {
|
||||
// "fullname": "John Mike",
|
||||
// "relationship": "Brother",
|
||||
// "phone_number": "07055566611",
|
||||
// "email": "refone@gmail.com",
|
||||
// "bvn": "11111111111"
|
||||
// },
|
||||
// {
|
||||
// "fullname": "Mary Paul",
|
||||
// "relationship": "Brother",
|
||||
// "phone_number": "07055577711",
|
||||
// "email": "reftwo@gmail.com",
|
||||
// "bvn": "22222222222"
|
||||
// }
|
||||
// ],
|
||||
// disbursement:{account: '1122334456'}
|
||||
// }
|
||||
@@ -0,0 +1,182 @@
|
||||
import { Button, InputCompOne, Stepper } from '../../shared/index';
|
||||
|
||||
import { state } from '../../../utils/states';
|
||||
|
||||
import {Formik, Form} from 'formik'
|
||||
import * as Yup from "yup";
|
||||
|
||||
type Props = {
|
||||
handleNextStep:(value:{})=>any
|
||||
}
|
||||
|
||||
const initialValues = {
|
||||
gender: "",
|
||||
address: "",
|
||||
marital_status: "",
|
||||
state: "",
|
||||
email:"",
|
||||
country:"NG"
|
||||
};
|
||||
|
||||
// To get the validation schema
|
||||
const validationSchema = Yup.object().shape({
|
||||
gender: Yup.string()
|
||||
.required("Required"),
|
||||
address: Yup.string()
|
||||
.required("Required"),
|
||||
marital_status: Yup.string()
|
||||
.required("Required"),
|
||||
state: Yup.string()
|
||||
.required("Required"),
|
||||
email: Yup.string()
|
||||
.email("Invalid")
|
||||
.required("Required"),
|
||||
country: Yup.string()
|
||||
.required("Required"),
|
||||
});
|
||||
|
||||
export default function DashboardHomeDetail({handleNextStep}:Props) {
|
||||
|
||||
//FUNCTION TO HANDLE SUBMIT
|
||||
const handleSubmit = (values:any) => {
|
||||
handleNextStep(values)
|
||||
};
|
||||
|
||||
return (
|
||||
<div className="w-full">
|
||||
<div className="w-full flex justify-center">
|
||||
<Stepper step={1} />
|
||||
</div>
|
||||
<Formik
|
||||
initialValues={initialValues}
|
||||
validationSchema={validationSchema}
|
||||
onSubmit={handleSubmit}
|
||||
>
|
||||
{(props)=>(
|
||||
<Form>
|
||||
<div className="mt-[3.25rem] flex flex-col gap-9">
|
||||
<div className="flex items-center gap-[4.125rem]">
|
||||
<InputCompOne
|
||||
parentClass="max-w-[25.875rem] w-full flex flex-col gap-4"
|
||||
name="gender"
|
||||
label="Select your gender"
|
||||
labelClass="font-bold text-[1.125rem]"
|
||||
select={true}
|
||||
selectClass="w-full h-[3.625rem] bg-[#EFEFEF] px-4 rounded-[.375rem]"
|
||||
selectOptions={gender}
|
||||
selectValue={props.values.gender}
|
||||
onChange={props.handleChange}
|
||||
error={(props.errors.gender && props.touched.gender) ? props.errors.gender : ''}
|
||||
/>
|
||||
<InputCompOne
|
||||
parentClass="max-w-[25.875rem] w-full flex flex-col gap-4"
|
||||
name="address"
|
||||
label="Residential address"
|
||||
labelClass="font-bold text-[1.125rem]"
|
||||
input
|
||||
inputClass="w-full h-[3.625rem] bg-[#EFEFEF] px-4 rounded-[.375rem]"
|
||||
placeholder="Somewhere in lagos"
|
||||
value={props.values.address}
|
||||
onChange={props.handleChange}
|
||||
error={(props.errors.address && props.touched.address) ? props.errors.address : ''}
|
||||
/>
|
||||
</div>
|
||||
<div className="flex items-center gap-[4.125rem]">
|
||||
<InputCompOne
|
||||
parentClass="max-w-[25.875rem] w-full flex flex-col gap-4"
|
||||
name="marital_status"
|
||||
label="Marital status"
|
||||
labelClass="font-bold text-[1.125rem]"
|
||||
select={true}
|
||||
selectClass="w-full h-[3.625rem] bg-[#EFEFEF] px-4 rounded-[.375rem]"
|
||||
selectOptions={maritalStatus}
|
||||
selectValue={props.values.marital_status}
|
||||
onChange={props.handleChange}
|
||||
error={(props.errors.marital_status && props.touched.marital_status) ? props.errors.marital_status : ''}
|
||||
/>
|
||||
<InputCompOne
|
||||
parentClass="max-w-[25.875rem] w-full flex flex-col gap-4"
|
||||
name="state"
|
||||
label="Select your state"
|
||||
labelClass="font-bold text-[1.125rem]"
|
||||
select={true}
|
||||
selectClass="w-full h-[3.625rem] bg-[#EFEFEF] px-4 rounded-[.375rem]"
|
||||
selectOptions={state}
|
||||
selectValue={props.values.state}
|
||||
onChange={props.handleChange}
|
||||
error={(props.errors.state && props.touched.state) ? props.errors.state : ''}
|
||||
/>
|
||||
</div>
|
||||
<div className="flex items-center gap-[4.125rem]">
|
||||
<InputCompOne
|
||||
parentClass="max-w-[25.875rem] w-full flex flex-col gap-4"
|
||||
name="email"
|
||||
label="Email address"
|
||||
labelClass="font-bold text-[1.125rem]"
|
||||
input
|
||||
inputClass="w-full h-[3.625rem] bg-[#EFEFEF] px-4 rounded-[.375rem]"
|
||||
placeholder="johndoe@gmail.com"
|
||||
value={props.values.email}
|
||||
onChange={props.handleChange}
|
||||
error={(props.errors.email && props.touched.email) ? props.errors.email : ''}
|
||||
/>
|
||||
<InputCompOne
|
||||
parentClass="max-w-[25.875rem] w-full flex flex-col gap-4"
|
||||
name="country"
|
||||
label="Select your country"
|
||||
labelClass="font-bold text-[1.125rem]"
|
||||
select={true}
|
||||
selectClass="w-full h-[3.625rem] bg-[#EFEFEF] px-4 rounded-[.375rem]"
|
||||
selectOptions={country}
|
||||
selectValue={props.values.country}
|
||||
onChange={props.handleChange}
|
||||
error={(props.errors.country && props.touched.country) ? props.errors.country : ''}
|
||||
/>
|
||||
</div>
|
||||
<Button
|
||||
className="my-8 max-w-[25.875rem] btn-Y text-black w-full h-11"
|
||||
text="Next"
|
||||
type="submit"
|
||||
/>
|
||||
</div>
|
||||
</Form>
|
||||
)}
|
||||
</Formik>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
interface SelectOption {
|
||||
loading: boolean;
|
||||
data: {value: string;
|
||||
label: string}[]
|
||||
}
|
||||
|
||||
|
||||
const gender: SelectOption = {
|
||||
loading: false,
|
||||
data: [
|
||||
{ value: "", label: "Please Select" },
|
||||
{ value: "male", label: "Male" },
|
||||
{ value: "female", label: "Female" },
|
||||
{ value: "others", label: "Prefer not to say" },
|
||||
]
|
||||
}
|
||||
|
||||
const maritalStatus: SelectOption = {
|
||||
loading: false,
|
||||
data: [
|
||||
{ value: "", label: "Please Select" },
|
||||
{ value: "single", label: "Single" },
|
||||
{ value: "married", label: "Married" },
|
||||
{ value: "divorced", label: "Divorced" },
|
||||
]
|
||||
}
|
||||
|
||||
const country: SelectOption = {
|
||||
loading: false,
|
||||
data: [
|
||||
{ value: "", label: "Please Select" },
|
||||
{ value: "NG", label: "Nigeria" },
|
||||
]
|
||||
}
|
||||
@@ -0,0 +1,353 @@
|
||||
import {useState, useEffect} from 'react'
|
||||
import { Button, InputCompOne, Stepper } from '../../shared/index';
|
||||
|
||||
import {Formik, Form} from 'formik'
|
||||
import * as Yup from "yup";
|
||||
|
||||
import { getEmployersList } from '../../../core/apiRequest';
|
||||
|
||||
type Props = {
|
||||
handleNextStep:(value:{})=>any
|
||||
}
|
||||
|
||||
// type EmployerProps = {
|
||||
// loading?: boolean,
|
||||
// data?: Array<{[index:string]: string}> | {[index:string]: Array<{[index:string]: string}> }
|
||||
// }
|
||||
|
||||
const initialValues = {
|
||||
job_title: "",
|
||||
name: "",
|
||||
sector: "",
|
||||
industry: "",
|
||||
resumption_date: "",
|
||||
email:"",
|
||||
annual_income: "",
|
||||
monthly_salary: "",
|
||||
salary_payment_date: "",
|
||||
employment_id: "",
|
||||
highest_eductaion: "",
|
||||
employer_uid: "",
|
||||
isChecked: false
|
||||
};
|
||||
|
||||
// To get the validation schema
|
||||
const validationSchema = Yup.object().shape({
|
||||
isChecked: Yup.bool(), // use bool instead of boolean
|
||||
// .oneOf([true, false], "You must accept the terms and conditions"),
|
||||
job_title: Yup.string()
|
||||
.required("Required"),
|
||||
name: Yup.string().when('isChecked', {
|
||||
is: true,
|
||||
then: () => Yup.string().required('required'),
|
||||
otherwise: () => Yup.string(),
|
||||
}),
|
||||
sector: Yup.string().when('isChecked', {
|
||||
is: true,
|
||||
then: () => Yup.string().required('required'),
|
||||
}),
|
||||
industry: Yup.string().when('isChecked', {
|
||||
is: true,
|
||||
then: () => Yup.string().required('required'),
|
||||
}),
|
||||
resumption_date: Yup.string()
|
||||
.required("Required"),
|
||||
email: Yup.string().when('isChecked', {
|
||||
is: true,
|
||||
then: () => Yup.string().required('required'),
|
||||
})
|
||||
.email("Invalid"),
|
||||
annual_income: Yup.string()
|
||||
.required("Required")
|
||||
.test("no-e", "Invalid", (value:any) => {
|
||||
if (value && /^[0-9]*$/.test(value) == false) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}),
|
||||
monthly_salary: Yup.string()
|
||||
.required("Required")
|
||||
.test("no-e", "Invalid", (value:any) => {
|
||||
if (value && /^[0-9]*$/.test(value) == false) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}),
|
||||
salary_payment_date: Yup.string()
|
||||
.required("Required"),
|
||||
employment_id: Yup.string()
|
||||
.required("Required"),
|
||||
highest_eductaion: Yup.string()
|
||||
.required("Required"),
|
||||
employer_uid: Yup.string().when('isChecked', {
|
||||
is: false,
|
||||
then: () => Yup.string().required('required'),
|
||||
}),
|
||||
});
|
||||
|
||||
export default function DashboardHomeEmploymentInfo({handleNextStep}:Props) {
|
||||
|
||||
const [employersList, setEmployersList] = useState<any>({
|
||||
loading: true,
|
||||
data: []
|
||||
})
|
||||
|
||||
|
||||
//FUNCTION TO HANDLE SUBMIT
|
||||
const handleSubmit = (values:any) => {
|
||||
// Remember to changed the checked value's name
|
||||
if(values.employer_uid){
|
||||
let employer_uid = values.employer_uid
|
||||
delete values.employer_uid
|
||||
handleNextStep({employer_uid, employment: values})
|
||||
}else{
|
||||
handleNextStep({employment: values})
|
||||
}
|
||||
};
|
||||
|
||||
useEffect(()=>{
|
||||
getEmployersList().then(res => {
|
||||
setEmployersList({loading:false, data:res?.data})
|
||||
}).catch(err => {
|
||||
console.log(err)
|
||||
setEmployersList({loading:false, data:[]})
|
||||
})
|
||||
},[])
|
||||
|
||||
|
||||
return (
|
||||
<div className="w-full">
|
||||
<div className="w-full flex justify-center">
|
||||
<Stepper step={2} />
|
||||
</div>
|
||||
<Formik
|
||||
initialValues={initialValues}
|
||||
validationSchema={validationSchema}
|
||||
onSubmit={handleSubmit}
|
||||
>
|
||||
{(props)=>(
|
||||
<Form>
|
||||
<div className="mt-[3.25rem] flex flex-col gap-9">
|
||||
<p className='text-red-500 text-lg md:text-2xl'>Employment Informaton</p>
|
||||
<div className="flex flex-col lg:flex-row items-start gap-[2rem]">
|
||||
<div className='w-full lg:max-w-[30rem] flex flex-col'>
|
||||
<div className='w-full gap-[2rem]'>
|
||||
<InputCompOne
|
||||
parentClass="w-full"
|
||||
name="employer_uid"
|
||||
floatLabel="Employer Name"
|
||||
// labelClass="font-bold text-[1.125rem]"
|
||||
select={true}
|
||||
selectClass="w-full h-[3.625rem] bg-[#EFEFEF] px-4 rounded-[.375rem]"
|
||||
selectOptions={{loading:employersList?.loading, data: employersList?.data?.records}}
|
||||
selectValue={props.values.employer_uid}
|
||||
onChange={props.handleChange}
|
||||
error={(props.errors.employer_uid && props.touched.employer_uid) ? props.errors.employer_uid : ''}
|
||||
disabled={props.values.isChecked}
|
||||
/>
|
||||
<div className='flex gap-4 items-start my-2'>
|
||||
<input
|
||||
type='checkbox'
|
||||
name="isChecked"
|
||||
className='w-4 h-4 p-2 accent-purple-600 text-purple-600 bg-gray-100 border-gray-300 rounded focus:ring-purple-500'
|
||||
onChange={props.handleChange}
|
||||
checked={props.values.isChecked}
|
||||
/>
|
||||
<p className='text-[12px] text-justify'>Check here if employer is not on the list</p>
|
||||
</div>
|
||||
<div className={`hidden p-4 ${props.values.isChecked && 'hidden'}`}>
|
||||
Name: {'Name'}
|
||||
</div>
|
||||
</div>
|
||||
<div className={`w-full flex flex-col gap-[2rem] ${!props.values.isChecked && 'hidden'}`}>
|
||||
<InputCompOne
|
||||
parentClass="w-full"
|
||||
name="name"
|
||||
floatLabel="Employer name"
|
||||
// labelClass="font-bold text-[1.125rem]"
|
||||
input
|
||||
inputClass="w-full h-[3.625rem] bg-[#EFEFEF] px-4 rounded-[.375rem]"
|
||||
placeholder="Mr. Mark John"
|
||||
value={props.values.name}
|
||||
onChange={props.handleChange}
|
||||
error={(props.errors.name && props.touched.name) ? props.errors.name : ''}
|
||||
/>
|
||||
<InputCompOne
|
||||
parentClass="w-full"
|
||||
name="email"
|
||||
floatLabel="Employers official email"
|
||||
// labelClass="font-bold text-[1.125rem]"
|
||||
input
|
||||
inputClass="w-full h-[3.625rem] bg-[#EFEFEF] px-4 rounded-[.375rem]"
|
||||
placeholder="example@gmail.com"
|
||||
value={props.values.email}
|
||||
onChange={props.handleChange}
|
||||
error={(props.errors.email && props.touched.email) ? props.errors.email : ''}
|
||||
/>
|
||||
<InputCompOne
|
||||
parentClass="w-full"
|
||||
name="industry"
|
||||
floatLabel="Select your industry"
|
||||
// labelClass="font-bold text-[1.125rem]"
|
||||
select={true}
|
||||
selectClass="w-full h-[3.625rem] bg-[#EFEFEF] px-4 rounded-[.375rem]"
|
||||
selectOptions={industry}
|
||||
selectValue={props.values.industry}
|
||||
onChange={props.handleChange}
|
||||
error={(props.errors.industry && props.touched.industry) ? props.errors.industry : ''}
|
||||
/>
|
||||
<InputCompOne
|
||||
parentClass="w-full"
|
||||
name="sector"
|
||||
floatLabel="Job Sector"
|
||||
// labelClass="font-bold text-[1.125rem]"
|
||||
select={true}
|
||||
selectClass="w-full h-[3.625rem] bg-[#EFEFEF] px-4 rounded-[.375rem]"
|
||||
selectOptions={jobSector}
|
||||
selectValue={props.values.sector}
|
||||
onChange={props.handleChange}
|
||||
error={(props.errors.sector && props.touched.sector) ? props.errors.sector : ''}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className='w-full lg:max-w-[30rem] flex flex-col gap-[2rem]'>
|
||||
<InputCompOne
|
||||
parentClass="w-full"
|
||||
name="job_title"
|
||||
floatLabel="Job Title"
|
||||
// labelClass="font-bold text-[1.125rem]"
|
||||
input
|
||||
inputClass="w-full h-[3.625rem] bg-[#EFEFEF] px-4 rounded-[.375rem]"
|
||||
placeholder="Software Engineer"
|
||||
value={props.values.job_title}
|
||||
onChange={props.handleChange}
|
||||
error={(props.errors.job_title && props.touched.job_title) ? props.errors.job_title : ''}
|
||||
/>
|
||||
<InputCompOne
|
||||
parentClass="w-full"
|
||||
name="highest_eductaion"
|
||||
floatLabel="Highest level of education"
|
||||
// labelClass="font-bold text-[1.125rem]"
|
||||
select={true}
|
||||
selectClass="w-full h-[3.625rem] bg-[#EFEFEF] px-4 rounded-[.375rem]"
|
||||
selectOptions={highestEductaion}
|
||||
selectValue={props.values.highest_eductaion}
|
||||
onChange={props.handleChange}
|
||||
error={(props.errors.highest_eductaion && props.touched.highest_eductaion) ? props.errors.highest_eductaion : ''}
|
||||
/>
|
||||
<div className="w-full flex flex-col sm:flex-row items-center gap-4">
|
||||
<InputCompOne
|
||||
parentClass="w-full"
|
||||
name="resumption_date"
|
||||
floatLabel="Date of resumption"
|
||||
// labelClass="font-bold text-[1.125rem]"
|
||||
input
|
||||
inputType='date'
|
||||
inputClass="w-full h-[3.625rem] bg-[#EFEFEF] px-4 rounded-[.375rem]"
|
||||
placeholder="12/12/2015"
|
||||
value={props.values.resumption_date}
|
||||
onChange={props.handleChange}
|
||||
error={(props.errors.resumption_date && props.touched.resumption_date) ? props.errors.resumption_date : ''}
|
||||
/>
|
||||
<InputCompOne
|
||||
parentClass="w-full"
|
||||
name="salary_payment_date"
|
||||
floatLabel="Salary payment date"
|
||||
// labelClass="font-bold text-[1.125rem]"
|
||||
input
|
||||
inputType='date'
|
||||
inputClass="w-full h-[3.625rem] bg-[#EFEFEF] px-4 rounded-[.375rem]"
|
||||
placeholder="30th of every month"
|
||||
value={props.values.salary_payment_date}
|
||||
onChange={props.handleChange}
|
||||
error={(props.errors.salary_payment_date && props.touched.salary_payment_date) ? props.errors.salary_payment_date : ''}
|
||||
/>
|
||||
</div>
|
||||
<div className="w-full flex flex-col sm:flex-row items-center gap-4">
|
||||
<InputCompOne
|
||||
parentClass="w-full"
|
||||
name="annual_income"
|
||||
floatLabel="Annual Income"
|
||||
// labelClass="font-bold text-[1.125rem]"
|
||||
input
|
||||
inputClass="w-full h-[3.625rem] bg-[#EFEFEF] px-4 rounded-[.375rem] text-right"
|
||||
placeholder="1,200,000"
|
||||
value={props.values.annual_income}
|
||||
onChange={props.handleChange}
|
||||
error={(props.errors.annual_income && props.touched.annual_income) ? props.errors.annual_income : ''}
|
||||
/>
|
||||
<InputCompOne
|
||||
parentClass="w-full"
|
||||
name="monthly_salary"
|
||||
floatLabel="Net monthly salary"
|
||||
// labelClass="font-bold text-[1.125rem]"
|
||||
input
|
||||
inputClass="w-full h-[3.625rem] bg-[#EFEFEF] px-4 rounded-[.375rem] text-right"
|
||||
placeholder="100,000"
|
||||
value={props.values.monthly_salary}
|
||||
onChange={props.handleChange}
|
||||
error={(props.errors.monthly_salary && props.touched.monthly_salary) ? props.errors.monthly_salary : ''}
|
||||
/>
|
||||
</div>
|
||||
<InputCompOne
|
||||
parentClass="w-full"
|
||||
name="employment_id"
|
||||
floatLabel="Employee ID"
|
||||
// labelClass="font-bold text-[1.125rem]"
|
||||
input
|
||||
inputClass="w-full h-[3.625rem] bg-[#EFEFEF] px-4 rounded-[.375rem]"
|
||||
placeholder="LS/001/005"
|
||||
value={props.values.employment_id}
|
||||
onChange={props.handleChange}
|
||||
error={(props.errors.employment_id && props.touched.employment_id) ? props.errors.employment_id : ''}
|
||||
/>
|
||||
<div className="w-full">
|
||||
<Button
|
||||
className="my-4 btn-Y text-black w-full h-11"
|
||||
text="Next"
|
||||
type="submit"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</Form>
|
||||
)}
|
||||
</Formik>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
|
||||
interface SelectOption {
|
||||
loading: boolean;
|
||||
data: {value: string;
|
||||
label: string}[]
|
||||
}
|
||||
|
||||
|
||||
const jobSector: SelectOption = {
|
||||
loading: false,
|
||||
data: [
|
||||
{ value: "", label: "Please Select" },
|
||||
{ value: "private (non academic)", label: "Private (non academic)" },
|
||||
]
|
||||
}
|
||||
|
||||
const industry: SelectOption = {
|
||||
loading: false,
|
||||
data: [
|
||||
{ value: "", label: "Please Select" },
|
||||
{ value: "engineering", label: "Engineering" },
|
||||
]
|
||||
}
|
||||
|
||||
const highestEductaion: SelectOption = {
|
||||
loading: false,
|
||||
data: [
|
||||
{ value: "", label: "Please Select" },
|
||||
{ value: "b.sc + professional qualification", label: "B.Sc + Professional Qualification" },
|
||||
]
|
||||
}
|
||||
@@ -0,0 +1,248 @@
|
||||
import { Button, InputCompOne, Stepper } from '../../shared/index';
|
||||
|
||||
import {Formik, Form} from 'formik'
|
||||
import * as Yup from "yup";
|
||||
|
||||
type Props = {
|
||||
handleNextStep:(value:{})=>any
|
||||
}
|
||||
|
||||
const initialValues = {
|
||||
ref_name: "",
|
||||
ref_email: "",
|
||||
ref_phone_number: "",
|
||||
ref_relationship: "",
|
||||
ref_bvn: "",
|
||||
ref_two_name: "",
|
||||
ref_two_email: "",
|
||||
ref_two_phone_number: "",
|
||||
ref_two_relationship: "",
|
||||
ref_two_bvn: "",
|
||||
};
|
||||
|
||||
// To get the validation schema
|
||||
const validationSchema = Yup.object().shape({
|
||||
ref_name: Yup.string()
|
||||
.required("Required"),
|
||||
ref_email: Yup.string()
|
||||
.email("Invalid")
|
||||
.required("Required"),
|
||||
ref_phone_number: Yup.string()
|
||||
.required("Required"),
|
||||
ref_relationship: Yup.string()
|
||||
.required("Required"),
|
||||
ref_bvn: Yup.string()
|
||||
.required("BVN is required")
|
||||
.test("no-e", "Invalid number", (value:any) => {
|
||||
if (value && /^[0-9]*$/.test(value) == false) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
})
|
||||
.min(11, "must be 11 digits")
|
||||
.max(11, "must be 11 digits"),
|
||||
ref_two_name: Yup.string()
|
||||
.notOneOf([Yup.ref('ref_name')], "Name cannot be the same")
|
||||
.required("Required"),
|
||||
ref_two_email: Yup.string()
|
||||
.email("Invalid")
|
||||
.notOneOf([Yup.ref('ref_email')], "Email cannot be the same")
|
||||
.required("Required"),
|
||||
ref_two_phone_number: Yup.string()
|
||||
.notOneOf([Yup.ref('ref_phone_number')], "Phone number cannot be the same")
|
||||
.required("Required"),
|
||||
ref_two_relationship: Yup.string()
|
||||
.required("Required"),
|
||||
ref_two_bvn: Yup.string()
|
||||
.notOneOf([Yup.ref('ref_bvn')], "BVN number cannot be the same")
|
||||
.required("Required")
|
||||
.test("no-e", "Invalid number", (value:any) => {
|
||||
if (value && /^[0-9]*$/.test(value) == false) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
})
|
||||
.min(11, "must be 11 digits")
|
||||
.max(11, "must be 11 digits"),
|
||||
});
|
||||
|
||||
export default function DashboardHomeRefereeInfo({handleNextStep}:Props) {
|
||||
|
||||
//FUNCTION TO HANDLE SUBMIT
|
||||
const handleSubmit = (values:any) => {
|
||||
let refOne = {
|
||||
fullname: values.ref_name,
|
||||
relationship: values.ref_relationship,
|
||||
phone_number: values.ref_phone_number,
|
||||
email: values.ref_email,
|
||||
bvn: values.ref_bvn
|
||||
}
|
||||
let refTwo = {
|
||||
fullname: values.ref_two_name,
|
||||
relationship: values.ref_two_relationship,
|
||||
phone_number: values.ref_two_phone_number,
|
||||
email: values.ref_two_email,
|
||||
bvn: values.ref_two_bvn
|
||||
}
|
||||
handleNextStep({loan_reference:[refOne, refTwo]})
|
||||
};
|
||||
|
||||
|
||||
return (
|
||||
<div className="w-full">
|
||||
<div className="w-full flex justify-center">
|
||||
<Stepper step={3} />
|
||||
</div>
|
||||
<Formik
|
||||
initialValues={initialValues}
|
||||
validationSchema={validationSchema}
|
||||
onSubmit={handleSubmit}
|
||||
>
|
||||
{(props)=>(
|
||||
<Form>
|
||||
<div className="mt-[3.25rem] flex flex-col gap-9">
|
||||
<p className='text-red-500 text-lg md:text-2xl'>Reference Details <span className='text-base'>(Must be 18 years and above)</span></p>
|
||||
<div className="flex items-center gap-[4.125rem]">
|
||||
<div className='w-full max-w-[25.875rem]'>
|
||||
<p className='text-red-500 text-base'>Reference one</p>
|
||||
<div className='w-full flex flex-col gap-9'>
|
||||
<InputCompOne
|
||||
parentClass="max-w-[25.875rem] w-full flex flex-col gap-4"
|
||||
name="ref_name"
|
||||
floatLabel="Full name"
|
||||
// labelClass="font-bold text-[1.125rem]"
|
||||
input
|
||||
inputClass="w-full h-[3.625rem] bg-[#EFEFEF] px-4 rounded-[.375rem]"
|
||||
placeholder="John James"
|
||||
value={props.values.ref_name}
|
||||
onChange={props.handleChange}
|
||||
error={(props.errors.ref_name && props.touched.ref_name) ? props.errors.ref_name : ''}
|
||||
/>
|
||||
<InputCompOne
|
||||
parentClass="max-w-[25.875rem] w-full flex flex-col gap-4"
|
||||
name="ref_relationship"
|
||||
floatLabel="Relationship"
|
||||
// labelClass="font-bold text-[1.125rem]"
|
||||
input
|
||||
inputClass="w-full h-[3.625rem] bg-[#EFEFEF] px-4 rounded-[.375rem]"
|
||||
placeholder="Sister"
|
||||
value={props.values.ref_relationship}
|
||||
onChange={props.handleChange}
|
||||
error={(props.errors.ref_relationship && props.touched.ref_relationship) ? props.errors.ref_relationship : ''}
|
||||
/>
|
||||
<InputCompOne
|
||||
parentClass="max-w-[25.875rem] w-full flex flex-col gap-4"
|
||||
name="ref_phone_number"
|
||||
floatLabel="Phone number"
|
||||
// labelClass="font-bold text-[1.125rem]"
|
||||
input
|
||||
inputClass="w-full h-[3.625rem] bg-[#EFEFEF] px-4 rounded-[.375rem]"
|
||||
placeholder="07000000000"
|
||||
value={props.values.ref_phone_number}
|
||||
onChange={props.handleChange}
|
||||
error={(props.errors.ref_phone_number && props.touched.ref_phone_number) ? props.errors.ref_phone_number : ''}
|
||||
/>
|
||||
<InputCompOne
|
||||
parentClass="max-w-[25.875rem] w-full flex flex-col gap-4"
|
||||
name="ref_email"
|
||||
floatLabel="Email address"
|
||||
// labelClass="font-bold text-[1.125rem]"
|
||||
input
|
||||
inputClass="w-full h-[3.625rem] bg-[#EFEFEF] px-4 rounded-[.375rem]"
|
||||
placeholder="demo@gamil.com"
|
||||
value={props.values.ref_email}
|
||||
onChange={props.handleChange}
|
||||
error={(props.errors.ref_email && props.touched.ref_email) ? props.errors.ref_email : ''}
|
||||
/>
|
||||
<InputCompOne
|
||||
parentClass="max-w-[25.875rem] w-full flex flex-col gap-4"
|
||||
name="ref_bvn"
|
||||
floatLabel="BVN"
|
||||
// labelClass="font-bold text-[1.125rem]"
|
||||
input
|
||||
inputClass="w-full h-[3.625rem] bg-[#EFEFEF] px-4 rounded-[.375rem]"
|
||||
placeholder="2228457896"
|
||||
value={props.values.ref_bvn}
|
||||
onChange={props.handleChange}
|
||||
error={(props.errors.ref_bvn && props.touched.ref_bvn) ? props.errors.ref_bvn : ''}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<div className='w-full max-w-[25.875rem]'>
|
||||
<p className='text-red-500 text-base'>Reference two</p>
|
||||
<div className='w-full flex flex-col gap-9'>
|
||||
<InputCompOne
|
||||
parentClass="max-w-[25.875rem] w-full flex flex-col gap-4"
|
||||
name="ref_two_name"
|
||||
floatLabel="Full name"
|
||||
// labelClass="font-bold text-[1.125rem]"
|
||||
input
|
||||
inputClass="w-full h-[3.625rem] bg-[#EFEFEF] px-4 rounded-[.375rem]"
|
||||
placeholder="John James"
|
||||
value={props.values.ref_two_name}
|
||||
onChange={props.handleChange}
|
||||
error={(props.errors.ref_two_name && props.touched.ref_two_name) ? props.errors.ref_two_name : ''}
|
||||
/>
|
||||
<InputCompOne
|
||||
parentClass="max-w-[25.875rem] w-full flex flex-col gap-4"
|
||||
name="ref_two_relationship"
|
||||
floatLabel="Relationship"
|
||||
// labelClass="font-bold text-[1.125rem]"
|
||||
input
|
||||
inputClass="w-full h-[3.625rem] bg-[#EFEFEF] px-4 rounded-[.375rem]"
|
||||
placeholder="Sister"
|
||||
value={props.values.ref_two_relationship}
|
||||
onChange={props.handleChange}
|
||||
error={(props.errors.ref_two_relationship && props.touched.ref_two_relationship) ? props.errors.ref_two_relationship : ''}
|
||||
/>
|
||||
<InputCompOne
|
||||
parentClass="max-w-[25.875rem] w-full flex flex-col gap-4"
|
||||
name="ref_two_phone_number"
|
||||
floatLabel="Phone number"
|
||||
// labelClass="font-bold text-[1.125rem]"
|
||||
input
|
||||
inputClass="w-full h-[3.625rem] bg-[#EFEFEF] px-4 rounded-[.375rem]"
|
||||
placeholder="07000000000"
|
||||
value={props.values.ref_two_phone_number}
|
||||
onChange={props.handleChange}
|
||||
error={(props.errors.ref_two_phone_number && props.touched.ref_two_phone_number) ? props.errors.ref_two_phone_number : ''}
|
||||
/>
|
||||
<InputCompOne
|
||||
parentClass="max-w-[25.875rem] w-full flex flex-col gap-4"
|
||||
name="ref_two_email"
|
||||
floatLabel="Email address"
|
||||
// labelClass="font-bold text-[1.125rem]"
|
||||
input
|
||||
inputClass="w-full h-[3.625rem] bg-[#EFEFEF] px-4 rounded-[.375rem]"
|
||||
placeholder="demo@gamil.com"
|
||||
value={props.values.ref_two_email}
|
||||
onChange={props.handleChange}
|
||||
error={(props.errors.ref_two_email && props.touched.ref_two_email) ? props.errors.ref_two_email : ''}
|
||||
/>
|
||||
<InputCompOne
|
||||
parentClass="max-w-[25.875rem] w-full flex flex-col gap-4"
|
||||
name="ref_two_bvn"
|
||||
floatLabel="BVN"
|
||||
// labelClass="font-bold text-[1.125rem]"
|
||||
input
|
||||
inputClass="w-full h-[3.625rem] bg-[#EFEFEF] px-4 rounded-[.375rem]"
|
||||
placeholder="2228457896"
|
||||
value={props.values.ref_two_bvn}
|
||||
onChange={props.handleChange}
|
||||
error={(props.errors.ref_two_bvn && props.touched.ref_two_bvn) ? props.errors.ref_two_bvn : ''}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<Button
|
||||
className="my-8 max-w-[25.875rem] btn-Y text-black w-full h-11"
|
||||
text="Next"
|
||||
type="submit"
|
||||
/>
|
||||
</div>
|
||||
</Form>
|
||||
)}
|
||||
</Formik>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,4 @@
|
||||
import DashboardHome from './DashboardHome'
|
||||
import DashboardProfile from './DashboardProfile';
|
||||
|
||||
export { DashboardHome, DashboardProfile };
|
||||
@@ -0,0 +1,176 @@
|
||||
import {useEffect, useState} from 'react'
|
||||
import {useLocation, useNavigate} from 'react-router-dom'
|
||||
import { Button, InputCompOne } from "../../shared";
|
||||
import {Formik, Form} from 'formik'
|
||||
import * as Yup from "yup";
|
||||
import { RouteHandler } from '../../../router/routes';
|
||||
import { getLoanDetail } from '../../../core/apiRequest';
|
||||
import CustomSpinner from '../../CustomSpinner';
|
||||
|
||||
interface LoanDetail {
|
||||
loan_amount: string;
|
||||
payment_month: string;
|
||||
sales_agent: string;
|
||||
[key: string]: any; // to accommodate any additional properties
|
||||
}
|
||||
|
||||
// interface InitialValues {
|
||||
// loan_amount: string;
|
||||
// payment_month: string;
|
||||
// sales_agent: string;
|
||||
// }
|
||||
|
||||
const initialValues = {
|
||||
loan_amount: "",
|
||||
payment_month: "",
|
||||
sales_agent: "",
|
||||
};
|
||||
|
||||
// To get the validation schema
|
||||
const validationSchema = Yup.object().shape({
|
||||
payment_month: Yup.string()
|
||||
.required("Required"),
|
||||
loan_amount: Yup.string()
|
||||
.required("Required")
|
||||
.test("no-e", "Invalid", (value:any) => {
|
||||
if (value && /^[0-9]*$/.test(value) == false) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}),
|
||||
sales_agent: Yup.string()
|
||||
});
|
||||
|
||||
type LocationState = {
|
||||
application_uid: string
|
||||
}
|
||||
|
||||
export default function ReferenceDetails() {
|
||||
|
||||
const location = useLocation()
|
||||
const navigate = useNavigate()
|
||||
|
||||
// const applicationUID = location?.state?.application_uid
|
||||
const stateExist = location?.state as LocationState
|
||||
|
||||
//FUNCTION TO HANDLE SUBMIT
|
||||
const handleSubmit = (values:{}) => {
|
||||
// handleNextStep(values)
|
||||
console.log(values)
|
||||
};
|
||||
|
||||
const [loanDetail, setLoanDetail] = useState<{loading:boolean, data:LoanDetail}>({
|
||||
loading: true,
|
||||
data: {
|
||||
loan_amount: '',
|
||||
payment_month: '',
|
||||
sales_agent: '',
|
||||
},
|
||||
})
|
||||
useEffect(()=>{
|
||||
if(!stateExist){
|
||||
navigate(RouteHandler.dashboardHome)
|
||||
return
|
||||
}
|
||||
getLoanDetail({application_uid:stateExist.application_uid}).then(res => {
|
||||
setLoanDetail({loading:false, data:res?.data?.loan})
|
||||
}).catch(err => {
|
||||
console.log(err)
|
||||
setLoanDetail((prev:any) => ({...prev, loading:false}))
|
||||
})
|
||||
},[])
|
||||
|
||||
// const formInitialValue:LoanDetail = (loanDetail.loading) ? initialValues : loanDetail?.data
|
||||
const formInitialValue = (loanDetail.loading || Object.keys(loanDetail?.data)?.length < 1) ? initialValues : {...initialValues, ...loanDetail?.data}
|
||||
|
||||
return (
|
||||
<>
|
||||
{loanDetail.loading ?
|
||||
<div className='flex flex-col justify-center items-center h-96'>
|
||||
<CustomSpinner width='w-8' height='h-8' />
|
||||
</div>
|
||||
:
|
||||
<div className="w-full">
|
||||
<Formik
|
||||
initialValues={formInitialValue}
|
||||
validationSchema={validationSchema}
|
||||
onSubmit={handleSubmit}
|
||||
>
|
||||
{(props)=>(
|
||||
<Form>
|
||||
<div className="mt-[3.25rem] flex flex-col gap-9">
|
||||
<p className='text-red-500 text-lg md:text-2xl'>Loan Details</p>
|
||||
<div className='w-full flex flex-wrap items-center gap-4'>
|
||||
<InputCompOne
|
||||
parentClass="w-full sm:max-w-[10rem] flex flex-col gap-4"
|
||||
name="loan_amount"
|
||||
label="Amount"
|
||||
labelClass="font-bold text-[1.125rem]"
|
||||
input
|
||||
inputClass="w-full h-[3.625rem] bg-[#EFEFEF] px-4 rounded-[.375rem] text-right"
|
||||
placeholder="350,000"
|
||||
value={props.values.loan_amount}
|
||||
disabled={true}
|
||||
onChange={props.handleChange}
|
||||
// error={(props.errors.loan_amount && props.touched.loan_amount) ? props.errors.loan_amount : ''}
|
||||
/>
|
||||
<InputCompOne
|
||||
parentClass="w-full sm:max-w-[10rem] flex flex-col gap-4"
|
||||
name="payment_month"
|
||||
label="Months?"
|
||||
labelClass="font-bold text-[1.125rem]"
|
||||
select={true}
|
||||
disabled={true}
|
||||
selectClass="w-full h-[3.625rem] bg-[#EFEFEF] px-4 rounded-[.375rem]"
|
||||
selectOptions={props.values.payment_month}
|
||||
selectValue={props.values.payment_month}
|
||||
onChange={props.handleChange}
|
||||
// error={(props.errors.payment_month && props.touched.payment_month) ? props.errors.payment_month : ''}
|
||||
/>
|
||||
<InputCompOne
|
||||
parentClass="w-full sm:max-w-[10rem] flex flex-col gap-4"
|
||||
name="sales_agent"
|
||||
label="agent ID"
|
||||
labelClass="font-bold text-[1.125rem]"
|
||||
floatLabel='Enter agent ID'
|
||||
input
|
||||
disabled={true}
|
||||
inputClass="w-full h-[3.625rem] bg-[#EFEFEF] px-4 rounded-[.375rem]"
|
||||
placeholder="Agent ID"
|
||||
value={props.values.sales_agent}
|
||||
onChange={props.handleChange}
|
||||
// error={(props.errors.sales_agent && props.touched.sales_agent) ? props.errors.sales_agent : ''}
|
||||
/>
|
||||
</div>
|
||||
<Button
|
||||
className="hidden my-8 max-w-[25.875rem] btn-Y text-black w-full h-11"
|
||||
text="Next"
|
||||
type="submit"
|
||||
/>
|
||||
</div>
|
||||
</Form>
|
||||
)}
|
||||
</Formik>
|
||||
</div>
|
||||
}
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
// interface SelectOption {
|
||||
// loading: boolean;
|
||||
// data: {value: string;
|
||||
// label: string}[]
|
||||
// }
|
||||
|
||||
|
||||
// const paymentMonth: SelectOption = {
|
||||
// loading: false,
|
||||
// data: [
|
||||
// { value: "", label: "Please Select" },
|
||||
// { value: "6", label: "6 Months" },
|
||||
// { value: "12", label: "12 Months" },
|
||||
// { value: "18", label: "18 Months" },
|
||||
// { value: "24", label: "24 Months" },
|
||||
// ]
|
||||
// }
|
||||
@@ -1,52 +0,0 @@
|
||||
import { Link, useLocation, useNavigate } from "react-router-dom";
|
||||
|
||||
import { Icons } from "../index";
|
||||
|
||||
export default function Aside() {
|
||||
|
||||
const {pathname} = useLocation()
|
||||
|
||||
const navigate = useNavigate()
|
||||
|
||||
return (
|
||||
<div className="py-5 px-10 flex flex-col h-full">
|
||||
<div className="flex justify-center items-center text-sm">
|
||||
<p className="w-14 h-14 rounded-full bg-[#5C2684]/50 flex items-center justify-center">
|
||||
AC
|
||||
</p>
|
||||
</div>
|
||||
<div className="mt-10 h-full overflow-y-auto">
|
||||
{asideLinks.map((link, index) => (
|
||||
<Link
|
||||
key={index}
|
||||
to={link.link}
|
||||
className={`w-full my-4 flex items-center gap-2 py-2 pl-5 rounded-lg text-base font-medium border-2 ${pathname == link.link ? 'border-[#5C2684] text-[#5C2684]' : 'border-transparent text-[#585858]'}`}
|
||||
>
|
||||
<Icons name={link.icon} fillColor={`${pathname == link.link ? '#5C2684' : '#585858'}`} />
|
||||
{link.name}
|
||||
</Link>
|
||||
))}
|
||||
</div>
|
||||
<div className="w-full flex justify-center items-center">
|
||||
<button className="py-3 px-6 bg-red-100 text-red-500 font-medium" onClick={()=>navigate('/login', {replace:true})}>
|
||||
Log out
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
type AsideLinksType = {
|
||||
name: string,
|
||||
link: string,
|
||||
icon: string,
|
||||
nestedlink?:boolean
|
||||
}[]
|
||||
const asideLinks:AsideLinksType = [
|
||||
{name: 'Home', link: '/dashboard/home', icon: 'home'},
|
||||
{name: 'My Profile', link: '/dashboard/profile', icon: 'profile'},
|
||||
{name: 'Verification', link: '/dashboard/verification', icon: 'verification'},
|
||||
{name: 'Payments', link: '/dashboard/payments', icon: 'payments'},
|
||||
{name: 'Legals', link: '/dashboard/legals', icon: 'legals'},
|
||||
]
|
||||
@@ -1,10 +0,0 @@
|
||||
import DashboardLayout from "./DashboardLayout";
|
||||
import { Outlet } from "react-router-dom";
|
||||
|
||||
export default function DashboardAuth() {
|
||||
return (
|
||||
<DashboardLayout>
|
||||
<Outlet />
|
||||
</DashboardLayout>
|
||||
)
|
||||
}
|
||||
@@ -1,92 +0,0 @@
|
||||
import { ReactNode, useState, useEffect } from 'react'
|
||||
|
||||
import Aside from './Aside'
|
||||
|
||||
export default function DashboardLayout({children}:{children: ReactNode}) {
|
||||
|
||||
const [showAside, setShowAside] = useState<boolean>(false)
|
||||
|
||||
const AsideDisplay = () => {
|
||||
setShowAside(prev => !prev)
|
||||
}
|
||||
|
||||
useEffect(() => {
|
||||
const handleResize = () => {
|
||||
return setShowAside(false)
|
||||
// setWidthSize(window.innerWidth)
|
||||
}
|
||||
window.addEventListener('resize', handleResize)
|
||||
|
||||
// return () => {
|
||||
// window.removeEventListener('resize', screenResized)
|
||||
// }
|
||||
}, [])
|
||||
|
||||
|
||||
return (
|
||||
<div className='w-full max-w-[2000px] mx-auto h-screen flex bg-[#020202] text-black'>
|
||||
<aside className='w-[300px] bg-white hidden md:block border-r-2 border-[#E6E6E6]'>
|
||||
<Aside />
|
||||
</aside>
|
||||
|
||||
<aside className={`w-[300px] md:hidden bg-white border-r-2 border-[#E6E6E6] fixed top-0 bottom-0 z-50 transition-all duration-500 ${showAside ? 'left-0' : '-left-[200%]'}`}>
|
||||
<Aside />
|
||||
</aside>
|
||||
|
||||
<main className={`dash-bg-image bg-[#F9F9F9] relative w-full overflow-y-auto overflow-x-hidden`}>
|
||||
<header className={`p-5 sticky z-10 top-0 w-full bg-[#F9F9F9] border-b-2 border-[#E6E6E6] bg-[url('../../../src/assets/images/dashboard/Ellipse1.png')] bg-no-repeat bg-[top_right]`}>
|
||||
<div className='h-14 w-full flex justify-end items-center gap-5'>
|
||||
{/* <div className=''>
|
||||
<button className='px-4 py-2 rounded-lg shadow-lg bg-white/50'>DarkMode</button>
|
||||
</div> */}
|
||||
{/* MENU HAND BURGER */}
|
||||
<div className='w-full'>Welcome Austin Catherine</div>
|
||||
<div
|
||||
className="relative md:hidden w-5 h-[20px] flex flex-col items-center justify-between"
|
||||
onClick={AsideDisplay}
|
||||
>
|
||||
<div
|
||||
className={`absolute left-0 w-5 h-1 bg-black dark:bg-white transition-all duration-500 ${
|
||||
showAside ? "top-1/2 -translate-y-1/2 rotate-45" : "top-0"
|
||||
}`}
|
||||
></div>
|
||||
<div
|
||||
className={`absolute left-0 w-5 h-1 bg-black dark:bg-white transition-all duration-300 ${
|
||||
showAside
|
||||
? "top-1/2 -translate-y-1/2 rotate-[2000deg] opacity-0"
|
||||
: "top-1/2 -translate-y-1/2"
|
||||
}`}
|
||||
></div>
|
||||
<div
|
||||
className={`absolute left-0 w-5 h-1 bg-black dark:bg-white transition-all duration-500 ${
|
||||
showAside
|
||||
? "top-1/2 -translate-y-1/2 -rotate-45"
|
||||
: "bottom-0"
|
||||
}`}
|
||||
></div>
|
||||
</div>
|
||||
</div>
|
||||
</header>
|
||||
<div className='flex p-5 relative'>
|
||||
<div className='w-full p-5'>
|
||||
{children}
|
||||
</div>
|
||||
|
||||
{/* <div className={`pl-5 w-[300px] min-w-[300px] hidden lg:block`}>
|
||||
<div className='sticky top-16'>
|
||||
<div className='w-full min-h-72 bg-white/50 shadow-lg rounded-lg p-5'>
|
||||
<p className='text-base font-semibold tracking-wide pb-2' title='Answered question is marked green'>Answered Question</p>
|
||||
<div className='grid grid-cols-8 gap-2'>
|
||||
{Array.from({length: 50}, (_, i) => i + 1).map((item, index) => (
|
||||
<span key={index} className={`w-6 h-6 text-sm rounded-full flex justify-center items-center shadow-lg cursor-pointer ${index%2 == 0 ? 'bg-emerald-600' : ''}`}>{item}</span>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div> */}
|
||||
</div>
|
||||
</main>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
@@ -0,0 +1,69 @@
|
||||
import { footerCustomerLinks, footerSocialLinks } from "../../utils/data";
|
||||
|
||||
interface FooterLinksProps {
|
||||
href: string;
|
||||
icon?: string;
|
||||
text?: string;
|
||||
}
|
||||
|
||||
const BottomFooterOne = () => {
|
||||
const date: number = new Date().getFullYear();
|
||||
|
||||
return (
|
||||
<footer className="pt-[1.25rem] pb-[1.875rem]">
|
||||
<div className="containerMode flex flex-col gap-2 w-full">
|
||||
<div className="flex flex-wrap flex-[100] justify-between w-full gap-2">
|
||||
<SocialIconButtons />
|
||||
<CustomerLinks />
|
||||
</div>
|
||||
<p className="text-[.8125rem] text-[#333] leading-[1.42857]">
|
||||
© <span>{date}</span> {import.meta.env.VITE_BANK_NAME} (Licensed by the
|
||||
Central Bank of Nigeria)
|
||||
</p>
|
||||
</div>
|
||||
</footer>
|
||||
);
|
||||
};
|
||||
|
||||
export default BottomFooterOne;
|
||||
|
||||
const SocialIconButtons = () => {
|
||||
const icons = footerSocialLinks.map(
|
||||
({ href, icon }: FooterLinksProps, idx: number) => (
|
||||
<li key={idx}>
|
||||
<a
|
||||
href={href}
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
className="bg-[#592B81] py-[.3125rem] px-[.625rem] text-white w-[2.625rem] h-[2.625rem] flex items-center justify-center rounded-[3.125rem]"
|
||||
>
|
||||
{icon && <img src={icon} alt="icon" />}
|
||||
</a>
|
||||
</li>
|
||||
)
|
||||
);
|
||||
|
||||
return <ul className="flex flex-[33.333] items-center gap-1">{icons}</ul>;
|
||||
};
|
||||
|
||||
const CustomerLinks = () => {
|
||||
const links = footerCustomerLinks.map(
|
||||
({ href, text }: FooterLinksProps, idx: number) => (
|
||||
<li key={idx} className="list-none">
|
||||
<a
|
||||
href={href}
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
className="py-[.75rem] text-[.8125rem] uppercase text-[#606161] flex items-center justify-center"
|
||||
>
|
||||
{text}
|
||||
</a>
|
||||
</li>
|
||||
)
|
||||
);
|
||||
return (
|
||||
<div className="flex-[66.667] flex items-center flex-nowrap md:flex-wrap gap-2">
|
||||
{links}
|
||||
</div>
|
||||
);
|
||||
};
|
||||
@@ -1,22 +1,33 @@
|
||||
import { Link } from "react-router-dom";
|
||||
// import { Link } from "react-router-dom";
|
||||
import { socialsIcons } from "../../utils/data";
|
||||
|
||||
export default function Footer() {
|
||||
const date = new Date().getFullYear();
|
||||
|
||||
return (
|
||||
<div className="w-full h-[5.4375rem] bg-[F7F7F7] flex items-center">
|
||||
<div className="containerMode flex justify-between items-center flex-wrap gap-2">
|
||||
<div className="containerMode flex justify-center md:justify-between items-center flex-wrap gap-2">
|
||||
<p className="text-[.9375rem] tracking-[2%] font-semibold text-[#969696]">
|
||||
{date} @ First City Monument Bank Limited
|
||||
{date} @ {import.meta.env.VITE_BANK_NAME} Limited
|
||||
</p>
|
||||
<div className="footer-social-icons flex justify-end items-center gap-2">
|
||||
{socialsIcons.map((icon, index) => (
|
||||
<Link key={index} className="w-[1.875rem] h-[1.875rem]" to="#">
|
||||
<img src={icon.image} alt={icon.name} />
|
||||
</Link>
|
||||
))}
|
||||
{renderSocialLinks()}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
function renderSocialLinks(): JSX.Element {
|
||||
const link = socialsIcons.map(function (item, index) {
|
||||
let { name, image, link } = item;
|
||||
|
||||
return (
|
||||
<a href={link} key={index} className="w-[1.875rem] h-[1.875rem]">
|
||||
<img src={image} alt={name} />
|
||||
</a>
|
||||
);
|
||||
});
|
||||
|
||||
return <ul className="flex items-center gap-2">{link}</ul>;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,15 @@
|
||||
import styles from "./footer.module.css"
|
||||
|
||||
const MidFooter = () => {
|
||||
return (
|
||||
<div className={`h-[2.3125rem] text-[1.25rem] ${styles.lower_footer}`}>
|
||||
<div className="containerMode flex justify-end p-[.375rem] w-full text-white font-medium text-[.6875rem] md:text-[1.25rem]">
|
||||
<div className="flex gap-2 items-center justify-end px-2 text-[11px] md:text-[13px]">
|
||||
<p className="text-[20px] font-extralight">my bank and I</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
export default MidFooter
|
||||
@@ -0,0 +1,35 @@
|
||||
import { footerItems } from "../../utils/data";
|
||||
import TopFooterOneMenu from "./TopFooterOneMenu";
|
||||
|
||||
export interface TopFooterOneMenuProps {
|
||||
category: string;
|
||||
subItems: {
|
||||
text: string;
|
||||
href?: string;
|
||||
}[];
|
||||
}
|
||||
|
||||
const TopFooterOne = () => {
|
||||
const footerListItems: TopFooterOneMenuProps[] = footerItems;
|
||||
|
||||
return (
|
||||
<footer className="bg-[#f7f7f7] text-[#898B8B] border border-[#ececec] p-5">
|
||||
<div className="containerMode w-full flex flex-col gap-[1.875rem]">
|
||||
<h4 className="uppercase text-[1.3125rem] font-bold my-[.625rem] cursor-default">
|
||||
sitemap
|
||||
</h4>
|
||||
<div className="grid grid-cols-2 md:grid-cols-6 gap-2 md:gap-0">
|
||||
{footerListItems.map(({ category, subItems }, index) => (
|
||||
<TopFooterOneMenu
|
||||
key={`${category}-${index}`}
|
||||
category={category}
|
||||
subItems={subItems}
|
||||
/>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
</footer>
|
||||
);
|
||||
};
|
||||
|
||||
export default TopFooterOne;
|
||||
@@ -0,0 +1,29 @@
|
||||
import React from "react";
|
||||
import { Link } from "react-router-dom";
|
||||
import { TopFooterOneMenuProps } from "./TopFooterOne";
|
||||
|
||||
|
||||
const TopFooterOneMenu: React.FC<TopFooterOneMenuProps> = ({
|
||||
category,
|
||||
subItems,
|
||||
}) => {
|
||||
return (
|
||||
<ul className="flex gap-2 flex-col">
|
||||
<li className="text-[.6875rem] font-bold text-[#5e2785] cursor-default">
|
||||
{category}
|
||||
</li>
|
||||
<ul className="flex flex-col gap-1">
|
||||
{subItems.map(({ href = "#", text }) => (
|
||||
<li
|
||||
key={text}
|
||||
className="text-[.6875rem] text-[#5e2785] hover:underline w-fit"
|
||||
>
|
||||
{href ? <Link to={href}>{text}</Link> : <span>{text}</span>}
|
||||
</li>
|
||||
))}
|
||||
</ul>
|
||||
</ul>
|
||||
);
|
||||
};
|
||||
|
||||
export default TopFooterOneMenu;
|
||||
@@ -0,0 +1,8 @@
|
||||
.lower_footer{
|
||||
background: url(../../assets/images/footer_back.jpg) no-repeat;
|
||||
background-size: cover;
|
||||
/* padding: 0.4rem 0; */
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
}
|
||||
@@ -1,3 +1,6 @@
|
||||
import Footer from "./Footer";
|
||||
import TopFooterOne from "./TopFooterOne";
|
||||
import MidFooter from "./MidFooter";
|
||||
import BottomFooterOne from "./BottomFooterOne";
|
||||
|
||||
export { Footer };
|
||||
export { Footer, TopFooterOne, MidFooter, BottomFooterOne };
|
||||
|
||||
@@ -0,0 +1,29 @@
|
||||
import React from "react";
|
||||
import DebitAccount from "./DebitAccount";
|
||||
|
||||
|
||||
const ApplicantsAttestation: React.FC = () => {
|
||||
return (
|
||||
<>
|
||||
<div className="flex justify-between items-center w-full mt-8 mb-[45px]">
|
||||
<h1 className="font-semibold text-[2.375rem] text-[#5C2684] my-[.5rem] max-w-[34rem]">
|
||||
Applicant’s Attestation and Debit Instruction
|
||||
</h1>
|
||||
<div className="flex flex-col gap-[.4375rem]">
|
||||
<p className="font-extrabold tracking-[3%] text-[#FBB700] underline">
|
||||
For more enquiries and support
|
||||
</p>
|
||||
<p className="font-extrabold tracking-[3%] text-[#5A5A5A]">
|
||||
Call: 09099000000
|
||||
</p>
|
||||
<p className="font-extrabold tracking-[3%] text-[#5A5A5A]">
|
||||
Email: fcmbloan@support.com
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
<DebitAccount />
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
export default ApplicantsAttestation;
|
||||
@@ -0,0 +1,89 @@
|
||||
import React from "react";
|
||||
import * as Yup from "yup";
|
||||
import { Form, Formik } from "formik";
|
||||
import { InputCompOne } from "../shared";
|
||||
|
||||
// To get the validation schema
|
||||
const validationSchema = Yup.object().shape({
|
||||
bvn: Yup.string()
|
||||
.required("BVN is required")
|
||||
.test("no-e", "Invalid number", (value:any) => {
|
||||
if (value && /^[0-9]*$/.test(value) == false) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
})
|
||||
.min(11, "must be 11 digits")
|
||||
.max(11, "must be 11 digits")
|
||||
});
|
||||
|
||||
// initial values for formik
|
||||
let initialValues = {
|
||||
bvn: ''
|
||||
};
|
||||
|
||||
type Props = {
|
||||
handleNextStep:()=>any
|
||||
}
|
||||
|
||||
const BVN = ({handleNextStep}:Props) => {
|
||||
|
||||
const firstInputRef = React.useRef<HTMLInputElement>(null);
|
||||
|
||||
const handleSubmit = (values:any) => {
|
||||
console.log('values', values)
|
||||
handleNextStep()
|
||||
};
|
||||
|
||||
return (
|
||||
<Formik
|
||||
initialValues={initialValues}
|
||||
validationSchema={validationSchema}
|
||||
onSubmit={handleSubmit}
|
||||
>
|
||||
{(props:any) => (
|
||||
<Form className="">
|
||||
<div className="w-full">
|
||||
<div className="containerMode flex justify-between gap-1 xl:gap-8 flex-col">
|
||||
<div className="my-[4rem] flex items-center justify-center w-full">
|
||||
<h1 className="font-bold text-[2.375rem] text-[#5C2684] my-[.5rem] text-center">
|
||||
Let’s Get You Started
|
||||
</h1>
|
||||
</div>
|
||||
<div className="mx-auto flex flex-col gap-8 max-w-[31.625rem]">
|
||||
<InputCompOne
|
||||
parentClass="flex flex-col gap-2"
|
||||
label="Enter Your BVN "
|
||||
name="bvn"
|
||||
parentInputClass="w-full"
|
||||
labelSpan="( To get your BVN, dial *565*0# )"
|
||||
labelSpanClass="text-[13px] text-[#5a5a5a] font-semibold"
|
||||
placeholder="Enter your BVN"
|
||||
labelClass="font-bold text-[18px] leading-[21.78px] tracking-[2%] text-[#282828] mb-[2px] flex item-center gap-[4px]"
|
||||
input
|
||||
inputClass="w-full h-[3.625rem] rounded bg-[#EFEFEF] px-4"
|
||||
value={props.values.bvn}
|
||||
onChange={props.handleChange}
|
||||
ref={firstInputRef}
|
||||
maxLength={11}
|
||||
error={(props.errors.bvn && props.touched.bvn) && props.errors.bvn}
|
||||
/>
|
||||
<button
|
||||
type='submit'
|
||||
className="w-full h-[3.625rem] rounded bg-[#FBB700] rounded-2 px-4 text-[18px] text-[#282828] font-semibold disabled:text-[#282828] disabled:text-opacity-50"
|
||||
>
|
||||
Enter
|
||||
</button>
|
||||
<p className="text-[#5C2684] mt-[1.5625rem] max-w-[31.625rem]">
|
||||
***Every personal information attached to your BVN is safe and secured. It is only inportant for us to verify your information and also give you access to your application profile/account
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</Form>
|
||||
)}
|
||||
</Formik>
|
||||
);
|
||||
};
|
||||
|
||||
export default BVN;
|
||||
@@ -0,0 +1,311 @@
|
||||
import React from "react";
|
||||
|
||||
import { Button, InputCompOne } from "..";
|
||||
|
||||
import {Formik, Form} from 'formik'
|
||||
import * as Yup from "yup";
|
||||
|
||||
const initialValues = {
|
||||
title: "",
|
||||
marital_status: "",
|
||||
agent_id: "",
|
||||
bvn: "",
|
||||
first_name: "",
|
||||
phone: "",
|
||||
email: "",
|
||||
surname: "",
|
||||
dob: "",
|
||||
second_name: "",
|
||||
spouse_bvn: "",
|
||||
};
|
||||
|
||||
// To get the validation schema
|
||||
const validationSchema = Yup.object().shape({
|
||||
title: Yup.string()
|
||||
.required("Required"),
|
||||
marital_status: Yup.string()
|
||||
.required("Required"),
|
||||
agent_id: Yup.string()
|
||||
.required("Required"),
|
||||
bvn: Yup.string()
|
||||
.required("BVN is required")
|
||||
.test("no-e", "Invalid number", (value:any) => {
|
||||
if (value && /^[0-9]*$/.test(value) == false) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
})
|
||||
.min(11, "must be 11 digits")
|
||||
.max(11, "must be 11 digits"),
|
||||
first_name: Yup.string()
|
||||
.required("Required"),
|
||||
phone: Yup.string()
|
||||
.required("Required"),
|
||||
email: Yup.string()
|
||||
.required("Required")
|
||||
.email("Wrong email format"),
|
||||
surname: Yup.string()
|
||||
.required("Required"),
|
||||
dob: Yup.string()
|
||||
.required("Required"),
|
||||
});
|
||||
|
||||
interface BasicInfoProps {
|
||||
handleNextStep: any;
|
||||
}
|
||||
|
||||
const BasicInfo: React.FC<BasicInfoProps> = ({
|
||||
handleNextStep,
|
||||
}) => {
|
||||
// const inputRef = useRef<HTMLInputElement>(null);
|
||||
|
||||
// const handleInput = (e: React.FormEvent<HTMLInputElement>) => {
|
||||
// const { name, value } = e.target as HTMLInputElement;
|
||||
|
||||
// if (name === "bvn") {
|
||||
// const isNumeric = /^[0-9]+$/.test(value);
|
||||
|
||||
// if (isNumeric) {
|
||||
// if (value.length === 10) {
|
||||
// setHideOTPComponent(false);
|
||||
// } else {
|
||||
// setHideOTPComponent(true);
|
||||
// }
|
||||
// } else {
|
||||
// console.log("Invalid BVN");
|
||||
// }
|
||||
// }
|
||||
// };
|
||||
|
||||
|
||||
//FUNCTION TO HANDLE SUBMIT
|
||||
const handleSubmit = (values:any) => {
|
||||
console.log(values)
|
||||
handleNextStep()
|
||||
};
|
||||
|
||||
return (
|
||||
<>
|
||||
{/* Header */}
|
||||
<h1 className="font-semibold text-[2.375rem] text-[#5C2684] my-[.5rem]">
|
||||
Let’s Get You Started
|
||||
</h1>
|
||||
<Formik
|
||||
initialValues={initialValues}
|
||||
validationSchema={validationSchema}
|
||||
onSubmit={handleSubmit}
|
||||
>
|
||||
{(props)=>(
|
||||
<Form>
|
||||
<div className="w-full rounded py-3 bg-[#5C2684] px-5">
|
||||
<p className="text-base text-[#FBB700] tracking-[3%] font-extrabold w-fit">
|
||||
BASIC INFORMATION
|
||||
</p>
|
||||
</div>
|
||||
<div className="mt-8 grid grid-cols-2">
|
||||
<div className="flex flex-col gap-4 max-w-[15.6875rem]">
|
||||
<InputCompOne
|
||||
parentInputClass="max-w-[224px] w-full"
|
||||
parentClass="max-w-[25.875rem] w-full flex flex-col gap-4"
|
||||
name="title"
|
||||
label="Title"
|
||||
labelClass="font-bold text-[18px] leading-[21.78px] tracking-[2%] text-[#5C2684] mb-[2px]"
|
||||
select={true}
|
||||
selectClass="w-full h-[36px] rounded-[6px]"
|
||||
selectOptions={titleOptions}
|
||||
selectValue={props.values.title}
|
||||
onChange={props.handleChange}
|
||||
error={(props.errors.title && props.touched.title) ? props.errors.title : ''}
|
||||
/>
|
||||
<InputCompOne
|
||||
parentInputClass="max-w-[224px] w-full"
|
||||
parentClass="max-w-[25.875rem] w-full flex flex-col gap-4"
|
||||
name="marital_status"
|
||||
label="Marital Status"
|
||||
labelClass="font-bold text-[18px] leading-[21.78px] tracking-[2%] text-[#5C2684] mb-[2px]"
|
||||
select={true}
|
||||
selectClass="w-full h-[36px] rounded-[6px]"
|
||||
selectOptions={maritalStatusOptions}
|
||||
selectValue={props.values.marital_status}
|
||||
onChange={props.handleChange}
|
||||
error={(props.errors.marital_status && props.touched.marital_status) ? props.errors.marital_status : ''}
|
||||
/>
|
||||
<InputCompOne
|
||||
parentInputClass="max-w-[224px] w-full"
|
||||
parentClass="max-w-[25.875rem] w-full flex flex-col gap-4"
|
||||
name="agent_id"
|
||||
label="Direct Sales Agent ID"
|
||||
labelClass="font-bold text-[18px] leading-[21.78px] tracking-[2%] text-[#5C2684] mb-[2px]"
|
||||
select={true}
|
||||
selectClass="w-full h-[36px] rounded-[6px]"
|
||||
selectOptions={{loading: false, data:[{ value: "", label: "Select" }, { value: "dd", label: "AB001" }]}}
|
||||
selectValue={props.values.agent_id}
|
||||
onChange={props.handleChange}
|
||||
error={(props.errors.agent_id && props.touched.agent_id) ? props.errors.agent_id : ''}
|
||||
/>
|
||||
<InputCompOne
|
||||
label="BVN"
|
||||
name="bvn"
|
||||
parentInputClass="w-full"
|
||||
labelSpan="( To get your BVN, dial *565*0# )"
|
||||
labelSpanClass="text-[11px] text-[#7a7373]"
|
||||
placeholder="Enter your BVN"
|
||||
labelClass="font-bold text-[18px] leading-[21.78px] tracking-[2%] text-[#5C2684] mb-[2px] gap-[2px]"
|
||||
input={true}
|
||||
inputClass="w-full h-[36px] bg-[#EFEFEF] rounded-[6px]"
|
||||
value={props.values.bvn}
|
||||
onChange={props.handleChange}
|
||||
error={(props.errors.bvn && props.touched.bvn) ? props.errors.bvn : ''}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="mt-5">
|
||||
<div className="grid grid-cols-2 gap-4">
|
||||
<InputCompOne
|
||||
parentClass="max-w-[20.3125rem]"
|
||||
label="First Name"
|
||||
name="first_name"
|
||||
parentInputClass="w-full"
|
||||
labelClass="font-bold text-[1.125rem] leading-[1.3613rem] tracking-[2%] text-[#5C2684] mb-[.125rem]"
|
||||
input
|
||||
inputClass="w-full h-[2.25rem] bg-[#EFEFEF] rounded-[.375rem]"
|
||||
value={props.values.first_name}
|
||||
onChange={props.handleChange}
|
||||
error={(props.errors.first_name && props.touched.first_name) ? props.errors.first_name : ''}
|
||||
/>
|
||||
<InputCompOne
|
||||
parentClass="max-w-[20.3125rem]"
|
||||
label="Phone Number"
|
||||
name="phone"
|
||||
parentInputClass="w-full"
|
||||
labelClass="font-bold text-[1.125rem] leading-[1.3613rem] tracking-[2%] text-[#5C2684] mb-[.125rem]"
|
||||
input
|
||||
inputClass="w-full h-[2.25rem] bg-[#EFEFEF] rounded-[.375rem]"
|
||||
value={props.values.phone}
|
||||
onChange={props.handleChange}
|
||||
error={(props.errors.phone && props.touched.phone) ? props.errors.phone : ''}
|
||||
/>
|
||||
<InputCompOne
|
||||
parentClass="max-w-[20.3125rem]"
|
||||
label="Email Address"
|
||||
name="email"
|
||||
parentInputClass="w-full"
|
||||
labelClass="font-bold text-[1.125rem] leading-[1.3613rem] tracking-[2%] text-[#5C2684] mb-[.125rem]"
|
||||
input
|
||||
inputClass="w-full h-[2.25rem] bg-[#EFEFEF] rounded-[.375rem]"
|
||||
value={props.values.email}
|
||||
onChange={props.handleChange}
|
||||
error={(props.errors.email && props.touched.email) ? props.errors.email : ''}
|
||||
/>
|
||||
|
||||
<InputCompOne
|
||||
parentClass="max-w-[20.3125rem]"
|
||||
label="Surname"
|
||||
name="surname"
|
||||
parentInputClass="w-full"
|
||||
labelClass="font-bold text-[1.125rem] leading-[1.3613rem] tracking-[2%] text-[#5C2684] mb-[.125rem]"
|
||||
input
|
||||
inputClass="w-full h-[2.25rem] bg-[#EFEFEF] rounded-[.375rem] px-3"
|
||||
value={props.values.surname}
|
||||
onChange={props.handleChange}
|
||||
error={(props.errors.surname && props.touched.surname) ? props.errors.surname : ''}
|
||||
/>
|
||||
<InputCompOne
|
||||
parentClass="max-w-[20.3125rem]"
|
||||
label="Date of Birth"
|
||||
name="dob"
|
||||
parentInputClass="w-full"
|
||||
labelClass="font-bold text-[1.125rem] leading-[1.3613rem] tracking-[2%] text-[#5C2684] mb-[.125rem]"
|
||||
input
|
||||
inputType='date'
|
||||
inputClass="w-full h-[2.25rem] bg-[#EFEFEF] rounded-[.375rem] px-3"
|
||||
value={props.values.dob}
|
||||
onChange={props.handleChange}
|
||||
error={(props.errors.dob && props.touched.dob) ? props.errors.dob : ''}
|
||||
/>
|
||||
<InputCompOne
|
||||
parentClass="max-w-[20.3125rem]"
|
||||
label="Second Name"
|
||||
name="second_name"
|
||||
parentInputClass="w-full"
|
||||
labelClass="font-bold text-[1.125rem] leading-[1.3613rem] tracking-[2%] text-[#5C2684] mb-[.125rem]"
|
||||
input
|
||||
inputClass="w-full h-[2.25rem] bg-[#EFEFEF] rounded-[.375rem] px-3"
|
||||
value={props.values.second_name}
|
||||
onChange={props.handleChange}
|
||||
error={(props.errors.second_name && props.touched.second_name) ? props.errors.second_name : ''}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className='w-full'>
|
||||
<div className="w-full rounded py-3 bg-[#5C2684] px-5 mt-5">
|
||||
<p className="text-base text-[#FBB700] tracking-[3%] font-extrabold w-fit">
|
||||
SPOUSE DETAILS ( If not applicable, please move to the next stage )
|
||||
</p>
|
||||
</div>
|
||||
<div className="mt-8 grid grid-cols-2">
|
||||
<div className="flex flex-col gap-4 max-w-[15.6875rem]">
|
||||
<InputCompOne
|
||||
parentClass="max-w-[20.3125rem]"
|
||||
label="BVN"
|
||||
name="spouse_bvn"
|
||||
parentInputClass="w-full"
|
||||
labelSpan="( To get your BVN, dial *565*0# )"
|
||||
labelSpanClass="text-[11px] text-[#7a7373]"
|
||||
labelClass="font-bold text-[1.125rem] leading-[1.3613rem] tracking-[2%] text-[#5C2684] mb-[.125rem]"
|
||||
input
|
||||
inputClass="w-full h-[2.25rem] bg-[#EFEFEF] rounded-[.375rem]"
|
||||
value={props.values.spouse_bvn}
|
||||
onChange={props.handleChange}
|
||||
error={(props.errors.spouse_bvn && props.touched.spouse_bvn) ? props.errors.spouse_bvn : ''}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<>
|
||||
<Button
|
||||
className="mt-8 btn-R bg-[#5A2C82]"
|
||||
text="Enter"
|
||||
type="submit"
|
||||
/>
|
||||
</>
|
||||
</Form>
|
||||
)}
|
||||
</Formik>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
export default BasicInfo;
|
||||
|
||||
|
||||
|
||||
interface SelectOption {
|
||||
loading: boolean;
|
||||
data: {value: string;
|
||||
label: string}[]
|
||||
}
|
||||
|
||||
const maritalStatusOptions: SelectOption = {
|
||||
loading: false,
|
||||
data: [
|
||||
{ value: "", label: "Select" },
|
||||
{ value: "single", label: "Single" },
|
||||
{ value: "married", label: "Married" },
|
||||
{ value: "divorced", label: "Divorced" },
|
||||
{ value: "widowed", label: "Widowed" },
|
||||
]
|
||||
}
|
||||
|
||||
const titleOptions: SelectOption = {
|
||||
loading: false,
|
||||
data: [
|
||||
{ value: "", label: "Select" },
|
||||
{ value: "ms", label: "Ms" },
|
||||
{ value: "mr", label: "Mr" },
|
||||
{ value: "miss", label: "Miss" },
|
||||
{ value: "mrs", label: "Mrs" },
|
||||
]
|
||||
}
|
||||
@@ -0,0 +1,27 @@
|
||||
import React from "react";
|
||||
import { InputCompOne } from "..";
|
||||
|
||||
const CreditAccount: React.FC = () => {
|
||||
return (
|
||||
<>
|
||||
<div className="w-full rounded py-3 bg-[#5C2684] px-5">
|
||||
<p className="text-base text-[#FBB700] tracking-[3%] font-extrabold w-fit">
|
||||
CREDIT ACCOUNT ( Your account to receive your loan )
|
||||
</p>
|
||||
</div>
|
||||
<InputCompOne
|
||||
parentClass="max-w-[29.4375rem] w-full my-5 ml-5"
|
||||
label="Disbursement Account Number "
|
||||
name="disbursementAccountNumber"
|
||||
labelSpan="( Your FCMB Account )"
|
||||
labelSpanClass="text-[12px] text-[#5C2684] ml-1"
|
||||
parentInputClass="w-full"
|
||||
labelClass="font-bold text-[18px] leading-[21.7808px] tracking-[2%] text-[#5C2684] mb-[2px]"
|
||||
input
|
||||
inputClass="w-full h-[36px] bg-[#EFEFEF] px-[2px] rounded-[6px]"
|
||||
/>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
export default CreditAccount;
|
||||
@@ -0,0 +1,146 @@
|
||||
import React from "react";
|
||||
import {useNavigate} from 'react-router-dom'
|
||||
import { Button, InputCompOne } from "..";
|
||||
import { RouteHandler } from "../../router/routes";
|
||||
|
||||
import {Formik, Form} from 'formik'
|
||||
import * as Yup from "yup";
|
||||
|
||||
const initialValues = {
|
||||
disburse_account: "",
|
||||
bank_name: "",
|
||||
account_name: "",
|
||||
account_number: "",
|
||||
checked: false
|
||||
};
|
||||
|
||||
// To get the validation schema
|
||||
const validationSchema = Yup.object().shape({
|
||||
disburse_account: Yup.string()
|
||||
.required("Required"),
|
||||
bank_name: Yup.string()
|
||||
.required("Required"),
|
||||
account_name: Yup.string()
|
||||
.required("Required"),
|
||||
account_number: Yup.string()
|
||||
.required("Required"),
|
||||
checked: Yup.bool() // use bool instead of boolean
|
||||
.oneOf([true], "You must accept the terms and conditions")
|
||||
});
|
||||
|
||||
const DebitAccount: React.FC = () => {
|
||||
const navigate = useNavigate()
|
||||
|
||||
//FUNCTION TO HANDLE SUBMIT
|
||||
const handleSubmit = (values:any) => {
|
||||
console.log(values)
|
||||
navigate(RouteHandler.letsGetStarted, {replace:true})
|
||||
};
|
||||
|
||||
return (
|
||||
<>
|
||||
<div className="w-full rounded py-3 mb-9 bg-[#5C2684] px-5">
|
||||
<p className="text-base text-[#FBB700] tracking-[3%] font-extrabold w-fit">
|
||||
CREDIT ACCOUNT ( Your account to receive your loan )
|
||||
</p>
|
||||
</div>
|
||||
<Formik
|
||||
initialValues={initialValues}
|
||||
validationSchema={validationSchema}
|
||||
onSubmit={handleSubmit}
|
||||
>
|
||||
{(props)=>(
|
||||
<Form>
|
||||
<InputCompOne
|
||||
parentClass="max-w-[29.4375rem] w-full my-5 ml-5"
|
||||
label="Disbursement Account Number "
|
||||
name="disburse_account"
|
||||
labelSpan="( Your FCMB Account )"
|
||||
labelSpanClass="text-[12px] text-[#5C2684] ml-1"
|
||||
parentInputClass="w-full"
|
||||
labelClass="font-bold text-[18px] leading-[21.7808px] tracking-[2%] text-[#5C2684] mb-[2px]"
|
||||
input
|
||||
inputClass="w-full h-[36px] bg-[#EFEFEF] rounded-[6px]"
|
||||
value={props.values.disburse_account}
|
||||
onChange={props.handleChange}
|
||||
error={(props.errors.disburse_account && props.touched.disburse_account) ? props.errors.disburse_account : ''}
|
||||
/>
|
||||
|
||||
<div className="mt-9 flex flex-col gap-9">
|
||||
<div className="w-full rounded py-3 bg-[#5C2684] px-5">
|
||||
<p className="text-base text-[#FBB700] tracking-[3%] font-extrabold w-fit">
|
||||
DEBIT ACCOUNT ( Your salary account for monthly repayment )
|
||||
</p>
|
||||
</div>
|
||||
<InputCompOne
|
||||
parentClass="max-w-[471px] w-full ml-5"
|
||||
label="Bank Name"
|
||||
name="bank_name"
|
||||
parentInputClass="w-full"
|
||||
labelClass="font-bold text-[18px] leading-[21.7808px] tracking-[2%] text-[#5C2684] mb-[2px]"
|
||||
input
|
||||
inputClass="w-full h-[36px] bg-[#EFEFEF] rounded-[6px]"
|
||||
value={props.values.bank_name}
|
||||
onChange={props.handleChange}
|
||||
error={(props.errors.bank_name && props.touched.bank_name) ? props.errors.bank_name : ''}
|
||||
/>
|
||||
|
||||
<div className="flex items-center gap-[59px]">
|
||||
<InputCompOne
|
||||
parentClass="max-w-[471px] w-full ml-5"
|
||||
label="Account Number"
|
||||
name="account_number"
|
||||
parentInputClass="w-full"
|
||||
labelClass="font-bold text-[18px] leading-[21.7808px] tracking-[2%] text-[#5C2684] mb-[2px]"
|
||||
input
|
||||
inputClass="w-full h-[36px] bg-[#EFEFEF] rounded-[6px]"
|
||||
value={props.values.account_number}
|
||||
onChange={props.handleChange}
|
||||
error={(props.errors.account_number && props.touched.account_number) ? props.errors.account_number : ''}
|
||||
/>
|
||||
<InputCompOne
|
||||
parentClass="max-w-[471px] w-full ml-5"
|
||||
label="Account Name"
|
||||
name="account_name"
|
||||
parentInputClass="w-full"
|
||||
labelClass="font-bold text-[18px] leading-[21.7808px] tracking-[2%] text-[#5C2684] mb-[2px]"
|
||||
input
|
||||
inputClass="w-full h-[36px] bg-[#EFEFEF] rounded-[6px]"
|
||||
value={props.values.account_name}
|
||||
onChange={props.handleChange}
|
||||
error={(props.errors.account_name && props.touched.account_name) ? props.errors.account_name : ''}
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div className="max-w-[578px] flex items-center">
|
||||
<input
|
||||
type="checkbox"
|
||||
// checked={true}
|
||||
|
||||
name='checked'
|
||||
onChange={props.handleChange}
|
||||
className="form-checkbox h-[25px] w-[25px] rounded-sm text-[#5c2684] "
|
||||
style={{ backgroundColor: "#5C2684" }}
|
||||
/>
|
||||
<label className="ml-2 text-gray-700">
|
||||
I have read, understood and accept the{" "}
|
||||
<span className="text-[#4545CB]">applicant's attestation</span> and
|
||||
all the <span className="text-[#4545CB]">terms and conditions</span>{" "}
|
||||
for FCMB premium salary loan.
|
||||
</label>
|
||||
{props.errors.checked && props.touched.checked && <span className='text-[10px] text-red-500'>{props.errors.checked}</span>}
|
||||
</div>
|
||||
<Button
|
||||
className="my-8 max-w-[33.875rem] btn-R bg-[#5A2C82] w-full h-11"
|
||||
text="Apply"
|
||||
type="submit"
|
||||
/>
|
||||
</div>
|
||||
</Form>
|
||||
)}
|
||||
</Formik>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
export default DebitAccount;
|
||||
@@ -1,17 +1,24 @@
|
||||
import { FC } from "react";
|
||||
import BasicInfo from "./BasicInfo";
|
||||
import YourAreAlmostThere from "./YourAreAlmostThere";
|
||||
import LoanAmountComp from "./LoanAmountComp";
|
||||
import ApplicantsAttestation from "./ApplicantsAttestation";
|
||||
|
||||
const GetStarted = ({handleNextStep, step}:{handleNextStep:any, step:string|number|any}) => {
|
||||
|
||||
const GetStarted: FC = () => {
|
||||
return (
|
||||
<div className="w-full flex items-center justify-center">
|
||||
<div className="containerMode">
|
||||
<h1 className="font-semibold text-[2.375rem] text-[#5C2684] my-[.5rem]">
|
||||
Let’s Get You Started
|
||||
</h1>
|
||||
<div className="w-full rounded py-3 bg-[#5C2684] px-5">
|
||||
<p className="text-base text-[#FBB700] tracking-[3%] font-extrabold w-fit">
|
||||
BASIC INFORMATION
|
||||
</p>
|
||||
</div>
|
||||
{/* Main */}
|
||||
<main>
|
||||
{step === 2 && (
|
||||
<BasicInfo
|
||||
handleNextStep={handleNextStep}
|
||||
/>
|
||||
)}
|
||||
{step === 3 && <YourAreAlmostThere handleNextStep={handleNextStep} />}
|
||||
{step === 4 && <LoanAmountComp handleNextStep={handleNextStep} />}
|
||||
{step === 5 && <ApplicantsAttestation />}
|
||||
</main>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
|
||||
@@ -0,0 +1,202 @@
|
||||
import { Button, InputCompOne } from "..";
|
||||
|
||||
import {Formik, Form} from 'formik'
|
||||
import * as Yup from "yup";
|
||||
|
||||
const initialValues = {
|
||||
monthly_salary: "",
|
||||
loan_amount: "",
|
||||
duration: 6
|
||||
};
|
||||
|
||||
// To get the validation schema
|
||||
const validationSchema = Yup.object().shape({
|
||||
duration: Yup.number()
|
||||
.required("Required"),
|
||||
monthly_salary: Yup.string()
|
||||
.required("Required")
|
||||
.test("no-e", "Invalid", (value:any) => {
|
||||
if (value && /^[0-9]*$/.test(value) == false) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}),
|
||||
loan_amount: Yup.string()
|
||||
.required("Required")
|
||||
.test("no-e", "Invalid", (value:any) => {
|
||||
if (value && /^[0-9]*$/.test(value) == false) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}),
|
||||
});
|
||||
|
||||
// interface SliderProps {
|
||||
// handleSliderChange: (e: React.ChangeEvent<HTMLInputElement>) => void;
|
||||
// value: number;
|
||||
// }
|
||||
|
||||
interface LoanAmountProps {
|
||||
handleNextStep: any;
|
||||
}
|
||||
|
||||
const LoanAmountComp: React.FC<LoanAmountProps> = ({ handleNextStep }) => {
|
||||
// const [value, setValue] = React.useState(6);
|
||||
|
||||
// const handleSliderChange = (e: any) => {
|
||||
// setValue(e.target.value);
|
||||
// };
|
||||
|
||||
//FUNCTION TO HANDLE SUBMIT
|
||||
const handleSubmit = (values:any) => {
|
||||
console.log(values)
|
||||
handleNextStep()
|
||||
};
|
||||
|
||||
return (
|
||||
<>
|
||||
<div className="flex justify-between items-center w-full mt-8 mb-[2.8125rem]">
|
||||
<h1 className="font-semibold text-[38px] text-[#5C2684] my-[8px]">
|
||||
Loan Amount
|
||||
</h1>
|
||||
<div className="flex flex-col gap-[7px]">
|
||||
<p className="font-extrabold tracking-[3%] text-[#FBB700] underline">
|
||||
For more enquiries and support
|
||||
</p>
|
||||
<p className="font-extrabold tracking-[3%] text-[#5A5A5A]">
|
||||
Call: 09099000000
|
||||
</p>
|
||||
<p className="font-extrabold tracking-[3%] text-[#5A5A5A]">
|
||||
Email: fcmbloan@support.com
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
<Formik
|
||||
initialValues={initialValues}
|
||||
validationSchema={validationSchema}
|
||||
onSubmit={handleSubmit}
|
||||
>
|
||||
{(props)=>(
|
||||
<Form>
|
||||
<div className="flex flex-col gap-[45px] justify-center ml-[40px] mb-[40px]">
|
||||
<InputCompOne
|
||||
parentClass="max-w-[29.4375rem] w-full"
|
||||
label="Your Monthly Salary*"
|
||||
name="monthly_salary"
|
||||
parentInputClass="w-full"
|
||||
labelClass="font-bold text-[18px] leading-[21.7808px] tracking-[2%] text-[#5C2684] mb-[2px]"
|
||||
input
|
||||
inputClass="w-full h-[51px] bg-[#EFEFEF] rounded-[6px] placeholder:text-green-600 placeholder:font-bold px-4"
|
||||
placeholder="150,000"
|
||||
value={props.values.monthly_salary}
|
||||
onChange={props.handleChange}
|
||||
error={(props.errors.monthly_salary && props.touched.monthly_salary) ? props.errors.monthly_salary : ''}
|
||||
/>
|
||||
|
||||
<div className="w-full border-[.1875rem] rounded-xl border-black min-h-[884px] py-4 px-8 max-w-[784px]">
|
||||
<p className="leading-[22px] tracking-[3%] text-[#5C2684] w-[729px] mb-[2.625rem]">
|
||||
The maximum amount you can apply for on this offer is based on the
|
||||
information you shared with us in your loan application. We have
|
||||
made this offer to suit your monthly remuneration and to enable you
|
||||
pay your loan on-time
|
||||
</p>
|
||||
<InputCompOne
|
||||
parentClass="max-w-[45.8125rem] w-full mb-3"
|
||||
label="How much do you want to apply for?"
|
||||
name="loan_amount"
|
||||
parentInputClass="w-full"
|
||||
labelClass="font-bold text-[18px] leading-[21.7808px] tracking-[2%] text-[#5C2684] mb-[2px]"
|
||||
input
|
||||
inputClass="w-full h-[51px] bg-[#EFEFEF] rounded-[6px] placeholder:text-green-600 placeholder:font-bold px-9"
|
||||
placeholder="350,000"
|
||||
value={props.values.loan_amount}
|
||||
onChange={props.handleChange}
|
||||
error={(props.errors.loan_amount && props.touched.loan_amount) ? props.errors.loan_amount : ''}
|
||||
/>
|
||||
<div className="flex items-center justify-between w-full">
|
||||
<div className=" h-[68px] flex flex-col py-1 px-[13px] shadow-md text-[#5C2684]">
|
||||
<span>Minimum Offer:</span>
|
||||
<p>
|
||||
<b>N</b>100,000
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div className="h-[68px] flex flex-col py-1 px-[13px] shadow-md text-[#5C2684]">
|
||||
<span>Maximum Offer:</span>
|
||||
<p>
|
||||
<b>N</b>500,000
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<>
|
||||
<div className="flex flex-col items-start mt-11 mb-16">
|
||||
<p className="text-lg font-semibold">For how many months? <span className='text-[10px] text-red-500'>{(props.errors.duration && props.touched.duration) ? props.errors.duration : ''}</span></p>
|
||||
<div className="w-full">
|
||||
<input
|
||||
type="range"
|
||||
min="6"
|
||||
max="24"
|
||||
value={props.values.duration}
|
||||
onChange={props.handleChange}
|
||||
className="slider w-full h-2 bg-gray-300 rounded-lg appearance-none cursor-pointer"
|
||||
style={{
|
||||
background: `linear-gradient(90deg, #6B21A8 ${
|
||||
((props.values.duration - 6) / 18) * 100
|
||||
}%, #D1D5DB ${((props.values.duration - 6) / 18) * 100}%)`,
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
<div className="mt-4 text-lg font-semibold text-gray-700 w-full flex items-center text-center justify-center">
|
||||
{props.values.duration} months
|
||||
</div>
|
||||
</div>
|
||||
</>
|
||||
|
||||
<div className="w-full flex items-center justify-center flex-col">
|
||||
<div className="w-[279px] h-[130px] mb-[76px] flex items-center justify-center flex-col">
|
||||
<p className="text-[#FBB700]">Your Monthly Repayment</p>
|
||||
<p>N</p>
|
||||
</div>
|
||||
<Button
|
||||
className="max-w-[462px] w-full bg-[#5C2684] rounded h-[2.75rem]"
|
||||
text="Submit"
|
||||
type='submit'
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</Form>
|
||||
)}
|
||||
</Formik>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
export default LoanAmountComp;
|
||||
|
||||
// const Slider: React.FC<SliderProps> = ({ handleSliderChange, value }) => {
|
||||
// return (
|
||||
// <div className="flex flex-col items-start mt-11 mb-16">
|
||||
// <p className="text-lg font-semibold">For how many months?</p>
|
||||
// <div className="w-full">
|
||||
// <input
|
||||
// type="range"
|
||||
// min="6"
|
||||
// max="24"
|
||||
// value={value}
|
||||
// onChange={handleSliderChange}
|
||||
// className="slider w-full h-2 bg-gray-300 rounded-lg appearance-none cursor-pointer"
|
||||
// style={{
|
||||
// background: `linear-gradient(90deg, #6B21A8 ${
|
||||
// ((value - 6) / 18) * 100
|
||||
// }%, #D1D5DB ${((value - 6) / 18) * 100}%)`,
|
||||
// }}
|
||||
// />
|
||||
// </div>
|
||||
// <div className="mt-4 text-lg font-semibold text-gray-700 w-full flex items-center text-center justify-center">
|
||||
// {value} months
|
||||
// </div>
|
||||
// </div>
|
||||
// );
|
||||
// };
|
||||
@@ -0,0 +1,402 @@
|
||||
import React from "react";
|
||||
import { Button, InputCompOne } from "..";
|
||||
|
||||
import {Formik, Form} from 'formik'
|
||||
import * as Yup from "yup";
|
||||
|
||||
const initialValues = {
|
||||
job_title: "",
|
||||
employer_name: "",
|
||||
employer_email:"",
|
||||
date_of_resumption: "",
|
||||
employee_id: "",
|
||||
annual_income: "",
|
||||
monthly_salary: "",
|
||||
salary_payment_date: "",
|
||||
ref_name: "",
|
||||
ref_email: "",
|
||||
ref_number: "",
|
||||
ref_relationship: "",
|
||||
ref_bvn: "",
|
||||
ref_two_name: "",
|
||||
ref_two_email: "",
|
||||
ref_two_number: "",
|
||||
ref_two_relationship: "",
|
||||
ref_two_bvn: "",
|
||||
};
|
||||
|
||||
// To get the validation schema
|
||||
const validationSchema = Yup.object().shape({
|
||||
job_title: Yup.string()
|
||||
.required("Required"),
|
||||
employer_name: Yup.string()
|
||||
.required("Required"),
|
||||
date_of_resumption: Yup.string()
|
||||
.required("Required"),
|
||||
employer_email: Yup.string()
|
||||
.email("Invalid")
|
||||
.required("Required"),
|
||||
annual_income: Yup.string()
|
||||
.required("Required")
|
||||
.test("no-e", "Invalid", (value:any) => {
|
||||
if (value && /^[0-9]*$/.test(value) == false) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}),
|
||||
monthly_salary: Yup.string()
|
||||
.required("Required")
|
||||
.test("no-e", "Invalid", (value:any) => {
|
||||
if (value && /^[0-9]*$/.test(value) == false) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}),
|
||||
salary_payment_date: Yup.string()
|
||||
.required("Required"),
|
||||
employee_id: Yup.string()
|
||||
.required("Required"),
|
||||
ref_name: Yup.string()
|
||||
.required("Required"),
|
||||
ref_email: Yup.string()
|
||||
.email("Invalid")
|
||||
.required("Required"),
|
||||
ref_number: Yup.string()
|
||||
.required("Required"),
|
||||
ref_relationship: Yup.string()
|
||||
.required("Required"),
|
||||
ref_bvn: Yup.string()
|
||||
.required("BVN is required")
|
||||
.test("no-e", "Invalid number", (value:any) => {
|
||||
if (value && /^[0-9]*$/.test(value) == false) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
})
|
||||
.min(11, "must be 11 digits")
|
||||
.max(11, "must be 11 digits"),
|
||||
ref_two_name: Yup.string()
|
||||
.required("Required"),
|
||||
ref_two_email: Yup.string()
|
||||
.email("Invalid")
|
||||
.required("Required"),
|
||||
ref_two_number: Yup.string()
|
||||
.required("Required"),
|
||||
ref_two_relationship: Yup.string()
|
||||
.required("Required"),
|
||||
ref_two_bvn: Yup.string()
|
||||
.required("BVN is required")
|
||||
.test("no-e", "Invalid number", (value:any) => {
|
||||
if (value && /^[0-9]*$/.test(value) == false) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
})
|
||||
.min(11, "must be 11 digits")
|
||||
.max(11, "must be 11 digits"),
|
||||
});
|
||||
|
||||
interface YourAreAlmostThereProps {
|
||||
handleNextStep: any;
|
||||
}
|
||||
|
||||
const YourAreAlmostThere: React.FC<YourAreAlmostThereProps> = ({ handleNextStep }) => {
|
||||
|
||||
//FUNCTION TO HANDLE SUBMIT
|
||||
const handleSubmit = (values:any) => {
|
||||
console.log(values)
|
||||
handleNextStep()
|
||||
};
|
||||
|
||||
return (
|
||||
<>
|
||||
<h1 className="font-semibold text-[2.375rem] text-[#5C2684] my-[.5rem]">
|
||||
You’re almost there
|
||||
</h1>
|
||||
<Formik
|
||||
initialValues={initialValues}
|
||||
validationSchema={validationSchema}
|
||||
onSubmit={handleSubmit}
|
||||
>
|
||||
{(props)=>(
|
||||
<Form>
|
||||
<div className="flex flex-col gap-6">
|
||||
<>
|
||||
<div className="w-full rounded py-3 bg-[#5C2684] px-5">
|
||||
<p className="text-base text-[#FBB700] tracking-[3%] font-extrabold w-fit">
|
||||
EMPLOYMENT DETAILS
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div className="flex flex-col gap-4">
|
||||
<InputCompOne
|
||||
parentClass="max-w-[17.9375rem] w-full"
|
||||
label="Job Title"
|
||||
name="job_title"
|
||||
parentInputClass="w-full"
|
||||
labelClass="font-bold text-[1.125rem] leading-[1.3613rem] tracking-[2%] text-[#5C2684] mb-[.125rem]"
|
||||
input
|
||||
inputClass="w-full h-[2.25rem] bg-[#EFEFEF] rounded-[.375rem]"
|
||||
value={props.values.job_title}
|
||||
onChange={props.handleChange}
|
||||
error={(props.errors.job_title && props.touched.job_title) ? props.errors.job_title : ''}
|
||||
/>
|
||||
<div className="flex items-center gap-[3.6875rem]">
|
||||
<InputCompOne
|
||||
parentClass="max-w-[23.1875rem] w-full"
|
||||
label="Employer’s Name"
|
||||
name="employer_name"
|
||||
parentInputClass="w-full"
|
||||
labelClass="font-bold text-[1.125rem] leading-[1.3613rem] tracking-[2%] text-[#5C2684] mb-[.125rem]"
|
||||
input
|
||||
inputClass="w-full h-[2.25rem] bg-[#EFEFEF] rounded-[.375rem]"
|
||||
value={props.values.employer_name}
|
||||
onChange={props.handleChange}
|
||||
error={(props.errors.employer_name && props.touched.employer_name) ? props.errors.employer_name : ''}
|
||||
/>
|
||||
<InputCompOne
|
||||
parentClass="max-w-[23.1875rem] w-full"
|
||||
label="Employer’s Official Email"
|
||||
name="employer_email"
|
||||
parentInputClass="w-full"
|
||||
labelClass="font-bold text-[1.125rem] leading-[1.3613rem] tracking-[2%] text-[#5C2684] mb-[.125rem]"
|
||||
input
|
||||
inputClass="w-full h-[2.25rem] bg-[#EFEFEF] rounded-[.375rem]"
|
||||
value={props.values.employer_email}
|
||||
onChange={props.handleChange}
|
||||
error={(props.errors.employer_email && props.touched.employer_email) ? props.errors.employer_email : ''}
|
||||
/>
|
||||
</div>
|
||||
<div className="flex items-center gap-[9rem]">
|
||||
<InputCompOne
|
||||
parentClass="max-w-[17.9375rem] w-full"
|
||||
label="Resumption Date"
|
||||
name="date_of_resumption"
|
||||
parentInputClass="w-full"
|
||||
labelClass="font-bold text-[1.125rem] leading-[1.3613rem] tracking-[2%] text-[#5C2684] mb-[.125rem]"
|
||||
input
|
||||
inputType='date'
|
||||
inputClass="w-full h-[2.25rem] bg-[#EFEFEF] rounded-[.375rem]"
|
||||
value={props.values.date_of_resumption}
|
||||
onChange={props.handleChange}
|
||||
error={(props.errors.date_of_resumption && props.touched.date_of_resumption) ? props.errors.date_of_resumption : ''}
|
||||
/>
|
||||
<InputCompOne
|
||||
parentClass="max-w-[17.9375rem] w-full"
|
||||
label="Employee ID."
|
||||
name="employee_id"
|
||||
labelSpan="Upload your work ID"
|
||||
labelSpanClass="text-[11px] text-[#7a7373]"
|
||||
parentInputClass="w-full"
|
||||
labelClass="font-bold text-[1.125rem] leading-[1.3613rem] tracking-[2%] text-[#5C2684] mb-[.125rem]"
|
||||
input
|
||||
inputClass="w-full h-[2.25rem] bg-[#EFEFEF] rounded-[.375rem]"
|
||||
value={props.values.employee_id}
|
||||
onChange={props.handleChange}
|
||||
error={(props.errors.employee_id && props.touched.employee_id) ? props.errors.employee_id : ''}
|
||||
/>
|
||||
</div>
|
||||
<div className="flex items-center gap-[3.6875rem]">
|
||||
<InputCompOne
|
||||
parentClass="max-w-[23.1875rem] w-full"
|
||||
label="Salary ( Gross annual income )"
|
||||
name="annual_income"
|
||||
parentInputClass="w-full"
|
||||
labelClass="font-bold text-[1.125rem] leading-[1.3613rem] tracking-[2%] text-[#5C2684] mb-[.125rem]"
|
||||
input
|
||||
inputClass="w-full h-[2.25rem] bg-[#EFEFEF] rounded-[.375rem]"
|
||||
value={props.values.annual_income}
|
||||
onChange={props.handleChange}
|
||||
error={(props.errors.annual_income && props.touched.annual_income) ? props.errors.annual_income : ''}
|
||||
/>
|
||||
<InputCompOne
|
||||
parentClass="max-w-[23.1875rem] w-full"
|
||||
label="Salary ( Net monthly Income )"
|
||||
name="monthly_salary"
|
||||
parentInputClass="w-full"
|
||||
labelClass="font-bold text-[1.125rem] leading-[1.3613rem] tracking-[2%] text-[#5C2684] mb-[.125rem]"
|
||||
input
|
||||
inputClass="w-full h-[2.25rem] bg-[#EFEFEF] rounded-[.375rem]"
|
||||
value={props.values.monthly_salary}
|
||||
onChange={props.handleChange}
|
||||
error={(props.errors.monthly_salary && props.touched.monthly_salary) ? props.errors.monthly_salary : ''}
|
||||
/>
|
||||
</div>
|
||||
<InputCompOne
|
||||
parentClass="max-w-[20.3125rem]"
|
||||
label="Salary Payment Date"
|
||||
name="salary_payment_date"
|
||||
parentInputClass="w-full"
|
||||
labelClass="font-bold text-[1.125rem] leading-[1.3613rem] tracking-[2%] text-[#5C2684] mb-[.125rem]"
|
||||
input
|
||||
inputType='date'
|
||||
inputClass="w-full h-[2.25rem] bg-[#EFEFEF] rounded-[.375rem]"
|
||||
value={props.values.salary_payment_date}
|
||||
onChange={props.handleChange}
|
||||
error={(props.errors.salary_payment_date && props.touched.salary_payment_date) ? props.errors.salary_payment_date : ''}
|
||||
/>
|
||||
</div>
|
||||
</>
|
||||
|
||||
<>
|
||||
<div className="w-full rounded py-3 bg-[#5C2684] px-5">
|
||||
<p className="text-base text-[#FBB700] tracking-[3%] font-extrabold w-fit">
|
||||
REFERENCE DETAILS ( Must be 18 years and above )
|
||||
</p>
|
||||
</div>
|
||||
<div className="">
|
||||
<div className="flex flex-col gap-[3.4375rem]">
|
||||
<div className="flex flex-col gap-4">
|
||||
<div className="flex items-center gap-[6.5625rem]">
|
||||
<InputCompOne
|
||||
parentClass="max-w-[20.3125rem] w-full"
|
||||
label="Name"
|
||||
name="ref_name"
|
||||
labelSpan="1st reference"
|
||||
labelSpanClass="text-[12px] text-[#5C2684] ml-1"
|
||||
parentInputClass="w-full"
|
||||
labelClass="font-bold text-[1.125rem] leading-[1.3613rem] tracking-[2%] text-[#5C2684] mb-[.125rem]"
|
||||
input
|
||||
inputClass="w-full h-[2.25rem] bg-[#EFEFEF] px-[.125rem] rounded-[.375rem]"
|
||||
value={props.values.ref_name}
|
||||
onChange={props.handleChange}
|
||||
error={(props.errors.ref_name && props.touched.ref_name) ? props.errors.ref_name : ''}
|
||||
/>
|
||||
<InputCompOne
|
||||
parentClass="max-w-[20.3125rem] w-full"
|
||||
label="Relationship with He/She"
|
||||
name="ref_relationship"
|
||||
parentInputClass="w-full"
|
||||
labelClass="font-bold text-[1.125rem] leading-[1.3613rem] tracking-[2%] text-[#5C2684] mb-[.125rem]"
|
||||
input
|
||||
inputClass="w-full h-[2.25rem] bg-[#EFEFEF] px-[.125rem] rounded-[.375rem]"
|
||||
value={props.values.ref_relationship}
|
||||
onChange={props.handleChange}
|
||||
error={(props.errors.ref_relationship && props.touched.ref_relationship) ? props.errors.ref_relationship : ''}
|
||||
/>
|
||||
</div>
|
||||
<div className="flex items-center gap-[6.5625rem]">
|
||||
<InputCompOne
|
||||
parentClass="max-w-[20.3125rem] w-full"
|
||||
label="Phone Number"
|
||||
name="ref_number"
|
||||
parentInputClass="w-full"
|
||||
labelClass="font-bold text-[1.125rem] leading-[1.3613rem] tracking-[2%] text-[#5C2684] mb-[.125rem]"
|
||||
input
|
||||
inputClass="w-full h-[2.25rem] bg-[#EFEFEF] rounded-[.375rem]"
|
||||
value={props.values.ref_number}
|
||||
onChange={props.handleChange}
|
||||
error={(props.errors.ref_number && props.touched.ref_number) ? props.errors.ref_number : ''}
|
||||
/>
|
||||
<InputCompOne
|
||||
parentClass="max-w-[20.3125rem] w-full"
|
||||
label="Email Address"
|
||||
name="ref_email"
|
||||
parentInputClass="w-full"
|
||||
labelClass="font-bold text-[1.125rem] leading-[1.3613rem] tracking-[2%] text-[#5C2684] mb-[.125rem]"
|
||||
input
|
||||
inputClass="w-full h-[2.25rem] bg-[#EFEFEF] rounded-[.375rem]"
|
||||
value={props.values.ref_email}
|
||||
onChange={props.handleChange}
|
||||
error={(props.errors.ref_email && props.touched.ref_email) ? props.errors.ref_email : ''}
|
||||
/>
|
||||
</div>
|
||||
<InputCompOne
|
||||
parentClass="max-w-[20.3125rem] w-full"
|
||||
label="BVN"
|
||||
name="ref_bvn"
|
||||
parentInputClass="w-full"
|
||||
labelClass="font-bold text-[1.125rem] leading-[1.3613rem] tracking-[2%] text-[#5C2684] mb-[.125rem]"
|
||||
input
|
||||
inputClass="w-full h-[2.25rem] bg-[#EFEFEF] rounded-[.375rem]"
|
||||
value={props.values.ref_bvn}
|
||||
onChange={props.handleChange}
|
||||
error={(props.errors.ref_bvn && props.touched.ref_bvn) ? props.errors.ref_bvn : ''}
|
||||
/>
|
||||
</div>
|
||||
<div className="flex flex-col gap-4">
|
||||
<div className="flex items-center gap-[6.5625rem]">
|
||||
<InputCompOne
|
||||
parentClass="max-w-[20.3125rem] w-full"
|
||||
label="Name"
|
||||
name="ref_two_name"
|
||||
labelSpan="2nd reference"
|
||||
labelSpanClass="text-[12px] text-[#5C2684] ml-[4px]"
|
||||
parentInputClass="w-full"
|
||||
labelClass="font-bold text-[1.125rem] leading-[1.3613rem] tracking-[2%] text-[#5C2684] mb-[.125rem]"
|
||||
input
|
||||
inputClass="w-full h-[2.25rem] bg-[#EFEFEF] rounded-[.375rem]"
|
||||
value={props.values.ref_two_name}
|
||||
onChange={props.handleChange}
|
||||
error={(props.errors.ref_two_name && props.touched.ref_two_name) ? props.errors.ref_two_name : ''}
|
||||
/>
|
||||
<InputCompOne
|
||||
parentClass="max-w-[20.3125rem] w-full"
|
||||
label="Relationship with He/She"
|
||||
name="ref_two_relationship"
|
||||
parentInputClass="w-full"
|
||||
labelClass="font-bold text-[1.125rem] leading-[1.3613rem] tracking-[2%] text-[#5C2684] mb-[.125rem]"
|
||||
input
|
||||
inputClass="w-full h-[2.25rem] bg-[#EFEFEF] rounded-[.375rem]"
|
||||
value={props.values.ref_two_relationship}
|
||||
onChange={props.handleChange}
|
||||
error={(props.errors.ref_two_relationship && props.touched.ref_two_relationship) ? props.errors.ref_two_relationship : ''}
|
||||
/>
|
||||
</div>
|
||||
<div className="flex items-center gap-[6.5625rem]">
|
||||
<InputCompOne
|
||||
parentClass="max-w-[20.3125rem] w-full"
|
||||
label="Phone Number"
|
||||
name="ref_two_number"
|
||||
parentInputClass="w-full"
|
||||
labelClass="font-bold text-[1.125rem] leading-[1.3613rem] tracking-[2%] text-[#5C2684] mb-[.125rem]"
|
||||
input
|
||||
inputClass="w-full h-[2.25rem] bg-[#EFEFEF] rounded-[.375rem]"
|
||||
value={props.values.ref_two_number}
|
||||
onChange={props.handleChange}
|
||||
error={(props.errors.ref_two_number && props.touched.ref_two_number) ? props.errors.ref_two_number : ''}
|
||||
/>
|
||||
<InputCompOne
|
||||
parentClass="max-w-[20.3125rem] w-full"
|
||||
label="Email Address"
|
||||
name="ref_two_email"
|
||||
parentInputClass="w-full"
|
||||
labelClass="font-bold text-[1.125rem] leading-[1.3613rem] tracking-[2%] text-[#5C2684] mb-[.125rem]"
|
||||
input
|
||||
inputClass="w-full h-[2.25rem] bg-[#EFEFEF] rounded-[.375rem]"
|
||||
value={props.values.ref_two_email}
|
||||
onChange={props.handleChange}
|
||||
error={(props.errors.ref_two_email && props.touched.ref_two_email) ? props.errors.ref_two_email : ''}
|
||||
/>
|
||||
</div>
|
||||
<InputCompOne
|
||||
parentClass="max-w-[20.3125rem] w-full"
|
||||
label="BVN"
|
||||
name="ref_two_bvn"
|
||||
parentInputClass="w-full"
|
||||
labelClass="font-bold text-[1.125rem] leading-[1.3613rem] tracking-[2%] text-[#5C2684] mb-[.125rem]"
|
||||
input
|
||||
inputClass="w-full h-[2.25rem] bg-[#EFEFEF] rounded-[.375rem]"
|
||||
value={props.values.ref_two_bvn}
|
||||
onChange={props.handleChange}
|
||||
error={(props.errors.ref_two_bvn && props.touched.ref_two_bvn) ? props.errors.ref_two_bvn : ''}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</>
|
||||
|
||||
<Button
|
||||
className="my-8 max-w-[20.3125rem] btn-R bg-[#5A2C82]"
|
||||
text="Continue"
|
||||
type="submit"
|
||||
/>
|
||||
</div>
|
||||
</Form>
|
||||
)}
|
||||
</Formik>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
export default YourAreAlmostThere;
|
||||
@@ -1,13 +1,16 @@
|
||||
import { useState, ChangeEvent, FC } from "react";
|
||||
import React, { useState, ChangeEvent } from "react";
|
||||
import Logo from "../../assets/icons/logo.svg";
|
||||
import Button from "../shared/Button";
|
||||
import { lowerMenuItems } from "../../utils/data";
|
||||
import { _lowerMenuItems } from "../../utils/data";
|
||||
import Sidebar from "./Sidebar";
|
||||
import { Link } from "react-router-dom";
|
||||
import HeaderMenuItem from "./HeaderMenuItem";
|
||||
import { RouteHandler } from "../../router/routes";
|
||||
|
||||
type LowerMenuItem = {
|
||||
id: string | number;
|
||||
export type LowerMenuItem = {
|
||||
name: string;
|
||||
linkPath: string;
|
||||
subItems?: LowerMenuItem[];
|
||||
};
|
||||
|
||||
type HiddenMenuItems = {
|
||||
@@ -15,7 +18,7 @@ type HiddenMenuItems = {
|
||||
hideMenu?: boolean;
|
||||
};
|
||||
|
||||
const Header: FC<HiddenMenuItems> = ({
|
||||
const Header: React.FC<HiddenMenuItems> = ({
|
||||
hideSidebar = false,
|
||||
hideMenu = false,
|
||||
}) => {
|
||||
@@ -31,7 +34,7 @@ const Header: FC<HiddenMenuItems> = ({
|
||||
};
|
||||
|
||||
return (
|
||||
<div className="relative my-2 py-2 flex items-center justify-center border-b-2 border-[#E3DEDA]">
|
||||
<div className="relative mt-2 py-2 flex items-center justify-center border-b-2 border-[#E3DEDA]">
|
||||
{!hideSidebar && (
|
||||
<Sidebar toggleSidebar={toggleSidebar} isSidebarOpen={isSidebarOpen} />
|
||||
)}
|
||||
@@ -48,20 +51,25 @@ const Header: FC<HiddenMenuItems> = ({
|
||||
{!hideMenu && (
|
||||
<div className="flex flex-col-reverse lg:flex-col grow lg:grow-0 justify-between items-end">
|
||||
<ul className="flex gap-0 lg:gap-[10px] items-center justify-end w-full flex-wrap">
|
||||
{["Open An Account", "Internet Banking", "Contact Us"].map(
|
||||
(text: string) => (
|
||||
<li key={text} className="hidden sm:flex">
|
||||
<a href="#">
|
||||
<Button
|
||||
className={
|
||||
text === "Open An Account" ? "btn-active" : ""
|
||||
}
|
||||
text={text}
|
||||
/>
|
||||
</a>
|
||||
</li>
|
||||
)
|
||||
)}
|
||||
{[
|
||||
{ text: "Open An Account", href: RouteHandler.letsGetStarted },
|
||||
{
|
||||
text: "Internet Banking",
|
||||
href: RouteHandler.businessBanking,
|
||||
},
|
||||
{ text: "Contact Us", href: RouteHandler.cooperateBanking },
|
||||
].map((item: { text: string; href: string }) => (
|
||||
<li key={item.text} className="hidden sm:flex">
|
||||
<a href={item.href}>
|
||||
<Button
|
||||
className={
|
||||
item.text === "Open An Account" ? "btn-active" : ""
|
||||
}
|
||||
text={item.text}
|
||||
/>
|
||||
</a>
|
||||
</li>
|
||||
))}
|
||||
<li className="w-full lg:w-fit">
|
||||
<SearchInput
|
||||
onChange={handleSearchChange}
|
||||
@@ -69,6 +77,7 @@ const Header: FC<HiddenMenuItems> = ({
|
||||
/>
|
||||
</li>
|
||||
</ul>
|
||||
|
||||
<div className="flex lg:hidden">
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
@@ -88,14 +97,9 @@ const Header: FC<HiddenMenuItems> = ({
|
||||
/>
|
||||
</svg>
|
||||
</div>
|
||||
<ul className="hidden lg:flex gap-[10px] items-center justify-end flex-wrap">
|
||||
{lowerMenuItems.map((item: LowerMenuItem) => (
|
||||
<li
|
||||
key={item.id}
|
||||
className="cursor-pointer text-[13.5px] font-medium text-[#525252] tracking-[1px] leading-[-0.3pt]"
|
||||
>
|
||||
{item.name}
|
||||
</li>
|
||||
<ul className="hidden lg:flex gap-[10px] items-center justify-end flex-wrap relative">
|
||||
{_lowerMenuItems.map((item: LowerMenuItem, idx: number) => (
|
||||
<HeaderMenuItem key={idx} item={item} />
|
||||
))}
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
@@ -0,0 +1,35 @@
|
||||
import React, { useState } from "react";
|
||||
import { LowerMenuItem } from "./Header";
|
||||
|
||||
interface MenuItemProps {
|
||||
item: LowerMenuItem;
|
||||
}
|
||||
|
||||
const HeaderMenuItem: React.FC<MenuItemProps> = ({ item }) => {
|
||||
const [showSubMenu, setShowSubMenu] = useState<boolean>(false);
|
||||
|
||||
const toggleSubMenu = () => {
|
||||
setShowSubMenu(!showSubMenu);
|
||||
};
|
||||
|
||||
return (
|
||||
<li
|
||||
className={`cursor-pointer text-[13.5px] font-medium text-[#525252] tracking-[1px] leading-[-0.3pt]`}
|
||||
onMouseEnter={toggleSubMenu}
|
||||
onMouseLeave={toggleSubMenu}
|
||||
>
|
||||
<a href={item.linkPath}>{item.name}</a>
|
||||
{showSubMenu && item.subItems && (
|
||||
<ul
|
||||
className={`absolute bg-white shadow-md p-4 z-20 `}
|
||||
>
|
||||
{item.subItems.map((subItem, index) => (
|
||||
<HeaderMenuItem key={index} item={subItem} />
|
||||
))}
|
||||
</ul>
|
||||
)}
|
||||
</li>
|
||||
);
|
||||
};
|
||||
|
||||
export default HeaderMenuItem;
|
||||
@@ -1,4 +1,5 @@
|
||||
import { top_header_data } from "../../utils/data";
|
||||
import { Link } from "react-router-dom";
|
||||
import styles from "./header.module.css";
|
||||
|
||||
const TopHeader = () => {
|
||||
@@ -7,28 +8,36 @@ const TopHeader = () => {
|
||||
<div className="flex flex-col sm:hidden bg-[#5c2684]">
|
||||
<ul className="flex flex-col justify-center items-center pt-[0.4rem] text-[13px] font-light">
|
||||
{["Open An Account", "Internet Banking", "Contact Us"].map((text) => (
|
||||
<li key={text} className="w-full">
|
||||
<a href="#" className={`p-2 cursor-pointer text-white w-full items-center justify-center flex`}>
|
||||
<li key={text} className="w-full">
|
||||
<a
|
||||
href="#"
|
||||
className={`p-2 cursor-pointer text-white w-full items-center justify-center flex`}
|
||||
>
|
||||
{text}
|
||||
</a>
|
||||
</li>
|
||||
))}
|
||||
<li className="w-full flex items-center justify-center">
|
||||
<a href="#" className={`p-2 mt-2 flex gap-2 bg-[#74449E] cursor-pointer text-white w-full items-center justify-center`}>
|
||||
<a
|
||||
href="#"
|
||||
className={`p-2 mt-2 flex gap-2 bg-[#74449E] cursor-pointer text-white w-full items-center justify-center`}
|
||||
>
|
||||
<p className="uppercase">Today's Share price:</p>
|
||||
<span className="text-[#F8B51F] text-base md:text-lg">$ 4.00</span>
|
||||
</a>
|
||||
</li>
|
||||
<span className="text-[#F8B51F] text-base md:text-lg">
|
||||
$ 4.00
|
||||
</span>
|
||||
</a>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
<div className={styles.top_header}>
|
||||
<div className="containerMode flex justify-between w-full text-white font-medium text-[11px] md:text-[13px]">
|
||||
<ul className="flex items-center py-[0.4rem] flex-wrap">
|
||||
{top_header_data.map(({ id, name }) => (
|
||||
{top_header_data.map(({ id, name, href }) => (
|
||||
<li key={id}>
|
||||
<a href="#" className={`py-[11px] px-[15px]`}>
|
||||
<Link to={href} className={`py-[11px] px-[15px]`}>
|
||||
{name}
|
||||
</a>
|
||||
</Link>
|
||||
</li>
|
||||
))}
|
||||
</ul>
|
||||
|
||||
@@ -2,10 +2,10 @@ import styles from "./hero.module.css";
|
||||
const Hero = () => {
|
||||
return (
|
||||
<div
|
||||
className={`w-full relative mt-[.9375rem] mb-0 sm:mb-[2.25rem] regLap:h-[30rem] xl:h-[26.875rem] lg:h-[25rem] md:h-[21.875rem] sm:h-[18.75rem] h-[15.625rem] object-cover ${styles.heroBg}`}
|
||||
className={`w-full relative mb-0 sm:mb-[2.25rem] regLap:h-[30rem] xl:h-[26.875rem] lg:h-[25rem] md:h-[21.875rem] sm:h-[18.75rem] h-[15.625rem] object-cover ${styles.heroBg}`}
|
||||
>
|
||||
<div className="containerMode flex justify-between gap-1 xl:gap-8">
|
||||
<h1 className="max-w-[32.9375rem] font-extrabold text-[1.3rem] leading-[2.5rem] sm:text-[3.625rem] sm:leading-[4.3869rem] text-[#5C2684]">
|
||||
<h1 className="max-w-[32.9375rem] font-extrabold text-[1.3rem] leading-[2.5rem] sm:text-[3.625rem] sm:leading-[4.3869rem] text-[#5C2684] cursor-default">
|
||||
PREMIUM SALARY LOAN
|
||||
</h1>
|
||||
</div>
|
||||
|
||||
@@ -0,0 +1,36 @@
|
||||
import React from "react";
|
||||
import styles from "./hero.module.css";
|
||||
import { Link } from "react-router-dom";
|
||||
|
||||
interface PersonalHeroProps {
|
||||
heading?: string;
|
||||
body?: string;
|
||||
buttonLink?: string;
|
||||
buttonText?: string;
|
||||
}
|
||||
const PersonalHero: React.FC<PersonalHeroProps> = ({
|
||||
heading,
|
||||
body,
|
||||
buttonLink = "#",
|
||||
buttonText,
|
||||
}) => {
|
||||
return (
|
||||
<div
|
||||
className={`w-full max-[28.125rem] relative mb-0 sm:mb-[2.25rem] regLap:h-[30rem] xl:h-[26.875rem] lg:h-[25rem] md:h-[21.875rem] sm:h-[18.75rem] h-[15.625rem] object-cover ${styles.personalHeroBg}`}
|
||||
>
|
||||
<div className="containerMode flex justify-between gap-1 xl:gap-8 flex-col">
|
||||
<h1 className="max-w-[32.9375rem] font-extrabold text-[1.3rem] leading-[2.5rem] sm:text-[3.625rem] sm:leading-[4.3869rem] text-[#5C2684] cursor-default">
|
||||
{heading}
|
||||
</h1>
|
||||
<p className="p-[.3125rem] pr-5 font-medium table w-[25.375rem]">{body}</p>
|
||||
<Link to={buttonLink}>
|
||||
<button className="bg-[#A6368C] text-white text-[.9375rem] w-[10.9375rem] py-[.4375rem] px-[.625rem]">
|
||||
{buttonText}
|
||||
</button>
|
||||
</Link>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default PersonalHero;
|
||||
@@ -6,4 +6,14 @@
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
.personalHeroBg{
|
||||
background: url(../../../assets/images/personal-page.jpg) no-repeat;
|
||||
background-size: cover;
|
||||
background-position: center;
|
||||
/* padding: 0.4rem 0; */
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
}
|
||||
@@ -1,3 +1,4 @@
|
||||
import Hero from "./Hero";
|
||||
import PersonalHero from "./PersonalHero";
|
||||
|
||||
export { Hero };
|
||||
export { Hero, PersonalHero };
|
||||
|
||||
@@ -12,7 +12,7 @@ const EligiblityBox = () => {
|
||||
You must have a valid BVN
|
||||
</li>
|
||||
<li className="text-base leading-[1.5625rem]">
|
||||
Must have a salary or current bank account with FCMB
|
||||
Must have a salary or current bank account with {import.meta.env.VITE_BANK_NAME_SHORT}
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
@@ -1,11 +1,10 @@
|
||||
import { FC } from "react";
|
||||
import { Link } from "react-router-dom";
|
||||
import { RouteHandler } from "../../../router/routes";
|
||||
|
||||
const FeatureText: FC = () => {
|
||||
const FeatureText = () => {
|
||||
return (
|
||||
<div className="w-full sm:w-2/3 px-0 sm:px-[15px] flex flex-col">
|
||||
<div className="mt-5 text-[.9375rem] text-[#454545] leading-[1.4375rem]">
|
||||
<div className="mt-5 text-[.9375rem] text-[#454545] leading-[1.4375rem] cursor-default">
|
||||
<p className="mb-[.9375rem] text-justify sm:text-left">
|
||||
Premium Salary Plus loan provides confirmed staff of commercial
|
||||
organizations more usable funds. The employee’s organization must have
|
||||
@@ -28,14 +27,17 @@ const FeatureText: FC = () => {
|
||||
</ul>
|
||||
</div>
|
||||
<Link
|
||||
to={RouteHandler.getStarted}
|
||||
className="text-[#5C2684] hover:underline mt-[1.5625rem] w-fit"
|
||||
to={RouteHandler.letsGetStarted}
|
||||
className="text-[#5C2684] mt-[1.5625rem] w-fit"
|
||||
>
|
||||
***Click here to apply
|
||||
*** <span className="hover:underline">Click here to apply</span>
|
||||
</Link>
|
||||
<p className="mt-[.5625rem] font-bold cursor-default">
|
||||
<Link
|
||||
to={RouteHandler.termsAndConditions}
|
||||
className="mt-[.5625rem] font-bold"
|
||||
>
|
||||
Terms and conditions apply
|
||||
</p>
|
||||
</Link>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { Hero } from "./Hero";
|
||||
import { Hero, PersonalHero } from "./Hero";
|
||||
import { Requirements } from "./Requirements";
|
||||
|
||||
export {Hero, Requirements}
|
||||
export {Hero, Requirements, PersonalHero}
|
||||
@@ -1,37 +1,121 @@
|
||||
type Props = {
|
||||
name:string,
|
||||
fillColor?:string
|
||||
}
|
||||
import { FaCaretDown, FaCaretRight } from "react-icons/fa";
|
||||
|
||||
export default function Icons({name, fillColor}:Props) {
|
||||
import dashIcon from "../../assets/images/dashboard/dashDefault.svg";
|
||||
type Props = {
|
||||
name: string;
|
||||
fillColor?: string;
|
||||
className?:string;
|
||||
};
|
||||
|
||||
export default function Icons({ name, fillColor, className }: Props) {
|
||||
return (
|
||||
<>
|
||||
{name == 'home' ?
|
||||
<svg width="20" height="20" viewBox="0 0 20 20" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M14.1667 14.9875V19.9875H17.5C18.8807 19.9875 20 18.8682 20 17.4875V9.88673C20.0002 9.4538 19.832 9.03778 19.5308 8.72673L12.4492 1.07087C11.1996 -0.281086 9.09074 -0.364094 7.73879 0.885437C7.67457 0.944812 7.6127 1.00665 7.55336 1.07087L0.48418 8.72423C0.173945 9.03657 -0.000117128 9.45899 5.9134e-08 9.89923V17.4875C5.9134e-08 18.8682 1.1193 19.9875 2.5 19.9875H5.83332V14.9875C5.84891 12.7152 7.68355 10.8596 9.89867 10.8061C12.1879 10.7509 14.1492 12.6381 14.1667 14.9875Z" fill={fillColor ? fillColor : '#5C2684'}/>
|
||||
<path d="M10 12.4875C8.6193 12.4875 7.5 13.6068 7.5 14.9875V19.9875H12.5V14.9875C12.5 13.6068 11.3807 12.4875 10 12.4875Z" fill={fillColor ? fillColor : '#5C2684'}/>
|
||||
</svg>
|
||||
:name == 'profile'?
|
||||
<svg width="20" height="20" viewBox="0 0 20 20" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M10 10C12.7614 10 15 7.76142 15 5C15 2.23858 12.7614 0 10 0C7.23858 0 5 2.23858 5 5C5 7.76142 7.23858 10 10 10Z" fill={fillColor ? fillColor : '#5C2684'}/>
|
||||
<path d="M10 11.6667C5.85977 11.6713 2.50461 15.0265 2.5 19.1667C2.5 19.6269 2.87309 20 3.33332 20H16.6666C17.1269 20 17.5 19.6269 17.5 19.1667C17.4954 15.0265 14.1402 11.6713 10 11.6667Z" fill={fillColor ? fillColor : '#5C2684'}/>
|
||||
</svg>
|
||||
:name == 'verification'?
|
||||
<svg width="20" height="20" viewBox="0 0 20 20" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M19.728 6.8281L18.1812 5.28145C17.814 4.91432 17.2203 4.91432 16.857 5.28145L7.50177 14.6356L3.15031 10.2807C2.78313 9.91359 2.1894 9.91359 1.82613 10.2807L0.275384 11.8313C-0.0917946 12.1984 -0.0917946 12.7921 0.275384 13.1592L6.83772 19.7246C7.2049 20.0918 7.79864 20.0918 8.16191 19.7246L19.7241 8.15603C20.0913 7.78499 20.0913 7.19133 19.728 6.8281ZM7.06037 10.9681C7.30256 11.2142 7.70098 11.2142 7.94316 10.9681L16.068 2.8365C16.3101 2.59044 16.3101 2.19597 16.068 1.95382L14.3024 0.184543C14.0602 -0.0615144 13.6618 -0.0615144 13.4196 0.184543L7.50177 6.10164L5.33776 3.93399C5.09558 3.68794 4.69715 3.68794 4.45497 3.93399L2.68548 5.70327C2.4433 5.94932 2.4433 6.3438 2.68548 6.58595L7.06037 10.9681Z" fill={fillColor ? fillColor : '#5C2684'}/>
|
||||
</svg>
|
||||
:name == 'payments'?
|
||||
<svg width="20" height="16" viewBox="0 0 20 16" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M0 14.2857C0 15.2321 0.746528 16 1.66667 16H18.3333C19.2535 16 20 15.2321 20 14.2857V8H0V14.2857ZM6.66667 11.8571C6.66667 11.6214 6.85417 11.4286 7.08333 11.4286H11.8056C12.0347 11.4286 12.2222 11.6214 12.2222 11.8571V13.2857C12.2222 13.5214 12.0347 13.7143 11.8056 13.7143H7.08333C6.85417 13.7143 6.66667 13.5214 6.66667 13.2857V11.8571ZM2.22222 11.8571C2.22222 11.6214 2.40972 11.4286 2.63889 11.4286H5.13889C5.36806 11.4286 5.55556 11.6214 5.55556 11.8571V13.2857C5.55556 13.5214 5.36806 13.7143 5.13889 13.7143H2.63889C2.40972 13.7143 2.22222 13.5214 2.22222 13.2857V11.8571ZM20 1.71429V3.42857H0V1.71429C0 0.767857 0.746528 0 1.66667 0H18.3333C19.2535 0 20 0.767857 20 1.71429Z" fill={fillColor ? fillColor : '#5C2684'}/>
|
||||
</svg>
|
||||
:name == 'legals'?
|
||||
<svg width="20" height="16" viewBox="0 0 20 16" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M8 10.5H7.99937C7.99937 9.99438 8.04125 10.2272 5.34156 4.82781C4.79 3.725 3.21062 3.72281 2.65812 4.82781C-0.0643752 10.2734 0.000625 10.0103 0.000625 10.5H0C0 11.8806 1.79094 13 4 13C6.20906 13 8 11.8806 8 10.5ZM4 5.5L6.25 10H1.75L4 5.5ZM19.9994 10.5C19.9994 9.99438 20.0413 10.2272 17.3416 4.82781C16.79 3.725 15.2106 3.72281 14.6581 4.82781C11.9356 10.2734 12.0006 10.0103 12.0006 10.5H12C12 11.8806 13.7909 13 16 13C18.2091 13 20 11.8806 20 10.5H19.9994ZM13.75 10L16 5.5L18.25 10H13.75ZM16.5 14H11V4.78906C11.7347 4.4675 12.2863 3.80531 12.4497 3H16.5C16.7763 3 17 2.77625 17 2.5V1.5C17 1.22375 16.7763 1 16.5 1H11.9888C11.5325 0.39625 10.8153 0 10 0C9.18469 0 8.4675 0.39625 8.01125 1H3.5C3.22375 1 3 1.22375 3 1.5V2.5C3 2.77625 3.22375 3 3.5 3H7.55031C7.71375 3.805 8.265 4.4675 9 4.78906V14H3.5C3.22375 14 3 14.2238 3 14.5V15.5C3 15.7762 3.22375 16 3.5 16H16.5C16.7763 16 17 15.7762 17 15.5V14.5C17 14.2238 16.7763 14 16.5 14Z" fill={fillColor ? fillColor : '#5C2684'}/>
|
||||
</svg>
|
||||
:
|
||||
null
|
||||
}
|
||||
{name == "home" ? (
|
||||
<svg
|
||||
width="20"
|
||||
height="20"
|
||||
viewBox="0 0 20 20"
|
||||
fill="none"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
>
|
||||
<path
|
||||
d="M14.1667 14.9875V19.9875H17.5C18.8807 19.9875 20 18.8682 20 17.4875V9.88673C20.0002 9.4538 19.832 9.03778 19.5308 8.72673L12.4492 1.07087C11.1996 -0.281086 9.09074 -0.364094 7.73879 0.885437C7.67457 0.944812 7.6127 1.00665 7.55336 1.07087L0.48418 8.72423C0.173945 9.03657 -0.000117128 9.45899 5.9134e-08 9.89923V17.4875C5.9134e-08 18.8682 1.1193 19.9875 2.5 19.9875H5.83332V14.9875C5.84891 12.7152 7.68355 10.8596 9.89867 10.8061C12.1879 10.7509 14.1492 12.6381 14.1667 14.9875Z"
|
||||
fill={fillColor ? fillColor : "#5C2684"}
|
||||
/>
|
||||
<path
|
||||
d="M10 12.4875C8.6193 12.4875 7.5 13.6068 7.5 14.9875V19.9875H12.5V14.9875C12.5 13.6068 11.3807 12.4875 10 12.4875Z"
|
||||
fill={fillColor ? fillColor : "#5C2684"}
|
||||
/>
|
||||
</svg>
|
||||
) : name == "profile" ? (
|
||||
<svg
|
||||
width="20"
|
||||
height="20"
|
||||
viewBox="0 0 20 20"
|
||||
fill="none"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
>
|
||||
<path
|
||||
d="M10 10C12.7614 10 15 7.76142 15 5C15 2.23858 12.7614 0 10 0C7.23858 0 5 2.23858 5 5C5 7.76142 7.23858 10 10 10Z"
|
||||
fill={fillColor ? fillColor : "#5C2684"}
|
||||
/>
|
||||
<path
|
||||
d="M10 11.6667C5.85977 11.6713 2.50461 15.0265 2.5 19.1667C2.5 19.6269 2.87309 20 3.33332 20H16.6666C17.1269 20 17.5 19.6269 17.5 19.1667C17.4954 15.0265 14.1402 11.6713 10 11.6667Z"
|
||||
fill={fillColor ? fillColor : "#5C2684"}
|
||||
/>
|
||||
</svg>
|
||||
) : name == "verification" ? (
|
||||
<svg
|
||||
width="20"
|
||||
height="20"
|
||||
viewBox="0 0 20 20"
|
||||
fill="none"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
>
|
||||
<path
|
||||
d="M19.728 6.8281L18.1812 5.28145C17.814 4.91432 17.2203 4.91432 16.857 5.28145L7.50177 14.6356L3.15031 10.2807C2.78313 9.91359 2.1894 9.91359 1.82613 10.2807L0.275384 11.8313C-0.0917946 12.1984 -0.0917946 12.7921 0.275384 13.1592L6.83772 19.7246C7.2049 20.0918 7.79864 20.0918 8.16191 19.7246L19.7241 8.15603C20.0913 7.78499 20.0913 7.19133 19.728 6.8281ZM7.06037 10.9681C7.30256 11.2142 7.70098 11.2142 7.94316 10.9681L16.068 2.8365C16.3101 2.59044 16.3101 2.19597 16.068 1.95382L14.3024 0.184543C14.0602 -0.0615144 13.6618 -0.0615144 13.4196 0.184543L7.50177 6.10164L5.33776 3.93399C5.09558 3.68794 4.69715 3.68794 4.45497 3.93399L2.68548 5.70327C2.4433 5.94932 2.4433 6.3438 2.68548 6.58595L7.06037 10.9681Z"
|
||||
fill={fillColor ? fillColor : "#5C2684"}
|
||||
/>
|
||||
</svg>
|
||||
) : name == "payments" ? (
|
||||
<svg
|
||||
width="20"
|
||||
height="16"
|
||||
viewBox="0 0 20 16"
|
||||
fill="none"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
>
|
||||
<path
|
||||
d="M0 14.2857C0 15.2321 0.746528 16 1.66667 16H18.3333C19.2535 16 20 15.2321 20 14.2857V8H0V14.2857ZM6.66667 11.8571C6.66667 11.6214 6.85417 11.4286 7.08333 11.4286H11.8056C12.0347 11.4286 12.2222 11.6214 12.2222 11.8571V13.2857C12.2222 13.5214 12.0347 13.7143 11.8056 13.7143H7.08333C6.85417 13.7143 6.66667 13.5214 6.66667 13.2857V11.8571ZM2.22222 11.8571C2.22222 11.6214 2.40972 11.4286 2.63889 11.4286H5.13889C5.36806 11.4286 5.55556 11.6214 5.55556 11.8571V13.2857C5.55556 13.5214 5.36806 13.7143 5.13889 13.7143H2.63889C2.40972 13.7143 2.22222 13.5214 2.22222 13.2857V11.8571ZM20 1.71429V3.42857H0V1.71429C0 0.767857 0.746528 0 1.66667 0H18.3333C19.2535 0 20 0.767857 20 1.71429Z"
|
||||
fill={fillColor ? fillColor : "#5C2684"}
|
||||
/>
|
||||
</svg>
|
||||
) : name == "legals" ? (
|
||||
<svg
|
||||
width="20"
|
||||
height="16"
|
||||
viewBox="0 0 20 16"
|
||||
fill="none"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
>
|
||||
<path
|
||||
d="M8 10.5H7.99937C7.99937 9.99438 8.04125 10.2272 5.34156 4.82781C4.79 3.725 3.21062 3.72281 2.65812 4.82781C-0.0643752 10.2734 0.000625 10.0103 0.000625 10.5H0C0 11.8806 1.79094 13 4 13C6.20906 13 8 11.8806 8 10.5ZM4 5.5L6.25 10H1.75L4 5.5ZM19.9994 10.5C19.9994 9.99438 20.0413 10.2272 17.3416 4.82781C16.79 3.725 15.2106 3.72281 14.6581 4.82781C11.9356 10.2734 12.0006 10.0103 12.0006 10.5H12C12 11.8806 13.7909 13 16 13C18.2091 13 20 11.8806 20 10.5H19.9994ZM13.75 10L16 5.5L18.25 10H13.75ZM16.5 14H11V4.78906C11.7347 4.4675 12.2863 3.80531 12.4497 3H16.5C16.7763 3 17 2.77625 17 2.5V1.5C17 1.22375 16.7763 1 16.5 1H11.9888C11.5325 0.39625 10.8153 0 10 0C9.18469 0 8.4675 0.39625 8.01125 1H3.5C3.22375 1 3 1.22375 3 1.5V2.5C3 2.77625 3.22375 3 3.5 3H7.55031C7.71375 3.805 8.265 4.4675 9 4.78906V14H3.5C3.22375 14 3 14.2238 3 14.5V15.5C3 15.7762 3.22375 16 3.5 16H16.5C16.7763 16 17 15.7762 17 15.5V14.5C17 14.2238 16.7763 14 16.5 14Z"
|
||||
fill={fillColor ? fillColor : "#5C2684"}
|
||||
/>
|
||||
</svg>
|
||||
) : name == "arrow" ? (
|
||||
<svg
|
||||
width="15"
|
||||
height="15"
|
||||
viewBox="0 0 15 15"
|
||||
fill="none"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
>
|
||||
<path
|
||||
d="M6.3072 1.05822L7.03885 0.34847C7.34865 0.0479466 7.8496 0.0479466 8.15611 0.34847L14.563 6.56035C14.8728 6.86087 14.8728 7.34682 14.563 7.64415L8.15611 13.8592C7.84631 14.1597 7.34536 14.1597 7.03885 13.8592L6.3072 13.1495C5.9941 12.8458 6.00069 12.3502 6.32038 12.0529L10.2917 8.38267H0.819787C0.381453 8.38267 0.0288086 8.04058 0.0288086 7.61538V6.59232C0.0288086 6.16711 0.381453 5.82503 0.819787 5.82503H10.2917L6.32038 2.15481C5.9974 1.85748 5.99081 1.36194 6.3072 1.05822Z"
|
||||
fill={fillColor ? fillColor : "#FBB700"}
|
||||
/>
|
||||
</svg>
|
||||
) : name == "greater-than" ? (
|
||||
<svg
|
||||
width="11"
|
||||
height="16"
|
||||
viewBox="0 0 11 16"
|
||||
fill="none"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
>
|
||||
<path
|
||||
d="M9.74365 8.64502L3.10303 15.2856C2.64404 15.7446 1.90186 15.7446 1.44775 15.2856L0.344238 14.1821C-0.114746 13.7231 -0.114746 12.981 0.344238 12.5269L5.05127 7.81982L0.344238 3.11279C-0.114746 2.65381 -0.114746 1.91162 0.344238 1.45752L1.44287 0.344238C1.90186 -0.114746 2.64404 -0.114746 3.09814 0.344238L9.73877 6.98486C10.2026 7.44385 10.2026 8.18604 9.74365 8.64502Z"
|
||||
fill={fillColor ? fillColor : "#FFF"}
|
||||
/>
|
||||
</svg>
|
||||
) :name == 'arrow-down'?
|
||||
<FaCaretDown className={`text-xl ${className && className}`} />
|
||||
:name == 'arrow-right'?
|
||||
<FaCaretRight className={`text-xl ${className && className}`} />
|
||||
:name == "dash-icon" ? (
|
||||
<img src={dashIcon} alt="dash-icon" />
|
||||
) : null}
|
||||
</>
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,9 @@
|
||||
import React from 'react'
|
||||
|
||||
const InternetBanking: React.FC = () => {
|
||||
return (
|
||||
<div>InternetBanking</div>
|
||||
)
|
||||
}
|
||||
|
||||
export default InternetBanking
|
||||
@@ -0,0 +1,3 @@
|
||||
import InternetBanking from "./InternetBanking";
|
||||
|
||||
export { InternetBanking };
|
||||
@@ -0,0 +1,260 @@
|
||||
import React from 'react';
|
||||
import * as Yup from 'yup';
|
||||
import { Form, Formik } from 'formik';
|
||||
import { InputCompOne } from '..';
|
||||
import { useNavigate } from 'react-router-dom';
|
||||
import { RouteHandler } from '../../router/routes';
|
||||
|
||||
import { useDispatch } from 'react-redux';
|
||||
import { updateUserDetails } from '../../store/UserDetails';
|
||||
|
||||
import { validateBVN, verifyOTP } from '../../core/apiRequest';
|
||||
import { RequestStatus } from '../../core/models';
|
||||
|
||||
// To get the validation schema
|
||||
const validationSchema = Yup.object().shape({
|
||||
bvn: Yup.string()
|
||||
.required('BVN is required')
|
||||
.test('no-e', 'Invalid number', (value: any) => {
|
||||
if (value && /^[0-9]*$/.test(value) == false) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
})
|
||||
.min(11, 'must be 11 digits')
|
||||
.max(11, 'must be 11 digits'),
|
||||
otp: Yup.string()
|
||||
// .when('require_otp', {
|
||||
// is: true,
|
||||
// then: Yup.string().required("OTP is required")
|
||||
// })
|
||||
// .required("OTP is required")
|
||||
.test('no-e', 'Invalid number', (value: any) => {
|
||||
if (value && /^[0-9]*$/.test(value) == false) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
})
|
||||
.min(5, 'must be 5 digits')
|
||||
.max(5, 'must be 5 digits'),
|
||||
// .test("no-e", "must be 11 characters", (value:any) => {
|
||||
// if (value.length < 11) {
|
||||
// return false;
|
||||
// }
|
||||
// return true;
|
||||
// })
|
||||
});
|
||||
|
||||
// initial values for formik
|
||||
let initialValues = {
|
||||
bvn: '',
|
||||
otp: '',
|
||||
};
|
||||
|
||||
type ValidBVN = {
|
||||
verification_id: string;
|
||||
valid: undefined | boolean;
|
||||
};
|
||||
|
||||
const LetsGetStarted: React.FC = () => {
|
||||
const dispatch = useDispatch();
|
||||
const navigate = useNavigate();
|
||||
// const [pinValues, setPinValues] = React.useState({
|
||||
// bvn: "",
|
||||
// otp: "",
|
||||
// });
|
||||
// const otpInputRef = React.useRef<HTMLInputElement>(null);
|
||||
|
||||
const [requestStatusBVN, setRequestStatusBVN] = React.useState<RequestStatus>(
|
||||
{ loading: false, status: undefined, message: '' }
|
||||
);
|
||||
|
||||
const [requestStatusOTP, setRequestStatusOTP] = React.useState<RequestStatus>(
|
||||
{ loading: false, status: undefined, message: '' }
|
||||
);
|
||||
|
||||
const [bvnIsValid, setBvnIsValid] = React.useState<ValidBVN>({
|
||||
verification_id: '',
|
||||
valid: undefined,
|
||||
});
|
||||
|
||||
// e: React.FormEvent<HTMLInputElement>
|
||||
// let { value } = e.target as HTMLInputElement;
|
||||
const bvnValidation = (values: any) => {
|
||||
// Function to Validate BVN
|
||||
let bvn = values.bvn;
|
||||
setRequestStatusBVN({ loading: true, status: false, message: '' });
|
||||
validateBVN({ bvn })
|
||||
.then((res) => {
|
||||
if (!res || !res.data.call_return) {
|
||||
setBvnIsValid({ verification_id: '', valid: false });
|
||||
setRequestStatusBVN({
|
||||
loading: false,
|
||||
status: false,
|
||||
message: 'unable to verify BVN',
|
||||
});
|
||||
return setTimeout(() => {
|
||||
setRequestStatusBVN({ loading: false, status: false, message: '' });
|
||||
}, 4000);
|
||||
}
|
||||
setBvnIsValid({
|
||||
verification_id: res.data.verification_id,
|
||||
valid: true,
|
||||
});
|
||||
setRequestStatusBVN({
|
||||
loading: false,
|
||||
status: true,
|
||||
message: 'verified',
|
||||
});
|
||||
})
|
||||
.catch((err) => {
|
||||
setBvnIsValid({ verification_id: '', valid: false });
|
||||
console.log(err);
|
||||
});
|
||||
};
|
||||
|
||||
const handleSubmit = (values: any) => {
|
||||
// Function to VERIFY OTP AND LOGIN USER
|
||||
setRequestStatusOTP({ loading: true, status: false, message: '' });
|
||||
// console.log('values', values)
|
||||
verifyOTP({ ...values, verification_id: bvnIsValid.verification_id })
|
||||
.then((res) => {
|
||||
if (!res || !res.data.call_return) {
|
||||
setRequestStatusOTP({
|
||||
loading: false,
|
||||
status: false,
|
||||
message: 'wrong otp',
|
||||
});
|
||||
return setTimeout(() => {
|
||||
setRequestStatusOTP({ loading: false, status: false, message: '' });
|
||||
}, 4000);
|
||||
}
|
||||
// console.log(res.data)
|
||||
localStorage.setItem('token', res.data?.token);
|
||||
localStorage.setItem('uid', res?.data?.customer[0]?.uid);
|
||||
dispatch(updateUserDetails({ ...res?.data?.customer[0] }));
|
||||
navigate(RouteHandler.dashboardHome, { replace: true });
|
||||
})
|
||||
.catch((err) => {
|
||||
setRequestStatusOTP({
|
||||
loading: false,
|
||||
status: false,
|
||||
message: 'something went wrong, try again',
|
||||
});
|
||||
console.log(err);
|
||||
return setTimeout(() => {
|
||||
setRequestStatusOTP({ loading: false, status: false, message: '' });
|
||||
}, 4000);
|
||||
});
|
||||
};
|
||||
|
||||
return (
|
||||
<Formik
|
||||
initialValues={initialValues}
|
||||
validationSchema={validationSchema}
|
||||
onSubmit={bvnIsValid.valid ? handleSubmit : bvnValidation}
|
||||
>
|
||||
{(props: any) => (
|
||||
<Form className="">
|
||||
<div className="w-full">
|
||||
<div className="containerMode flex justify-between gap-1 xl:gap-8 flex-col">
|
||||
<div className="my-[4rem] flex items-center justify-center w-full">
|
||||
<h1 className="font-bold text-[2.375rem] text-[#5C2684] my-[.5rem] text-center">
|
||||
Let’s Get You Started
|
||||
</h1>
|
||||
</div>
|
||||
<div className="mx-auto flex flex-col gap-8 max-w-[31.625rem] ">
|
||||
<div className="w-full">
|
||||
<InputCompOne
|
||||
parentClass="flex flex-col gap-2"
|
||||
label="Enter Your BVN "
|
||||
name="bvn"
|
||||
parentInputClass="w-full"
|
||||
labelSpan="( To get your BVN, dial *565*0# )"
|
||||
labelSpanClass="text-[13px] text-[#5a5a5a] font-semibold"
|
||||
placeholder="Enter your BVN"
|
||||
labelClass="font-bold text-[18px] leading-[21.78px] tracking-[2%] text-[#282828] mb-[2px] flex item-center gap-[4px]"
|
||||
input
|
||||
inputClass="w-full h-[3.625rem] rounded bg-[#EFEFEF] px-4"
|
||||
value={props.values.bvn}
|
||||
onChange={props.handleChange}
|
||||
error={
|
||||
props.errors.bvn && props.touched.bvn && props.errors.bvn
|
||||
}
|
||||
maxLength={11}
|
||||
/>
|
||||
<p
|
||||
className={`p-2 ${
|
||||
!requestStatusBVN.status
|
||||
? 'text-red-500'
|
||||
: 'text-emerald-500'
|
||||
}`}
|
||||
>
|
||||
{requestStatusBVN.loading
|
||||
? 'verifying...'
|
||||
: requestStatusBVN.message}
|
||||
</p>
|
||||
</div>
|
||||
{bvnIsValid.valid && (
|
||||
<InputCompOne
|
||||
parentClass="flex flex-col gap-2"
|
||||
label="Enter OTP "
|
||||
name="otp"
|
||||
parentInputClass="w-full"
|
||||
labelSpan="( Please check your BVN phone number for verification pin )"
|
||||
labelSpanClass="text-[13px] text-[#5a5a5a] font-semibold"
|
||||
placeholder="Enter your OTP"
|
||||
labelClass="font-bold text-[18px] leading-[21.78px] tracking-[2%] text-[#282828] mb-[2px] flex item-center gap-[4px]"
|
||||
input
|
||||
inputClass="w-full h-[3.625rem] rounded bg-[#EFEFEF] px-4"
|
||||
value={props.values.otp}
|
||||
onChange={props.handleChange}
|
||||
error={
|
||||
props.errors.otp && props.touched.otp && props.errors.otp
|
||||
}
|
||||
maxLength={5}
|
||||
/>
|
||||
)}
|
||||
<button
|
||||
type="submit"
|
||||
className="w-full h-[3.625rem] rounded bg-[#FBB700] rounded-2 px-4 text-[18px] text-[#282828] font-semibold disabled:text-[#282828] disabled:text-opacity-50"
|
||||
disabled={
|
||||
requestStatusBVN.loading ||
|
||||
(!props.values.otp && bvnIsValid.valid)
|
||||
}
|
||||
>
|
||||
Enter
|
||||
</button>
|
||||
|
||||
<p
|
||||
className={`p-2 ${
|
||||
!requestStatusOTP.status
|
||||
? 'text-red-500'
|
||||
: 'text-emerald-500'
|
||||
}`}
|
||||
>
|
||||
{requestStatusOTP.message}
|
||||
</p>
|
||||
|
||||
{bvnIsValid.valid || bvnIsValid.valid == undefined ? (
|
||||
<p className="text-[#5C2684] mt-[1.5625rem] w-fit">
|
||||
***Every personal information attached to your BVN is safe
|
||||
and secure. It is only important for us to verify your
|
||||
information and also give you access to your application
|
||||
profile/account.
|
||||
</p>
|
||||
) : (
|
||||
<p className="text-[#5C2684] mt-[1.5625rem] w-fit">
|
||||
***Did not receive OTP? Click to resend
|
||||
</p>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</Form>
|
||||
)}
|
||||
</Formik>
|
||||
);
|
||||
};
|
||||
|
||||
export default LetsGetStarted;
|
||||
@@ -0,0 +1,22 @@
|
||||
import React from "react";
|
||||
import { Link } from "react-router-dom";
|
||||
import Logo from "../../assets/icons/logo.svg";
|
||||
|
||||
|
||||
const LetsGetStartedNav: React.FC = () => {
|
||||
return (
|
||||
<div className="w-full">
|
||||
<div className="containerMode flex justify-between gap-1 xl:gap-8">
|
||||
<Link to="/">
|
||||
<img
|
||||
src={Logo}
|
||||
alt="Logo"
|
||||
className="w-[52px] h-[43px] xl:w-[72px] xl:h-[63px]"
|
||||
/>
|
||||
</Link>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default LetsGetStartedNav;
|
||||
@@ -0,0 +1,4 @@
|
||||
import LetsGetStarted from "./LetsGetStarted";
|
||||
import LetsGetStartedNav from "./LetsGetStartedNav";
|
||||
|
||||
export { LetsGetStarted, LetsGetStartedNav };
|
||||
@@ -0,0 +1,30 @@
|
||||
type Props ={
|
||||
text: string
|
||||
onClick?: ()=>any
|
||||
className?: string
|
||||
shrinkAside?: boolean
|
||||
icon?: string
|
||||
loading?: boolean
|
||||
disabled?: boolean
|
||||
}
|
||||
|
||||
export default function MainBtn({
|
||||
onClick,
|
||||
className,
|
||||
text,
|
||||
shrinkAside,
|
||||
icon,
|
||||
loading,
|
||||
disabled
|
||||
}:Props) {
|
||||
return (
|
||||
<button
|
||||
disabled={disabled}
|
||||
className={`py-3 px-4 rounded-md ${className || ''} ${(disabled || loading) && 'opacity-60'}`}
|
||||
onClick={onClick}
|
||||
>
|
||||
{icon && <i className={`fa-solid ${icon}`}></i>}
|
||||
{shrinkAside ? '' : loading? 'Loading...' : text}
|
||||
</button>
|
||||
)
|
||||
}
|
||||
@@ -0,0 +1,64 @@
|
||||
import {useEffect, useState} from 'react'
|
||||
import { getPaymentDetails } from '../../core/apiRequest'
|
||||
|
||||
type Props = {
|
||||
reference: string | null
|
||||
}
|
||||
|
||||
// type PaymentPayloads = {
|
||||
// uid?: string
|
||||
// event?: string
|
||||
// customer_code?: string
|
||||
// plan_name?: string
|
||||
// plan_code?: string
|
||||
// subscription_code?: string | null,
|
||||
// amount?: string
|
||||
// authorization_code?: string
|
||||
// gateway_response?: string
|
||||
// gateway_status?: string
|
||||
// reference?: string
|
||||
// added?: string
|
||||
// }
|
||||
|
||||
export default function Payment({reference}:Props) {
|
||||
|
||||
const [paymentDetails, setPaymentDetails] = useState<any>({
|
||||
loading: true,
|
||||
data: {}
|
||||
})
|
||||
|
||||
useEffect(()=>{
|
||||
getPaymentDetails({reference}).then(res => {
|
||||
setPaymentDetails({loading:false, data:res?.data?.payment})
|
||||
console.log(res?.data?.payment)
|
||||
}).catch(err => {
|
||||
setPaymentDetails({loading:false, data:{}})
|
||||
console.log(err)
|
||||
})
|
||||
},[])
|
||||
|
||||
return (
|
||||
<div className='w-full'>
|
||||
<div className='p-4'>
|
||||
<h1 className='p-2 mb-3 text-2xl'>Confirmation</h1>
|
||||
<div className='p-8 w-full max-w-2xl bg-white shadow-md rounded-md'>
|
||||
{paymentDetails.loading ?
|
||||
<p>Loading...</p>
|
||||
:
|
||||
(paymentDetails?.data && Object.keys(paymentDetails?.data).length > 0) ?
|
||||
<>
|
||||
{Object.keys(paymentDetails?.data).map((item) => (
|
||||
<div key={item} className='p-2 flex gap-1'>
|
||||
<p className='w-64 font-semibold'>{item}</p>
|
||||
<p className=''>{paymentDetails?.data[item]}</p>
|
||||
</div>
|
||||
))}
|
||||
</>
|
||||
:
|
||||
<p className='p-2'>No Payment Found!</p>
|
||||
}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
@@ -0,0 +1,428 @@
|
||||
import React from "react";
|
||||
|
||||
const Main: React.FC = () => {
|
||||
return (
|
||||
<div className="w-full my-3">
|
||||
<div className="containerMode">
|
||||
<p className="mb-[15px]">
|
||||
<strong>PRIVACY POLICY</strong>
|
||||
</p>
|
||||
<p className="mb-[15px]">
|
||||
<strong>
|
||||
<span>1.Your Privacy is important to us.</span>
|
||||
</strong>
|
||||
</p>
|
||||
<p className="mb-[15px]">
|
||||
This privacy statement sets out the privacy policy of
|
||||
fcmbgroupplc.com, which provides a portal, or gateway, to the
|
||||
financial services offered by the First City Monument Bank Limited and
|
||||
the other members of the FCMB Group Plc (Collectively, “FCMB”).
|
||||
</p>
|
||||
<p className="mb-[15px]">
|
||||
This policy explains how we collect, share, use, and protect
|
||||
information when you visit or use this website and any other online
|
||||
services, platforms, or products offered by FCMB or any of its banking
|
||||
and non-banking affiliates and subsidiaries that link to or reference
|
||||
this policy (collectively, our “services”).
|
||||
</p>
|
||||
<p className="mb-[15px]">
|
||||
<strong>1.1 FCMB and You</strong>
|
||||
</p>
|
||||
<p className="mb-[15px]">
|
||||
First City Monument Bank Limited is a private limited liability
|
||||
company registered in the Federal Republic of Nigeria under RC No.
|
||||
46713. Its head-office is at Primrose Tower, 17A Tinubu Street, Lagos
|
||||
State, Nigeria.
|
||||
</p>
|
||||
<p className="mb-[15px]">
|
||||
FCMB Group Plc hosts the fcmbgroupplc.com website and provides
|
||||
technical support, access and links to the Local Sites of First City
|
||||
Group members. fcmbgroupplc.com does not offer financial services or
|
||||
products. Financial services and products may only be obtained by
|
||||
registering with a Local Site. The First City Group provides financial
|
||||
products and services to a global clientele through its affiliated
|
||||
companies and branches located in 36 states and the Federal Capital
|
||||
Territory in Nigeria, and in the UK. Privacy and personal data
|
||||
protection principles vary from one country to another. When you
|
||||
access or link to a Local Site, please read the privacy statement
|
||||
issued by the Local Site to determine the policies that apply to
|
||||
information or data maintained by the Local Site.
|
||||
</p>
|
||||
<p className="mb-[15px]">
|
||||
<strong>
|
||||
<span>2. Information we may collect about you </span>
|
||||
</strong>
|
||||
</p>
|
||||
<p className="mb-[15px]">
|
||||
<strong>2.1 Your Personal Information</strong>
|
||||
</p>
|
||||
<p className="mb-[15px]">
|
||||
At FCMB, we strive to meet your needs and provide you with exceptional
|
||||
services. In the course of consuming our services through various
|
||||
channels, such as forms, phone calls, correspondence, service point
|
||||
interfaces, and other available channels, we collect information that
|
||||
you provide to us. This information may include, but is not limited
|
||||
to, contact data, log/Technical information, Financial Data, Marketing
|
||||
and Communications Data, identity verification details (this includes
|
||||
Personally Identifiable Information (PII), otherwise known as Personal
|
||||
Information or Personal Data, which includes email address, phone
|
||||
number, contact address, limited financial information, location data,
|
||||
device data etc.) and documents, services consumed or desired, mode of
|
||||
consumption, preferences, location, general events, and instructions
|
||||
and transactions relating to the services.
|
||||
</p>
|
||||
<p className="mb-[15px]">
|
||||
The lawful basis we rely on for processing your Personal Information
|
||||
are:
|
||||
</p>
|
||||
<ol>
|
||||
<li>
|
||||
Your Consent: Where you agree to us collecting your Personal
|
||||
Information by using our Services.
|
||||
</li>
|
||||
<li>
|
||||
We have a contractual obligation: Without your Personal Information,
|
||||
we cannot provide our Services to you.
|
||||
</li>
|
||||
<li>
|
||||
We have a legal obligation: To ensure we are fully compliant with
|
||||
all applicable financial legislations such as Anti-Money Laundering
|
||||
and Countering the Financing of Terrorism (AML/CFT) Laws, we must
|
||||
collect and store your Personal Information. We protect against
|
||||
fraud by checking your identity with your Personal Information.
|
||||
</li>
|
||||
</ol>
|
||||
<p className="mb-[15px]">
|
||||
Additionally, to better serve your needs, we may utilize information
|
||||
about you collected from third parties and service partners. It is
|
||||
important to note that these third-party sources are not under the
|
||||
control of FCMB, and we are not responsible for how they use the
|
||||
information.
|
||||
</p>
|
||||
<p className="mb-[15px]">
|
||||
<strong>2.2 Usage and other information</strong>
|
||||
</p>
|
||||
<p className="mb-[15px]">
|
||||
In addition to the personal information described above, we may
|
||||
collect certain information about your use of our online services. For
|
||||
example, we may capture the IP address of the device you use to
|
||||
connect to the online service, the type of operating system and
|
||||
browser you use, and information about the site you came from, the
|
||||
parts of our online service you access, and the site you visit next.
|
||||
FCMB or our third-party partners may also use cookies, web beacons or
|
||||
other technologies to collect and store other information about your
|
||||
visit to, or use of, our online services. In addition, we may later
|
||||
associate the usage and other information we collect online with
|
||||
personal information about you.
|
||||
</p>
|
||||
<p className="mb-[15px]">
|
||||
<strong>2.3 FCMB Mobile</strong>
|
||||
</p>
|
||||
<p className="mb-[15px]">
|
||||
For the convenience of our FCMB customers, we provide access to our
|
||||
products and services through our mobile applications and
|
||||
mobile-optimized websites ("FCMB Mobile"). When you engage with us
|
||||
through FCMB Mobile, we may collect certain information to enhance
|
||||
your experience. This information may include unique device
|
||||
identifiers for your mobile device, screen resolution, device
|
||||
settings, location information, and analytical data regarding your
|
||||
mobile device usage. Please note that we may request your permission
|
||||
before collecting specific information, such as precise geo-location
|
||||
data,- contact or image data and other personal identifiable
|
||||
information through FCMB Mobile. Rest assured that any information
|
||||
collected is handled with the utmost care and in accordance with our
|
||||
privacy policy.
|
||||
</p>
|
||||
<p className="mb-[15px]">
|
||||
<strong>2.4 Additional sources of information</strong>
|
||||
</p>
|
||||
<p className="mb-[15px]">
|
||||
We may also collect information about you from additional online and
|
||||
offline sources including from co-branded partner sites or
|
||||
commercially available third-party sources, such as credit reporting
|
||||
agencies. We may combine this information with the personal and other
|
||||
information we have collected about you under this Privacy Policy.
|
||||
</p>
|
||||
<p className="mb-[15px]">
|
||||
<strong>2.5 Non-Personal Information</strong>
|
||||
</p>
|
||||
<p className="mb-[15px]">
|
||||
In order to achieve our goal of providing you with the best-in-class
|
||||
service, we may also collect, store, use and transfer non-personal
|
||||
information or anonymized data such as statistical or demographic
|
||||
data. These may be collected or sourced during your visits to perform
|
||||
certain tasks such as grant you access to some parts of our web site
|
||||
or conduct research on your behaviour on our site in order to improve
|
||||
our services. We will not disclose your information to any person
|
||||
outside our organization except as described in this Privacy Policy.
|
||||
</p>
|
||||
<p className="mb-[15px]">
|
||||
<strong>
|
||||
<span>3. Our Use of Information</span>
|
||||
</strong>
|
||||
</p>
|
||||
<p className="mb-[15px]">
|
||||
FCMB and/or subsidiaries may use or process the information discussed
|
||||
above in a number of ways, such as to:
|
||||
</p>
|
||||
<ol>
|
||||
<li>Manage your preferences;</li>
|
||||
<li>
|
||||
Create and manage any accounts or transactions you may have with us,
|
||||
verify your identity, provide our services, and respond to your
|
||||
inquiries;
|
||||
</li>
|
||||
<li>
|
||||
Process your applications and transactions (including authorization,
|
||||
clearing, chargebacks and other related dispute resolution
|
||||
activities);
|
||||
</li>
|
||||
<li>
|
||||
Protect against and prevent fraud, unauthorized transactions, claims
|
||||
and other liabilities as well as enhance the security of your
|
||||
account or our online services;
|
||||
</li>
|
||||
<li>
|
||||
Provide, administer and communicate with you about our products,
|
||||
services, offers, programs and promotions as well as those of our
|
||||
merchants and partners;
|
||||
</li>
|
||||
<li>
|
||||
Evaluate your interest in employment and contact you regarding
|
||||
possible employment with FCMB;
|
||||
</li>
|
||||
<li>
|
||||
Evaluate and improve our business, including developing new products
|
||||
and services;
|
||||
</li>
|
||||
<li>To target advertisements, newsletters, and service updates;</li>
|
||||
<li>As necessary to establish, exercise and defend legal rights;</li>
|
||||
<li>
|
||||
Perform analytics concerning your use of our online services,
|
||||
including your responses to our emails and the pages and
|
||||
advertisements you view;
|
||||
</li>
|
||||
<li>
|
||||
As may be required by applicable laws and regulations, including for
|
||||
compliance with Know Your Customers and risk assessment, Anti-Money
|
||||
Laundering, anti-corruption and sanctions screening requirements, or
|
||||
as requested by any judicial process, law enforcement or
|
||||
governmental agency having or claiming jurisdiction over FCMB or
|
||||
affiliates;
|
||||
</li>
|
||||
<li>
|
||||
To use data analytics to improve our Website, products, or services,
|
||||
and user experiences;
|
||||
</li>
|
||||
<li>
|
||||
For other purposes for which we provide specific notice at the time
|
||||
you provide or we collect your information.
|
||||
</li>
|
||||
</ol>
|
||||
<p className="mb-[15px]">
|
||||
We may also use data that we collect on an aggregate or anonymous
|
||||
basis (such that it does not identify any individual customers) for
|
||||
various business purposes, where permissible under applicable laws and
|
||||
regulations.
|
||||
</p>
|
||||
<p className="mb-[15px]">
|
||||
<strong>
|
||||
<span>4. Cookies</span>
|
||||
</strong>
|
||||
</p>
|
||||
<p className="mb-[15px]">
|
||||
This website, along with most other major websites, uses cookies.
|
||||
Cookies are pieces of information that a website transfers to the
|
||||
cookie file on your computer’s hard disk. Cookies enable users to
|
||||
navigate around the website and (where appropriate) enable us to
|
||||
tailor the content to fit the needs of visitors who have accessed the
|
||||
site.
|
||||
</p>
|
||||
<p className="mb-[15px]">
|
||||
Firstcitygroup.com uses two types of cookies on this website:
|
||||
</p>
|
||||
<ol>
|
||||
<li>
|
||||
Session cookies, which are temporary cookies that remain in the
|
||||
cookie file of your computer until you close your browser (at which
|
||||
point they are deleted).
|
||||
</li>
|
||||
<li>
|
||||
Persistent or stored cookies that remain permanently on the cookie
|
||||
file of your computer.
|
||||
</li>
|
||||
</ol>
|
||||
<p className="mb-[15px]">
|
||||
Cookies cannot look into your computer and obtain information about
|
||||
you or your family or read any material kept on your hard drive and,
|
||||
unless you have logged onto an authenticated page, cookies cannot be
|
||||
used to identify who you are.
|
||||
</p>
|
||||
<p className="mb-[15px]">
|
||||
Cookies cannot be used by anyone else who has access to the computer
|
||||
to find out anything about you, other than the fact that someone using
|
||||
the computer has visited a certain website. Cookies do not in any way
|
||||
compromise the security of your computer.
|
||||
</p>
|
||||
<p className="mb-[15px]">
|
||||
Cookies will not be used to contact you for marketing purposes other
|
||||
than by means of advertisements offered within this website.
|
||||
</p>
|
||||
<p className="mb-[15px]">
|
||||
Cookies may be used to record details of pages relating to particular
|
||||
products and services that you have visited on this website. This is
|
||||
to provide fcmb.com with generic usage statistics to allow the company
|
||||
to improve this website and to provide you with information that may
|
||||
interest you.
|
||||
</p>
|
||||
<p className="mb-[15px]">
|
||||
The web browsers of most computers are initially set up to accept
|
||||
cookies. If you prefer, you can set your web browser to disable
|
||||
cookies or to inform you when a website is attempting to add a cookie.
|
||||
You can also delete cookies that have previously been added to your
|
||||
computer’s cookie file.
|
||||
</p>
|
||||
<p className="mb-[15px]">
|
||||
You can set your browser to disable persistent cookies and/or session
|
||||
cookies but if you disable session cookies, although you will be able
|
||||
to view this website’s unsecured pages, you may not be able to log
|
||||
onto any authenticated pages.
|
||||
</p>
|
||||
<p className="mb-[15px]">
|
||||
Please visit{" "}
|
||||
<a href="http://www.allaboutcookies.org/manage-cookies/">
|
||||
http://www.allaboutcookies.org/manage-cookies/
|
||||
</a>{" "}
|
||||
to discover how to disable and delete cookies.
|
||||
</p>
|
||||
<p className="mb-[15px]">
|
||||
<strong>
|
||||
<span>5. Disclosures</span>
|
||||
</strong>
|
||||
</p>
|
||||
<p className="mb-[15px]">
|
||||
<strong>5.1 Disclosures</strong>
|
||||
</p>
|
||||
<p className="mb-[15px]">
|
||||
We may divulge individual data to any individual performing review,
|
||||
lawful, operational, or different services for us. We will utilize
|
||||
data which does not identify the person for these exercises at
|
||||
whatever point achievable. Data divulged to vendors or contractors for
|
||||
operational purposes may not be re-disclosed to others by such a
|
||||
vendor or contractor. We may reveal individual data when needed to do
|
||||
as such by a court request, or court order. We may divulge individual
|
||||
data as we esteem it proper to secure the wellbeing of our customers
|
||||
or for an investigation identified with open security or to report an
|
||||
action that has all the earmarks of being disregarding law. We may
|
||||
divulge individual data to ensure the security and dependability of
|
||||
this site and to take safety measures against accountability.
|
||||
</p>
|
||||
<p className="mb-[15px]">
|
||||
<strong>5.2 Disclosures to Third Parties</strong>
|
||||
</p>
|
||||
<p className="mb-[15px]">
|
||||
Data about you that is accessible to you by means of fcmb.com,
|
||||
including your personal data, can become subject to the legal systems
|
||||
and laws in force in the country where the data is held, received or
|
||||
stored by you or us. Such data can become subject to disclosure
|
||||
pursuant to the laws of the country.
|
||||
</p>
|
||||
<p className="mb-[15px]">
|
||||
We may reveal your name and other personal data and other monetary
|
||||
data about you at the request of regulatory agency or in connection
|
||||
with an examination of us as a bank. This information could be
|
||||
revealed to internal and external attorneys or auditors, and to others
|
||||
whom we are required to make such revelations.
|
||||
</p>
|
||||
<p className="mb-[15px]">
|
||||
<strong>
|
||||
<span>6. Information Security and Retention</span>
|
||||
</strong>
|
||||
</p>
|
||||
<p className="mb-[15px]">
|
||||
At FCMB, we are fully committed to protecting the information we
|
||||
collect. We maintain administrative, technical, and physical controls
|
||||
to actively safeguard the Personal Information you provide or we
|
||||
collect. These controls are designed to protect against loss, theft,
|
||||
unauthorized access, disclosure, copying, misuse, or modification.
|
||||
</p>
|
||||
<p className="mb-[15px]">
|
||||
Our security measures actively include secure servers, firewalls, data
|
||||
encryption, and restricted access granted only to employees for
|
||||
fulfilling their job responsibilities.
|
||||
</p>
|
||||
<p className="mb-[15px]">
|
||||
When using a password for any of your accounts, it is essential that
|
||||
you actively ensure its confidentiality and refrain from sharing it
|
||||
with anyone.
|
||||
</p>
|
||||
<p className="mb-[15px]">
|
||||
We actively conduct our business in accordance with these principles
|
||||
to actively ensure the confidentiality and protection of your Personal
|
||||
Information. While transmitting information online may not be entirely
|
||||
secure, we actively take all reasonable steps to ensure the security
|
||||
and protection of your Personal Information.
|
||||
</p>
|
||||
<p className="mb-[15px]">
|
||||
We will only retain personal information on our servers for as long as
|
||||
it is actively necessary while providing services to you. In the event
|
||||
you close your account, we actively store your information on our
|
||||
servers to comply with regulatory obligations and actively monitor,
|
||||
detect, and prevent fraud. Any retention of your Personal Data is
|
||||
solely for such length of time as may be required by law, regulation,
|
||||
and the internal policies of FCMB, her members and/or affiliates.
|
||||
</p>
|
||||
<p className="mb-[15px]">
|
||||
<strong>6.1 Data Protection on the Internet</strong>
|
||||
</p>
|
||||
<p className="mb-[15px]">
|
||||
At FCMB we utilize encryption innovation to ensure the transmission of
|
||||
data to or from you by means of fcmb.com. For security reasons and to
|
||||
protect the security of your information, access to fcmb.com is
|
||||
restricted to authorized users only. However, because information
|
||||
about you, your account data and other transactions can be accessed
|
||||
through a public network, the Internet, there can be no guarantee that
|
||||
your account information will remain secure and you accept the risk
|
||||
that unauthorized persons may view such information. If you believe
|
||||
that an unauthorized person has accessed your information, please
|
||||
contact the Bank immediately.
|
||||
</p>
|
||||
<p className="mb-[15px]">
|
||||
<strong>
|
||||
<span>7. Updates to this Policy</span>
|
||||
</strong>
|
||||
</p>
|
||||
<p className="mb-[15px]">
|
||||
From time to time, we may change, amend or review this Privacy Policy
|
||||
from time to time to reflect new services or changes in our Privacy
|
||||
Policy and place any updates on this page. All changes made will be
|
||||
posted on this page and where changes will materially affect you, we
|
||||
will notify you of this change by placing a notice online or via mail.
|
||||
If you keep using our Services, you consent to all amendments of this
|
||||
Privacy Policy.
|
||||
</p>
|
||||
<p className="mb-[15px]">
|
||||
<strong>
|
||||
<span>8. Contact us</span>
|
||||
</strong>
|
||||
</p>
|
||||
<p className="mb-[15px]">
|
||||
For issues relating to personal data, please contact us via any of the
|
||||
below:
|
||||
</p>
|
||||
<p className="mb-[15px]">
|
||||
Corporate Address: Primrose Tower, 17A, Tinubu Street, Marina, Lagos
|
||||
</p>
|
||||
<p className="mb-[15px]">Telephone: 07003290000, 01-2798800</p>
|
||||
<p className="mb-[15px]">
|
||||
Email: <a href="#">customerservice@fcmb.com</a>
|
||||
</p>
|
||||
<p className="mb-[15px]">
|
||||
Whatsapp: (+234) 09099999814 or (+234) 09099999815
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default Main;
|
||||
@@ -0,0 +1,3 @@
|
||||
import Main from "./Main";
|
||||
|
||||
export { Main as TsAndCs };
|
||||
@@ -3,5 +3,9 @@ export * from "./Home";
|
||||
export * from "./GetStarted";
|
||||
export * from "./shared";
|
||||
export * from "./Footer";
|
||||
export * from "./DashboardLayout";
|
||||
// export * from "./DashboardLayout";
|
||||
export * from "./Icons";
|
||||
export * from "./Dashboard";
|
||||
export * from "./Cards";
|
||||
export * from "./LetsGetStated";
|
||||
export * from "./TsAndCs";
|
||||
|
||||
@@ -0,0 +1,14 @@
|
||||
import { ReactNode } from "react"
|
||||
|
||||
type Props = {
|
||||
children: ReactNode
|
||||
}
|
||||
|
||||
export default function ModalWrapper({children}:Props) {
|
||||
return (
|
||||
<div className="z-50 fixed inset-0">
|
||||
<div className="bg-black/50 fixed inset-0"></div>
|
||||
<div className="relative h-full flex flex-col justify-center items-center">{children}</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
@@ -0,0 +1,134 @@
|
||||
import { ReactNode, useEffect, useState } from "react";
|
||||
import { PendingTableList } from "../../core/models";
|
||||
|
||||
type PaginatedListProps = {
|
||||
data: Array<PendingTableList>,
|
||||
itemsPerPage?: number,
|
||||
filterItem?: string[],
|
||||
tableTitle?: string,
|
||||
titleClass?:string,
|
||||
children: (data:PendingTableList) => ReactNode;
|
||||
}
|
||||
|
||||
export default function PendingList({
|
||||
data,
|
||||
itemsPerPage = 5,
|
||||
filterItem,
|
||||
tableTitle,
|
||||
titleClass,
|
||||
children,
|
||||
}:PaginatedListProps) {
|
||||
const [searchTerm, setSearchTerm] = useState("");
|
||||
const [filteredData, setFilteredData] = useState(data);
|
||||
|
||||
const [currentPage, setCurrentPage] = useState(0);
|
||||
const [newData, setNewData] = useState<any>([]);
|
||||
|
||||
const numberOfSelection = itemsPerPage;
|
||||
|
||||
const handlePrev = () => {
|
||||
if (currentPage != 0) {
|
||||
setCurrentPage((prev:any) => prev - numberOfSelection);
|
||||
}
|
||||
};
|
||||
const handleNext = () => {
|
||||
if (currentPage < data.length) {
|
||||
setCurrentPage((prev:any) => prev + numberOfSelection);
|
||||
}
|
||||
};
|
||||
|
||||
const handleSearch = ({ target: { value } }:{target: {value:string}}, name:string) => {
|
||||
setSearchTerm(value);
|
||||
let newFilteredData:any = data.filter((item:any) =>
|
||||
item[name].toLowerCase().startsWith(value.toLowerCase())
|
||||
);
|
||||
setFilteredData(newFilteredData);
|
||||
setCurrentPage(0);
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
setNewData(
|
||||
filteredData?.slice(currentPage, numberOfSelection + currentPage)
|
||||
);
|
||||
}, [currentPage, filteredData]);
|
||||
|
||||
useEffect(()=>{
|
||||
setCurrentPage(0)
|
||||
},[itemsPerPage])
|
||||
|
||||
return (
|
||||
<div className="w-full">
|
||||
<h1 className={`text-2xl mb-5 font-semibold ${titleClass && titleClass}`}>{tableTitle}</h1>
|
||||
|
||||
{data.length > 0 && filterItem && (
|
||||
<div className="mb-10 flex justify-end items-center gap-2">
|
||||
{filterItem.map((item, index) => (
|
||||
<label
|
||||
key={index}
|
||||
className="flex flex-col sm:flex-row items-center gap-2 text-slate-600 dark:text-slate-100 transition-all duration-500"
|
||||
>
|
||||
Search by {item[0].toUpperCase() + item.slice(1)}
|
||||
<input
|
||||
name={item}
|
||||
type="text"
|
||||
className="py-1 px-2 text-sm min-w-[100px] text-black dark:text-white bg-white dark:bg-slate-800 rounded-full border-0 outline-none ring-1 ring-slate-300 dark:ring-white transition-all duration-500"
|
||||
value={searchTerm}
|
||||
onChange={(e) => {
|
||||
handleSearch(e, item);
|
||||
}}
|
||||
/>
|
||||
</label>
|
||||
))}
|
||||
</div>
|
||||
)}
|
||||
|
||||
{children(newData)}
|
||||
|
||||
{/* show prev and next button if data exist */}
|
||||
{(data.length > 0 && data.length > itemsPerPage) && (
|
||||
<div className="mt-5 md:mt-10 w-full flex gap-4 justify-center items-center">
|
||||
<button
|
||||
onClick={handlePrev}
|
||||
className={`w-6 h-6 md:w-12 md:h-12 text-sm md:text-lg rounded-full flex justify-center items-center transition-all duration-300 ${
|
||||
currentPage == 0
|
||||
? "text-slate-400 border-slate-400 dark:text-slate-400 dark:border-slate-400 pointer-events-none"
|
||||
: "text-slate-600 border-slate-600 dark:text-white dark:border-white"
|
||||
}`}
|
||||
>
|
||||
<
|
||||
</button>
|
||||
|
||||
{data.length && data.map((item, index)=>{
|
||||
item = item
|
||||
if(index%itemsPerPage == 0 && index >= currentPage && index <= currentPage+itemsPerPage){
|
||||
return (
|
||||
<button
|
||||
key={index}
|
||||
onClick={handleNext}
|
||||
className={`w-6 h-6 md:w-12 md:h-12 text-sm md:text-lg rounded-full flex justify-center items-center border transition-all duration-300 ${
|
||||
currentPage != index
|
||||
? "text-slate-400 border-slate-400 dark:text-slate-400 dark:border-slate-400"
|
||||
: "text-slate-600 border-slate-600 dark:text-white dark:border-white pointer-events-none"
|
||||
}`}
|
||||
>
|
||||
{index/itemsPerPage +1}
|
||||
</button>
|
||||
)
|
||||
}
|
||||
})}
|
||||
|
||||
<button
|
||||
onClick={handleNext}
|
||||
className={`w-6 h-6 md:w-12 md:h-12 text-sm md:text-lg rounded-full flex justify-center items-center transition-all duration-300 ${
|
||||
currentPage + numberOfSelection >= data.length
|
||||
? "text-slate-400 border-slate-400 dark:text-slate-400 dark:border-slate-400 pointer-events-none"
|
||||
: "text-slate-600 border-slate-600 dark:text-white dark:border-white"
|
||||
}`}
|
||||
>
|
||||
>
|
||||
</button>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -1,11 +1,27 @@
|
||||
import React from "react";
|
||||
|
||||
type ButtonProps = {
|
||||
className?: string;
|
||||
interface ButtonProps {
|
||||
text: string;
|
||||
};
|
||||
className?: string;
|
||||
type?: "button" | "submit" | "reset";
|
||||
onClick?: React.MouseEventHandler<HTMLButtonElement>;
|
||||
}
|
||||
|
||||
const Button = ({ text, className }: ButtonProps) => {
|
||||
return <button className={`btn-primary uppercase text-[11px] lg:text-[13px] p-[6px] lg:px-[10px] ${className}`}>{text}</button>;
|
||||
const Button: React.FC<ButtonProps> = ({
|
||||
text,
|
||||
className,
|
||||
onClick,
|
||||
type = "button",
|
||||
}) => {
|
||||
return (
|
||||
<button
|
||||
className={`btn-primary uppercase text-[11px] lg:text-[13px] p-[6px] lg:px-[10px] ${className}`}
|
||||
onClick={onClick}
|
||||
type={type}
|
||||
>
|
||||
{text}
|
||||
</button>
|
||||
);
|
||||
};
|
||||
|
||||
export default Button;
|
||||
|
||||
@@ -0,0 +1,154 @@
|
||||
import React, { forwardRef } from "react";
|
||||
import { Icons } from "../Icons";
|
||||
|
||||
export interface InputCompOneProps {
|
||||
label?: string;
|
||||
labelClass?: string;
|
||||
labelSpan?: string;
|
||||
labelSpanClass?: string;
|
||||
floatLabel?: string;
|
||||
placeholder?: string;
|
||||
value?: string | any;
|
||||
onChange?: (e:any) => any;
|
||||
onInput?: (e:any) => any;
|
||||
name: string;
|
||||
tabIndex?: number;
|
||||
ref?: React.RefObject<HTMLInputElement>;
|
||||
selectValue?: string;
|
||||
input?: boolean;
|
||||
select?: boolean;
|
||||
selectOptions?: {loading:boolean, data:{ [index: string]: string; }[]} | any;
|
||||
inputType?: string;
|
||||
inputClass?: string;
|
||||
parentInputClass?: string;
|
||||
selectClass?: string;
|
||||
parentClass?: string;
|
||||
maxLength?: number;
|
||||
error?: string;
|
||||
disabled?: boolean
|
||||
}
|
||||
|
||||
const InputCompOne = forwardRef<HTMLInputElement, InputCompOneProps>(
|
||||
(
|
||||
{
|
||||
label,
|
||||
labelClass,
|
||||
labelSpan,
|
||||
labelSpanClass,
|
||||
floatLabel,
|
||||
placeholder,
|
||||
value,
|
||||
onChange,
|
||||
onInput,
|
||||
name,
|
||||
tabIndex,
|
||||
selectValue,
|
||||
input = false,
|
||||
select = false,
|
||||
selectOptions = {loading:false, data:[]},
|
||||
inputType = "text",
|
||||
inputClass,
|
||||
parentInputClass,
|
||||
selectClass,
|
||||
parentClass,
|
||||
maxLength,
|
||||
error,
|
||||
disabled=false
|
||||
},
|
||||
forwardedRef
|
||||
) => {
|
||||
return (
|
||||
<div className={parentClass}>
|
||||
{label && (
|
||||
<label htmlFor={label ? label : floatLabel} className={`flex gap-2 items-center flex-wrap ${labelClass}`}>
|
||||
{label}
|
||||
{labelSpan && <span className={labelSpanClass}>{labelSpan}</span>}
|
||||
{error && label && <span className='text-[10px] text-red-500'>{error}</span>}
|
||||
</label>
|
||||
)}
|
||||
{input && (
|
||||
<div className={`relative ${parentInputClass}`}>
|
||||
<input
|
||||
type={inputType}
|
||||
placeholder={placeholder}
|
||||
value={value}
|
||||
onChange={onChange}
|
||||
onInput={onInput}
|
||||
name={name}
|
||||
tabIndex={tabIndex}
|
||||
ref={forwardedRef}
|
||||
className={`px-4 ${floatLabel && 'peer pt-4 placeholder:text-transparent'} ${inputClass}`}
|
||||
maxLength={maxLength}
|
||||
id={label ? label : floatLabel}
|
||||
disabled={disabled}
|
||||
/>
|
||||
{floatLabel &&
|
||||
<label
|
||||
htmlFor={label ? label : floatLabel}
|
||||
className={`flex items-center gap-2 cursor-pointer text-sm text-black/70 dark:text-white absolute left-4 top-0 translate-y-0 peer-focus:top-0 peer-focus:translate-y-0 peer-placeholder-shown:top-1/2 peer-placeholder-shown:-translate-y-1/2 transition-all duration-500`}
|
||||
>
|
||||
{floatLabel}
|
||||
{error && floatLabel && !label && <span className='text-[10px] text-red-500'>{error}</span>}
|
||||
</label>
|
||||
}
|
||||
</div>
|
||||
)}
|
||||
{select && (
|
||||
<div className={`relative ${parentInputClass}`}>
|
||||
<select
|
||||
name={name}
|
||||
id={label ? label : floatLabel}
|
||||
value={selectValue}
|
||||
className={`px-4 appearance-none ${floatLabel && 'peer pt-4'} ${selectClass} ${disabled && 'opacity-50'}`}
|
||||
onChange={onChange}
|
||||
disabled={disabled}
|
||||
>
|
||||
{typeof(selectOptions) == 'string' ?
|
||||
<option value={selectValue}>{selectValue}</option>
|
||||
:selectOptions.loading ?
|
||||
<option value=''>Loading...</option>
|
||||
: selectOptions.data.length && name == 'employer_uid' ?
|
||||
<>
|
||||
<option value=''>Please Select</option>
|
||||
{selectOptions.data.map(({ uid, name }:any) => (
|
||||
<option key={uid} value={uid}>
|
||||
{name}
|
||||
</option>
|
||||
))}
|
||||
</>
|
||||
: selectOptions.data.length && name != 'employer_uid' ?
|
||||
selectOptions.data.map(({ value, label }:any) => (
|
||||
<option key={value} value={value}>
|
||||
{label}
|
||||
</option>
|
||||
))
|
||||
:
|
||||
<option value=''>Not Found</option>
|
||||
}
|
||||
{/* {selectOptions.map(({ value, label }) => (
|
||||
<option key={value} value={value}>
|
||||
{label}
|
||||
</option>
|
||||
))} */}
|
||||
</select>
|
||||
{floatLabel &&
|
||||
<label
|
||||
htmlFor={label ? label : floatLabel}
|
||||
className={`flex items-center gap-2 cursor-pointer text-sm text-black/70 dark:text-white absolute left-4 top-0 translate-y-0 peer-focus:top-0 peer-focus:translate-y-0 transition-all duration-500`}
|
||||
>
|
||||
{floatLabel}
|
||||
{error && floatLabel && !label && <span className='text-[10px] text-red-500'>{error}</span>}
|
||||
</label>
|
||||
}
|
||||
{/* select custon arrow */}
|
||||
<div className='absolute right-4 top-1/2 -translate-y-1/2'>
|
||||
<Icons name='arrow-down' />
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
);
|
||||
|
||||
export default InputCompOne;
|
||||
@@ -0,0 +1,25 @@
|
||||
import React from "react";
|
||||
|
||||
interface StepperProps {
|
||||
step: number;
|
||||
}
|
||||
|
||||
const Stepper: React.FC<StepperProps> = ({ step = 0 }) => {
|
||||
// const [activeStep, setActiveStep] = useState(step);
|
||||
|
||||
return (
|
||||
<div className="flex justify-between items-center gap-5">
|
||||
{[...Array(5)].map((_, index) => (
|
||||
<div
|
||||
key={index}
|
||||
className={`w-[1.875rem] border-[.1875rem] rounded-sm ${(step === index
|
||||
? "border-[#E8B4FF]"
|
||||
: "border-[#5C2684]")}`}
|
||||
// onClick={() => setActiveStep(index)}
|
||||
/>
|
||||
))}
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default Stepper;
|
||||
@@ -1,4 +1,6 @@
|
||||
import Button from "./Button";
|
||||
import InputCompOne from "./InputCompOne";
|
||||
import FloatLabelInput from "./FloatLabelInput";
|
||||
import Stepper from "./Stepper";
|
||||
|
||||
export {Button, FloatLabelInput}
|
||||
export { Button, FloatLabelInput, InputCompOne, Stepper };
|
||||
|
||||
@@ -0,0 +1,149 @@
|
||||
import { ReactNode, useEffect, useState } from "react";
|
||||
import MainBtn from "../MainBtn";
|
||||
|
||||
const data1:{ name: string; email: string; status: string; location: string; }[] = [];
|
||||
|
||||
type PaginatedListProps = {
|
||||
data: any,
|
||||
itemsPerPage: number,
|
||||
filterItem?: string[],
|
||||
titleClass?:string,
|
||||
children: (data:any) => ReactNode;
|
||||
}
|
||||
|
||||
export default function TableWrapper({
|
||||
data = data1,
|
||||
itemsPerPage = 5,
|
||||
filterItem,
|
||||
children,
|
||||
}:PaginatedListProps) {
|
||||
const [isLoading, setIsLoading] = useState(true)
|
||||
const [searchTerm, setSearchTerm] = useState("");
|
||||
const [filteredData, setFilteredData] = useState(data);
|
||||
|
||||
const [currentPage, setCurrentPage] = useState(0);
|
||||
const [newData, setNewData] = useState<{ name: string; email: string; status: string; location: string; }[]>([]);
|
||||
|
||||
const numberOfSelection = itemsPerPage;
|
||||
|
||||
const handlePrev = () => {
|
||||
if (currentPage != 0) {
|
||||
setCurrentPage((prev:any) => prev - numberOfSelection);
|
||||
}
|
||||
};
|
||||
const handleNext = () => {
|
||||
if (currentPage < data.length) {
|
||||
setCurrentPage((prev:any) => prev + numberOfSelection);
|
||||
}
|
||||
};
|
||||
|
||||
const handleSearch = ({ target: { value } }:{target: {value:string}}, name:string) => {
|
||||
setSearchTerm(value);
|
||||
let newFilteredData:any = data.filter((item:any) =>
|
||||
item[name].toLowerCase().startsWith(value.toLowerCase())
|
||||
);
|
||||
setFilteredData(newFilteredData);
|
||||
setCurrentPage(0);
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
setIsLoading(true)
|
||||
setTimeout(()=>{
|
||||
setNewData(
|
||||
filteredData?.slice(currentPage, numberOfSelection + currentPage)
|
||||
);
|
||||
setIsLoading(false)
|
||||
},1000)
|
||||
}, [currentPage, filteredData]);
|
||||
|
||||
useEffect(()=>{
|
||||
setCurrentPage(0)
|
||||
},[itemsPerPage])
|
||||
|
||||
return (
|
||||
<div className="p-2 w-full bg-white border-b dark:bg-gray-800 rounded-md">
|
||||
{data.length > 0 && filterItem && (
|
||||
<div className="mb-10 flex justify-end items-center gap-2">
|
||||
{filterItem.map((item, index) => (
|
||||
<label
|
||||
key={index}
|
||||
className="flex flex-col sm:flex-row items-center gap-2 text-slate-600 dark:text-slate-100 transition-all duration-500"
|
||||
>
|
||||
Search by {item[0].toUpperCase() + item.slice(1)}
|
||||
<input
|
||||
name={item}
|
||||
type="text"
|
||||
className="py-1 px-2 text-sm min-w-[100px] text-black dark:text-white bg-white dark:bg-slate-800 rounded-full border-0 outline-none ring-1 ring-slate-300 dark:ring-white transition-all duration-500"
|
||||
value={searchTerm}
|
||||
onChange={(e) => {
|
||||
handleSearch(e, item);
|
||||
}}
|
||||
/>
|
||||
</label>
|
||||
))}
|
||||
</div>
|
||||
)}
|
||||
|
||||
<div className="flex flex-col">
|
||||
<div className="w-full">
|
||||
{children({ data: newData })}
|
||||
</div>
|
||||
|
||||
{/* PAGINATION BUTTON */}
|
||||
{(newData.length > 0 && data.length > itemsPerPage) &&
|
||||
<div className='p-2 w-full flex flex-col lg:flex-row justify-center items-center gap-3 md:gap-6'>
|
||||
<div className="text-sm text-center lg:text-left font-normal text-gray-500 dark:text-gray-400 block w-full">Showing <span className="font-semibold text-gray-900 dark:text-white">
|
||||
{isLoading ? '----' : `${currentPage + 1}-${currentPage + numberOfSelection >= data.length ? data.length : (currentPage + numberOfSelection)}`}</span> of <span className="font-semibold text-gray-900 dark:text-white">{data.length}</span>
|
||||
</div>
|
||||
<div className='flex items-center gap-3 md:gap-6'>
|
||||
<MainBtn
|
||||
onClick={handlePrev}
|
||||
text='Prev'
|
||||
className={`${currentPage == 0 ? 'bg-sky-600/50 pointer-events-none' : 'bg-sky-600'} text-white-light`}
|
||||
disabled={isLoading}
|
||||
/>
|
||||
<MainBtn
|
||||
onClick={handleNext}
|
||||
text='Next'
|
||||
className={`${currentPage + numberOfSelection >= data.length ? 'bg-sky-600/50 pointer-events-none' : 'bg-sky-600'} text-white-light`}
|
||||
disabled={isLoading}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
}
|
||||
</div>
|
||||
|
||||
{/* show prev and next button if data exist */}
|
||||
{/* {data.length > 0 && (
|
||||
{data.length && data.map((item, index)=>{
|
||||
item = item
|
||||
if(index%itemsPerPage == 0 && index >= currentPage && index <= currentPage+itemsPerPage){
|
||||
return (
|
||||
<button
|
||||
key={index}
|
||||
onClick={handleNext}
|
||||
className={`w-6 h-6 md:w-12 md:h-12 text-sm md:text-lg rounded-full flex justify-center items-center border transition-all duration-300 ${
|
||||
currentPage != index
|
||||
? "text-slate-400 border-slate-400 dark:text-slate-400 dark:border-slate-400"
|
||||
: "text-slate-600 border-slate-600 dark:text-white dark:border-white pointer-events-none"
|
||||
}`}
|
||||
>
|
||||
{index/itemsPerPage +1}
|
||||
</button>
|
||||
)
|
||||
}
|
||||
})}
|
||||
</div>
|
||||
)} */}
|
||||
{isLoading && <TableIsLoading />}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
const TableIsLoading = () => {
|
||||
return (
|
||||
<div className="w-full fixed z-[991] inset-0 flex justify-center items-center bg-white/50">
|
||||
<p className="rounded-md shadow-md p-4 bg-white/90 dark:bg-gray-900 text-brown dark:text-white">Loading...</p>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
@@ -0,0 +1,94 @@
|
||||
import { postAuxEnd, getAuxEnd } from "./axiosCall";
|
||||
|
||||
// FUNCTION TO START BVN VALIDATION
|
||||
export const validateBVN = (postData:any) => {
|
||||
let reqData = {
|
||||
...postData
|
||||
}
|
||||
return postAuxEnd('/bvn', reqData)
|
||||
}
|
||||
|
||||
|
||||
// FUNCTION TO VERIFY OTP AND LOGIN
|
||||
export const verifyOTP = (postData:any) => {
|
||||
let reqData = {
|
||||
...postData
|
||||
}
|
||||
return postAuxEnd('/bvn/verify', reqData)
|
||||
}
|
||||
|
||||
// FUNCTION TO APPLY FOR LOAN
|
||||
export const applyForLoan = (postData:any) => {
|
||||
let reqData = {
|
||||
customer_uid: localStorage.getItem('uid'),
|
||||
...postData
|
||||
}
|
||||
return postAuxEnd('/loan/apply', reqData)
|
||||
}
|
||||
|
||||
// FUNCTION TO GET USER BY CUSTOMER UID
|
||||
export const getUserByID = (uid:string) => {
|
||||
let reqData = {
|
||||
// customer_uid: localStorage.getItem('uid'),
|
||||
}
|
||||
return getAuxEnd(`/profile?uid=${uid}`, reqData)
|
||||
}
|
||||
|
||||
// FUNCTION TO GET USER BY CUSTOMER UID
|
||||
export const getUserPendingLoanList = (uid:string) => {
|
||||
let reqData = {
|
||||
// customer_uid: localStorage.getItem('uid'),
|
||||
}
|
||||
return getAuxEnd(`/dash?uid=${uid}`, reqData)
|
||||
}
|
||||
|
||||
// FUNCTION TO GET LIST OF EMPLOYERS
|
||||
export const getEmployersList = () => {
|
||||
let reqData = {
|
||||
// customer_uid: localStorage.getItem('uid'),
|
||||
}
|
||||
return getAuxEnd(`/employers`, reqData)
|
||||
}
|
||||
|
||||
// FUNCTION TO GET USER EMPLOYER
|
||||
export const getEmployer = () => {
|
||||
let reqData = {
|
||||
uid: localStorage.getItem('uid'),
|
||||
}
|
||||
return getAuxEnd(`/dash/employer?uid=${reqData?.uid}`, null)
|
||||
}
|
||||
|
||||
// FUNCTION TO GET LOAN DETAILS
|
||||
export const getLoanDetail = (postData:any) => {
|
||||
let reqData = {
|
||||
uid: localStorage.getItem('uid'),
|
||||
...postData
|
||||
}
|
||||
return getAuxEnd(`/loan/loandetail?uid=${reqData?.uid}&application_uid=${reqData?.application_uid}`, null)
|
||||
}
|
||||
|
||||
// FUNCTION TO GET PAYMENT DETAILS
|
||||
export const getPaymentDetails = (postData:any) => {
|
||||
let reqData = {
|
||||
uid: localStorage.getItem('uid'),
|
||||
...postData
|
||||
}
|
||||
return getAuxEnd(`/payment/status?uid=${reqData?.uid}&reference=${reqData?.reference}`, null)
|
||||
}
|
||||
|
||||
// FUNCTION TO ADD CARD
|
||||
export const addCard = (postData:any) => {
|
||||
let reqData = {
|
||||
uid: localStorage.getItem('uid'),
|
||||
...postData
|
||||
}
|
||||
return postAuxEnd('/addcard', reqData)
|
||||
}
|
||||
|
||||
// FUNCTION TO GET USER AGREEMENTS LIST
|
||||
export const getAgreementsList = () => {
|
||||
let reqData = {
|
||||
uid: localStorage.getItem('uid'),
|
||||
}
|
||||
return getAuxEnd(`/agreements?uid=${reqData?.uid}`, null)
|
||||
}
|
||||
@@ -0,0 +1,81 @@
|
||||
import axios from "axios";
|
||||
|
||||
export function postAuxEnd(uri: string, reqData: any): Promise<any> {
|
||||
const endPoint = import.meta.env.VITE_USERS_ENDPOINT + uri;
|
||||
const formData = new FormData();
|
||||
for (let value in reqData) {
|
||||
if (typeof reqData[value] === "object") {
|
||||
// for (let innerValue in reqData[value]) {
|
||||
// let innerReqData = reqData[value]
|
||||
// console.log('SAMPLE',innerReqData)
|
||||
// formData.append(reqData[value][innerValue], reqData[value][innerValue]);
|
||||
// }
|
||||
formData.append(value, JSON.stringify(reqData[value]));
|
||||
// formData.append(value, reqData[value]);
|
||||
} else {
|
||||
formData.append(value, reqData[value]);
|
||||
}
|
||||
}
|
||||
return axios
|
||||
.post(endPoint, formData)
|
||||
.then((response: {}) => {
|
||||
// if (response.data.internal_return == "-9999") {
|
||||
// localStorage.clear();
|
||||
// window.location.href = `/login?sessionExpired=true`;
|
||||
// }
|
||||
return response;
|
||||
})
|
||||
.catch((error: any) => {
|
||||
if (error.response) {
|
||||
//response status is an error code
|
||||
console.log(
|
||||
"ERROR-------------------------------------------------------"
|
||||
);
|
||||
console.log(error.response.status);
|
||||
console.log(
|
||||
"ERROR-------------------------------------------------------"
|
||||
);
|
||||
} else if (error.request) {
|
||||
//response not received though the request was sent
|
||||
console.log(
|
||||
"ERROR2-------------------------------------------------------"
|
||||
);
|
||||
console.log(error?.request);
|
||||
console.log(
|
||||
"ERROR2-------------------------------------------------------"
|
||||
);
|
||||
} else {
|
||||
//an error occurred when setting up the request
|
||||
console.log(
|
||||
"ERROR3-------------------------------------------------------"
|
||||
);
|
||||
console.log(error);
|
||||
console.log(
|
||||
"ERROR3-------------------------------------------------------"
|
||||
);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
export function getAuxEnd(uri: string, reqData: any): Promise<any> {
|
||||
const endPoint = import.meta.env.VITE_USERS_ENDPOINT + uri;
|
||||
const formData = new FormData();
|
||||
for (let value in reqData) {
|
||||
formData.append(value, reqData[value]);
|
||||
}
|
||||
return axios
|
||||
.get(endPoint, reqData)
|
||||
.then((response: {}) => {
|
||||
// if (response.data.internal_return == "-9999") {
|
||||
// localStorage.clear();
|
||||
// window.location.href = `/login?sessionExpired=true`;
|
||||
// }
|
||||
return response;
|
||||
})
|
||||
.catch((error: any) => {
|
||||
console.log(
|
||||
"ERROR3-------------------------------------------------------", error
|
||||
);
|
||||
});
|
||||
}
|
||||
@@ -0,0 +1,32 @@
|
||||
export interface RequestStatus {
|
||||
loading?:boolean
|
||||
status?:boolean | undefined
|
||||
message?:string
|
||||
name?:string
|
||||
data?:{}[] | [any] | {}
|
||||
}
|
||||
|
||||
|
||||
export interface User {
|
||||
firstname?:string
|
||||
lastname?:string
|
||||
last_login?:string
|
||||
message?:string
|
||||
token?:string
|
||||
customer_uid?:string
|
||||
call_return?:string
|
||||
}
|
||||
|
||||
|
||||
export type PendingTableList = {
|
||||
status?: string | boolean
|
||||
application_uid?: string
|
||||
added?: string
|
||||
loan_amount?: string
|
||||
payment_month?: string
|
||||
status_text?: {
|
||||
text?: string
|
||||
button?: boolean
|
||||
advise?: string
|
||||
}
|
||||
};
|
||||
@@ -14,9 +14,36 @@ body {
|
||||
|
||||
@layer components {
|
||||
.containerMode {
|
||||
@apply container mx-auto px-5 xxs:max-w-full sm:max-w-[98%] lg:max-w-[1100px];
|
||||
/* @apply container mx-auto px-5 xxs:max-w-full sm:max-w-[98%] lg:max-w-[1100px]; */
|
||||
@apply container mx-auto px-5 max-w-[1500px]
|
||||
}
|
||||
.dash-bg-image{
|
||||
background: url('../src/assets/images/dashboard/bg_ellipse1.png') right top no-repeat, url('../src/assets/images/dashboard/bg_ellipse2.png') -8% bottom no-repeat;;
|
||||
|
||||
.custom-btn{@apply min-w-[100px] transition-all duration-300 p-2 rounded-full}
|
||||
|
||||
|
||||
/* MODAL COMPONENT */
|
||||
.modal-container{
|
||||
animation-name: zoom;
|
||||
animation-duration: .2s;
|
||||
animation-timing-function: linear;
|
||||
@apply w-4/5 max-w-[600px] bg-white shadow-md rounded-2xl overflow-hidden
|
||||
}
|
||||
.modal-header {
|
||||
@apply w-full flex items-center justify-between p-5 py-8 border-b bg-sky-500
|
||||
}
|
||||
.modal-title {
|
||||
@apply text-2xl leading-8 font-bold text-white dark:text-white tracking-wide flex items-center
|
||||
}
|
||||
.modal-close-btn {
|
||||
@apply text-white dark:text-red-500
|
||||
}
|
||||
.modal-body {
|
||||
@apply w-full p-5 min-h-[150px] max-h-[500px] overflow-y-auto break-words flex flex-col justify-center items-center
|
||||
}
|
||||
.modal-footer {
|
||||
@apply w-full p-5 border-t flex justify-between items-center gap-4
|
||||
}
|
||||
/* MODAL COMPONENT */
|
||||
|
||||
|
||||
}
|
||||
@@ -0,0 +1,225 @@
|
||||
import { useState, useEffect } from 'react';
|
||||
import { Link, useLocation } from 'react-router-dom';
|
||||
import Logo from '../../assets/icons/logo.svg';
|
||||
import { Icons } from '../../components';
|
||||
import { contactDetails } from '../../utils/data';
|
||||
|
||||
type Props = {
|
||||
asideDisplay?: () => void;
|
||||
logoutUser: () => void;
|
||||
};
|
||||
|
||||
export default function Aside({ asideDisplay, logoutUser }: Props) {
|
||||
const { pathname } = useLocation();
|
||||
|
||||
const [openNestedLink, setOpenNestedLink] = useState<{ name: string | null }>(
|
||||
{ name: '' }
|
||||
);
|
||||
|
||||
const handleOpenNestedLink = (e: any) => {
|
||||
if (!e || !e.target) {
|
||||
return setOpenNestedLink({ name: '' });
|
||||
}
|
||||
if (openNestedLink.name && openNestedLink.name == e.target.name) {
|
||||
setOpenNestedLink({ name: '' });
|
||||
} else {
|
||||
setOpenNestedLink({ name: e.target.name });
|
||||
}
|
||||
};
|
||||
|
||||
// Track user activity
|
||||
useEffect(() => {
|
||||
let timeout: number;
|
||||
|
||||
const resetTimeout = () => {
|
||||
clearTimeout(timeout);
|
||||
timeout = window.setTimeout(() => {
|
||||
// Logout user after 7 minutes of inactivity
|
||||
logoutUser();
|
||||
}, 7 * 60 * 1000); // 7 minutes in milliseconds
|
||||
};
|
||||
|
||||
const handleUserActivity = () => {
|
||||
resetTimeout();
|
||||
};
|
||||
|
||||
// Attach event listeners to track user activity
|
||||
document.addEventListener('mousemove', handleUserActivity);
|
||||
document.addEventListener('keypress', handleUserActivity);
|
||||
|
||||
// Initialize timeout
|
||||
resetTimeout();
|
||||
|
||||
// Clear timeout and remove event listeners on component unmount
|
||||
return () => {
|
||||
clearTimeout(timeout);
|
||||
document.removeEventListener('mousemove', handleUserActivity);
|
||||
document.removeEventListener('keypress', handleUserActivity);
|
||||
};
|
||||
}, [logoutUser]);
|
||||
|
||||
return (
|
||||
<div className="py-5 px-10 flex flex-col h-full bg-inherit">
|
||||
<Link to="/">
|
||||
<img src={Logo} alt="Logo" className="w-[72px] h-[63px]" />
|
||||
</Link>
|
||||
<div className="mt-10 h-full overflow-y-auto bg-inherit">
|
||||
{asideLinks.map((link, index) => {
|
||||
if (link.nestedLink?.length) {
|
||||
let allNestedLinks = link.nestedLink.map((item) => item.link);
|
||||
return (
|
||||
<div
|
||||
key={index}
|
||||
className="w-full relative bg-inherit overflow-hidden"
|
||||
>
|
||||
<button
|
||||
name={link.name}
|
||||
onClick={(e) => handleOpenNestedLink(e)}
|
||||
className={`py-2 pl-2 text-left relative w-full overflow-hidden rounded-lg flex justify-between items-center z-10 bg-inherit ${
|
||||
allNestedLinks.includes(pathname)
|
||||
? ' text-[#5C2684]'
|
||||
: ' text-[#585858]'
|
||||
}`}
|
||||
>
|
||||
{link.name}
|
||||
{/* <div className={`mr-2 ${openNestedLink.name == link.name ? '-rotate-90' : 'rotate-90'} transition-all duration-300`}>
|
||||
<Icons
|
||||
name='greater-than'
|
||||
fillColor={`${openNestedLink.name == link.name ? '#5C2684' : '#585858'}`}
|
||||
/>
|
||||
</div> */}
|
||||
</button>
|
||||
<div
|
||||
className={`transition-all duration-300 w-full z-1 ${
|
||||
openNestedLink.name == link.name
|
||||
? 'relative top-0'
|
||||
: 'absolute -top-[500px]'
|
||||
}`}
|
||||
>
|
||||
{link.nestedLink.map((nextLink, index) => (
|
||||
<Link
|
||||
onClick={() => {
|
||||
asideDisplay && asideDisplay();
|
||||
}}
|
||||
key={index}
|
||||
to={nextLink.link ? nextLink.link : '#'}
|
||||
className={`w-full my-1 flex items-center gap-2 py-2 pl-5 text-base font-medium ${
|
||||
pathname == nextLink.link
|
||||
? ' text-[#5C2684]'
|
||||
: 'text-[#585858]'
|
||||
}`}
|
||||
>
|
||||
<Icons
|
||||
name={nextLink.icon}
|
||||
fillColor={`${
|
||||
pathname == nextLink.link ? '#5C2684' : '#585858'
|
||||
}`}
|
||||
/>
|
||||
{nextLink.name}
|
||||
</Link>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
} else {
|
||||
return (
|
||||
<Link
|
||||
onClick={() => {
|
||||
asideDisplay && asideDisplay();
|
||||
}}
|
||||
key={index}
|
||||
to={link.link ? link.link : '#'}
|
||||
className={`w-full my-4 flex items-center gap-2 py-2 pl-5 rounded-lg text-base font-medium ${
|
||||
pathname == link.link ? 'text-[#5C2684]' : 'text-[#585858]'
|
||||
}`}
|
||||
>
|
||||
<Icons
|
||||
name={link.icon}
|
||||
fillColor={`${pathname == link.link ? '#5C2684' : '#585858'}`}
|
||||
/>
|
||||
{link.name}
|
||||
</Link>
|
||||
);
|
||||
}
|
||||
})}
|
||||
</div>
|
||||
<div className="w-full flex justify-center items-center flex-col gap-3">
|
||||
<button
|
||||
className="py-3 px-6 bg-red-100 text-red-500 font-medium rounded-md w-full"
|
||||
onClick={() => logoutUser()}
|
||||
>
|
||||
Log out
|
||||
</button>
|
||||
|
||||
<div className="flex flex-col gap-[.4375rem] text-[.75rem]">
|
||||
<p className="font-extrabold tracking-[3%] text-[#FBB700] underline">
|
||||
For more enquiries and support
|
||||
</p>
|
||||
{/* <p className="font-extrabold tracking-[3%] text-[#5A5A5A]">
|
||||
Call: 09099000000
|
||||
</p>
|
||||
<p className="font-extrabold tracking-[3%] text-[#5A5A5A]">
|
||||
Email: fcmbloan@support.com
|
||||
</p> */}
|
||||
{contactDetails.map(({ name, value }) => (
|
||||
<p
|
||||
key={name}
|
||||
className="font-extrabold tracking-[3%] text-[#5A5A5A]"
|
||||
>
|
||||
{name}: {value}
|
||||
</p>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
type AsideLinksType = {
|
||||
name: string;
|
||||
link?: string;
|
||||
icon: string;
|
||||
nestedLink?: {
|
||||
name: string;
|
||||
link: string;
|
||||
icon: string;
|
||||
}[];
|
||||
}[];
|
||||
|
||||
const asideLinks: AsideLinksType = [
|
||||
{
|
||||
name: 'Dashboard',
|
||||
link: '/dashboard/home',
|
||||
icon: 'dash-icon',
|
||||
nestedLink: [],
|
||||
},
|
||||
{
|
||||
name: 'Your Profile',
|
||||
link: '/dashboard/profile',
|
||||
icon: 'dash-icon',
|
||||
nestedLink: [],
|
||||
},
|
||||
{
|
||||
name: 'Employment Details',
|
||||
link: '/dashboard/verification',
|
||||
icon: 'dash-icon',
|
||||
nestedLink: [],
|
||||
},
|
||||
{
|
||||
name: 'Reference Details',
|
||||
link: "/dashboard/reference",
|
||||
icon: 'dash-icon',
|
||||
nestedLink: [],
|
||||
},
|
||||
{
|
||||
name: 'Agreements',
|
||||
link: '/dashboard/legals',
|
||||
icon: 'dash-icon',
|
||||
nestedLink: [],
|
||||
},
|
||||
// {name: 'Nested Link', icon: 'home', nestedLink:[
|
||||
// {name: 'Link 2', link: '/dashboard/not-found', icon: 'legals'},
|
||||
// {name: 'Link 1', link: '/dashboard/not-found', icon: 'home'}
|
||||
// ]
|
||||
// },
|
||||
];
|
||||
@@ -0,0 +1,61 @@
|
||||
|
||||
import {useState, useEffect} from 'react'
|
||||
import DashboardLayout from "./DashboardLayout";
|
||||
import { Outlet, useNavigate } from "react-router-dom";
|
||||
import { useSelector, useDispatch } from "react-redux";
|
||||
import { RouteHandler } from '../../router/routes';
|
||||
import { updateUserDetails } from '../../store/UserDetails';
|
||||
import { getUserByID } from '../../core/apiRequest';
|
||||
|
||||
import Logo from '../../assets/images/logo.png'
|
||||
|
||||
export default function DashboardAuth() {
|
||||
|
||||
const navigate = useNavigate()
|
||||
const dispatch = useDispatch()
|
||||
|
||||
const { userDetails } = useSelector((state:any) => state?.userDetails); // CHECKS IF USER Details are avaliable
|
||||
|
||||
const [loading, setLoading] = useState(true)
|
||||
|
||||
useEffect(()=>{
|
||||
let token = localStorage.getItem('token')
|
||||
let uid = localStorage.getItem('uid')
|
||||
if(!token || !uid){
|
||||
navigate(RouteHandler.letsGetStarted, {replace:true})
|
||||
return
|
||||
}
|
||||
const getUser = () => { // FUNCTION TO GET USER BY ID
|
||||
// let data = {firstname:'firstname', lastname:'lastname', uid:'28273737646466464'}
|
||||
getUserByID(uid).then(res=>{
|
||||
if(!res.data.call_return || !Object.keys(res.data.customer).length){
|
||||
navigate(RouteHandler.letsGetStarted, {replace:true})
|
||||
return
|
||||
}
|
||||
setLoading(false)
|
||||
dispatch(updateUserDetails(res.data.customer));
|
||||
}).catch(err=>{
|
||||
navigate(RouteHandler.letsGetStarted, {replace:true})
|
||||
console.log('USER ERROR', err)
|
||||
})
|
||||
}
|
||||
if(!Object.keys(userDetails).length){
|
||||
getUser()
|
||||
}
|
||||
},[])
|
||||
|
||||
return (
|
||||
<>
|
||||
{loading && !Object.keys(userDetails).length ?
|
||||
<div className='w-full h-screen flex flex-col justify-center items-center gap-4'>
|
||||
<img className='animate-pulse' src={Logo} alt='Logo' />
|
||||
<p className='animate-pulse'>loading...</p>
|
||||
</div>
|
||||
:
|
||||
<DashboardLayout>
|
||||
<Outlet />
|
||||
</DashboardLayout>
|
||||
}
|
||||
</>
|
||||
)
|
||||
}
|
||||
@@ -0,0 +1,124 @@
|
||||
import { ReactNode, useState, useEffect } from "react";
|
||||
import { RouteHandler } from "../../router/routes";
|
||||
import { useNavigate } from "react-router-dom";
|
||||
|
||||
import Aside from "./Aside";
|
||||
|
||||
export default function DashboardLayout({ children }: { children: ReactNode }) {
|
||||
const navigate = useNavigate();
|
||||
|
||||
const [showAside, setShowAside] = useState<boolean>(false);
|
||||
const asideDisplay = (): void => {
|
||||
setShowAside((prev) => !prev);
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
const handleResize = () => {
|
||||
return setShowAside(false);
|
||||
};
|
||||
window.addEventListener("resize", handleResize);
|
||||
return () => {
|
||||
window.removeEventListener("resize", handleResize);
|
||||
};
|
||||
}, []);
|
||||
|
||||
// Assume this interface for ChildProps
|
||||
// interface ChildProps {
|
||||
// customProp?: string;
|
||||
// }
|
||||
|
||||
// const enhanceChildren = React.Children.map(children, (child) => {
|
||||
// if (React.isValidElement<ChildProps>(child)) {
|
||||
// return React.cloneElement(child, { customProp: "Hello, World!" });
|
||||
// }
|
||||
// return child;
|
||||
// });
|
||||
|
||||
const logoutUser = () => {
|
||||
localStorage.clear()
|
||||
navigate(RouteHandler.letsGetStarted, {replace:true})
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="w-full max-w-[2000px] mx-auto h-screen flex bg-[#020202] text-black">
|
||||
<aside className="max-w-[18.75rem] w-full bg-white hidden md:block border-r-2 border-[#E6E6E6]">
|
||||
<Aside logoutUser={logoutUser} />
|
||||
</aside>
|
||||
<aside
|
||||
className={`max-w-[18.75rem] w-full md:hidden bg-white border-r-2 border-[#E6E6E6] fixed top-0 bottom-0 z-50 transition-all duration-500 ${
|
||||
showAside ? "left-0" : "-left-[200%]"
|
||||
}`}
|
||||
>
|
||||
<Aside logoutUser={logoutUser} asideDisplay={asideDisplay} />
|
||||
</aside>
|
||||
<main className="dash-bg-image bg-[#F9F9F9] relative w-full overflow-y-auto overflow-x-hidden">
|
||||
|
||||
<header className={`p-5 md:hidden sticky z-10 top-0 w-full bg-[#F9F9F9] border-b-2 border-[#E6E6E6]`}>
|
||||
<div className='h-14 w-full flex justify-end items-center gap-5'>
|
||||
{/* MENU HAND BURGER */}
|
||||
{/* <div className='w-full'>Welcome Austin Catherine</div> */}
|
||||
<div
|
||||
className="relative md:hidden w-5 h-[20px] flex flex-col items-center justify-between"
|
||||
onClick={asideDisplay}
|
||||
>
|
||||
<div
|
||||
className={`absolute left-0 w-5 h-1 bg-black/80 dark:bg-white transition-all duration-500 ${
|
||||
showAside ? "top-1/2 -translate-y-1/2 rotate-45" : "top-0"
|
||||
}`}
|
||||
></div>
|
||||
<div
|
||||
className={`absolute left-0 w-5 h-1 bg-black/80 dark:bg-white transition-all duration-300 ${
|
||||
showAside
|
||||
? "top-1/2 -translate-y-1/2 rotate-[2000deg] opacity-0"
|
||||
: "top-1/2 -translate-y-1/2"
|
||||
}`}
|
||||
></div>
|
||||
<div
|
||||
className={`absolute left-0 w-5 h-1 bg-black/80 dark:bg-white transition-all duration-500 ${
|
||||
showAside
|
||||
? "top-1/2 -translate-y-1/2 -rotate-45"
|
||||
: "bottom-0"
|
||||
}`}
|
||||
></div>
|
||||
</div>
|
||||
</div>
|
||||
</header>
|
||||
<div className="flex p-2 md:p-5 relative">
|
||||
<div className="w-full p-2 md:p-5">{children}</div>
|
||||
</div>
|
||||
</main>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
// {/* <header className={`p-5 sticky z-10 top-0 w-full bg-[#F9F9F9] border-b-2 border-[#E6E6E6]`}>
|
||||
// <div className='h-14 w-full flex justify-end items-center gap-5'>
|
||||
|
||||
// {/* MENU HAND BURGER */}
|
||||
// <div className='w-full'>Welcome Austin Catherine</div>
|
||||
// <div
|
||||
// className="relative md:hidden w-5 h-[20px] flex flex-col items-center justify-between"
|
||||
// onClick={asideDisplay}
|
||||
// >
|
||||
// <div
|
||||
// className={`absolute left-0 w-5 h-1 bg-black/80 dark:bg-white transition-all duration-500 ${
|
||||
// showAside ? "top-1/2 -translate-y-1/2 rotate-45" : "top-0"
|
||||
// }`}
|
||||
// ></div>
|
||||
// <div
|
||||
// className={`absolute left-0 w-5 h-1 bg-black/80 dark:bg-white transition-all duration-300 ${
|
||||
// showAside
|
||||
// ? "top-1/2 -translate-y-1/2 rotate-[2000deg] opacity-0"
|
||||
// : "top-1/2 -translate-y-1/2"
|
||||
// }`}
|
||||
// ></div>
|
||||
// <div
|
||||
// className={`absolute left-0 w-5 h-1 bg-black/80 dark:bg-white transition-all duration-500 ${
|
||||
// showAside
|
||||
// ? "top-1/2 -translate-y-1/2 -rotate-45"
|
||||
// : "bottom-0"
|
||||
// }`}
|
||||
// ></div>
|
||||
// </div>
|
||||
// </div>
|
||||
// </header> */}
|
||||
@@ -0,0 +1,24 @@
|
||||
import React from "react";
|
||||
import { Footer, Header } from "../components";
|
||||
|
||||
interface GetStartedLayoutProps {
|
||||
children: React.ReactNode;
|
||||
}
|
||||
|
||||
const GetStartedLayout: React.FC<GetStartedLayoutProps> = ({ children }) => {
|
||||
return (
|
||||
<div className="containerMode mb-[5.4375rem]">
|
||||
<div className='sticky z-50 top-0 bg-white'>
|
||||
<Header hideSidebar={true} hideMenu={true} />
|
||||
</div>
|
||||
<div className="flex flex-col min-h-[70vh] justify-between">
|
||||
{children}
|
||||
</div>
|
||||
<div className="fixed bottom-0 left-0 bg-white w-full">
|
||||
<Footer />
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default GetStartedLayout;
|
||||
@@ -0,0 +1,27 @@
|
||||
import React, { ReactNode } from "react";
|
||||
import {
|
||||
BottomFooterOne,
|
||||
Header,
|
||||
MidFooter,
|
||||
TopFooterOne,
|
||||
TopHeader,
|
||||
} from "../components";
|
||||
|
||||
interface HomeLayoutProps {
|
||||
children: ReactNode;
|
||||
}
|
||||
|
||||
const HomeLayout: React.FC<HomeLayoutProps> = ({ children }) => {
|
||||
return (
|
||||
<>
|
||||
<TopHeader />
|
||||
<Header />
|
||||
{children}
|
||||
<TopFooterOne />
|
||||
<MidFooter />
|
||||
<BottomFooterOne />
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
export default HomeLayout;
|
||||
@@ -0,0 +1,24 @@
|
||||
import React from "react";
|
||||
import { Footer, LetsGetStartedNav } from "../components";
|
||||
// import layoutImage from "../assets/images/test1-reverse.png";
|
||||
|
||||
const LetsGetStartedLayout = ({ children }: { children: React.ReactNode }) => {
|
||||
return (
|
||||
<div className='containerMode mb-[5.4375rem]'>
|
||||
<div className="w-full min-h-[90vh] grid lg:grid-cols-2">
|
||||
<div className="w-full flex flex-col my-3">
|
||||
<LetsGetStartedNav />
|
||||
{children}
|
||||
</div>
|
||||
<div className="w-full h-96 lg:h-full bg-[url(../src/assets/images/test1-reverse.png)] bg-cover bg-no-repeat">
|
||||
{/* <img src={layoutImage} alt="" className="w-full h-full object-cover" /> */}
|
||||
</div>
|
||||
</div>
|
||||
<div className="fixed bottom-0 left-0 bg-[#F7F7F7] w-full">
|
||||
<Footer />
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default LetsGetStartedLayout;
|
||||
@@ -0,0 +1,7 @@
|
||||
import HomeLayout from "./HomeLayout";
|
||||
import LetsGetStartedLayout from "./LetsGetStartedLayout";
|
||||
import GetStartedLayout from "./GetStartedLayout";
|
||||
import DashboardLayout from "./DashboardLayout/DashboardLayout";
|
||||
import { DashboardAuth } from "./DashboardLayout";
|
||||
|
||||
export { HomeLayout, LetsGetStartedLayout, GetStartedLayout, DashboardLayout, DashboardAuth };
|
||||
@@ -0,0 +1,24 @@
|
||||
// FUNCTION TO RETURN AMOUNT TO TWO DECIMAL PLACES
|
||||
export const FormatAmount = (
|
||||
amount = "00",
|
||||
) => {
|
||||
// Convert the number to a string
|
||||
let numStr = String(amount);
|
||||
|
||||
// Split the string into integer and decimal parts
|
||||
let parts = numStr.split(".");
|
||||
let integerPart = parts[0] || "";
|
||||
let decimalPart = parts[1] || "";
|
||||
|
||||
// Add thousands separators to the integer part
|
||||
let formattedInteger = integerPart.replace(/\B(?=(\d{3})+(?!\d))/g, ',');
|
||||
|
||||
// Truncate or pad the decimal part to two decimal points
|
||||
let formattedDecimal = decimalPart.slice(0, 2).padEnd(2, "0");
|
||||
|
||||
// Combine the formatted integer and decimal parts
|
||||
let formattedNumber = '₦' + formattedInteger + '.' + formattedDecimal;
|
||||
|
||||
// return formattedNumber;
|
||||
return formattedNumber;
|
||||
};
|
||||
@@ -0,0 +1,18 @@
|
||||
|
||||
export function NewDateTimeFormatter(isoDateString:any, addHour = true) {
|
||||
const date = new Date(isoDateString);
|
||||
if (addHour) {
|
||||
date.setTime(date.getTime() + 1 * 60 * 60 * 1000);
|
||||
}
|
||||
const formattedDate = date.toLocaleDateString("en-US", {
|
||||
year: "numeric",
|
||||
month: "numeric",
|
||||
day: "numeric",
|
||||
hour: "2-digit",
|
||||
minute: "2-digit",
|
||||
// second: "2-digit",
|
||||
hour12: true,
|
||||
timeZone: "UTC",
|
||||
});
|
||||
return formattedDate;
|
||||
}
|
||||
@@ -4,10 +4,15 @@ import { BrowserRouter } from "react-router-dom";
|
||||
import App from "./App.tsx";
|
||||
import "./index.css";
|
||||
|
||||
import { Provider } from "react-redux";
|
||||
import store from "./store/store";
|
||||
|
||||
ReactDOM.createRoot(document.getElementById("root")!).render(
|
||||
<React.StrictMode>
|
||||
<BrowserRouter>
|
||||
<App />
|
||||
<Provider store={store}>
|
||||
<App />
|
||||
</Provider>
|
||||
</BrowserRouter>
|
||||
</React.StrictMode>
|
||||
);
|
||||
|
||||
@@ -0,0 +1,19 @@
|
||||
import React from "react";
|
||||
import { HomeLayout } from "../layouts";
|
||||
import { PersonalHero } from "../components";
|
||||
|
||||
const BusinessBankingPage: React.FC = () => {
|
||||
return (
|
||||
<HomeLayout>
|
||||
<PersonalHero
|
||||
heading="Business Account"
|
||||
body="Open a personal or business account in minutes, tailored to your unique needs."
|
||||
buttonLink="#"
|
||||
buttonText="Click here"
|
||||
/>
|
||||
Business Banking
|
||||
</HomeLayout>
|
||||
);
|
||||
};
|
||||
|
||||
export default BusinessBankingPage;
|
||||
@@ -0,0 +1,19 @@
|
||||
import React from "react";
|
||||
import { HomeLayout } from "../layouts";
|
||||
import { PersonalHero } from "../components";
|
||||
|
||||
const CooperateBankingPage: React.FC = () => {
|
||||
return (
|
||||
<HomeLayout>
|
||||
<PersonalHero
|
||||
heading="Cooperate Account"
|
||||
body="Open a personal or business account in minutes, tailored to your unique needs."
|
||||
buttonLink="#"
|
||||
buttonText="Click here"
|
||||
/>
|
||||
Cooperate Banking
|
||||
</HomeLayout>
|
||||
);
|
||||
};
|
||||
|
||||
export default CooperateBankingPage;
|
||||
@@ -1,5 +0,0 @@
|
||||
export default function DashboardHome() {
|
||||
return (
|
||||
<div className=''>DashboardHome</div>
|
||||
)
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
import { DashboardHome } from "../components"
|
||||
|
||||
export default function DashboardHomePage() {
|
||||
return (
|
||||
<div className=''>
|
||||
<DashboardHome />
|
||||
</div>
|
||||
)
|
||||
}
|
||||