Compare commits
210 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 0756747143 | |||
| 99eb1591a2 | |||
| 85b9aab229 | |||
| 67a0a34288 | |||
| 814e4c9693 | |||
| e74eb38caf | |||
| 67db1a72bf | |||
| 41e46d8030 | |||
| 8707411dda | |||
| 327a4a42ae | |||
| 455f4001f7 | |||
| 775bcd5005 | |||
| 492bda021f | |||
| eacfae19f0 | |||
| 376cf44a9c | |||
| 1aab0c2910 | |||
| 1f5fb8c7c3 | |||
| a303b24b53 | |||
| 322927045c | |||
| 3cf1dc7166 | |||
| 8ed331e909 | |||
| 2428923f73 | |||
| 139f87c919 | |||
| c24fbedf2c | |||
| 1a817e723e | |||
| c023911cd4 | |||
| 907dc298ab | |||
| 8095006386 | |||
| d9d28791da | |||
| a98d967ba4 | |||
| 59a5e84ee2 | |||
| 35ed1d48a3 | |||
| f9d385dfec | |||
| 033af03221 | |||
| b3afb94030 | |||
| 0b1aa4e7d9 | |||
| 18c4f31322 | |||
| 97e3344953 | |||
| 9565ca0d35 | |||
| 51cc4edc1d | |||
| d12a1fd579 | |||
| 21f1173e66 | |||
| cf5ae81918 | |||
| 235bb7d667 | |||
| 18a319a437 | |||
| d3e2cc6744 | |||
| a08d8feb7d | |||
| 7cb5cc558b | |||
| 36025ade56 | |||
| b80c21358a | |||
| f5ad68f7bb | |||
| 27cba1ac68 | |||
| dbb21d8a08 | |||
| a4597599b7 | |||
| 894f986574 | |||
| e42a358da6 | |||
| 23c2e840c2 | |||
| 7e7176ae5c | |||
| 306fa1300d | |||
| 7b3178bd1a | |||
| 5e94ddee48 | |||
| 32171ae3ce | |||
| 2c99fedd8c | |||
| 7e926cc7bc | |||
| 587bcaa411 | |||
| 756628ee1a | |||
| 575710a807 | |||
| c77d8e5693 | |||
| 7e9d734e6f | |||
| 9ddb127bd3 | |||
| a85e5fdb91 | |||
| 09ad8a94ca | |||
| 0c1db6b4b5 | |||
| 6ddffdf2e6 | |||
| 5043540abb | |||
| 5f39accdd6 | |||
| b5aeaf59a4 | |||
| f3e63d2ef6 | |||
| 44a055e76b | |||
| 67c1b2aaa0 | |||
| 313cfc1f28 | |||
| 59a7f110be | |||
| 52e8447f6a | |||
| c60e28928a | |||
| 58357fd501 | |||
| 5402980d6c | |||
| 32c7b3d513 | |||
| a30b22170d | |||
| e3314b0460 | |||
| 86eb1f16bb | |||
| 5660d74e75 | |||
| 612016784d | |||
| 988aadfb7e | |||
| 54851b5f77 | |||
| 91f3b92138 | |||
| f48de9d65f | |||
| c920c35a9c | |||
| 7bcaead120 | |||
| 828f56ed84 | |||
| 07118c10a8 | |||
| ca5923a7f8 | |||
| 4f0d432176 | |||
| dd3f9d99f3 | |||
| 8807b671ad | |||
| 7020e6d4dc | |||
| d66093a2ad | |||
| b79eb6b158 | |||
| a759daaf90 | |||
| 600aec62cf | |||
| 1612bf11ce | |||
| f4e261eb6c | |||
| b622398b6a | |||
| e60957e6be | |||
| 6a2082d732 | |||
| 452c6bf8a1 | |||
| 9f89376aa9 | |||
| 4e91e47978 | |||
| fcaa485b17 | |||
| e4526652d3 | |||
| 64056bb2a4 | |||
| e49c4d66f8 | |||
| 8fdb939b72 | |||
| 79325450f3 | |||
| 7849a027b4 | |||
| 38432a6d50 | |||
| 83a54ff3ef | |||
| 5e0fdffa1e | |||
| 11d2cb3e3a | |||
| 93ac55b44b | |||
| 237ce13a6c | |||
| 4253174494 | |||
| da0ed0364b | |||
| 466175c49d | |||
| ed148ce267 | |||
| 6de795c07b | |||
| 436498bef9 | |||
| 086b1202a4 | |||
| a81ccdc4d7 | |||
| 33abbbcd2b | |||
| 04844af733 | |||
| 46d919090d | |||
| 8e67892f16 | |||
| a67d2b7bb4 | |||
| 99c0c24489 | |||
| 1a1d59a8c7 | |||
| 31dcfcfd0b | |||
| e9aa58f2f5 | |||
| 55fe2bf6d2 | |||
| 738eb1589e | |||
| 139e0c2827 | |||
| 30642bba14 | |||
| f5921612b8 | |||
| 38afece043 | |||
| d44447c6b3 | |||
| d942c6641a | |||
| beed565ba0 | |||
| 329e27b83d | |||
| 3145656d80 | |||
| 5098a45bd5 | |||
| 9e65a6e7d5 | |||
| 83ac1087f3 | |||
| 55b2bccdf0 | |||
| 0c51474dd2 | |||
| 8c7ffb52a5 | |||
| 6c698bc399 | |||
| 540ad6f9ad | |||
| 11342c32c6 | |||
| 356c11d029 | |||
| 934d95e822 | |||
| 0aa49b49a9 | |||
| be52792271 | |||
| c23e61e06b | |||
| 24b9a0e348 | |||
| fcb154ba49 | |||
| c0065ed569 | |||
| 05a2ebec61 | |||
| 63f7deaa2b | |||
| d762ef42e8 | |||
| 3e34c81789 | |||
| a2340af314 | |||
| 2a445fd2d4 | |||
| b5e52fb34e | |||
| 87512bbc23 | |||
| 1e59059394 | |||
| e59c83d216 | |||
| 395569dab5 | |||
| 25a537c5bc | |||
| 5244a2875e | |||
| 54560b3fec | |||
| 078d26317f | |||
| 8dbf638c7f | |||
| a68af7c3a4 | |||
| 905d48ceb7 | |||
| 1e5dac0104 | |||
| 11e52a02f6 | |||
| 4e74198ae5 | |||
| 5d673a630c | |||
| adcb33764b | |||
| 305bff1167 | |||
| 1ebe9fe12b | |||
| 3fbaf6b78a | |||
| 421a24c185 | |||
| 6386fbcda6 | |||
| 2e09d77597 | |||
| 10dae9193c | |||
| 3f487337f4 | |||
| 7eabee8855 | |||
| 360f0500a9 | |||
| 9b6c5b72ad | |||
| afc5e25cfc |
@@ -91,4 +91,8 @@ REACT_APP_SHOW_UPLOAD_PROFILE_PICTURE=0
|
||||
|
||||
#GOOGLE RECAPTCHA SITEKEY
|
||||
REACT_APP_GOOGLE_RECAPTCHA_SITEKEY=6Ld_qKooAAAAADNL1TPzRLmcBA8vlpvx__39Rj39
|
||||
#6LeIxAcTAAAAAJcZVRqyHh71UMIEGNQ_MXjiZKhI
|
||||
#6LeIxAcTAAAAAJcZVRqyHh71UMIEGNQ_MXjiZKhI
|
||||
|
||||
#FAMILY MEMBER MINIMUM AGE
|
||||
REACT_APP_FAMILY_MINIMUM_AGE=4
|
||||
REACT_APP_FAMILY_MAXIMUM_AGE=18
|
||||
+5
-1
@@ -59,4 +59,8 @@ REACT_APP_SHOW_UPLOAD_PROFILE_PICTURE=0
|
||||
|
||||
#GOOGLE RECAPTCHA SITEKEY
|
||||
REACT_APP_GOOGLE_RECAPTCHA_SITEKEY=6Ld_qKooAAAAADNL1TPzRLmcBA8vlpvx__39Rj39
|
||||
#6LeIxAcTAAAAAJcZVRqyHh71UMIEGNQ_MXjiZKhI
|
||||
#6LeIxAcTAAAAAJcZVRqyHh71UMIEGNQ_MXjiZKhI
|
||||
|
||||
#FAMILY MEMBER MINIMUM AGE
|
||||
REACT_APP_FAMILY_MINIMUM_AGE=4
|
||||
REACT_APP_FAMILY_MAXIMUM_AGE=18
|
||||
@@ -66,3 +66,7 @@ REACT_APP_SHOW_UPLOAD_PROFILE_PICTURE=0
|
||||
#GOOGLE RECAPTCHA SITEKEY
|
||||
REACT_APP_GOOGLE_RECAPTCHA_SITEKEY=6Ld_qKooAAAAADNL1TPzRLmcBA8vlpvx__39Rj39
|
||||
#6LeIxAcTAAAAAJcZVRqyHh71UMIEGNQ_MXjiZKhI
|
||||
|
||||
#FAMILY MEMBER MINIMUM AGE
|
||||
REACT_APP_FAMILY_MINIMUM_AGE=4
|
||||
REACT_APP_FAMILY_MAXIMUM_AGE=18
|
||||
|
||||
@@ -19,6 +19,8 @@
|
||||
.env.test.local
|
||||
.env.production.local
|
||||
|
||||
#package-lock.json
|
||||
|
||||
npm-debug.log*
|
||||
yarn-debug.log*
|
||||
yarn-error.log*
|
||||
|
||||
Generated
+74
@@ -22,13 +22,17 @@
|
||||
"flutterwave-react-v3": "^1.3.0",
|
||||
"formik": "^2.2.9",
|
||||
"react": "^18.2.0",
|
||||
"react-apple-login": "^1.1.6",
|
||||
"react-chartjs-2": "^4.1.0",
|
||||
"react-countup": "^6.2.0",
|
||||
"react-dom": "^18.2.0",
|
||||
"react-google-recaptcha": "^3.1.0",
|
||||
"react-qr-code": "^2.0.11",
|
||||
"react-redux": "^8.0.5",
|
||||
"react-router-dom": "^6.0.2",
|
||||
"react-scripts": "5.0.1",
|
||||
"react-slick": "^0.29.0",
|
||||
"react-to-print": "^2.14.12",
|
||||
"react-toastify": "^9.0.1",
|
||||
"redux": "^4.2.0",
|
||||
"slick-carousel": "^1.8.1",
|
||||
@@ -14801,6 +14805,11 @@
|
||||
"teleport": ">=0.2.0"
|
||||
}
|
||||
},
|
||||
"node_modules/qr.js": {
|
||||
"version": "0.0.0",
|
||||
"resolved": "https://registry.npmjs.org/qr.js/-/qr.js-0.0.0.tgz",
|
||||
"integrity": "sha512-c4iYnWb+k2E+vYpRimHqSu575b1/wKl4XFeJGpFmrJQz5I88v9aY2czh7s0w36srfCM1sXgC/xpoJz5dJfq+OQ=="
|
||||
},
|
||||
"node_modules/qs": {
|
||||
"version": "6.11.0",
|
||||
"resolved": "https://registry.npmjs.org/qs/-/qs-6.11.0.tgz",
|
||||
@@ -14944,6 +14953,32 @@
|
||||
"url": "https://opencollective.com/core-js"
|
||||
}
|
||||
},
|
||||
"node_modules/react-apple-login": {
|
||||
"version": "1.1.6",
|
||||
"resolved": "https://registry.npmjs.org/react-apple-login/-/react-apple-login-1.1.6.tgz",
|
||||
"integrity": "sha512-ySV6ax0aB+ksA7lKzhr4MvsgjwSH068VtdHJXS+7rL380IJnNQNl14SszR31k3UqB8q8C1H1oyjJFGq4MyO6tw==",
|
||||
"engines": {
|
||||
"node": ">=8",
|
||||
"npm": ">=5"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"prop-types": "^15.5.4",
|
||||
"react": "^15.0.0 || ^16.0.0 || ^17.0.0 || ^18.0.0",
|
||||
"react-dom": "^15.0.0 || ^16.0.0 || ^17.0.0 || ^18.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/react-async-script": {
|
||||
"version": "1.2.0",
|
||||
"resolved": "https://registry.npmjs.org/react-async-script/-/react-async-script-1.2.0.tgz",
|
||||
"integrity": "sha512-bCpkbm9JiAuMGhkqoAiC0lLkb40DJ0HOEJIku+9JDjxX3Rcs+ztEOG13wbrOskt3n2DTrjshhaQ/iay+SnGg5Q==",
|
||||
"dependencies": {
|
||||
"hoist-non-react-statics": "^3.3.0",
|
||||
"prop-types": "^15.5.0"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"react": ">=16.4.1"
|
||||
}
|
||||
},
|
||||
"node_modules/react-chartjs-2": {
|
||||
"version": "4.3.1",
|
||||
"resolved": "https://registry.npmjs.org/react-chartjs-2/-/react-chartjs-2-4.3.1.tgz",
|
||||
@@ -15044,11 +15079,41 @@
|
||||
"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-google-recaptcha": {
|
||||
"version": "3.1.0",
|
||||
"resolved": "https://registry.npmjs.org/react-google-recaptcha/-/react-google-recaptcha-3.1.0.tgz",
|
||||
"integrity": "sha512-cYW2/DWas8nEKZGD7SCu9BSuVz8iOcOLHChHyi7upUuVhkpkhYG/6N3KDiTQ3XAiZ2UAZkfvYKMfAHOzBOcGEg==",
|
||||
"dependencies": {
|
||||
"prop-types": "^15.5.0",
|
||||
"react-async-script": "^1.2.0"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"react": ">=16.4.1"
|
||||
}
|
||||
},
|
||||
"node_modules/react-is": {
|
||||
"version": "16.13.1",
|
||||
"resolved": "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz",
|
||||
"integrity": "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ=="
|
||||
},
|
||||
"node_modules/react-qr-code": {
|
||||
"version": "2.0.12",
|
||||
"resolved": "https://registry.npmjs.org/react-qr-code/-/react-qr-code-2.0.12.tgz",
|
||||
"integrity": "sha512-k+pzP5CKLEGBRwZsDPp98/CAJeXlsYRHM2iZn1Sd5Th/HnKhIZCSg27PXO58zk8z02RaEryg+60xa4vyywMJwg==",
|
||||
"dependencies": {
|
||||
"prop-types": "^15.8.1",
|
||||
"qr.js": "0.0.0"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"react": "^16.x || ^17.x || ^18.x",
|
||||
"react-native-svg": "*"
|
||||
},
|
||||
"peerDependenciesMeta": {
|
||||
"react-native-svg": {
|
||||
"optional": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"node_modules/react-redux": {
|
||||
"version": "8.0.5",
|
||||
"resolved": "https://registry.npmjs.org/react-redux/-/react-redux-8.0.5.tgz",
|
||||
@@ -15218,6 +15283,15 @@
|
||||
"react-dom": "^0.14.0 || ^15.0.1 || ^16.0.0 || ^17.0.0 || ^18.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/react-to-print": {
|
||||
"version": "2.14.15",
|
||||
"resolved": "https://registry.npmjs.org/react-to-print/-/react-to-print-2.14.15.tgz",
|
||||
"integrity": "sha512-SKnwOzU2cJ8eaAkoJO7+gNhvfEDmm+Y34IdcHsjtHioUevUPhprqbVtvNJlZ2JkGJ8ExK2QNWM9pXECTDR5D8w==",
|
||||
"peerDependencies": {
|
||||
"react": "^15.0.0 || ^16.0.0 || ^17.0.0 || ^18.0.0",
|
||||
"react-dom": "^15.0.0 || ^16.0.0 || ^17.0.0 || ^18.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/react-toastify": {
|
||||
"version": "9.1.1",
|
||||
"resolved": "https://registry.npmjs.org/react-toastify/-/react-toastify-9.1.1.tgz",
|
||||
|
||||
@@ -49,6 +49,8 @@ import MyWaitingJobsPage from "./views/MyWaitingJobsPage";
|
||||
import FamilyMarketPage from "./views/FamilyMarketPage";
|
||||
import FacebookRedirect from "./views/FacebookRedirect";
|
||||
import AppleRedirectPage from "./views/AppleRedirectPage";
|
||||
import LndPage from "./views/LndPage";
|
||||
import FamilySettingsPage from "./views/FamilySettingsPage";
|
||||
|
||||
export default function Routers() {
|
||||
return (
|
||||
@@ -76,6 +78,7 @@ export default function Routers() {
|
||||
<Route path="/vemail" element={<VerifyLinkPages />} />
|
||||
<Route path="/complereset" element={<VerifyPasswordPages />} />
|
||||
<Route exact path="/outmessage" element={<VerifyYouPages />} />
|
||||
<Route exact path="/lnd/*" element={<LndPage />} />
|
||||
|
||||
{/* private route */}
|
||||
<Route element={<AuthRoute />}>
|
||||
@@ -96,6 +99,7 @@ export default function Routers() {
|
||||
<Route exact path="/market-place" element={<MarketPlacePage />} />
|
||||
<Route exact path="/market" element={<MarketPlacePage />} />
|
||||
<Route exact path="/familymarket" element={<FamilyMarketPage />} />
|
||||
<Route exact path="/familysettings" element={<FamilySettingsPage />} />
|
||||
<Route exact path="/notification" element={<Notification />} />
|
||||
<Route exact path="/mytask" element={<MyTaskPage />} />
|
||||
<Route exact path="/myjobs" element={<MyJobsPage />} />
|
||||
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 71 KiB |
@@ -0,0 +1 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 48 48" id="DownloadArrow"><g fill="#4687ba" class="color9a7baa svgShape"><path d="M30.179 3.525V1.857c0-1.551-1.784-2.415-3.011-1.449L24 2.907 20.824.402c-1.216-.959-3.002-.093-3.002 1.456v1.668a1 1 0 0 0 1 1h10.357c.552-.001 1-.449 1-1.001zM30.179 25.172v-2.524a1 1 0 0 0-1-1H18.822a1 1 0 0 0-1 1v2.524a1 1 0 0 0 1 1h10.357a1 1 0 0 0 1-1zM30.179 10.74V8.217a1 1 0 0 0-1-1H18.822a1 1 0 0 0-1 1v2.524a1 1 0 0 0 1 1h10.357c.552-.001 1-.448 1-1.001zM30.179 17.956v-2.524a1 1 0 0 0-1-1H18.822a1 1 0 0 0-1 1v2.524a1 1 0 0 0 1 1h10.357a1 1 0 0 0 1-1zM25.748 47.029l9.336-15.018c.852-1.371-.133-3.145-1.748-3.145H14.664c-1.614 0-2.6 1.774-1.748 3.145l9.336 15.018a2.058 2.058 0 0 0 3.496 0z" fill="#000000" class="color000000 svgShape"></path></g></svg>
|
||||
|
After Width: | Height: | Size: 799 B |
@@ -0,0 +1,10 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" enable-background="new 0 0 64 64" viewBox="0 0 64 64" id="wallet"><path d="M60.94,33.76H59.6V19.85c0-1.69-1.37-3.06-3.06-3.06h-1.61l-0.47-6.54c-0.12-1.67-1.57-2.94-3.27-2.84l-7.3,0.52
|
||||
l-0.18-2.48c-0.12-1.67-1.57-2.95-3.27-2.84L7.83,4.93c-0.82,0.06-1.56,0.43-2.1,1.05C5.19,6.6,4.93,7.39,4.99,8.2l0.61,8.59H3.06
|
||||
C1.37,16.79,0,18.17,0,19.85v38.48c0,1.69,1.37,3.06,3.06,3.06h53.48c1.69,0,3.06-1.37,3.06-3.06V44.43h1.33
|
||||
c1.69,0,3.06-1.37,3.06-3.06v-4.55C64,35.13,62.63,33.76,60.94,33.76z M51.42,9.94c0.27,0,0.51,0.22,0.53,0.49l0.45,6.36H18.55
|
||||
l-0.28-3.97c-0.01-0.19,0.07-0.32,0.13-0.38c0.05-0.06,0.17-0.17,0.36-0.18L51.42,9.94z M7.65,7.64C7.7,7.58,7.82,7.47,8.01,7.46
|
||||
l32.66-2.32c0.27,0,0.51,0.22,0.53,0.49l0.18,2.48L18.58,9.73c-0.82,0.06-1.56,0.43-2.1,1.05c-0.54,0.62-0.8,1.41-0.74,2.22
|
||||
l0.27,3.79H8.14L7.52,8.02C7.5,7.83,7.59,7.7,7.65,7.64z M57.07,58.34c0,0.28-0.24,0.53-0.53,0.53H3.06
|
||||
c-0.28,0-0.53-0.24-0.53-0.53V19.85c0-0.29,0.24-0.53,0.53-0.53h53.48c0.28,0,0.53,0.24,0.53,0.53v13.91h-4.43
|
||||
c-1.69,0-3.06,1.37-3.06,3.06v4.55c0,1.69,1.37,3.06,3.06,3.06h4.43V58.34z M61.47,41.37c0,0.28-0.24,0.53-0.53,0.53h-8.3
|
||||
c-0.28,0-0.53-0.24-0.53-0.53v-4.55c0-0.28,0.24-0.53,0.53-0.53h8.3c0.29,0,0.53,0.24,0.53,0.53V41.37z"></path></svg>
|
||||
|
After Width: | Height: | Size: 1.3 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 67 KiB |
@@ -1,74 +1,28 @@
|
||||
import { Field, Form, Formik } from "formik";
|
||||
import React, { useEffect, useState } from "react";
|
||||
import { useDispatch, useSelector } from "react-redux";
|
||||
import * as Yup from "yup";
|
||||
import usersService from "../../services/UsersService";
|
||||
import { tableReload } from "../../store/TableReloads";
|
||||
import InputCom from "../Helpers/Inputs/InputCom";
|
||||
import LoadingSpinner from "../Spinners/LoadingSpinner";
|
||||
import {
|
||||
validationSchema as VS,
|
||||
useDispatch,
|
||||
useSelector,
|
||||
usersService,
|
||||
initialValues as IV,
|
||||
initialReqState,
|
||||
useState,
|
||||
tableReload,
|
||||
Formik,
|
||||
InputCom,
|
||||
Field,
|
||||
Form,
|
||||
LoadingSpinner,
|
||||
} from "./settings";
|
||||
|
||||
const validationSchema = Yup.object().shape({
|
||||
country: Yup.string()
|
||||
.min(1, "Minimum 3 characters")
|
||||
.max(25, "Maximum 25 characters")
|
||||
.required("Currency is required"),
|
||||
price: Yup.string()
|
||||
.typeError("Invalid number")
|
||||
.min(1, "Price must be greater than 0")
|
||||
.test("no-e", "Invalid number", (value) => {
|
||||
if (value && /\d+e/.test(value)) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
})
|
||||
.required("Price is required"),
|
||||
title: Yup.string()
|
||||
.min(5, "Minimum 5 characters")
|
||||
.max(149, "Maximum 149 characters")
|
||||
.required("Title is required"),
|
||||
description: Yup.string()
|
||||
.min(5, "Minimum 5 characters")
|
||||
.max(299, "Maximum 299 characters")
|
||||
.required("Description is required"),
|
||||
job_detail: Yup.string()
|
||||
.min(3, "Minimum 3 characters")
|
||||
.max(499, "Maximum 499 characters")
|
||||
.required("Details is required"),
|
||||
timeline_days: Yup.number()
|
||||
.typeError("you must specify a number")
|
||||
.min(1, "Price must be greater than 0")
|
||||
.required("Timeline is required"),
|
||||
category: Yup.array().min(1, "Select at least one checkbox"),
|
||||
});
|
||||
const validationSchema = VS;
|
||||
|
||||
function AddJob({ popUpHandler, categories }) {
|
||||
const ApiCall = new usersService();
|
||||
const { walletDetails } = useSelector((state) => state.walletDetails);
|
||||
|
||||
let dispatch = useDispatch();
|
||||
|
||||
let initialValues = {
|
||||
// initial values for formik
|
||||
country: "",
|
||||
price: "",
|
||||
title: "",
|
||||
description: "",
|
||||
job_detail: "",
|
||||
timeline_days: "",
|
||||
category: [],
|
||||
};
|
||||
|
||||
let [requestStatus, setRequestStatus] = useState({
|
||||
loading: false,
|
||||
status: false,
|
||||
message: "",
|
||||
}); // Holds state when submit button is pressed
|
||||
|
||||
const getWalletDetail = (country) => { // A FUNCTION TO GET USER BALANCE BASED ON COUNTRY SELECTED
|
||||
const walletChecker = walletDetails?.data.find(
|
||||
(item) => item.country === country
|
||||
);
|
||||
return walletChecker ? walletChecker.amount : 0;
|
||||
};
|
||||
const [requestStatus, setRequestStatus] = useState(initialReqState); // Holds state when submit button is pressed
|
||||
|
||||
const handleAddJob = async (values, helpers) => {
|
||||
const reqData = {
|
||||
@@ -81,21 +35,6 @@ function AddJob({ popUpHandler, categories }) {
|
||||
category: values.category?.join("@"),
|
||||
};
|
||||
|
||||
const walletAmount = getWalletDetail(reqData.country); // GETTING USER BALANCE BASED ON COUNTRY SELECTED
|
||||
|
||||
if (reqData.price > walletAmount) {
|
||||
setRequestStatus({
|
||||
loading: false,
|
||||
status: false,
|
||||
message: "Insufficient Balance",
|
||||
});
|
||||
|
||||
setTimeout(() => {
|
||||
setRequestStatus({ loading: false, status: false, message: "" });
|
||||
}, 1500);
|
||||
return;
|
||||
}
|
||||
|
||||
setRequestStatus({ loading: true, status: false, message: "" });
|
||||
|
||||
try {
|
||||
@@ -136,7 +75,7 @@ function AddJob({ popUpHandler, categories }) {
|
||||
return (
|
||||
<div className="add-job p-5 w-full bg-white dark:bg-dark-white dark:text-white rounded-md flex flex-col justify-between">
|
||||
<Formik
|
||||
initialValues={initialValues}
|
||||
initialValues={IV}
|
||||
validationSchema={validationSchema}
|
||||
onSubmit={handleAddJob}
|
||||
>
|
||||
@@ -167,7 +106,7 @@ function AddJob({ popUpHandler, categories }) {
|
||||
onChange={props.handleChange}
|
||||
onBlur={props.handleBlur}
|
||||
>
|
||||
{walletDetails.loading ? (
|
||||
{walletDetails?.loading ? (
|
||||
<option className="text-slate-500 text-lg" value="">
|
||||
Loading...
|
||||
</option>
|
||||
@@ -258,7 +197,7 @@ function AddJob({ popUpHandler, categories }) {
|
||||
<div className="sm:w-[60%] w-full">
|
||||
<label
|
||||
htmlFor="Job Delivery Details"
|
||||
className='className="input-label text-[#181c32] dark:text-white text-[13.975px] leading-[20.9625px] font-semibold flex items-center gap-1'
|
||||
className="input-label text-[#181c32] dark:text-white text-[13.975px] leading-[20.9625px] font-semibold flex items-center gap-1"
|
||||
>
|
||||
Job Delivery Details
|
||||
{props.errors.job_detail &&
|
||||
@@ -400,13 +339,12 @@ function AddJob({ popUpHandler, categories }) {
|
||||
</span>
|
||||
</button>
|
||||
|
||||
{requestStatus.loading ? (
|
||||
{requestStatus?.loading ? (
|
||||
<LoadingSpinner size="8" color="sky-blue" />
|
||||
) : (
|
||||
<button
|
||||
type="submit"
|
||||
className="w-[152px] h-[46px] flex justify-center items-center btn-gradient text-base rounded-full text-white"
|
||||
// className='w-20 h-11 flex justify-center items-center btn-gradient text-base rounded-full text-white'
|
||||
>
|
||||
Add Job
|
||||
</button>
|
||||
|
||||
@@ -0,0 +1,100 @@
|
||||
import { Field, Form, Formik } from "formik";
|
||||
import { useEffect, useState } from "react";
|
||||
import { useDispatch, useSelector } from "react-redux";
|
||||
import * as Yup from "yup";
|
||||
import usersService from "../../services/UsersService";
|
||||
import { tableReload } from "../../store/TableReloads";
|
||||
import InputCom from "../Helpers/Inputs/InputCom";
|
||||
import LoadingSpinner from "../Spinners/LoadingSpinner";
|
||||
|
||||
// Initialize state for request values
|
||||
const initialReqState = {
|
||||
loading: false,
|
||||
status: false,
|
||||
message: "",
|
||||
};
|
||||
|
||||
// For form initial values
|
||||
const initialValues = {
|
||||
// initial values for formik
|
||||
country: "",
|
||||
price: "",
|
||||
title: "",
|
||||
description: "",
|
||||
job_detail: "",
|
||||
timeline_days: "",
|
||||
category: [],
|
||||
};
|
||||
|
||||
// const getWalletDetail = (country) => { // A FUNCTION TO GET USER BALANCE BASED ON COUNTRY SELECTED
|
||||
// const walletChecker = walletDetails?.data.find(
|
||||
// (item) => item.country === country
|
||||
// );
|
||||
// return walletChecker ? walletChecker.amount : 0;
|
||||
// };
|
||||
|
||||
// To get the validation schema
|
||||
const validationSchema = Yup.object().shape({
|
||||
country: Yup.string()
|
||||
.min(1, "Minimum 3 characters")
|
||||
.max(25, "Maximum 25 characters")
|
||||
.required("Currency is required"),
|
||||
price: Yup.string()
|
||||
.typeError("Invalid number")
|
||||
.min(1, "Price must be greater than 0")
|
||||
.test("no-e", "Invalid number", (value) => {
|
||||
if (value && /\d+e/.test(value)) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
})
|
||||
.required("Price is required"),
|
||||
title: Yup.string()
|
||||
.min(5, "Minimum 5 characters")
|
||||
.max(149, "Maximum 149 characters")
|
||||
.required("Title is required"),
|
||||
description: Yup.string()
|
||||
.min(5, "Minimum 5 characters")
|
||||
.max(299, "Maximum 299 characters")
|
||||
.required("Description is required"),
|
||||
job_detail: Yup.string()
|
||||
.min(3, "Minimum 3 characters")
|
||||
.max(499, "Maximum 499 characters")
|
||||
.required("Details is required"),
|
||||
timeline_days: Yup.number()
|
||||
.typeError("you must specify a number")
|
||||
.min(1, "Price must be greater than 0")
|
||||
.required("Timeline is required"),
|
||||
category: Yup.array().min(1, "Select category"),
|
||||
});
|
||||
|
||||
const getWalletDetail = (countryParams, walletDetails) => {
|
||||
// A FUNCTION TO GET USER BALANCE BASED ON COUNTRY SELECTED
|
||||
const walletChecker = walletDetails?.data.find(
|
||||
(item) => item.country === countryParams
|
||||
);
|
||||
return walletChecker
|
||||
? {
|
||||
description: walletChecker.description,
|
||||
country: walletChecker.country,
|
||||
}
|
||||
: "";
|
||||
};
|
||||
|
||||
export {
|
||||
Field,
|
||||
Form,
|
||||
Formik,
|
||||
useState,
|
||||
useEffect,
|
||||
useDispatch,
|
||||
useSelector,
|
||||
usersService,
|
||||
InputCom,
|
||||
LoadingSpinner,
|
||||
initialReqState,
|
||||
initialValues,
|
||||
validationSchema,
|
||||
getWalletDetail,
|
||||
tableReload
|
||||
};
|
||||
@@ -17,10 +17,14 @@ import { updateUserDetails } from "../../../store/UserDetails";
|
||||
import ReCAPTCHA from "react-google-recaptcha";
|
||||
|
||||
export default function Login() {
|
||||
// eslint-disable-next-line no-restricted-globals
|
||||
const queryParams = new URLSearchParams(location?.search);
|
||||
const sessionExpired = queryParams.get("sessionExpired");
|
||||
|
||||
const dispatch = useDispatch();
|
||||
const { state } = useLocation();
|
||||
|
||||
const [validCaptcha, setValidCaptcha] = useState({show: false, valid:''}); // FOR CAPTCHA
|
||||
const [validCaptcha, setValidCaptcha] = useState({ show: false, valid: "" }); // FOR CAPTCHA
|
||||
|
||||
let [loginType, setLoginType] = useState("");
|
||||
|
||||
@@ -111,7 +115,8 @@ export default function Login() {
|
||||
}, Number(process.env.REACT_APP_LOGIN_ERROR_TIMEOUT));
|
||||
return;
|
||||
}
|
||||
if(name == "full" && !validCaptcha.valid && validCaptcha.show){ // RUNS AND DISPLAYS CAPTCHA, IF ERROR OCCURED DURING LOGIN FOR FULL LOGIN ALONE
|
||||
if (name == "full" && !validCaptcha.valid && validCaptcha.show) {
|
||||
// RUNS AND DISPLAYS CAPTCHA, IF ERROR OCCURED DURING LOGIN FOR FULL LOGIN ALONE
|
||||
setMsgError("Please Verify Captcha");
|
||||
setLoginLoading(false);
|
||||
setTimeout(() => {
|
||||
@@ -132,12 +137,15 @@ export default function Login() {
|
||||
// setMsgError("Wrong, email/password");
|
||||
setLoginError(true);
|
||||
setLoginLoading(false);
|
||||
setValidCaptcha(prev => ({...prev, show:true})) // DISPLAYS CAPTCHA IF ERROR
|
||||
setValidCaptcha((prev) => ({ ...prev, show: true })); // DISPLAYS CAPTCHA IF ERROR
|
||||
return;
|
||||
}
|
||||
localStorage.setItem("member_id", `${res.data.member_id}`);
|
||||
localStorage.setItem("uid", `${res.data.uid}`);
|
||||
localStorage.setItem("session_token", `${res.data.session}`);
|
||||
if (name === "family") {
|
||||
sessionStorage.setItem("family_uid", res.data?.family_uid);
|
||||
}
|
||||
// localStorage.setItem("session", `${res.data.session}`);
|
||||
dispatch(updateUserDetails({ ...res.data }));
|
||||
setTimeout(() => {
|
||||
@@ -148,7 +156,7 @@ export default function Login() {
|
||||
.catch((error) => {
|
||||
setMsgError("Unable to login, try again");
|
||||
setLoginLoading(false);
|
||||
setValidCaptcha(prev => ({...prev, show:true})) // DISPLAYS CAPTCHA IF ERROR
|
||||
setValidCaptcha((prev) => ({ ...prev, show: true })); // DISPLAYS CAPTCHA IF ERROR
|
||||
})
|
||||
.finally(() => {
|
||||
setTimeout(() => {
|
||||
@@ -159,11 +167,12 @@ export default function Login() {
|
||||
});
|
||||
};
|
||||
|
||||
function captchaChecker(value) { // FUNCTION TO VALIDATE CAPTCHA
|
||||
if(value){
|
||||
setValidCaptcha({show: true, valid:value})
|
||||
}else{
|
||||
setValidCaptcha({show: true, valid:''})
|
||||
function captchaChecker(value) {
|
||||
// FUNCTION TO VALIDATE CAPTCHA
|
||||
if (value) {
|
||||
setValidCaptcha({ show: true, valid: value });
|
||||
} else {
|
||||
setValidCaptcha({ show: true, valid: "" });
|
||||
}
|
||||
}
|
||||
|
||||
@@ -241,20 +250,32 @@ export default function Login() {
|
||||
</div>
|
||||
<div className="content-wrapper login shadow-md w-full lg:max-w-[530px] mx-auto flex justify-center items-center xl:bg-white dark:bg-dark-white 2xl:w-[828px] rounded-[0.475rem] sm:p-7 p-5">
|
||||
<div className="w-full">
|
||||
<div className="title-area flex flex-col justify-center items-center relative text-center mb-7">
|
||||
{/* <h1 className="text-[#181c32] font-semibold dark:text-white mb-3 leading-[27.3px] text-[22.75px]">
|
||||
{/* HIDES THIS IF USER SESSION HAS EXPIRED */}
|
||||
{sessionExpired != "true" && (
|
||||
<div className="title-area flex flex-col justify-center items-center relative text-center mb-7">
|
||||
{/* <h1 className="text-[#181c32] font-semibold dark:text-white mb-3 leading-[27.3px] text-[22.75px]">
|
||||
Sign In to WrenchBoard
|
||||
</h1> */}
|
||||
<span className="text-gray-400 font-medium text-[16.25px] leading-[24.375px]">
|
||||
New Here?{" "}
|
||||
<Link
|
||||
to="/signup"
|
||||
className="font-semibold text-[#4687ba] hover:text-[#009ef7] transition"
|
||||
>
|
||||
Create an Account
|
||||
</Link>
|
||||
</span>
|
||||
</div>
|
||||
<span className="text-gray-400 font-medium text-[16.25px] leading-[24.375px]">
|
||||
New Here?{" "}
|
||||
<Link
|
||||
to="/signup"
|
||||
className="font-semibold text-[#4687ba] hover:text-[#009ef7] transition"
|
||||
>
|
||||
Create an Account
|
||||
</Link>
|
||||
</span>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{/* SHOWS THIS IF USER SESSION HAS EXPIRED */}
|
||||
{sessionExpired == "true" && (
|
||||
<div className="w-full p-1 mb-7">
|
||||
<p className="text-red-500 text-base text-center">
|
||||
Your session expired and will need to login again
|
||||
</p>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{/* switch login component */}
|
||||
<div className="ml-7 flex justify-start items-center gap-3">
|
||||
@@ -320,14 +341,16 @@ export default function Login() {
|
||||
</div>
|
||||
|
||||
{/* hCaptha clone for the time being */}
|
||||
{validCaptcha.show &&
|
||||
<div className="mb-5 flex justify-center w-full">
|
||||
<ReCAPTCHA
|
||||
sitekey={process.env.REACT_APP_GOOGLE_RECAPTCHA_SITEKEY}
|
||||
onChange={captchaChecker}
|
||||
{validCaptcha.show && (
|
||||
<div className="mb-5 flex justify-center w-full">
|
||||
<ReCAPTCHA
|
||||
sitekey={
|
||||
process.env.REACT_APP_GOOGLE_RECAPTCHA_SITEKEY
|
||||
}
|
||||
onChange={captchaChecker}
|
||||
/>
|
||||
</div>
|
||||
}
|
||||
</div>
|
||||
)}
|
||||
|
||||
{loginError && (
|
||||
<div className="relative p-4 text-[#912741] bg-[#fcd9e2] border-[#fbc6d3] mb-4 rounded-[0.475rem] text-md font-thin leading-[19.5px] text-[13px]">
|
||||
@@ -484,8 +507,8 @@ export default function Login() {
|
||||
|
||||
{loginType == "full" && (
|
||||
<div className="pt-5 text-[#181c32] text-center font-semibold text-[13.975px] leading-[20.9625px]">
|
||||
This site is protected by hCaptcha and the our Privacy Policy
|
||||
and Terms of Service apply.
|
||||
This site is protected by a Captcha. Our Privacy Policy and
|
||||
Terms of Service apply.
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
|
||||
@@ -6,6 +6,7 @@ import InputCom from "../../Helpers/Inputs/InputCom";
|
||||
import AuthLayout from "../AuthLayout";
|
||||
|
||||
export default function SignUp() {
|
||||
// eslint-disable-next-line no-restricted-globals
|
||||
const queryParams = new URLSearchParams(location?.search);
|
||||
const country = queryParams.get("cnt")?.toUpperCase();
|
||||
|
||||
|
||||
@@ -54,6 +54,8 @@ export default function VerifyLink() {
|
||||
localStorage.setItem("member_id", `${data?.member_id}`);
|
||||
localStorage.setItem("session_token", `${data?.session}`);
|
||||
localStorage.setItem("session", `${data?.session}`);
|
||||
localStorage.setItem("uid", data?.uid)
|
||||
|
||||
|
||||
navigate("/", { replace: true });
|
||||
setLinkLoader(false);
|
||||
|
||||
@@ -47,7 +47,7 @@ export default function BlogItem(props) {
|
||||
</h1>
|
||||
</div>
|
||||
<div className="notification-wrapper w-full bg-white p-8 rounded-2xl">
|
||||
{blogdata.loading ?
|
||||
{blogdata?.loading ?
|
||||
<LoadingSpinner size='8' color='sky-blue' height='h-[100px]' />
|
||||
:
|
||||
blogdata?.data?.blogdata && blogdata.data?.blogdata.length ?
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
import { useEffect, useState } from "react";
|
||||
import dataImage2 from "../../assets/images/data-table-user-2.png";
|
||||
import { PriceFormatter } from "../Helpers/PriceFormatter";
|
||||
import MarketPopUp from "../MarketPlace/PopUp/MarketPopUp";
|
||||
|
||||
@@ -8,6 +7,7 @@ export default function AvailableJobsCard({
|
||||
datas,
|
||||
hidden = false,
|
||||
contentDisplay,
|
||||
image_server,
|
||||
}) {
|
||||
//debugger;
|
||||
const [marketPopUp, setMarketPopUp] = useState({ show: false, data: {} });
|
||||
@@ -23,6 +23,13 @@ export default function AvailableJobsCard({
|
||||
const imagePath = require(`../../assets/images/${datas.thumbnil}`); // Replace with your directory path for local images
|
||||
setImageUrl(imagePath);
|
||||
}, []);
|
||||
|
||||
const image = localStorage.getItem("session_token")
|
||||
? `${image_server}${localStorage.getItem("session_token")}/job/${
|
||||
datas.job_uid
|
||||
}`
|
||||
: "";
|
||||
|
||||
return (
|
||||
<>
|
||||
{contentDisplay == "grid" ? (
|
||||
@@ -131,7 +138,7 @@ export default function AvailableJobsCard({
|
||||
<div className="card-style-two w-full p-8 my-2 flex items-center gap-4 bg-white dark:bg-dark-white rounded-2xl section-shadow">
|
||||
<div className="flex gap-5 items-center w-full">
|
||||
<div className="w-full h-[60px] rounded-full overflow-hidden flex justify-center items-center flex-[0.1] min-w-[60px] max-w-[60px]">
|
||||
<img src={dataImage2} alt="data" className="w-full h-full" />
|
||||
<img src={image} alt="data" className="w-full h-full" />
|
||||
</div>
|
||||
<div className="flex flex-col flex-[0.9]">
|
||||
<h1
|
||||
|
||||
@@ -5,7 +5,7 @@ import localImgLoad from "../../lib/localImgLoad";
|
||||
import CountDown from "../Helpers/CountDown";
|
||||
import Icons from "../Helpers/Icons";
|
||||
|
||||
export default function FamilyActiveJobsCard({ datas, hidden = false }) {
|
||||
export default function FamilyActiveJobsCard({ datas, hidden = false, image_server }) {
|
||||
|
||||
let { pathname } = useLocation();
|
||||
|
||||
@@ -22,7 +22,8 @@ export default function FamilyActiveJobsCard({ datas, hidden = false }) {
|
||||
}
|
||||
};
|
||||
//debugger;
|
||||
const bannerName = datas.banner == null ?'default.jpg':datas.banner;
|
||||
// const bannerName = datas.banner == null ?'default.jpg':datas.banner;
|
||||
let image = `${image_server}${localStorage.getItem('session_token')}/job/${datas.origin_job_uid}`
|
||||
return (
|
||||
<div className="card-style-one flex flex-col justify-between w-full h-[387px] bg-white dark:bg-dark-white p-3 pb rounded-2xl">
|
||||
<div className="content">
|
||||
@@ -31,10 +32,13 @@ export default function FamilyActiveJobsCard({ datas, hidden = false }) {
|
||||
{/* thumbnail image */}
|
||||
<div
|
||||
className="thumbnail w-full h-full rounded-xl overflow-hidden px-4 pt-4"
|
||||
// style={{
|
||||
// background: `url(${localImgLoad(
|
||||
// `images/taskbanners/${bannerName}`
|
||||
// )}) center / contain no-repeat`,
|
||||
// }}
|
||||
style={{
|
||||
background: `url(${localImgLoad(
|
||||
`images/taskbanners/${bannerName}`
|
||||
)}) center / contain no-repeat`,
|
||||
background: `url(${image}) center / contain no-repeat`,
|
||||
}}
|
||||
>
|
||||
{/* <div className="product-options flex justify-between relative">*/}
|
||||
|
||||
@@ -2,13 +2,20 @@ import localImgLoad from "../../lib/localImgLoad";
|
||||
import CountDown from "../Helpers/CountDown";
|
||||
import { PriceFormatter } from "../Helpers/PriceFormatter";
|
||||
|
||||
export default function OfferCard({ datas, hidden = false, setOfferPopout }) {
|
||||
export default function OfferCard({
|
||||
datas,
|
||||
hidden = false,
|
||||
setOfferPopout,
|
||||
image_server,
|
||||
}) {
|
||||
let thePrice = PriceFormatter(
|
||||
datas?.price * 0.01,
|
||||
datas?.currency_code,
|
||||
datas?.currency
|
||||
);
|
||||
|
||||
let image = `${image_server}${localStorage.getItem("session_token")}/job/${datas.job_uid}`
|
||||
|
||||
return (
|
||||
<div className="card-style-one flex flex-col justify-between w-full h-[387px] bg-white dark:bg-dark-white p-3 pb rounded-2xl">
|
||||
<div className="content">
|
||||
@@ -18,9 +25,7 @@ export default function OfferCard({ datas, hidden = false, setOfferPopout }) {
|
||||
<div
|
||||
className="thumbnail w-full h-full rounded-xl overflow-hidden px-4 pt-4"
|
||||
style={{
|
||||
background: `url(${localImgLoad(
|
||||
`images/taskbanners/${datas?.banner || "default.jpg"}`
|
||||
)}) center / contain no-repeat`,
|
||||
background: `url(${image}) center / contain no-repeat`,
|
||||
}}
|
||||
>
|
||||
{hidden && <div className="flex justify-center"></div>}
|
||||
@@ -31,26 +36,7 @@ export default function OfferCard({ datas, hidden = false, setOfferPopout }) {
|
||||
<h1 className="text-xl font-bold text-dark-gray dark:text-white mb-2 capitalize line-clamp-1">
|
||||
{datas.title}
|
||||
</h1>
|
||||
{/* countdown */}
|
||||
{/* <div className="w-full h-[54px] flex justify-evenly items-center p-2 rounded-lg border border-[#E3E4FE] dark:border-[#a7a9b533] ">
|
||||
<div className="flex flex-col justify-between">
|
||||
<p className="text-sm text-thin-light-gray dark:text-white tracking-wide">
|
||||
Task Code
|
||||
</p>
|
||||
<p className="text-base font-bold tracking-wide text-dark-gray dark:text-white">
|
||||
{datas.contract}
|
||||
</p>
|
||||
</div>
|
||||
<div className="w-[1px] h-full bg-[#E3E4FE] dark:bg-[#a7a9b533] "></div>
|
||||
<div className="flex flex-col justify-between">
|
||||
<p className="text-sm text-thin-light-gray dark:text-white tracking-wide">
|
||||
Remaining Time
|
||||
</p>
|
||||
<p className="text-base font-bold tracking-wide text-dark-gray dark:text-white">
|
||||
<CountDown lastDate={datas.expire} />
|
||||
</p>
|
||||
</div>
|
||||
</div> */}
|
||||
|
||||
<div className="w-full p-2 rounded-lg border border-[#E3E4FE] dark:border-[#a7a9b533] ">
|
||||
<div className="grid grid-cols-2 gap-2">
|
||||
<div className="flex flex-col justify-between items-center border-r-2">
|
||||
@@ -96,7 +82,7 @@ export default function OfferCard({ datas, hidden = false, setOfferPopout }) {
|
||||
<button
|
||||
type="button"
|
||||
onClick={() =>
|
||||
setOfferPopout({ show: true, data: { ...datas, thePrice } })
|
||||
setOfferPopout({ show: true, data: { ...datas, thePrice, image } })
|
||||
}
|
||||
className="btn-shine w-[98px] h-[33px] text-white rounded-full text-sm bg-pink flex justify-center items-center"
|
||||
>
|
||||
|
||||
@@ -12,6 +12,7 @@ import localImgLoad from "../../lib/localImgLoad";
|
||||
import usersService from "../../services/UsersService";
|
||||
import LoadingSpinner from "../Spinners/LoadingSpinner";
|
||||
import AssignTaskPopout from "./FamilyPopout/AssignTaskPopout";
|
||||
import FamilyWallet from "./Tabs/FamilyWallet";
|
||||
|
||||
// Lazy Imports for components
|
||||
const FamilyWaitlist = lazy(() => import("./Tabs/FamilyWaitlist"));
|
||||
@@ -32,7 +33,7 @@ export default function FamilyManageTabs({
|
||||
loading: false,
|
||||
data: null,
|
||||
};
|
||||
|
||||
// console.log('accountDetails',accountDetails)
|
||||
// State for family details, tasks, waitlist, and pending
|
||||
const [details, setDetails] = useState({
|
||||
familyDetails: { ...initialDetailState },
|
||||
@@ -65,8 +66,10 @@ export default function FamilyManageTabs({
|
||||
// State for family task popout
|
||||
const [familyTaskPopout, setFamilyTaskPopout] = useState(false);
|
||||
|
||||
let [uploadStatus, setUploadStatus] = useState({loading: false, status: false, message:''}) // HOLDS STATE FOR UPLOAD PROFILE PICTURE STATUS
|
||||
|
||||
// State for profile image
|
||||
const [profileImg, setProfileImg] = useState(profile);
|
||||
const [profileImg, setProfileImg] = useState(accountDetails.image ? accountDetails.image : profile);
|
||||
|
||||
// Ref for profile image input
|
||||
const profileImgInput = useRef(null);
|
||||
@@ -89,24 +92,59 @@ export default function FamilyManageTabs({
|
||||
* Checks if the selected file exceeds the maximum file size limit and displays an alert if it does.
|
||||
* If the file is within the size limit, it reads the file using the FileReader API and sets the profile image state with the result.
|
||||
*/
|
||||
const profileImgChangeHandler = (e) => {
|
||||
const MAX_FILE_SIZE = 5 * 1024 * 1024; // 5MB
|
||||
|
||||
const file = e.target.files[0];
|
||||
if (file && file.size > MAX_FILE_SIZE) {
|
||||
alert("File size exceeds the limit.");
|
||||
return;
|
||||
const profileImgChangeHandler = (e) => {
|
||||
setUploadStatus({loading: false, status: false, message:''})
|
||||
let acceptedFormat = ["jpeg", "jpg", "png", "bmp", "gif"] // ARRAY OF SUPPORTED FORMATS
|
||||
let uploadedFile = e.target.files[0] //UPLOADED FILE
|
||||
|
||||
const fileFormat = uploadedFile?.type?.split("/")[1]?.toLowerCase();
|
||||
if(!acceptedFormat.includes(fileFormat)){ //CHECKING FOR CORRECT UPLOAD FORMAT
|
||||
const msg = `Please select ${acceptedFormat.slice(0, -1).join(', ')} or ${acceptedFormat.slice(-1)}`;
|
||||
setUploadStatus({loading: false, status: false, message:msg})
|
||||
return setTimeout(()=>{
|
||||
profileImgInput.current.value = '' // clear the input
|
||||
setUploadStatus({loading: false, status: false, message:''})
|
||||
},5000)
|
||||
}
|
||||
|
||||
if (file) {
|
||||
const imgReader = new FileReader();
|
||||
imgReader.onload = () => {
|
||||
const imageDataUrl = imgReader.result;
|
||||
if(uploadedFile.size > 5*1048576){ // CHECKING FOR CORRECT FILE SIZE
|
||||
setUploadStatus({loading: false, status: false, message:'File must not exceed 5MB'})
|
||||
return setTimeout(()=>{
|
||||
profileImgInput.current.value = '' // clear the input
|
||||
setUploadStatus({loading: false, status: false, message:''})
|
||||
},5000)
|
||||
}
|
||||
|
||||
// Set the profile image
|
||||
setProfileImg(imageDataUrl);
|
||||
if (e.target.value !== "") {
|
||||
const imgReader = new FileReader();
|
||||
imgReader.onload = (event) => {
|
||||
let base64Img = imgReader.result.split(",")[1];
|
||||
let reqData = { // PAYLOAD FOR API CALL
|
||||
family_uid: accountDetails?.family_uid,
|
||||
file_name: uploadedFile?.name,
|
||||
file_size: uploadedFile?.size,
|
||||
file_type: uploadedFile?.type?.split("/")[0]?.toLowerCase(),
|
||||
file_data: base64Img,
|
||||
msg_type: 'FILE',
|
||||
action: 11305
|
||||
}
|
||||
setUploadStatus({loading: true, status: false, message:'Loading...'})
|
||||
apiCall.sendFiles(reqData).then(res=>{
|
||||
if(res.status != 200 || res.data.internal_return < 0){
|
||||
return setUploadStatus({loading: false, status: false, message: 'Something went wrong, try again'})
|
||||
}
|
||||
setUploadStatus({loading: false, status: true, message: 'Uploaded successfully'})
|
||||
setProfileImg(event.target.result);
|
||||
}).catch(error=>{
|
||||
setUploadStatus({loading: false, status: false, message: 'Network error, try again'})
|
||||
}).finally(()=>{
|
||||
setTimeout(()=>{
|
||||
setUploadStatus({loading: false, status: false, message: ''})
|
||||
},5000)
|
||||
})
|
||||
};
|
||||
imgReader.readAsDataURL(file);
|
||||
imgReader.readAsDataURL(e.target.files[0]);
|
||||
}
|
||||
};
|
||||
|
||||
@@ -165,7 +203,8 @@ export default function FamilyManageTabs({
|
||||
handlePrint={useHandlePrint}
|
||||
/>
|
||||
),
|
||||
Profile: <FamilyProfile />,
|
||||
Profile: <FamilyProfile familyData={details.familyDetails.data} />,
|
||||
wallet: <FamilyWallet familyData={details.familyDetails.data} />,
|
||||
};
|
||||
|
||||
// Default tab component
|
||||
@@ -296,18 +335,19 @@ export default function FamilyManageTabs({
|
||||
profileImgChangeHandler={profileImgChangeHandler}
|
||||
browseProfileImg={browseProfileImg}
|
||||
accountDetails={accountDetails}
|
||||
uploadStatus={uploadStatus}
|
||||
/>
|
||||
<div className="mt-4 flex justify-start items-center gap-2">
|
||||
<div className="mt-4 flex flex-col justify-center items-center gap-2 lg:flex-row lg:justify-center lg:items-center xl:flex-col xl:justify-center xl:items-center 2xl:flex-row 2xl:justify-center 2xl:items-center 2xl:gap-[2px]">
|
||||
<button
|
||||
onClick={() => tabHandler("Account")}
|
||||
className="family-icon p-2 border-2 border-sky-blue rounded-2xl flex flex-col justify-between items-center max-w-[65px] w-full"
|
||||
>
|
||||
<img
|
||||
src={localImgLoad("images/icons/account.svg")}
|
||||
className="max-w-[30px] w-full"
|
||||
className="max-w-[30px]"
|
||||
alt="Settings-Icon"
|
||||
/>
|
||||
<p className="text-lg text-sky-blue">Acc.</p>
|
||||
<p className="text-[16px] text-sky-blue">Acc.</p>
|
||||
</button>
|
||||
<button
|
||||
onClick={() => tabHandler("Profile")}
|
||||
@@ -315,10 +355,21 @@ export default function FamilyManageTabs({
|
||||
>
|
||||
<img
|
||||
src={localImgLoad("images/icons/profile.svg")}
|
||||
className="max-w-[30px] w-full"
|
||||
className="max-w-[30px]"
|
||||
alt="Settings-Icon"
|
||||
/>
|
||||
<p className="text-lg text-sky-blue">Profile</p>
|
||||
<p className="text-[16px] text-sky-blue">Profile</p>
|
||||
</button>
|
||||
<button
|
||||
onClick={() => tabHandler("wallet")}
|
||||
className="family-icon p-2 border-2 border-sky-blue rounded-2xl flex flex-col justify-between items-center max-w-[65px] w-full"
|
||||
>
|
||||
<img
|
||||
src={localImgLoad("images/icons/wallet.svg")}
|
||||
className="max-w-[30px]"
|
||||
alt="Settings-Icon"
|
||||
/>
|
||||
<p className="text-[16px] text-sky-blue">Wallet</p>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -5,6 +5,8 @@ import { PriceFormatter } from "../../Helpers/PriceFormatter";
|
||||
import LoadingSpinner from "../../Spinners/LoadingSpinner";
|
||||
import Detail from "../../jobPopout/popoutcomponent/Detail";
|
||||
import { NewTasks } from "./forms";
|
||||
import { tableReload } from "../../../store/TableReloads";
|
||||
import { useDispatch } from "react-redux";
|
||||
|
||||
const AssignTaskPopout = React.memo(
|
||||
({
|
||||
@@ -19,6 +21,8 @@ const AssignTaskPopout = React.memo(
|
||||
}) => {
|
||||
const apiCall = new usersService();
|
||||
|
||||
const dispatch = useDispatch()
|
||||
|
||||
let [requestStatus, setRequestStatus] = useState({
|
||||
loading: false,
|
||||
status: false,
|
||||
@@ -90,11 +94,11 @@ const AssignTaskPopout = React.memo(
|
||||
const requiredFields = {
|
||||
banner,
|
||||
// category,
|
||||
country,
|
||||
currency:country,
|
||||
description,
|
||||
job_detail,
|
||||
'job detail':job_detail,
|
||||
price,
|
||||
timeline_days,
|
||||
timeline:timeline_days,
|
||||
title,
|
||||
};
|
||||
|
||||
@@ -104,7 +108,7 @@ const AssignTaskPopout = React.memo(
|
||||
setRequestStatus({
|
||||
loading: false,
|
||||
status: false,
|
||||
message: `${field} is empty`,
|
||||
message: `${field[0].toUpperCase()+field.slice(1).toLowerCase()} is empty`,
|
||||
});
|
||||
return setTimeout(() => {
|
||||
setRequestStatus({ loading: false, status: false, message: "" });
|
||||
@@ -145,6 +149,7 @@ const AssignTaskPopout = React.memo(
|
||||
message: "action successful",
|
||||
});
|
||||
setUpdatePage(prev => !prev) // Updates family task page by calling the useeffect hook
|
||||
dispatch(tableReload({ type: "WALLETTABLE" })); // RELOADS USER WALLET
|
||||
setTimeout(() => {
|
||||
setRequestStatus({ loading: false, status: false, message: "" });
|
||||
action(); // FUNCTION THAT CLOSES THE MODAL BOX
|
||||
@@ -201,7 +206,7 @@ const AssignTaskPopout = React.memo(
|
||||
</svg>
|
||||
</button>
|
||||
</div>
|
||||
{familyTask.loading ? (
|
||||
{familyTask?.loading ? (
|
||||
<div className="h-[100px] w-full flex justify-center items-center">
|
||||
<LoadingSpinner color="sky-blue" size="16" />
|
||||
</div>
|
||||
|
||||
@@ -69,7 +69,7 @@ export default function NewTasks({ formState, setFormState }) {
|
||||
onChange={handleInputChange}
|
||||
// onBlur={props.handleBlur}
|
||||
>
|
||||
{currency.loading ? (
|
||||
{currency?.loading ? (
|
||||
<option className="text-slate-500 text-[13.975px]" value="">
|
||||
Loading...
|
||||
</option>
|
||||
|
||||
@@ -0,0 +1,9 @@
|
||||
import React from 'react'
|
||||
|
||||
const AddFamily = () => {
|
||||
return (
|
||||
<div>Add Family</div>
|
||||
)
|
||||
}
|
||||
|
||||
export default AddFamily
|
||||
@@ -0,0 +1,153 @@
|
||||
import React, { useMemo, useRef, useState } from "react";
|
||||
import usersService from "../../../../services/UsersService";
|
||||
|
||||
const FamilyBanner = ({ imageServer }) => {
|
||||
const uploadedImage = `${imageServer}${localStorage.getItem(
|
||||
"session_token"
|
||||
)}/familybanner/${localStorage.getItem("uid")}`;
|
||||
|
||||
const familyBannerRef = useRef(null);
|
||||
const jobApi = useMemo(() => new usersService(), []);
|
||||
|
||||
const [familyBannerImage, setFamilyBannerImage] = useState(uploadedImage);
|
||||
const [uploadStatus, setUploadStatus] = useState({
|
||||
loading: false,
|
||||
status: false,
|
||||
message: "",
|
||||
});
|
||||
|
||||
/**
|
||||
* Handles the change event of the family image input field.
|
||||
* Checks if the selected file exceeds the maximum file size limit and displays an alert if it does.
|
||||
* If the file is within the size limit, it reads the file using the FileReader API and sets the profile image state with the result.
|
||||
*/
|
||||
|
||||
const familyBannerHandler = (e) => {
|
||||
setUploadStatus({ loading: false, status: false, message: "" });
|
||||
let acceptedFormat = ["jpeg", "jpg", "png", "bmp"]; // ARRAY OF SUPPORTED FORMATS
|
||||
let uploadedFile = e.target.files[0]; //UPLOADED FILE
|
||||
|
||||
const fileFormat = uploadedFile?.type?.split("/")[1]?.toLowerCase();
|
||||
if (!acceptedFormat.includes(fileFormat)) {
|
||||
//CHECKING FOR CORRECT UPLOAD FORMAT
|
||||
const msg = `Please select ${acceptedFormat
|
||||
.slice(0, -1)
|
||||
.join(", ")} or ${acceptedFormat.slice(-1)}`;
|
||||
setUploadStatus({ loading: false, status: false, message: msg });
|
||||
return setTimeout(() => {
|
||||
// profileImgInput.current.value = '' // clear the input
|
||||
setUploadStatus({ loading: false, status: false, message: "" });
|
||||
}, 5000);
|
||||
}
|
||||
|
||||
if (uploadedFile.size > 5 * 1048576) {
|
||||
// CHECKING FOR CORRECT FILE SIZE
|
||||
setUploadStatus({
|
||||
loading: false,
|
||||
status: false,
|
||||
message: "File must not exceed 5MB",
|
||||
});
|
||||
return setTimeout(() => {
|
||||
// profileImgInput.current.value = '' // clear the input
|
||||
setUploadStatus({ loading: false, status: false, message: "" });
|
||||
}, 5000);
|
||||
}
|
||||
|
||||
if (e.target.value !== "") {
|
||||
const imgReader = new FileReader();
|
||||
imgReader.onload = (event) => {
|
||||
let base64Img = imgReader.result.split(",")[1];
|
||||
let reqData = {
|
||||
// PAYLOAD FOR API CALL
|
||||
job_uid: localStorage.getItem("uid"),
|
||||
file_name: uploadedFile?.name.slice(0, 19),
|
||||
file_size: uploadedFile?.size,
|
||||
file_type: uploadedFile?.type?.split("/")[0]?.toLowerCase(),
|
||||
file_data: base64Img,
|
||||
msg_type: "FILE",
|
||||
action: 11303,
|
||||
};
|
||||
setUploadStatus({
|
||||
loading: true,
|
||||
status: false,
|
||||
message: "Loading...",
|
||||
});
|
||||
jobApi
|
||||
.sendFiles(reqData)
|
||||
.then((res) => {
|
||||
if (res.status != 200 || res.data.internal_return < 0) {
|
||||
return setUploadStatus({
|
||||
loading: false,
|
||||
status: false,
|
||||
message: "Something went wrong, try again",
|
||||
});
|
||||
}
|
||||
setUploadStatus({
|
||||
loading: false,
|
||||
status: true,
|
||||
message: "Uploaded successfully",
|
||||
});
|
||||
setFamilyBannerImage(event.target.result);
|
||||
})
|
||||
.catch((error) => {
|
||||
setUploadStatus({
|
||||
loading: false,
|
||||
status: false,
|
||||
message: "Network error, try again",
|
||||
});
|
||||
})
|
||||
.finally(() => {
|
||||
setTimeout(() => {
|
||||
setUploadStatus({ loading: false, status: false, message: "" });
|
||||
}, 5000);
|
||||
});
|
||||
};
|
||||
imgReader.readAsDataURL(e.target.files[0]);
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<div className="flex flex-col gap-4">
|
||||
<div className="w-full lg:h-[400px] rounded-2xl">
|
||||
<img
|
||||
src={familyBannerImage}
|
||||
alt="familyBanner"
|
||||
className="w-full h-full object-cover rounded-2xl"
|
||||
/>
|
||||
</div>
|
||||
<div className="flex justify-between w-full">
|
||||
<div>
|
||||
{uploadStatus.message && !uploadStatus.loading && (
|
||||
<p
|
||||
className={`text-center ${
|
||||
uploadStatus.status ? "text-green-500" : "text-red-500"
|
||||
}`}
|
||||
>
|
||||
{uploadStatus.message}
|
||||
</p>
|
||||
)}
|
||||
{uploadStatus.loading && (
|
||||
<p className="text-center">{uploadStatus.message}</p>
|
||||
)}
|
||||
</div>
|
||||
<div>
|
||||
<input
|
||||
ref={familyBannerRef}
|
||||
className="hidden"
|
||||
type="file"
|
||||
accept="image/*"
|
||||
onChange={familyBannerHandler}
|
||||
/>
|
||||
<button
|
||||
className="btn-gradient w-[153px] h-[46px] rounded-full text-white lg:flex hidden justify-center items-center"
|
||||
onClick={() => familyBannerRef.current.click()}
|
||||
>
|
||||
Change Banner
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default FamilyBanner;
|
||||
@@ -0,0 +1,9 @@
|
||||
import React from 'react'
|
||||
|
||||
const Relatives = () => {
|
||||
return (
|
||||
<div>Relatives</div>
|
||||
)
|
||||
}
|
||||
|
||||
export default Relatives
|
||||
@@ -0,0 +1,5 @@
|
||||
import AddFamily from "./AddFamily";
|
||||
import FamilyBanner from "./FamilyBanner";
|
||||
import Relatives from "./Relatives";
|
||||
|
||||
export {AddFamily, FamilyBanner, Relatives}
|
||||
@@ -0,0 +1,103 @@
|
||||
import React, { useEffect, useState } from "react";
|
||||
import { Link, useLocation } from "react-router-dom";
|
||||
import Icons from "../../Helpers/Icons";
|
||||
import Layout from "../../Partials/Layout";
|
||||
import { AddFamily, FamilyBanner, Relatives } from "./Tabs";
|
||||
|
||||
const FamilySettings = () => {
|
||||
let {state} = useLocation()
|
||||
const tabs = [
|
||||
{
|
||||
id: 1,
|
||||
name: "add_family",
|
||||
title: "Add Family",
|
||||
iconName: "new-family",
|
||||
},
|
||||
{
|
||||
id: 2,
|
||||
name: "relatives",
|
||||
title: "Relatives",
|
||||
iconName: "people-hover",
|
||||
},
|
||||
{
|
||||
id: 3,
|
||||
name: "family_banner",
|
||||
title: "Family Banner",
|
||||
iconName: "people-hover",
|
||||
},
|
||||
];
|
||||
|
||||
const [tab, setTab] = useState(() => {
|
||||
// Retrieve the active tab from local storage or use the default tab
|
||||
return localStorage.getItem("activeTab") || tabs[0].name;
|
||||
});
|
||||
|
||||
const tabHandler = (value) => {
|
||||
setTab(value);
|
||||
};
|
||||
|
||||
// Update local storage when the tab changes
|
||||
useEffect(() => {
|
||||
localStorage.setItem("activeTab", tab);
|
||||
}, [tab]);
|
||||
|
||||
const tabComponents = {
|
||||
add_family: <AddFamily />,
|
||||
relatives: <Relatives />,
|
||||
family_banner: <FamilyBanner imageServer={state.imageServer} />,
|
||||
};
|
||||
|
||||
const defaultTabComponent = Array(tabComponents)[0].add_family;
|
||||
|
||||
const selectedComponent = tabComponents[tab] || defaultTabComponent;
|
||||
|
||||
return (
|
||||
<Layout>
|
||||
<div className="notification-page w-full mb-10">
|
||||
<div className="notification-wrapper w-full">
|
||||
{/* heading */}
|
||||
<div className="sm:flex justify-between items-center mb-6">
|
||||
<div className="mb-5 sm:mb-0">
|
||||
<h1 className="text-26 font-bold inline-flex gap-3 text-dark-gray dark:text-white items-center">
|
||||
<span className={``}>Family Settings</span>
|
||||
</h1>
|
||||
</div>
|
||||
<Link to="/acc-family">Go Back</Link>
|
||||
</div>
|
||||
{/* Something Here */}
|
||||
{/* <form className="logout-modal-body w-full flex flex-col items-center px-10 py-8 gap-4"></form> */}
|
||||
<div className="w-full bg-white dark:bg-dark-white overflow-y-auto rounded-2xl section-shadow h-full ">
|
||||
<div className="update-table w-full h-full p-4 bg-white dark:bg-dark-white overflow-y-auto rounded-2xl section-shadow min-h-[520px] lg:flex lg:px-10 px-4 justify-between">
|
||||
<div className="content-tab-items lg:w-[230px] w-full mr-2">
|
||||
<ul className="overflow-hidden mb-10 lg:mb-0 py-8">
|
||||
{tabs.map(({ name, id, title, iconName }) => (
|
||||
<li
|
||||
onClick={() => tabHandler(name)}
|
||||
key={id}
|
||||
className={`flex lg:space-x-4 space-x-2 hover:text-purple transition-all duration-300 ease-in-out items-center cursor-pointer lg:mb-11 mb-2 mr-6 lg:mr-0 float-left lg:float-none overflow-hidden ${
|
||||
tab === name ? "text-purple" : " text-thin-light-gray"
|
||||
}`}
|
||||
>
|
||||
<div>
|
||||
<Icons name={iconName} />
|
||||
</div>
|
||||
<div>
|
||||
<p className="text-18 tracking-wide">{title}</p>
|
||||
</div>
|
||||
</li>
|
||||
))}
|
||||
</ul>
|
||||
</div>
|
||||
<div className="w-[1px] bg-[#E3E4FE] dark:bg-[#a7a9b533] mr-10"></div>
|
||||
<div className="flex-1">
|
||||
<div className="tab-item">{selectedComponent}</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</Layout>
|
||||
);
|
||||
};
|
||||
|
||||
export default FamilySettings;
|
||||
@@ -19,6 +19,7 @@ export default function FamilyTable({
|
||||
familyList,
|
||||
loader,
|
||||
popUpHandler,
|
||||
imageServer
|
||||
}) {
|
||||
const navigate = useNavigate();
|
||||
const [currentPage, setCurrentPage] = useState(0);
|
||||
@@ -52,28 +53,41 @@ export default function FamilyTable({
|
||||
task_count,
|
||||
family_uid,
|
||||
banner,
|
||||
enable_traking,
|
||||
profile_picture,
|
||||
imageServer
|
||||
}) => {
|
||||
// Check for valid dates
|
||||
const addedDate = added ? added.split(" ")[0] : "N/A";
|
||||
const loginDate = last_login ? formatDateString(last_login) : "N/A";
|
||||
const key = `family-${family_uid}`; // Assign a unique key
|
||||
const image = localStorage.getItem('session_token') ? `${imageServer}${localStorage.getItem('session_token')}/family/${family_uid}` : ''
|
||||
|
||||
const trackingStatus =
|
||||
enable_traking === "0"
|
||||
? "Stopped"
|
||||
: enable_traking === "100"
|
||||
? "Active"
|
||||
: "";
|
||||
|
||||
return (
|
||||
<tr
|
||||
className="bg-white dark:bg-dark-white border-b dark:border-[#5356fb29] hover:bg-gray-50"
|
||||
key={family_uid}
|
||||
className="bg-white dark:bg-dark-white border-b dark:border-[#5356fb29] hover:bg-gray-50"
|
||||
key={key}
|
||||
>
|
||||
<td className="py-4">
|
||||
<div className="flex space-x-2 items-center w-full">
|
||||
<div className="w-[60px] h-[60px] rounded-full overflow-hidden flex justify-center items-center flex-[0.1]">
|
||||
<img
|
||||
src={localImgLoad(`images/icons/${banner}`)}
|
||||
// src={profile_picture}
|
||||
src={image || profile_picture || localImgLoad(`images/icons/${banner}`)}
|
||||
alt={`Avatar of ${firstname} ${lastname}`}
|
||||
className="w-full h-full"
|
||||
/>
|
||||
</div>
|
||||
<div className="flex flex-col flex-[0.9]">
|
||||
<h1 className="font-bold text-xl text-dark-gray dark:text-white whitespace-nowrap">
|
||||
{`${firstname} ${lastname} (${age})`}
|
||||
{`${firstname} ${lastname} (${age < 10 ? `0${age}` : age})`}
|
||||
</h1>
|
||||
<span className="text-sm text-thin-light-gray">
|
||||
Added: <span className="text-purple ml-1">{addedDate}</span>
|
||||
@@ -85,6 +99,23 @@ export default function FamilyTable({
|
||||
</div>
|
||||
</div>
|
||||
</td>
|
||||
|
||||
<td className="text-center py-4 px-2">
|
||||
<div className="flex space-x-1 items-center justify-center">
|
||||
<span
|
||||
className={`text-base font-medium whitespace-nowrap ${
|
||||
enable_traking === "0"
|
||||
? "text-[#FF0000] dark:text-[#FF6666]"
|
||||
: enable_traking === "100"
|
||||
? "text-[#00A000] dark:text-[#00FF00]"
|
||||
: "text-dark-gray dark:text-white"
|
||||
}`}
|
||||
>
|
||||
{trackingStatus}
|
||||
</span>
|
||||
</div>
|
||||
</td>
|
||||
|
||||
<td className="text-center py-4 px-2">
|
||||
<div className="flex space-x-1 items-center justify-center">
|
||||
<span className="text-base text-dark-gray dark:text-white font-medium whitespace-nowrap">
|
||||
@@ -92,6 +123,7 @@ export default function FamilyTable({
|
||||
</span>
|
||||
</div>
|
||||
</td>
|
||||
|
||||
<td className="text-right py-4 px-2 flex items-center justify-center">
|
||||
<button
|
||||
onClick={() =>
|
||||
@@ -104,12 +136,25 @@ export default function FamilyTable({
|
||||
task_count,
|
||||
family_uid,
|
||||
banner,
|
||||
image
|
||||
})
|
||||
}
|
||||
type="button"
|
||||
className="w-20 h-11 flex justify-center items-center btn-gradient text-base rounded-full text-white"
|
||||
className="w-12 h-11 flex justify-center items-center btn-gradient text-base rounded-full text-white"
|
||||
>
|
||||
Manage
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
viewBox="0 0 11 20"
|
||||
id="Arrow"
|
||||
className="w-[0.7rem]"
|
||||
>
|
||||
<path
|
||||
fillRule="evenodd"
|
||||
d="M.366 19.708c.405.39 1.06.39 1.464 0l8.563-8.264a1.95 1.95 0 0 0 0-2.827L1.768.292A1.063 1.063 0 0 0 .314.282a.976.976 0 0 0-.011 1.425l7.894 7.617a.975.975 0 0 1 0 1.414L.366 18.295a.974.974 0 0 0 0 1.413"
|
||||
// fill=""
|
||||
className="color000000 svgShape fill-[#fff]"
|
||||
></path>
|
||||
</svg>
|
||||
</button>
|
||||
</td>
|
||||
</tr>
|
||||
@@ -134,13 +179,14 @@ export default function FamilyTable({
|
||||
<thead className="sticky top-0">
|
||||
<tr className="text-base text-thin-light-gray whitespace-nowrap border-b dark:border-[#5356fb29] default-border-bottom ">
|
||||
<th className="py-4">Name</th>
|
||||
<th className="py-4 text-center">Tracking</th>
|
||||
<th className="py-4 text-center">No of Tasks</th>
|
||||
<th className="py-4 text-right"></th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody className="h-full">
|
||||
{currentFamilyList?.map((familyMember) => {
|
||||
return <FamilyRow {...familyMember} />;
|
||||
{currentFamilyList?.map((familyMember, index) => {
|
||||
return <FamilyRow key={index} {...familyMember} imageServer={imageServer} />;
|
||||
})}
|
||||
</tbody>
|
||||
</table>
|
||||
@@ -171,7 +217,7 @@ export default function FamilyTable({
|
||||
<PaginatedList
|
||||
onClick={handlePagination}
|
||||
prev={currentPage == 0}
|
||||
next={currentPage + itemsPerPage >= familyList.length}
|
||||
next={currentPage + itemsPerPage >= familyList?.length}
|
||||
data={familyList}
|
||||
start={indexOfFirstItem}
|
||||
stop={indexOfLastItem}
|
||||
|
||||
@@ -54,7 +54,7 @@ export default function FamilyPending({
|
||||
value?.currency_code,
|
||||
value?.currency
|
||||
);
|
||||
let image = value.banner ? value.banner : "default.jpg";
|
||||
let image = `${familyData.session_image_server}${localStorage.getItem('session_token')}/job/${value.job_uid}`
|
||||
return (
|
||||
<tr
|
||||
key={index}
|
||||
@@ -64,9 +64,7 @@ export default function FamilyPending({
|
||||
<div className="flex space-x-2 items-center w-full">
|
||||
<div className="w-[60px] h-[60px] p-2 bg-alice-blue rounded-full overflow-hidden flex justify-center items-center">
|
||||
<img
|
||||
src={localImgLoad(
|
||||
`images/taskbanners/${image}`
|
||||
)}
|
||||
src={image}
|
||||
alt="data"
|
||||
className="w-full h-full rounded-full"
|
||||
/>
|
||||
|
||||
@@ -1,11 +1,308 @@
|
||||
export default function FamilyProfile({ className }) {
|
||||
import { useState } from "react";
|
||||
import { useNavigate } from "react-router-dom";
|
||||
import usersService from "../../../services/UsersService";
|
||||
import InputCom from "../../Helpers/Inputs/InputCom";
|
||||
import LoadingSpinner from "../../Spinners/LoadingSpinner";
|
||||
|
||||
import { Form, Formik } from "formik";
|
||||
import * as Yup from "yup";
|
||||
|
||||
export default function FamilyProfile({ familyData, className }) {
|
||||
const validationSchema = Yup.object().shape({
|
||||
firstname: Yup.string().required("Firstname is required"),
|
||||
lastname: Yup.string()
|
||||
// .min(3, "Minimum 3 characters")
|
||||
// .max(25, "Maximum 25 characters")
|
||||
.required("Lastname is required"),
|
||||
year: Yup.number()
|
||||
.required("Year is required")
|
||||
.min(
|
||||
new Date().getFullYear() - process.env.REACT_APP_FAMILY_MAXIMUM_AGE,
|
||||
`Age must be within ${process.env.REACT_APP_FAMILY_MINIMUM_AGE} to ${process.env.REACT_APP_FAMILY_MAXIMUM_AGE} years`
|
||||
)
|
||||
.max(
|
||||
new Date().getFullYear() - process.env.REACT_APP_FAMILY_MINIMUM_AGE,
|
||||
`Age must be within ${process.env.REACT_APP_FAMILY_MINIMUM_AGE} to ${process.env.REACT_APP_FAMILY_MAXIMUM_AGE} years`
|
||||
),
|
||||
month: Yup.number()
|
||||
.required("Month is required")
|
||||
.min(1, "Month is required"),
|
||||
});
|
||||
|
||||
const navigate = useNavigate();
|
||||
const api = new usersService();
|
||||
|
||||
let [requestStatus, setRequestStatus] = useState({
|
||||
loading: false,
|
||||
status: false,
|
||||
message: "",
|
||||
});
|
||||
|
||||
// let [updateDetails, setUpdateDetails] = useState({
|
||||
// family_uid: familyData.uid,
|
||||
// firstname: familyData.firstname,
|
||||
// lastname: familyData.lastname,
|
||||
// year: familyData.year,
|
||||
// month: familyData.month,
|
||||
// action: 22020
|
||||
// })
|
||||
|
||||
// initial values for formik
|
||||
let initialValues = {
|
||||
family_uid: familyData?.uid,
|
||||
firstname: familyData?.firstname,
|
||||
lastname: familyData?.lastname,
|
||||
year: familyData?.year,
|
||||
month: familyData?.month,
|
||||
enable_traking: familyData?.enable_traking,
|
||||
action: 22020,
|
||||
};
|
||||
|
||||
// const handleChange = ({ target: { name, value } }) => {
|
||||
// setUpdateDetails((prev) => ({ ...prev, [name]: value }));
|
||||
// };
|
||||
|
||||
const updateFamily = (values, helpers) => {
|
||||
setRequestStatus({ loading: true, status: false, message: "" });
|
||||
// let {firstname, lastname, year, month} = updateDetails
|
||||
// if(!firstname || !lastname || year == 0 || month == 0) {
|
||||
// setRequestStatus({loading:false, status:false, message: 'Please fill all fields'})
|
||||
// return setTimeout(()=>{
|
||||
// setRequestStatus({loading:false, status:false, message: ''})
|
||||
// }, 5000)
|
||||
// }
|
||||
api
|
||||
.getFamilyUpdate(values)
|
||||
.then((res) => {
|
||||
if (res.data.internal_return < 0) {
|
||||
return setRequestStatus({
|
||||
loading: false,
|
||||
status: false,
|
||||
message: "Failed, try again!",
|
||||
});
|
||||
}
|
||||
setRequestStatus({
|
||||
loading: false,
|
||||
status: true,
|
||||
message: "Family account updated",
|
||||
});
|
||||
setTimeout(() => {
|
||||
navigate("/acc-family", { replace: true });
|
||||
}, 5000);
|
||||
})
|
||||
.catch((error) => {
|
||||
setRequestStatus({
|
||||
loading: false,
|
||||
status: false,
|
||||
message: "Unable to update, try again!",
|
||||
});
|
||||
})
|
||||
.finally(() => {
|
||||
setTimeout(() => {
|
||||
setRequestStatus({ loading: false, status: false, message: "" });
|
||||
}, 5000);
|
||||
});
|
||||
};
|
||||
|
||||
return (
|
||||
<div
|
||||
className={`update-table w-full bg-white dark:bg-dark-white overflow-hidden rounded-2xl section-shadow lg:min-h-[538px] p-3 ${
|
||||
className={`w-full bg-white dark:bg-dark-white overflow-hidden rounded-2xl section-shadow lg:min-h-[538px] p-3 ${
|
||||
className || ""
|
||||
}`}
|
||||
>
|
||||
<h1>Profile</h1>
|
||||
<Formik
|
||||
initialValues={initialValues}
|
||||
validationSchema={validationSchema}
|
||||
onSubmit={updateFamily}
|
||||
>
|
||||
{(props) => {
|
||||
return (
|
||||
<Form className="logout-modal-body w-full lg:w-[500px] lg:mx-auto flex flex-col items-center px-10 py-8 gap-4">
|
||||
<InputCom
|
||||
placeholder="Firstname"
|
||||
label="First Name:"
|
||||
name="firstname"
|
||||
type="text"
|
||||
parentClass="flex items-center gap-1 w-full"
|
||||
labelClass="flex-[0.2] mb-0"
|
||||
inputClass={`flex-[0.8] input-curve lg border border-[#dce4e9] ${
|
||||
props.errors.firstname && props.touched.firstname
|
||||
? "border border-red-500"
|
||||
: ""
|
||||
}`}
|
||||
fieldClass="px-2"
|
||||
value={props.values.firstname}
|
||||
inputHandler={props.handleChange}
|
||||
/>
|
||||
<InputCom
|
||||
placeholder="Lastname"
|
||||
label="Last Name:"
|
||||
name="lastname"
|
||||
type="text"
|
||||
parentClass="flex items-center gap-1 w-full"
|
||||
labelClass="flex-[0.2] mb-0"
|
||||
inputClass={`flex-[0.8] input-curve lg border border-[#dce4e9] ${
|
||||
props.errors.lastname && props.touched.lastname
|
||||
? "border border-red-500"
|
||||
: ""
|
||||
}`}
|
||||
fieldClass="px-2"
|
||||
value={props.values?.lastname}
|
||||
inputHandler={props.handleChange}
|
||||
/>
|
||||
|
||||
<div className="input-com flex flex-col gap-1 w-full">
|
||||
{/* Age dropdown */}
|
||||
<div className="">
|
||||
<label
|
||||
className="input-label text-[#181c32] dark:text-white text-[15px] font-semibold"
|
||||
htmlFor="age-selection"
|
||||
>
|
||||
Birthday: (Year/Month)
|
||||
</label>
|
||||
{((props.errors.year && props.touched.year) ||
|
||||
(props.errors.month && props.touched.month)) && (
|
||||
<span className="text-[12px] text-red-500">
|
||||
{" "}
|
||||
{props.errors.year || props.errors.month}
|
||||
</span>
|
||||
)}
|
||||
</div>
|
||||
<div className="flex max-w-[330px] w-full self-end gap-4">
|
||||
<select
|
||||
name="year"
|
||||
id="yearDropdown"
|
||||
value={props.values.year}
|
||||
onChange={props.handleChange}
|
||||
className={`input-wrapper border border-[#f5f8fa] ${
|
||||
props.errors.year && props.touched.year
|
||||
? "border border-red-500"
|
||||
: ""
|
||||
} dark:border-[#5e6278] w-56 rounded-[35px] h-10 overflow-hidden relative font-medium leading-6 bg-clip-padding text-[#5e6278] dark:text-gray-100 bg-[#f5f8fa] dark:bg-[#5e6278] text-base focus-visible:border-transparent focus-visible:outline-0 focus-visible:ring-transparent px-4`}
|
||||
>
|
||||
<option value="">Select a Year</option>
|
||||
{years.map((year) => (
|
||||
<option key={year} value={year}>
|
||||
{year}
|
||||
</option>
|
||||
))}
|
||||
</select>
|
||||
|
||||
<select
|
||||
name="month"
|
||||
id="monthDropdown"
|
||||
// value={(months.filter(month => month.id == selectedMonth)[0]?.id) || ''}
|
||||
value={
|
||||
months.filter(
|
||||
(month) => month.id == props.values.month
|
||||
)[0]?.id || 0
|
||||
}
|
||||
onChange={props.handleChange}
|
||||
className={`input-wrapper border border-[#f5f8fa] ${
|
||||
props.errors.month && props.touched.month
|
||||
? "border border-red-500"
|
||||
: ""
|
||||
} dark:border-[#5e6278] w-56 rounded-[35px] h-10 overflow-hidden relative font-medium leading-6 bg-clip-padding text-[#5e6278] dark:text-gray-100 bg-[#f5f8fa] dark:bg-[#5e6278] text-base focus-visible:border-transparent focus-visible:outline-0 focus-visible:ring-transparent px-4`}
|
||||
>
|
||||
<option value="">Select a Month</option>
|
||||
{months.map(({ id, name }) => (
|
||||
<option key={id} value={id}>
|
||||
{name}
|
||||
</option>
|
||||
))}
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="input-com mb-7 flex flex-col gap-1 w-full">
|
||||
{/* Location dropdown */}
|
||||
<div className="">
|
||||
<label
|
||||
className="input-label text-[#181c32] dark:text-white text-[15px] font-semibold"
|
||||
htmlFor="age-selection"
|
||||
>
|
||||
Location Tracking
|
||||
</label>
|
||||
{/* {((props.errors.year && props.touched.year) ||
|
||||
(props.errors.month && props.touched.month)) && (
|
||||
<span className="text-[12px] text-red-500">
|
||||
{" "}
|
||||
{props.errors.year || props.errors.month}
|
||||
</span>
|
||||
)} */}
|
||||
</div>
|
||||
<div className="flex max-w-[330px] w-full self-end gap-4">
|
||||
<select
|
||||
name="enable_traking"
|
||||
id="enable_traking"
|
||||
value={props.values.enable_traking}
|
||||
onChange={props.handleChange}
|
||||
className={`input-wrapper border border-[#f5f8fa] dark:border-[#5e6278] w-56 rounded-[35px] h-10 overflow-hidden relative font-medium leading-6 bg-clip-padding text-[#5e6278] dark:text-gray-100 bg-[#f5f8fa] dark:bg-[#5e6278] text-base focus-visible:border-transparent focus-visible:outline-0 focus-visible:ring-transparent px-4`}
|
||||
>
|
||||
<option value={+0}>Disabled</option>
|
||||
<option value={+100}>Enabled</option>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{requestStatus.message && (
|
||||
<div
|
||||
className={`relative p-2 ${
|
||||
!requestStatus.status
|
||||
? "text-[#912741] bg-[#fcd9e2] border-[#fbc6d3]"
|
||||
: "text-green-700"
|
||||
} mb-2 rounded-[0.475rem] text-xs font-light leading-[19.5px]`}
|
||||
>
|
||||
{requestStatus.message}
|
||||
</div>
|
||||
)}
|
||||
<div className="flex justify-end w-full">
|
||||
{requestStatus.loading ? (
|
||||
<>
|
||||
<div className="signup btn-loader"></div>
|
||||
<LoadingSpinner size="4" />
|
||||
</>
|
||||
) : (
|
||||
<button
|
||||
type="submit"
|
||||
// onClick={updateFamily}
|
||||
// className={`rounded-[0.475rem] text-white flex justify-center bg-[#4687ba] hover:bg-[#009ef7] transition-all duration-300 items-center h-[42px] py-[0.8875rem] px-[1.81rem] text-[14.95px] btn-login`}
|
||||
className="text-white btn-gradient text-lg tracking-wide px-6 py-2 rounded-full"
|
||||
>
|
||||
Update
|
||||
</button>
|
||||
)}
|
||||
</div>
|
||||
</Form>
|
||||
);
|
||||
}}
|
||||
</Formik>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
// Get the current year
|
||||
// const currentYear = new Date().getFullYear() - process.env.REACT_APP_FAMILY_MINIMUM_AGE;
|
||||
|
||||
// Calculate the current year and the year 3 years before the current year
|
||||
const currentYear = new Date().getFullYear();
|
||||
const startYear = currentYear - 3;
|
||||
|
||||
// Generate an array of years from 4 years before the current year to 18 years before the current year
|
||||
const years = Array.from({ length: 15 }, (_, index) => startYear - index).reverse();
|
||||
// const latestYears = years.slice(0, process.env.REACT_APP_FAMILY_YEAR_RANGE);
|
||||
|
||||
const months = [
|
||||
{ id: "1", name: "January" },
|
||||
{ id: "2", name: "February" },
|
||||
{ id: "3", name: "March" },
|
||||
{ id: "4", name: "April" },
|
||||
{ id: "5", name: "May" },
|
||||
{ id: "6", name: "June" },
|
||||
{ id: "7", name: "July" },
|
||||
{ id: "8", name: "August" },
|
||||
{ id: "9", name: "September" },
|
||||
{ id: "10", name: "October" },
|
||||
{ id: "11", name: "November" },
|
||||
{ id: "12", name: "December" },
|
||||
];
|
||||
|
||||
@@ -67,9 +67,7 @@ export default function FamilyTasks({
|
||||
value?.currency_code,
|
||||
value?.currency
|
||||
);
|
||||
let image = value.banner
|
||||
? value.banner
|
||||
: "default.jpg";
|
||||
let image = `${familyData.session_image_server}${localStorage.getItem('session_token')}/job/${value.job_uid}`
|
||||
return (
|
||||
<tr
|
||||
key={index}
|
||||
@@ -79,9 +77,7 @@ export default function FamilyTasks({
|
||||
<div className="flex space-x-2 items-center w-full">
|
||||
<div className="w-[60px] h-[60px] p-2 bg-alice-blue rounded-full overflow-hidden flex justify-center items-center">
|
||||
<img
|
||||
src={localImgLoad(
|
||||
`images/taskbanners/${image}`
|
||||
)}
|
||||
src={image}
|
||||
alt="data"
|
||||
className="w-full h-full rounded-full"
|
||||
/>
|
||||
|
||||
@@ -67,6 +67,8 @@ const FamilyWaitlist = memo(
|
||||
const selectedImage = require(`../../../assets/images/family/${
|
||||
value?.banner || "default.jpg"
|
||||
}`);
|
||||
console.log('VALUE', value)
|
||||
// let image = `${familyData.session_image_server}${localStorage.getItem('session_token')}/job/${value.job_uid}`
|
||||
return (
|
||||
<tr
|
||||
className="bg-white dark:bg-dark-white border-b dark:border-[#5356fb29] hover:bg-gray-50"
|
||||
|
||||
@@ -0,0 +1,45 @@
|
||||
import React, { useEffect, useState } from 'react'
|
||||
|
||||
import usersService from '../../../services/UsersService'
|
||||
|
||||
import LoadingSpinner from '../../Spinners/LoadingSpinner'
|
||||
import background from '../../../assets/images/bg-sky-blue.jpg'
|
||||
import Wallet from './wallet/Wallet'
|
||||
|
||||
function FamilyWallet({familyData}) {
|
||||
const apiUrl = new usersService()
|
||||
|
||||
let [familyWallet, setFamilyWallet] = useState({loading:true, data: []})
|
||||
|
||||
let [familyWalletReload, setFamilyWalletReload] = useState(false) // STATE TO DETERMINE WHEN TO RELOAD FAMILY WALLET TAB/PAGE
|
||||
|
||||
useEffect(()=>{
|
||||
setFamilyWallet({loading:true, data: []})
|
||||
apiUrl.getFamilyWallet({family_uid:familyData?.uid}).then(res => {
|
||||
setFamilyWallet({loading:false, data: res?.data?.result_list || []})
|
||||
}).catch(error => {
|
||||
setFamilyWallet({loading:false, data: []})
|
||||
})
|
||||
},[familyWalletReload])
|
||||
|
||||
return (
|
||||
<div className='p-3 w-full h-full bg-white dark:bg-dark-white flex flex-col justify-start items-start'>
|
||||
{familyWallet.loading ?
|
||||
<div className='w-full h-20 flex justify-center items-center'>
|
||||
<LoadingSpinner size='8' color='sky-blue' />
|
||||
</div>
|
||||
:
|
||||
familyWallet?.data?.length > 0 ?
|
||||
<div className='w-full p-4 flex flex-col gap-2'>
|
||||
{familyWallet?.data?.map((wallet, index)=>(
|
||||
<Wallet key={index} wallet={wallet} familyData={familyData} setFamilyWalletReload={setFamilyWalletReload} />
|
||||
))}
|
||||
</div>
|
||||
:
|
||||
<p className='text-lg text-gray-500 dark:text-gray-400'>No Wallet Found!</p>
|
||||
}
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
export default FamilyWallet
|
||||
@@ -7,6 +7,7 @@ export default function ProfileInfo({
|
||||
profileImgChangeHandler,
|
||||
browseProfileImg,
|
||||
accountDetails,
|
||||
uploadStatus
|
||||
}) {
|
||||
// console.log(accountDetails.banner)
|
||||
return (
|
||||
@@ -52,6 +53,11 @@ export default function ProfileInfo({
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{/* DISPLAYS PROFILE UPLOADING STATUS */}
|
||||
<div className="w-full">
|
||||
{uploadStatus.message && !uploadStatus.loading && <p className={`text-center ${uploadStatus.status ? 'text-green-500':'text-red-500'}`}>{uploadStatus.message}</p>}
|
||||
{uploadStatus.loading && <p className="text-center">{uploadStatus.message}</p>}
|
||||
</div>
|
||||
<div className="flex flex-col justify-center gap-3 items-center">
|
||||
<h1 className="font-bold text-xl tracking-wide line-clamp-1 text-dark-gray dark:text-white capitalize">
|
||||
{accountDetails?.firstname}
|
||||
|
||||
@@ -0,0 +1,301 @@
|
||||
import React, { useEffect, useState } from 'react'
|
||||
import ModalCom from '../../../Helpers/ModalCom'
|
||||
import InputCom from '../../../Helpers/Inputs/InputCom'
|
||||
import { Form, Formik } from "formik";
|
||||
import * as Yup from "yup";
|
||||
|
||||
import {AmountTo2DP} from '../../../Helpers/PriceFormatter'
|
||||
import usersService from '../../../../services/UsersService';
|
||||
import LoadingSpinner from '../../../Spinners/LoadingSpinner';
|
||||
import { PriceFormatter } from '../../../Helpers/PriceFormatter';
|
||||
import { tableReload } from '../../../../store/TableReloads';
|
||||
import { useDispatch } from 'react-redux';
|
||||
|
||||
const validationSchema = Yup.object().shape({
|
||||
// amount: Yup.string()
|
||||
// .typeError("Invalid number")
|
||||
// .min(1, "Price must be greater than 0")
|
||||
// .test("no-e", "Invalid number", (value) => {
|
||||
// if (value && /\d+e/.test(value)) {
|
||||
// return false;
|
||||
// }
|
||||
// return true;
|
||||
// })
|
||||
// .required("Amount is required"),
|
||||
amount: Yup.number('Please enter a number')
|
||||
.min(1, "Price must be greater than 0")
|
||||
.required("Amount is required"),
|
||||
comment: Yup.string()
|
||||
.required("Comment is required"),
|
||||
});
|
||||
|
||||
function FamilyAddFundPopout({action, situation, wallet, familyData}) {
|
||||
|
||||
const dispatch = useDispatch()
|
||||
|
||||
const apiUrl = new usersService()
|
||||
|
||||
const [startTransfer, setStartTransfer] = useState({loading:true, data: {}})
|
||||
|
||||
const [requestStatus, setRequestStatus] = useState({loading:false, status:false, message:''})
|
||||
|
||||
|
||||
// initial values for formik
|
||||
let initialValues = {
|
||||
amount: '',
|
||||
from : AmountTo2DP(startTransfer?.data?.origing_current_balance*0.01),
|
||||
to: `${familyData.firstname} ${familyData.lastname}`,
|
||||
comment: ''
|
||||
};
|
||||
// FUNCTION TO PERFORM FAMILY TRANSFER
|
||||
const handleAddFund = (values) => {
|
||||
setRequestStatus({loading:true, status:false, message:''})
|
||||
|
||||
let senderBal = startTransfer?.data?.origing_current_balance || '' // SENDER'S ACCOUNT BALANCE
|
||||
let senderLimit = startTransfer?.data?.origing_transfer_limit || '' // SENDER'S TRANSFER LIMIT
|
||||
|
||||
let reqData = { // API REQUEST DATA
|
||||
family_uid : familyData.uid,
|
||||
wallet_uid : wallet.wallet_uid,
|
||||
origing_wallet_uid : startTransfer?.data?.origing_wallet_uid,
|
||||
currency : startTransfer?.data?.currency,
|
||||
amount : values.amount*100,
|
||||
description : values.comment,
|
||||
family_transfer_mode : 100,
|
||||
action : 22014
|
||||
}
|
||||
|
||||
if(!senderBal || !senderLimit){ // RETURNS UNAUTHORIZED, IF SENDER BAL OR LIMIT IS NOT AVAILABLE
|
||||
setRequestStatus({loading:false, status:false, message:'Unauthorized, try again later'})
|
||||
return setTimeout(()=>{
|
||||
setRequestStatus({loading:false, status:false, message:''})
|
||||
}, 5000)
|
||||
}
|
||||
|
||||
if(values.amount > senderBal*0.01){ // CHECKS TO SEE IF SENDER IS SENDING MORE THAN HIS BALANCE
|
||||
setRequestStatus({loading:false, status:false, message:'You cannot send more than your balance'})
|
||||
return setTimeout(()=>{
|
||||
setRequestStatus({loading:false, status:false, message:''})
|
||||
}, 5000)
|
||||
}
|
||||
|
||||
if(values.amount > senderLimit*0.01){ // CHECKS TO SEE IF SENDER IS SENDING MORE THAN HIS LIMIT
|
||||
setRequestStatus({loading:false, status:false, message:`You cannot exceed ${senderLimit*0.01} ${startTransfer?.data?.origing_currency.charAt(0).toUpperCase() + startTransfer?.data?.origing_currency.slice(1).toLowerCase()}`})
|
||||
return setTimeout(()=>{
|
||||
setRequestStatus({loading:false, status:false, message:''})
|
||||
}, 5000)
|
||||
}
|
||||
|
||||
apiUrl.familyTransfer(reqData).then(({data}) => {
|
||||
if(data.internal_return < 0 || data.credit_confirm == '' || data.pay_confirm == ''){
|
||||
setRequestStatus({loading:false, status:false, message:'Transfer Failed'})
|
||||
return setTimeout(()=>{
|
||||
setRequestStatus({loading:false, status:false, message:''})
|
||||
}, 5000)
|
||||
}
|
||||
setRequestStatus({loading:false, status:true, message:'Transfer Successful'})
|
||||
setTimeout(()=>{
|
||||
setRequestStatus({loading:false, status:false, message:''})
|
||||
dispatch(tableReload({ type: "WALLETTABLE" })); // UPDATES PARENT WALLET ACCOUNT
|
||||
action() // TO CLOSE THE MODAL
|
||||
}, 5000)
|
||||
}).catch(error => {
|
||||
setRequestStatus({loading:false, status:false, message:'Network Error, try again'})
|
||||
setTimeout(()=>{
|
||||
setRequestStatus({loading:false, status:false, message:''})
|
||||
}, 5000)
|
||||
})
|
||||
}
|
||||
|
||||
// LOAD FAMILY START TRANSFER
|
||||
useEffect(()=>{
|
||||
let reqData = {
|
||||
family_uid: familyData.uid,
|
||||
wallet_uid: wallet.wallet_uid,
|
||||
action: 22013
|
||||
}
|
||||
apiUrl.familyTransferStart(reqData).then(response => {
|
||||
setStartTransfer({loading:false, data:response?.data })
|
||||
}).catch(err => {
|
||||
setStartTransfer({loading:false, data: {}})
|
||||
})
|
||||
},[])
|
||||
|
||||
return (
|
||||
<ModalCom action={action} situation={situation}>
|
||||
<div className="relative logout-modal-wrapper lg:w-[500px] h-full lg:h-auto bg-white dark:bg-dark-white lg:rounded-2xl">
|
||||
<div className="logout-modal-header w-full flex items-center justify-between lg:px-10 lg:py-8 px-[30px] py-[23px] border-b border-light-purple dark:border-[#5356fb29] ">
|
||||
<h1 className="text-26 font-bold text-dark-gray dark:text-white tracking-wide">
|
||||
Add Fund
|
||||
</h1>
|
||||
<button
|
||||
type="button"
|
||||
className="text-[#374557] dark:text-red-500"
|
||||
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="logout-modal-body w-full flex flex-col items-center px-10 py-8">
|
||||
{startTransfer.loading && <LoadingSpinner size='16' color='sky-blue' height={'h-64'} />}
|
||||
|
||||
{ !startTransfer.loading &&
|
||||
<Formik
|
||||
initialValues={initialValues}
|
||||
validationSchema={validationSchema}
|
||||
onSubmit={handleAddFund}
|
||||
>
|
||||
{(props) => (
|
||||
<Form className="w-full">
|
||||
<div className="flex flex-col-reverse sm:flex-row">
|
||||
<div className="fields w-full">
|
||||
{/* AMOUNT */}
|
||||
<div className="field w-full mb-[0.5rem]">
|
||||
<InputCom
|
||||
placeholder="0"
|
||||
label={`Amount (${startTransfer?.data?.currency})`}
|
||||
name="amount"
|
||||
type="text"
|
||||
parentClass="flex items-center gap-1 w-full"
|
||||
labelClass="flex-[0.3] mb-0"
|
||||
inputClass={`flex-[0.7] input-curve lg border border-[#dce4e9] ${props.errors.amount && props.touched.amount ? 'border border-red-500' : ''}`}
|
||||
fieldClass="px-2 text-right"
|
||||
value={props.values.amount}
|
||||
inputHandler={props.handleChange}
|
||||
/>
|
||||
</div>
|
||||
|
||||
{/* FROM */}
|
||||
<div className="field w-full mb-[0.5rem]">
|
||||
<InputCom
|
||||
placeholder="From"
|
||||
label={`From (${startTransfer?.data?.origing_currency})`}
|
||||
name="from"
|
||||
type="text"
|
||||
parentClass="flex items-center gap-1 w-full"
|
||||
labelClass="flex-[0.3] mb-0"
|
||||
inputClass={`flex-[0.7] input-curve lg border border-[#dce4e9]`}
|
||||
fieldClass="px-2 text-right"
|
||||
value={props.values.from}
|
||||
disable={true}
|
||||
/>
|
||||
</div>
|
||||
|
||||
{/* TO */}
|
||||
<div className="field w-full mb-[0.5rem]">
|
||||
<InputCom
|
||||
placeholder="To"
|
||||
label="To:"
|
||||
name="to"
|
||||
type="text"
|
||||
parentClass="flex items-center gap-1 w-full"
|
||||
labelClass="flex-[0.3] mb-0"
|
||||
inputClass={`flex-[0.7] input-curve lg border border-[#dce4e9]`}
|
||||
fieldClass="px-2 text-right"
|
||||
value={props.values.to}
|
||||
disable={true}
|
||||
/>
|
||||
</div>
|
||||
|
||||
{/* COMMENT */}
|
||||
<div className="field w-full mb-[0.5rem]">
|
||||
<div className="w-full">
|
||||
<label
|
||||
htmlFor="Job Delivery Details"
|
||||
className="input-label text-[#181c32] dark:text-white text-[13.975px] leading-[20.9625px] font-semibold flex items-center gap-1"
|
||||
>
|
||||
Comment
|
||||
{/* {props.errors.comment && props.touched.comment && <span className='text-sm text-red-500'>{' '}{props.errors.comment}</span>} */}
|
||||
</label>
|
||||
<textarea
|
||||
// id="Job Delivery Details"
|
||||
rows="2"
|
||||
className={`input-field px-3 py-2 placeholder:text-base text-dark-gray dark:text-white w-full bg-slate-100 dark:bg-[#11131F] focus:ring-0 focus:outline-[#dce4e9] rounded-[10px] border ${props.errors.comment && props.touched.comment ? 'border border-red-500' : ''}`}
|
||||
style={{ resize: "none" }}
|
||||
name="comment"
|
||||
value={props.values.comment}
|
||||
onChange={props.handleChange}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
{/* inputs ends here */}
|
||||
</div>
|
||||
</div>
|
||||
{/* ERROR DISPLAY AND SUBMIT BUTTON */}
|
||||
<div className="content-footer w-full">
|
||||
{/* error or success display */}
|
||||
{requestStatus.message != "" &&
|
||||
(!requestStatus.status ? (
|
||||
<div
|
||||
className={`relative p-4 text-[#912741] bg-[#fcd9e2] border-[#fbc6d3] rounded-[0.475rem] text-md font-light leading-[19.5px] text-[13px]`}
|
||||
// className={`pb-1 absolute bottom-0 left-1/2 -translate-x-1/2 text-[#912741] rounded-[0.475rem] text-md font-light leading-[19.5px] text-[13px]`}
|
||||
|
||||
>
|
||||
{requestStatus.message}
|
||||
</div>
|
||||
) : (
|
||||
requestStatus.status && (
|
||||
<div
|
||||
className={`relative p-4 text-green-700 bg-slate-200 border-slate-800 rounded-[0.475rem] text-md font-light leading-[19.5px] text-[13px]`}
|
||||
// className={`pb-1 absolute bottom-0 left-1/2 -translate-x-1/2 text-green-700 rounded-[0.475rem] text-md font-light leading-[19.5px] text-[13px]`}
|
||||
>
|
||||
{requestStatus.message}
|
||||
</div>
|
||||
)
|
||||
))}
|
||||
{/* End of error or success display */}
|
||||
|
||||
<div className="pt-2 w-full border-t border-light-purple dark:border-[#5356fb29] flex justify-end items-center">
|
||||
<div className="w-full flex justify-between gap-2 items-center">
|
||||
<button
|
||||
type="button"
|
||||
onClick={action}
|
||||
className="w-[150px] h-[48px] rounded-full text-base text-white bg-red-600 hover:bg-red-500"
|
||||
>
|
||||
Cancel
|
||||
</button>
|
||||
<>
|
||||
{requestStatus.loading ?
|
||||
<LoadingSpinner size='6' color='sky-blue' />
|
||||
:
|
||||
<button
|
||||
type="submit"
|
||||
className={`w-[150px] h-[48px] rounded-full text-base text-white bg-sky-500 hover:bg-sky-400 ${requestStatus.status ? 'opacity-50' : ''}`}
|
||||
disabled={requestStatus.status}
|
||||
>
|
||||
Send
|
||||
</button>
|
||||
}
|
||||
</>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</Form>
|
||||
)}
|
||||
</Formik>
|
||||
}
|
||||
</div>
|
||||
</div>
|
||||
</ModalCom>
|
||||
)
|
||||
}
|
||||
|
||||
export default FamilyAddFundPopout
|
||||
@@ -0,0 +1,77 @@
|
||||
import React, { useState } from 'react'
|
||||
import { localImgLoad } from '../../../../lib'
|
||||
import { PriceFormatter } from '../../../Helpers/PriceFormatter'
|
||||
import FamilyAddFundPopout from './FamilyAddFundPopout'
|
||||
|
||||
function Wallet({wallet, familyData, setFamilyWalletReload}) {
|
||||
const [addFundPopout, setAddFundPopout] = useState({ show: false, data: {} })
|
||||
return (
|
||||
<div className='w-full p-4 bg-[aliceblue] rounded-lg'
|
||||
// style={{
|
||||
// background: `url(${background}) 0% 0% / cover no-repeat`,
|
||||
// }}
|
||||
>
|
||||
<div className="w-full flex justify-start items-center gap-3">
|
||||
<div className="min-w-[50px] min-h-[50px] max-w-min md:max-w-[80px] max-h-min md:max-h-[80px] rounded-full bg-[#e3e3e3] flex justify-center items-center">
|
||||
<img
|
||||
src={localImgLoad(`images/currency/${(wallet.code).toLowerCase()}.svg`)}
|
||||
className="w-full h-full"
|
||||
alt="currency-icon"
|
||||
/>
|
||||
</div>
|
||||
<div className="w-full flex flex-col">
|
||||
<div className="w-full flex gap-2">
|
||||
<p className="text-base md:text-lg text-thin-light-gray tracking-wide">
|
||||
Balance:
|
||||
</p>
|
||||
<p
|
||||
className="text-base md:text-lg font-bold text-purple tracking-wide leading-10"
|
||||
// className="text-[44px] lg:text-[62px] font-bold text-white tracking-wide leading-10 xxs:scale-100 lg:scale-100 xl:scale-125"
|
||||
>
|
||||
{PriceFormatter(
|
||||
Number(wallet.amount) * 0.01,
|
||||
wallet.code,
|
||||
wallet.country,
|
||||
""
|
||||
)}
|
||||
</p>
|
||||
</div>
|
||||
{/* BTN */}
|
||||
<div className='mt-1 flex justify-end items-center gap-2'>
|
||||
<button
|
||||
className="w-[150px] h-[48px] rounded-full text-base text-white bg-sky-500 hover:bg-sky-400"
|
||||
name="add"
|
||||
onClick={()=>{
|
||||
setAddFundPopout({ show: true, data: {} });
|
||||
}}
|
||||
>
|
||||
Add Money
|
||||
</button>
|
||||
<button
|
||||
className="w-[150px] h-[48px] rounded-full text-base text-white bg-[#4687ba] hover:bg-[#009ef7]"
|
||||
name="plan"
|
||||
// onClick={onClose}
|
||||
>
|
||||
Plan Wallet
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* MODAL TO ADD FUND */}
|
||||
{addFundPopout.show &&
|
||||
<FamilyAddFundPopout
|
||||
action={() => {
|
||||
setAddFundPopout({ show: false, data: {} });
|
||||
setFamilyWalletReload(prev => !prev) // TO RELOAD FAMILY WALLET // DETERMINES WHEN TO RELOAD FAMILY WALLET TAB/PAGE
|
||||
}}
|
||||
situation={addFundPopout.show}
|
||||
wallet={wallet}
|
||||
familyData={familyData}
|
||||
/>
|
||||
}
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
export default Wallet
|
||||
@@ -5,7 +5,8 @@ import React, {
|
||||
useMemo,
|
||||
useState,
|
||||
} from "react";
|
||||
import SiteService from "../../services/SiteService";
|
||||
import { Link } from "react-router-dom";
|
||||
import usersService from "../../services/UsersService";
|
||||
import InputCom from "../Helpers/Inputs/InputCom";
|
||||
import ModalCom from "../Helpers/ModalCom";
|
||||
import Layout from "../Partials/Layout";
|
||||
@@ -13,11 +14,10 @@ import LoadingSpinner from "../Spinners/LoadingSpinner";
|
||||
import FamilyTable from "./FamilyTable";
|
||||
|
||||
export default function FamilyAcc() {
|
||||
const [selectTab, setValue] = useState("today");
|
||||
// State to store the selected year and month
|
||||
const [selectedYear, setSelectedYear] = useState("");
|
||||
const [selectedMonth, setSelectedMonth] = useState("");
|
||||
const [familyList, setFamilyList] = useState([]);
|
||||
const [familyList, setFamilyList] = useState({});
|
||||
const [loader, setLoader] = useState(false);
|
||||
const [popUp, setPopUp] = useState(false);
|
||||
const [listReload, setListReload] = useState(false);
|
||||
@@ -27,16 +27,12 @@ export default function FamilyAcc() {
|
||||
last_name: "",
|
||||
});
|
||||
|
||||
const apiCall = useMemo(() => new SiteService(), []);
|
||||
const apiCall = useMemo(() => new usersService(), []);
|
||||
|
||||
const popUpHandler = () => {
|
||||
setPopUp((prev) => !prev);
|
||||
};
|
||||
|
||||
const filterHandler = (value) => {
|
||||
setValue(value);
|
||||
};
|
||||
|
||||
// Handle year selection
|
||||
const handleYearChange = (e) => {
|
||||
setSelectedYear(e.target.value);
|
||||
@@ -115,8 +111,8 @@ export default function FamilyAcc() {
|
||||
const res = await apiCall.familyListings(reqData);
|
||||
const { data } = res;
|
||||
if (data?.internal_return >= 0 && data?.status === "OK") {
|
||||
const { result_list } = data;
|
||||
setFamilyList(result_list);
|
||||
const { result_list, session_image_server } = data;
|
||||
setFamilyList({ result_list, session_image_server });
|
||||
setLoader(false);
|
||||
} else {
|
||||
return;
|
||||
@@ -146,12 +142,9 @@ export default function FamilyAcc() {
|
||||
<div className="sm:flex justify-between items-center mb-6">
|
||||
<div className="mb-5 sm:mb-0">
|
||||
<h1 className="text-26 font-bold inline-flex gap-3 text-dark-gray dark:text-white items-center">
|
||||
<span
|
||||
className={`${selectTab === "today" ? "block" : "hidden"}`}
|
||||
>
|
||||
Family Accounts
|
||||
</span>
|
||||
{familyList.length < process.env.REACT_APP_MAX_FAMILY_MEMBERS &&
|
||||
<span className={``}>Family Accounts</span>
|
||||
{familyList?.result_list?.length <
|
||||
process.env.REACT_APP_MAX_FAMILY_MEMBERS &&
|
||||
!loader && (
|
||||
<button
|
||||
onClick={popUpHandler}
|
||||
@@ -163,18 +156,38 @@ export default function FamilyAcc() {
|
||||
)}
|
||||
</h1>
|
||||
</div>
|
||||
<div className="slider-btns flex space-x-4">
|
||||
<div
|
||||
onClick={() => filterHandler("today")}
|
||||
className="relative"
|
||||
></div>
|
||||
</div>
|
||||
<Link
|
||||
to={`/familysettings`}
|
||||
state={{ imageServer: familyList?.session_image_server }}
|
||||
className="slider-btns flex space-x-4 w-12 h-12 rounded-md shadow-sm justify-center items-center cursor-pointer dark:bg-[linear-gradient(134.38deg,#f539f8_0%,#c342f9_43.55%,#5356fb_104.51%)]"
|
||||
>
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
fill="none"
|
||||
viewBox="0 0 24 24"
|
||||
strokeWidth={1.5}
|
||||
stroke="currentColor"
|
||||
className="w-6 h-6 dark:stroke-white"
|
||||
>
|
||||
<path
|
||||
strokeLinecap="round"
|
||||
strokeLinejoin="round"
|
||||
d="M9.594 3.94c.09-.542.56-.94 1.11-.94h2.593c.55 0 1.02.398 1.11.94l.213 1.281c.063.374.313.686.645.87.074.04.147.083.22.127.324.196.72.257 1.075.124l1.217-.456a1.125 1.125 0 011.37.49l1.296 2.247a1.125 1.125 0 01-.26 1.431l-1.003.827c-.293.24-.438.613-.431.992a6.759 6.759 0 010 .255c-.007.378.138.75.43.99l1.005.828c.424.35.534.954.26 1.43l-1.298 2.247a1.125 1.125 0 01-1.369.491l-1.217-.456c-.355-.133-.75-.072-1.076.124a6.57 6.57 0 01-.22.128c-.331.183-.581.495-.644.869l-.213 1.28c-.09.543-.56.941-1.11.941h-2.594c-.55 0-1.02-.398-1.11-.94l-.213-1.281c-.062-.374-.312-.686-.644-.87a6.52 6.52 0 01-.22-.127c-.325-.196-.72-.257-1.076-.124l-1.217.456a1.125 1.125 0 01-1.369-.49l-1.297-2.247a1.125 1.125 0 01.26-1.431l1.004-.827c.292-.24.437-.613.43-.992a6.932 6.932 0 010-.255c.007-.378-.138-.75-.43-.99l-1.004-.828a1.125 1.125 0 01-.26-1.43l1.297-2.247a1.125 1.125 0 011.37-.491l1.216.456c.356.133.751.072 1.076-.124.072-.044.146-.087.22-.128.332-.183.582-.495.644-.869l.214-1.281z"
|
||||
/>
|
||||
<path
|
||||
strokeLinecap="round"
|
||||
strokeLinejoin="round"
|
||||
d="M15 12a3 3 0 11-6 0 3 3 0 016 0z"
|
||||
/>
|
||||
</svg>
|
||||
</Link>
|
||||
</div>
|
||||
<Suspense fallback={<LoadingSpinner color="sky-blue" size="16" />}>
|
||||
<FamilyTable
|
||||
familyList={familyList}
|
||||
familyList={familyList?.result_list}
|
||||
loader={loader}
|
||||
popUpHandler={popUpHandler}
|
||||
imageServer={familyList?.session_image_server}
|
||||
/>
|
||||
</Suspense>
|
||||
</div>
|
||||
|
||||
@@ -2,6 +2,8 @@ import React from "react";
|
||||
// import Lottie from "react-lottie";
|
||||
import { useNavigate } from "react-router-dom";
|
||||
import * as animationData from "../../assets/images/Lotties/77618-website-404-error-animation.json";
|
||||
import { localImgLoad } from "../../lib";
|
||||
// import BgError from '../../assets/images/wrench-page-notfound.jpg'
|
||||
|
||||
export default function FourZeroFour() {
|
||||
const navigate = useNavigate();
|
||||
@@ -14,16 +16,21 @@ export default function FourZeroFour() {
|
||||
},
|
||||
};
|
||||
return (
|
||||
<div className="flex justify-center items-center w-full h-screen bg-[#232247]">
|
||||
<div>
|
||||
<div className={`my-custom-bg-class flex justify-center items-center w-full min-h-screen before:content-[''] before:absolute before:inset-0 before:bg-[#cdcdcd]/[.8]`}>
|
||||
<div className="relative pt-32 max-w-3xl">
|
||||
{/* <Lottie options={defaultOptions} width={600} height={600} /> */}
|
||||
<div className="flex justify-center">
|
||||
<div className="px-16 md:px-24 flex flex-col items-center justify-center gap-4">
|
||||
<img src={localImgLoad('images/404.png')} className="w-72" alt='404Image' />
|
||||
<p className="mt-8 text-red-600 font-black text-4xl md:text-5xl tracking-wider text-center">Sorry!</p>
|
||||
<h1 className="text-black text-4xl md:text-5xl font-black tracking-wide text-center leading-tight">The page can’t be found.</h1>
|
||||
<p className="px-2 md:px-8 text-slate-700 text-base md:text-lg text-center">The page you're looking for isn't available. Use the go back button below</p>
|
||||
<button
|
||||
onClick={() => navigate(-1)}
|
||||
type="button"
|
||||
className="btn-gradient text-white text-lg w-[150px] h-[50px] rounded-full"
|
||||
className="px-4 md:px-8 border-2 border-[#2b70fa] rounded-lg text-[#007bff] text-base md:text-lg h-[48px] flex justify-center items-center gap-1"
|
||||
>
|
||||
Go Back
|
||||
<span>Go Back</span>
|
||||
<span className="pb-1">→</span>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -478,13 +478,13 @@ export default function Icons({ name }) {
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
fill="none"
|
||||
viewBox="0 0 24 24"
|
||||
stroke-width="1.5"
|
||||
strokeWidth="1.5"
|
||||
stroke="currentColor"
|
||||
className="w-4 h-4"
|
||||
>
|
||||
<path
|
||||
stroke-linecap="round"
|
||||
stroke-linejoin="round"
|
||||
strokeLinecap="round"
|
||||
strokeLinejoin="round"
|
||||
d="M8.25 4.5l7.5 7.5-7.5 7.5"
|
||||
/>
|
||||
</svg>
|
||||
|
||||
@@ -50,3 +50,29 @@ export const PriceFormatter = (
|
||||
</span>
|
||||
);
|
||||
};
|
||||
|
||||
|
||||
// FUNCTION TO RETURN AMOUNT TO TWO DECIMAL PLACES
|
||||
export const AmountTo2DP = (
|
||||
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,106 @@
|
||||
import React, {useEffect, useState} from 'react'
|
||||
import Image from '../../assets/images/taskbanners/default.jpg'
|
||||
|
||||
import usersService from '../../services/UsersService';
|
||||
import { handlePagingFunc } from '../../components/Pagination/HandlePagination';
|
||||
import PaginatedList from '../../components/Pagination/PaginatedList';
|
||||
|
||||
import LoadingSpinner from '../Spinners/LoadingSpinner';
|
||||
|
||||
import { AmountTo2DP } from '../Helpers/PriceFormatter';
|
||||
|
||||
function JobsCompleted() {
|
||||
|
||||
const apiCall = new usersService()
|
||||
|
||||
let [familyRewardHistory, setFamilyRewardHistory] = useState({ // FOR PURCHASE HISTORY
|
||||
loading: true,
|
||||
data: [],
|
||||
error: false
|
||||
})
|
||||
|
||||
const [currentPage, setCurrentPage] = useState(0);
|
||||
const indexOfFirstItem = Number(currentPage);
|
||||
const indexOfLastItem = Number(indexOfFirstItem)+Number(process.env.REACT_APP_ITEM_PER_PAGE);
|
||||
const currentReward = familyRewardHistory?.data?.slice(indexOfFirstItem, indexOfLastItem);
|
||||
|
||||
const handlePagination = (e) => {
|
||||
handlePagingFunc(e,setCurrentPage)
|
||||
}
|
||||
|
||||
|
||||
//FUNCTION TO GET FAMILY REWARD HISTORY
|
||||
const getJobCompletedHistory = ()=>{
|
||||
// apiCall.getFamilyRewardHx().then((res)=>{
|
||||
// if(res.data.internal_return < 0){ // success but no data
|
||||
// setFamilyRewardHistory(prev => ({...prev, loading: false}))
|
||||
// return
|
||||
// }
|
||||
// setFamilyRewardHistory(prev => ({...prev, loading: false, data: res.data.result_list}))
|
||||
// }).catch((error)=>{
|
||||
// setFamilyRewardHistory(prev => ({...prev, loading: false, error: true}))
|
||||
// })
|
||||
setTimeout(()=>{
|
||||
setFamilyRewardHistory(prev => ({...prev, loading: false, error:true}))
|
||||
},3000)
|
||||
}
|
||||
|
||||
useEffect(()=>{
|
||||
getJobCompletedHistory()
|
||||
}, [])
|
||||
|
||||
return (
|
||||
<div className='flex flex-col justify-between min-h-[500px]'>
|
||||
{familyRewardHistory.loading ?
|
||||
<LoadingSpinner size='16' color='sky-blue' height='h-[500px]' />
|
||||
: familyRewardHistory.data.length ?
|
||||
<table className="wallet-activity w-full table-auto border-collapse text-left">
|
||||
<thead className='border-b-2'>
|
||||
<tr className='text-slate-600'>
|
||||
<th className="p-2"></th>
|
||||
<th className="p-2">Amount</th>
|
||||
<th className="p-2">Date</th>
|
||||
<th className="p-2">Confirmation</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{currentReward.map((item, index) => {
|
||||
let date = new Date(item.added).toLocaleDateString()
|
||||
return (
|
||||
<tr key={index} className='text-slate-500'>
|
||||
<td className="p-2">
|
||||
<div className='flex items-center gap-2'>
|
||||
<img src={item.icon} className='min-w-[60px] max-w-[60px] min-h-[60px] max-h-[60px] rounded-full bg-slate-500' alt='Reward Logo' />
|
||||
<div className='flex flex-col'>
|
||||
<h1 className='text-lg font-bold'>Reward to {item.rec_firstname} {item.rec_lastname}</h1>
|
||||
<p className='text-sm'>{item.description}</p>
|
||||
</div>
|
||||
</div>
|
||||
</td>
|
||||
<td className="p-2">{AmountTo2DP(item.amount*0.01)} {item.currency}</td>
|
||||
<td className="p-2">{date}</td>
|
||||
<td className="p-2">{item.confirmation}</td>
|
||||
</tr>
|
||||
)
|
||||
}
|
||||
)}
|
||||
</tbody>
|
||||
</table>
|
||||
:familyRewardHistory.error ?
|
||||
<div className="p-2 text-slate-500 flex flex-col grow justify-center items-center">
|
||||
<span>Opps! an error occurred. Please try again!</span>
|
||||
</div>
|
||||
:
|
||||
<div className="p-2 text-slate-500 flex flex-col grow justify-center items-center">
|
||||
<span>No Completed Job History Found!</span>
|
||||
</div>
|
||||
}
|
||||
|
||||
{/* PAGINATION BUTTON */}
|
||||
<PaginatedList onClick={handlePagination} prev={currentPage == 0 ? true : false} next={currentPage+Number(process.env.REACT_APP_ITEM_PER_PAGE) >= familyRewardHistory?.data?.length ? true : false} data={familyRewardHistory?.data} start={indexOfFirstItem} stop={indexOfLastItem} />
|
||||
{/* END OF PAGINATION BUTTON */}
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
export default JobsCompleted
|
||||
@@ -0,0 +1,103 @@
|
||||
import React, {useEffect, useState} from 'react'
|
||||
import Image from '../../assets/images/taskbanners/default.jpg'
|
||||
|
||||
import usersService from '../../services/UsersService';
|
||||
import { handlePagingFunc } from '../../components/Pagination/HandlePagination';
|
||||
import PaginatedList from '../../components/Pagination/PaginatedList';
|
||||
|
||||
import LoadingSpinner from '../Spinners/LoadingSpinner';
|
||||
|
||||
import { AmountTo2DP } from '../Helpers/PriceFormatter';
|
||||
|
||||
function RewardsTable() {
|
||||
|
||||
const apiCall = new usersService()
|
||||
|
||||
let [familyRewardHistory, setFamilyRewardHistory] = useState({ // FOR PURCHASE HISTORY
|
||||
loading: true,
|
||||
data: [],
|
||||
error: false
|
||||
})
|
||||
|
||||
const [currentPage, setCurrentPage] = useState(0);
|
||||
const indexOfFirstItem = Number(currentPage);
|
||||
const indexOfLastItem = Number(indexOfFirstItem)+Number(process.env.REACT_APP_ITEM_PER_PAGE);
|
||||
const currentReward = familyRewardHistory?.data?.slice(indexOfFirstItem, indexOfLastItem);
|
||||
|
||||
const handlePagination = (e) => {
|
||||
handlePagingFunc(e,setCurrentPage)
|
||||
}
|
||||
|
||||
|
||||
//FUNCTION TO GET FAMILY REWARD HISTORY
|
||||
const getFamilyRewardHistory = ()=>{
|
||||
apiCall.getFamilyRewardHx().then((res)=>{
|
||||
if(res.data.internal_return < 0){ // success but no data
|
||||
setFamilyRewardHistory(prev => ({...prev, loading: false}))
|
||||
return
|
||||
}
|
||||
setFamilyRewardHistory(prev => ({...prev, loading: false, data: res.data.result_list}))
|
||||
}).catch((error)=>{
|
||||
setFamilyRewardHistory(prev => ({...prev, loading: false, error: true}))
|
||||
})
|
||||
}
|
||||
|
||||
useEffect(()=>{
|
||||
getFamilyRewardHistory()
|
||||
}, [])
|
||||
|
||||
return (
|
||||
<div className='flex flex-col justify-between min-h-[500px]'>
|
||||
{familyRewardHistory.loading ?
|
||||
<LoadingSpinner size='16' color='sky-blue' height='h-[500px]' />
|
||||
: familyRewardHistory.data.length ?
|
||||
<table className="wallet-activity w-full table-auto border-collapse text-left">
|
||||
<thead className='border-b-2'>
|
||||
<tr className='text-slate-600'>
|
||||
<th className="p-2"></th>
|
||||
<th className="p-2">Amount</th>
|
||||
<th className="p-2">Date</th>
|
||||
<th className="p-2">Confirmation</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{currentReward.map((item, index) => {
|
||||
let date = new Date(item.added).toLocaleDateString()
|
||||
return (
|
||||
<tr key={index} className='text-slate-500'>
|
||||
<td className="p-2">
|
||||
<div className='flex items-center gap-2'>
|
||||
<img src={item.icon} className='min-w-[60px] max-w-[60px] min-h-[60px] max-h-[60px] rounded-full bg-slate-500' alt='Reward Logo' />
|
||||
<div className='flex flex-col'>
|
||||
<h1 className='text-lg font-bold'>Reward to {item.rec_firstname} {item.rec_lastname}</h1>
|
||||
<p className='text-sm'>{item.description}</p>
|
||||
</div>
|
||||
</div>
|
||||
</td>
|
||||
<td className="p-2">{AmountTo2DP(item.amount*0.01)} {item.currency}</td>
|
||||
<td className="p-2">{date}</td>
|
||||
<td className="p-2">{item.confirmation}</td>
|
||||
</tr>
|
||||
)
|
||||
}
|
||||
)}
|
||||
</tbody>
|
||||
</table>
|
||||
:familyRewardHistory.error ?
|
||||
<div className="p-2 text-slate-500 flex flex-col grow justify-center items-center">
|
||||
<span>Opps! an error occurred. Please try again!</span>
|
||||
</div>
|
||||
:
|
||||
<div className="p-2 text-slate-500 flex flex-col grow justify-center items-center">
|
||||
<span>No Rewards History Found!</span>
|
||||
</div>
|
||||
}
|
||||
|
||||
{/* PAGINATION BUTTON */}
|
||||
<PaginatedList onClick={handlePagination} prev={currentPage == 0 ? true : false} next={currentPage+Number(process.env.REACT_APP_ITEM_PER_PAGE) >= familyRewardHistory?.data?.length ? true : false} data={familyRewardHistory?.data} start={indexOfFirstItem} stop={indexOfLastItem} />
|
||||
{/* END OF PAGINATION BUTTON */}
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
export default RewardsTable
|
||||
@@ -10,6 +10,8 @@ import usersService from "../../services/UsersService";
|
||||
import PurchasesTable from "../MyWallet/WalletComponent/PurchasesTable";
|
||||
import RecentActivityTable from "../MyWallet/WalletComponent/RecentActivityTable";
|
||||
import LoadingSpinner from "../Spinners/LoadingSpinner";
|
||||
import RewardsTable from "./RewardsTable";
|
||||
import JobsCompleted from "./JobsCompleted";
|
||||
|
||||
export default function History() {
|
||||
|
||||
@@ -58,6 +60,9 @@ export default function History() {
|
||||
|
||||
useEffect(()=>{
|
||||
getPaymentHistory()
|
||||
}, [])
|
||||
|
||||
useEffect(()=>{
|
||||
getPurchaseHistory()
|
||||
}, [])
|
||||
|
||||
@@ -236,15 +241,33 @@ export default function History() {
|
||||
>
|
||||
Recent Activity
|
||||
</button>
|
||||
<button
|
||||
name="reward"
|
||||
onClick={(e) => setTab(e.target.name)}
|
||||
className={`px-4 py-1 rounded-t-2xl ${
|
||||
tab == "reward" ? "bg-[#4687ba] border-[2px] border-[#4687ba] text-white" : "bg-white text-[#000] border-t-[2px]"
|
||||
}`}
|
||||
>
|
||||
Rewards
|
||||
</button>
|
||||
<button
|
||||
name="jobs_completed"
|
||||
onClick={(e) => setTab(e.target.name)}
|
||||
className={`px-4 py-1 rounded-t-2xl ${
|
||||
tab == "jobs_completed" ? "bg-[#4687ba] border-[2px] border-[#4687ba] text-white" : "bg-white text-[#000] border-t-[2px]"
|
||||
}`}
|
||||
>
|
||||
Jobs Completed
|
||||
</button>
|
||||
</div>
|
||||
{/* END OF switch button */}
|
||||
<div className="history-tables w-full">
|
||||
{/* PURCHASE SECTION */}
|
||||
{tab == 'purchases' &&
|
||||
<div className="wallet w-full border-t">
|
||||
<h1 className="p-2 text-xl font-bold text-dark-gray dark:text-white tracking-wide">Purchases</h1>
|
||||
{/* <h1 className="p-2 text-xl font-bold text-dark-gray dark:text-white tracking-wide">Purchases</h1> */}
|
||||
{purchaseHistory.loading ?
|
||||
<LoadingSpinner size='16' color='sky-blue' />
|
||||
<LoadingSpinner size='16' color='sky-blue' height='h-[500px]' />
|
||||
:
|
||||
<PurchasesTable purchase={purchaseHistory} />
|
||||
}
|
||||
@@ -255,16 +278,32 @@ export default function History() {
|
||||
{/* RECENT ACTIVITY SECTION */}
|
||||
{tab == 'recent' &&
|
||||
<div className="wallet w-full border-t">
|
||||
<h1 className="p-2 text-xl font-bold text-dark-gray dark:text-white tracking-wide">Recent Activity</h1>
|
||||
{/* <h1 className="p-2 text-xl font-bold text-dark-gray dark:text-white tracking-wide">Recent Activity</h1> */}
|
||||
{/* <p className='text-base text-slate-500 dark:text-white'>Activity Report</p> */}
|
||||
{paymentHistory.loading ?
|
||||
<LoadingSpinner size='16' color='sky-blue' />
|
||||
<LoadingSpinner size='16' color='sky-blue' height='h-[500px]' />
|
||||
:
|
||||
<RecentActivityTable payment={paymentHistory} />
|
||||
}
|
||||
</div>
|
||||
}
|
||||
{/* END OF RECENT ACTIVITY SECTION */}
|
||||
|
||||
{/* REWARD SECTION */}
|
||||
{tab == 'reward' &&
|
||||
<div className="wallet w-full border-t">
|
||||
<RewardsTable />
|
||||
</div>
|
||||
}
|
||||
{/* END OF REWARD SECTION */}
|
||||
|
||||
{/* JOBS COMPLETED SECTION */}
|
||||
{tab == 'jobs_completed' &&
|
||||
<div className="wallet w-full border-t">
|
||||
<JobsCompleted />
|
||||
</div>
|
||||
}
|
||||
{/* END OF JOBS COMPLETED SECTION */}
|
||||
</div>
|
||||
</div>
|
||||
{/*<HistoryTable />*/}
|
||||
|
||||
@@ -4,7 +4,7 @@ import Icons from "../Helpers/Icons";
|
||||
import SliderCom from "../Helpers/SliderCom";
|
||||
import FamilyActiveJobsCard from "../Cards/FamilyActiveJobsCard";
|
||||
|
||||
export default function FamilyActiveLSlde({ className, trending }) {
|
||||
export default function FamilyActiveLSlde({ className, trending, image_server }) {
|
||||
const settings = {
|
||||
arrows: false,
|
||||
slidesToShow: 3,
|
||||
@@ -98,7 +98,7 @@ export default function FamilyActiveLSlde({ className, trending }) {
|
||||
{trending &&
|
||||
trending.length > 0 &&
|
||||
trending.map((item) => (
|
||||
<FamilyActiveJobsCard key={item.id} datas={item} />
|
||||
<FamilyActiveJobsCard key={item.id} datas={item} image_server={image_server} />
|
||||
))}
|
||||
</SliderCom>
|
||||
</div>
|
||||
|
||||
@@ -4,18 +4,22 @@ import MyOffersFamilyTable from "../MyTasks/MyOffersFamilyTable";
|
||||
import FamilyActiveLSlde from "./FamilyActiveLSlde";
|
||||
|
||||
export default function FamilyDash({ familyOffers, MyActiveJobList }) {
|
||||
console.log("PROPS IN FAMILY DASH->", familyOffers);
|
||||
// console.log("PROPS IN FAMILY DASH->", familyOffers?.result_list);
|
||||
|
||||
const trending = MyActiveJobList;
|
||||
return (
|
||||
<div>
|
||||
<div className="home-page-wrapper">
|
||||
{/* <CommonHead commonHeadData={props.commonHeadData} /> */}
|
||||
{familyOffers && familyOffers.length > 0 && (
|
||||
<MyOffersFamilyTable familyOffers={familyOffers} className="mb-10" />
|
||||
{familyOffers?.result_list && familyOffers?.result_list.length > 0 && (
|
||||
<MyOffersFamilyTable
|
||||
familyOffers={familyOffers?.result_list}
|
||||
image_server={familyOffers?.session_image_server}
|
||||
className="mb-10"
|
||||
/>
|
||||
)}
|
||||
{trending && trending.length > 0 && (
|
||||
<FamilyActiveLSlde trending={trending} className="mb-10" />
|
||||
<FamilyActiveLSlde trending={trending} className="mb-10" image_server={familyOffers?.session_image_server} />
|
||||
)}
|
||||
|
||||
{/*<TopSellerTopBuyerSliderSection className="mb-10" />*/}
|
||||
|
||||
@@ -34,6 +34,7 @@ export default function FullAccountDash(props) {
|
||||
<MyJobTable
|
||||
ActiveJobList={props.MyActiveJobList}
|
||||
Account={userDetails}
|
||||
imageServer={props.offersList?.data?.session_image_server}
|
||||
/>
|
||||
</>
|
||||
) : !props.offersList?.loading && !props.MyActiveJobList?.loading ? (
|
||||
|
||||
@@ -75,7 +75,7 @@ export default function Home(props) {
|
||||
<FamilyDash
|
||||
account={userDetails}
|
||||
commonHeadData={props.bannerList}
|
||||
familyOffers={MyOffersList?.data?.result_list}
|
||||
familyOffers={MyOffersList?.data}
|
||||
MyActiveJobList={MyActiveJobList?.data}
|
||||
/>
|
||||
) : userDetails && userDetails?.account_type == "FULL" ? (
|
||||
|
||||
@@ -0,0 +1,27 @@
|
||||
import React, { useEffect, useState } from 'react'
|
||||
import Layout from '../Partials/Layout'
|
||||
|
||||
import LoadingSpinner from '../Spinners/LoadingSpinner'
|
||||
|
||||
function Lnd() {
|
||||
|
||||
const [reqStatus, setReqStatus] = useState({loading:true, data: []})
|
||||
|
||||
useEffect(()=>{
|
||||
const timer = setTimeout(()=>{
|
||||
setReqStatus({loading:false, data: []})
|
||||
},2000)
|
||||
return () => clearTimeout(timer)
|
||||
}, [])
|
||||
return (
|
||||
<>
|
||||
{reqStatus.loading ?
|
||||
<LoadingSpinner color='sky-blue' size='32' height='min-h-screen' />
|
||||
:
|
||||
<div className='min-h-screen flex flex-col justify-center items-center'>Empty Dummy Page</div>
|
||||
}
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
||||
export default Lnd
|
||||
@@ -8,6 +8,7 @@ import SelectBox from "../Helpers/SelectBox";
|
||||
export default function MainSection({
|
||||
className,
|
||||
marketPlaceProduct,
|
||||
image_server,
|
||||
categories,
|
||||
}) {
|
||||
// Creating All cart..
|
||||
@@ -110,6 +111,7 @@ export default function MainSection({
|
||||
{({ datas }) => (
|
||||
<AvailableJobsCard
|
||||
contentDisplay={contentDisplay}
|
||||
image_server={image_server}
|
||||
key={datas.id}
|
||||
datas={datas}
|
||||
/>
|
||||
|
||||
@@ -7,6 +7,10 @@ import LoadingSpinner from "../../Spinners/LoadingSpinner";
|
||||
|
||||
const MarketPopUp = ({ details, onClose, situation, marketInt }) => {
|
||||
const [textValue, setTextValue] = useState("");
|
||||
const [errMsg, setErrMsg] = useState({
|
||||
market: false,
|
||||
manage: false,
|
||||
});
|
||||
const apiCall = useMemo(() => new usersService(), []);
|
||||
|
||||
const handleInputChange = ({ target: { value } }) => {
|
||||
@@ -48,20 +52,17 @@ const MarketPopUp = ({ details, onClose, situation, marketInt }) => {
|
||||
return;
|
||||
}
|
||||
|
||||
toast.success("Message sent", {
|
||||
autoClose: 2500,
|
||||
hideProgressBar: true,
|
||||
});
|
||||
|
||||
setMarketMsg({ data: marketMessageRes, state: true });
|
||||
setTimeout(() => onClose(), 2000);
|
||||
} catch (error) {
|
||||
setErrMsg({ market: true });
|
||||
setMarketMsg({ loading: false, state: false });
|
||||
throw new Error(error);
|
||||
} finally {
|
||||
setTextValue("");
|
||||
setTimeout(() => {
|
||||
setTextValue("");
|
||||
setMarketMsg({ loading: false });
|
||||
}, 2000);
|
||||
setMarketMsg({ loading: false, state: false });
|
||||
setErrMsg({ market: false });
|
||||
}, 5000);
|
||||
}
|
||||
};
|
||||
|
||||
@@ -117,7 +118,7 @@ const MarketPopUp = ({ details, onClose, situation, marketInt }) => {
|
||||
|
||||
return (
|
||||
<ModalCom action={onClose} situation={situation} className="edit-popup">
|
||||
<div className="logout-modal-wrapper md:w-[750px] md:h-[660px] h-full bg-white dark:bg-dark-white lg:rounded-2xl overflow-y-auto">
|
||||
<div className="logout-modal-wrapper md:w-[650px] md:h-[580px] h-full bg-white dark:bg-dark-white lg:rounded-2xl overflow-y-auto">
|
||||
<div className="logout-modal-header w-full flex items-center justify-between lg:p-6 px-[30px] py-[23px]">
|
||||
<h1 className="text-26 font-bold text-dark-gray dark:text-white tracking-wide">
|
||||
{details.offer_code}
|
||||
@@ -127,7 +128,7 @@ const MarketPopUp = ({ details, onClose, situation, marketInt }) => {
|
||||
|
||||
<div className="md:flex bg-white dark:bg-dark-white text-slate-900 dark:text-white rounded-lg">
|
||||
<div className="p-4 w-full md:w-[75%] md:border-r-1">
|
||||
<div className="min-h-[263px]">
|
||||
<div className="max-h-[240px] h-full">
|
||||
<h2 className="font-semibold text-slate-900 dark:text-white tracking-wide">
|
||||
{details?.title}
|
||||
</h2>
|
||||
@@ -158,9 +159,9 @@ const MarketPopUp = ({ details, onClose, situation, marketInt }) => {
|
||||
<div
|
||||
className={`w-full md:w-3/4 text-slate-900 dark:text-white market-pop ${
|
||||
name !== "Delivery Detail"
|
||||
? " h-full max-h-28 flex items-center"
|
||||
: " overflow-y-auto max-h-[100px]"
|
||||
}`}
|
||||
? " h-full flex items-center"
|
||||
: " overflow-y-auto max-h-[80px]"
|
||||
} ${name === "Description" && "max-h-14 h-full overflow-auto"}`}
|
||||
>
|
||||
{danger ? (
|
||||
<p
|
||||
@@ -195,40 +196,46 @@ const MarketPopUp = ({ details, onClose, situation, marketInt }) => {
|
||||
))}
|
||||
</div>
|
||||
<hr />
|
||||
<div className="my-3 w-full flex flex-col gap-3">
|
||||
<div className="w-full flex flex-col gap-3">
|
||||
<div className="w-full">
|
||||
<label className="w-full text-slate-900 dark:text-white tracking-wide">
|
||||
<label className="w-full text-slate-900 dark:text-white tracking-wide font-semibold">
|
||||
If you have any questions about this task:
|
||||
</label>
|
||||
<textarea
|
||||
className={`p-1 w-full text-sm text-slate-900 dark:text-white bg-transparent outline-none border border-slate-300 rounded-md`}
|
||||
rows="5"
|
||||
className={`p-1 w-full text-sm text-slate-900 dark:text-white ${
|
||||
marketMsg.loading && "italic text-[#9CA3AF]"
|
||||
} bg-transparent outline-none border-2 border-slate-300 rounded-md`}
|
||||
rows="3"
|
||||
style={{ resize: "none" }}
|
||||
placeholder="Enter message here ..."
|
||||
value={textValue}
|
||||
value={marketMsg.loading ? "Sending..." : textValue}
|
||||
onChange={handleInputChange}
|
||||
/>
|
||||
</div>
|
||||
<button
|
||||
className="self-end w-[150px] h-[52px] rounded-md text-base bg-yellow-500 text-white"
|
||||
name="market-message"
|
||||
onClick={MarketDetail}
|
||||
>
|
||||
{marketMsg.loading ? (
|
||||
<LoadingSpinner size={5} color="white" />
|
||||
) : !marketMsg.state ? (
|
||||
"Send Message"
|
||||
) : (
|
||||
"Message Sent"
|
||||
)}
|
||||
</button>
|
||||
<div className="relative flex w-full justify-end ">
|
||||
<span className="text-sm text-[#57cd89] absolute left-[8rem] top-4">
|
||||
{marketMsg.state && "Message Sent!"}
|
||||
{errMsg.market && "Something went wrong"}
|
||||
</span>
|
||||
<button
|
||||
className="self-end w-[150px] h-[48px] rounded-full text-base bg-yellow-500 text-white"
|
||||
name="market-message"
|
||||
onClick={MarketDetail}
|
||||
>
|
||||
{marketMsg.loading ? (
|
||||
<LoadingSpinner size={5} color="white" />
|
||||
) : (
|
||||
"Send Message"
|
||||
)}
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="w-full md:w-[23%] h-full ">
|
||||
<div className="mx-auto bg-[#f1f8ff] dark:bg-[#C2C8D3] p-4 rounded-md md:min-h-[498px] flex flex-col justify-between">
|
||||
<div className="w-full flex flex-col justify-center py-4 gap-2">
|
||||
<p className="w-full text-slate-900 tracking-wide my-1">
|
||||
<div className="w-full md:w-[23%] h-full flex flex-col">
|
||||
<div className="mx-auto bg-[#f1f8ff] dark:bg-[#C2C8D3] px-4 rounded-md md:min-h-[420px] flex flex-col justify-between">
|
||||
<div className="w-full flex flex-col justify-center pb-4 gap-2">
|
||||
<p className="w-full text-slate-900 tracking-wide my-1 font-semibold">
|
||||
Interested in the task?
|
||||
</p>
|
||||
<hr />
|
||||
@@ -270,7 +277,7 @@ const MarketPopUp = ({ details, onClose, situation, marketInt }) => {
|
||||
</div>
|
||||
</div>
|
||||
<button
|
||||
className="self-end w-[150px] mt-2 h-[52px] rounded-md text-base bg-transparent border border-red-500 text-red-500 mx-auto"
|
||||
className="self-center w-[150px] mt-2 h-[48px] rounded-full text-base bg-transparent border border-red-500 text-red-500 mx-auto"
|
||||
name="cancel"
|
||||
onClick={onClose}
|
||||
>
|
||||
|
||||
@@ -7,6 +7,7 @@ export default function MarketPlace({ commonHeadData }) {
|
||||
let { jobLists } = useSelector((state) => state.jobLists);
|
||||
const marketData = jobLists?.result_list;
|
||||
const categories = jobLists?.categories;
|
||||
const image_server = jobLists?.session_image_server;
|
||||
|
||||
return (
|
||||
<>
|
||||
@@ -15,6 +16,7 @@ export default function MarketPlace({ commonHeadData }) {
|
||||
<MainSection
|
||||
marketPlaceProduct={marketData}
|
||||
categories={categories}
|
||||
image_server={image_server}
|
||||
className="mb-10"
|
||||
/>
|
||||
</Layout>
|
||||
|
||||
@@ -30,21 +30,26 @@ export default function ActiveJobMessage({ activeJobMesList }) {
|
||||
{activeJobMesList?.data?.length ?
|
||||
(
|
||||
<tbody>
|
||||
{activeJobMesList.data.map((item, index) => (
|
||||
<tr key={index} className='text-slate-500'>
|
||||
<td>
|
||||
<div className={`msg_box ${item.who}`}>
|
||||
<div className="msg_header">{item.msg_date} {item.msg_firstname}</div>
|
||||
{item.msg_type == 'FILE' ?
|
||||
<a href='' className="p-2" dangerouslySetInnerHTML={{__html: item.message}}></a>
|
||||
:
|
||||
<span className="p-2" dangerouslySetInnerHTML={{__html: item.message}}></span>
|
||||
}
|
||||
</div>
|
||||
|
||||
</td>
|
||||
</tr>
|
||||
))}
|
||||
{activeJobMesList.data.map((item, index) =>
|
||||
{
|
||||
let imageLink = `${activeJobMesList?.image}${localStorage.getItem('session_token')}/contracts/${item.msg_uid}`
|
||||
return (
|
||||
<tr key={index} className='text-slate-500'>
|
||||
<td>
|
||||
<div className={`msg_box ${item.who}`}>
|
||||
<div className="msg_header">{item.msg_date} {item.msg_firstname}</div>
|
||||
{item.msg_type == 'FILE' ?
|
||||
<a href={imageLink} target="_blank" className="p-2" dangerouslySetInnerHTML={{__html: item.message}}></a>
|
||||
:
|
||||
<span className="p-2" dangerouslySetInnerHTML={{__html: item.message}}></span>
|
||||
}
|
||||
</div>
|
||||
|
||||
</td>
|
||||
</tr>
|
||||
)
|
||||
}
|
||||
)}
|
||||
</tbody>
|
||||
)
|
||||
:
|
||||
|
||||
@@ -34,7 +34,7 @@ function PastDueJobAction({jobDetails}) {
|
||||
let reqData = { // API PAYLOADS
|
||||
contract: jobDetails.contract,
|
||||
contract_uid: jobDetails.contract_uid,
|
||||
job_action: 'REQUEST_CANCEL',
|
||||
job_action: 'CANCEL_CONTRACT',
|
||||
}
|
||||
|
||||
if(!checked){ // CHECKS IF CHECKBOX IS SELECTED
|
||||
@@ -118,7 +118,11 @@ function PastDueJobAction({jobDetails}) {
|
||||
<tr>
|
||||
<td>
|
||||
<div className="flex justify-center items-center">
|
||||
<button type="button" onClick={popUpHandler} className="w-[180px] h-11 flex justify-center items-center btn-gradient text-base rounded-full text-white">
|
||||
<button
|
||||
type="button"
|
||||
onClick={popUpHandler}
|
||||
className="px-4 h-[48px] flex justify-center items-center btn-gradient text-base rounded-full text-white"
|
||||
>
|
||||
Cancel or Extend Timeline
|
||||
</button>
|
||||
</div>
|
||||
@@ -230,8 +234,12 @@ function PastDueJobAction({jobDetails}) {
|
||||
|
||||
{/* cancel btn */}
|
||||
<div className='flex justify-end items-center'>
|
||||
<button onClick={popUpHandler} type="button" className="w-20 h-11 flex justify-center items-center border-gradient text-base rounded-full text-white">
|
||||
<span className='text-gradient'>Cancel</span>
|
||||
<button onClick={popUpHandler} type="button"
|
||||
// className="w-20 h-11 flex justify-center items-center border-gradient text-base rounded-full text-white"
|
||||
className='w-[150px] mt-2 h-[48px] rounded-full text-base bg-transparent border border-red-500 text-red-500'
|
||||
>
|
||||
Cancel
|
||||
{/* <span className='text-gradient'>Cancel</span> */}
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -3,9 +3,12 @@ import ModalCom from '../../Helpers/ModalCom'
|
||||
import LoadingSpinner from '../../Spinners/LoadingSpinner'
|
||||
import { useNavigate } from 'react-router-dom'
|
||||
import usersService from '../../../services/UsersService'
|
||||
import { useDispatch } from 'react-redux'
|
||||
import { tableReload } from '../../../store/TableReloads'
|
||||
|
||||
function ReviewJobAction({jobDetails}) {
|
||||
|
||||
const dispatch = useDispatch()
|
||||
const apiCall = new usersService()
|
||||
const navigate = useNavigate()
|
||||
|
||||
@@ -48,6 +51,7 @@ function ReviewJobAction({jobDetails}) {
|
||||
return
|
||||
}
|
||||
setReqStatus({loading:false, status: true, message: 'job completion accepted successfully'})
|
||||
dispatch(tableReload({ type: "WALLETTABLE" }));
|
||||
setTimeout(()=>{ // Sets popout to false and navigates user to /my-review-jobs after 3 seconds
|
||||
popUpHandler()
|
||||
navigate('/my-review-jobs', {replace: true})
|
||||
|
||||
@@ -6,6 +6,7 @@ import { handlePagingFunc } from "../Pagination/HandlePagination";
|
||||
import PaginatedList from "../Pagination/PaginatedList";
|
||||
|
||||
export default function MyActiveJobTable({ MyJobList, className }) {
|
||||
|
||||
const navigate = useNavigate();
|
||||
let { pathname } = useLocation();
|
||||
|
||||
@@ -44,6 +45,7 @@ export default function MyActiveJobTable({ MyJobList, className }) {
|
||||
value?.currency_code,
|
||||
value?.currency
|
||||
);
|
||||
let image = `${MyJobList.session_image_server}${localStorage.getItem('session_token')}/job/${value.job_uid}`
|
||||
return (
|
||||
<tr
|
||||
key={index}
|
||||
@@ -51,11 +53,9 @@ export default function MyActiveJobTable({ MyJobList, className }) {
|
||||
>
|
||||
<td className=" py-4">
|
||||
<div className="flex space-x-2 items-center w-full">
|
||||
<div className="w-[60px] h-[60px] p-2 bg-alice-blue rounded-full overflow-hidden flex justify-center items-center">
|
||||
<div className="max-w-[60px] max-h-[60px] min-w-[60px] min-h-[60px] p-2 bg-alice-blue rounded-full overflow-hidden flex justify-center items-center">
|
||||
<img
|
||||
src={localImgLoad(
|
||||
`images/taskbanners/${value.banner}`
|
||||
)}
|
||||
src={image}
|
||||
alt="data"
|
||||
className="w-full h-full rounded-full"
|
||||
/>
|
||||
|
||||
@@ -21,7 +21,7 @@ export default function MyPastDueJobs(props) {
|
||||
<span
|
||||
className={`${selectTab === "today" ? "block" : "hidden"}`}
|
||||
>
|
||||
Pass Due Job(s)
|
||||
Past Due Job(s)
|
||||
</span>
|
||||
</h1>
|
||||
</div>
|
||||
|
||||
@@ -11,8 +11,8 @@ import PaginatedList from "../Pagination/PaginatedList";
|
||||
import EditJobPopOut from "../jobPopout/EditJobPopout";
|
||||
import { PriceFormatter } from "../Helpers/PriceFormatter";
|
||||
|
||||
import EditIcon from '../../assets/images/icon-edit.svg'
|
||||
import DeleteIcon from '../../assets/images/icon-delete.svg'
|
||||
import EditIcon from "../../assets/images/icon-edit.svg";
|
||||
import DeleteIcon from "../../assets/images/icon-delete.svg";
|
||||
import localImgLoad from "../../lib/localImgLoad";
|
||||
|
||||
export default function MyJobTable({ MyJobList, reloadJobList, className }) {
|
||||
@@ -101,12 +101,19 @@ export default function MyJobTable({ MyJobList, reloadJobList, className }) {
|
||||
}
|
||||
};
|
||||
|
||||
const JobListItem = ({ value, index }) => {
|
||||
const JobListItem = ({ value, index, image_server }) => {
|
||||
let thePrice = PriceFormatter(
|
||||
value?.price * 0.01,
|
||||
value?.currency_code,
|
||||
value?.currency
|
||||
);
|
||||
|
||||
const image = localStorage.getItem("session_token")
|
||||
? `${image_server}${localStorage.getItem("session_token")}/job/${
|
||||
value.job_uid
|
||||
}`
|
||||
: "";
|
||||
|
||||
return (
|
||||
<tr
|
||||
key={index}
|
||||
@@ -116,7 +123,11 @@ export default function MyJobTable({ MyJobList, reloadJobList, className }) {
|
||||
<div className="sm:flex sm:space-x-2 sm:justify-between sm:items-center job-items">
|
||||
<div className="flex space-x-2 items-center job-items w-full">
|
||||
<div className="w-[60px] h-[60px] p-2 bg-alice-blue rounded-full overflow-hidden flex justify-center items-center">
|
||||
<img src={localImgLoad(`images/taskbanners/${value.banner ? value.banner : 'default.jpg'}`)} alt="data" className="w-full h-full rounded-full" />
|
||||
<img
|
||||
src={image}
|
||||
alt="data"
|
||||
className="w-full h-full rounded-full"
|
||||
/>
|
||||
</div>
|
||||
<div className="flex flex-col flex-[0.9]">
|
||||
<h1 className="font-bold text-xl text-dark-gray dark:text-white">
|
||||
@@ -147,7 +158,11 @@ export default function MyJobTable({ MyJobList, reloadJobList, className }) {
|
||||
});
|
||||
}}
|
||||
>
|
||||
<img className="w-[21px] h-[21px]" src={DeleteIcon} alt='delete-icon' />
|
||||
<img
|
||||
className="w-[21px] h-[21px]"
|
||||
src={DeleteIcon}
|
||||
alt="delete-icon"
|
||||
/>
|
||||
</button>
|
||||
<div className="mx-[4px] h-full w-[1px] bg-black dark:bg-dark-gray"></div>
|
||||
<button
|
||||
@@ -160,7 +175,11 @@ export default function MyJobTable({ MyJobList, reloadJobList, className }) {
|
||||
});
|
||||
}}
|
||||
>
|
||||
<img className="w-[21px] h-[21px]" src={EditIcon} alt='edit-icon' />
|
||||
<img
|
||||
className="w-[21px] h-[21px]"
|
||||
src={EditIcon}
|
||||
alt="edit-icon"
|
||||
/>
|
||||
<span className="text-sm text-sky-blue">Edit</span>
|
||||
</button>
|
||||
</div>
|
||||
@@ -193,7 +212,7 @@ export default function MyJobTable({ MyJobList, reloadJobList, className }) {
|
||||
<h1 className="text-xl font-bold text-dark-gray dark:text-white tracking-wide">
|
||||
{filterCategories[selectedCategory]} Jobs
|
||||
</h1>
|
||||
</div>
|
||||
</div>
|
||||
<SelectBox
|
||||
action={handleSetCategory}
|
||||
datas={Object.values(filterCategories)}
|
||||
@@ -201,7 +220,7 @@ export default function MyJobTable({ MyJobList, reloadJobList, className }) {
|
||||
contentBodyClasses="w-auto min-w-max"
|
||||
/>
|
||||
</div>
|
||||
{MyJobList.loading ? (
|
||||
{MyJobList?.loading ? (
|
||||
<LoadingSpinner size="16" color="sky-blue" />
|
||||
) : (
|
||||
<div className="relative w-full overflow-x-auto sm:rounded-lg flex flex-col justify-between min-h-[520px]">
|
||||
@@ -213,7 +232,12 @@ export default function MyJobTable({ MyJobList, reloadJobList, className }) {
|
||||
MyJobList.data?.result_list.length > 0 ? (
|
||||
filteredCurrentJobList?.length ? (
|
||||
filteredCurrentJobList.map((value, index) => (
|
||||
<JobListItem index={index} key={index} value={value} />
|
||||
<JobListItem
|
||||
index={index}
|
||||
key={index}
|
||||
value={value}
|
||||
image_server={MyJobList?.data.session_image_server}
|
||||
/>
|
||||
))
|
||||
) : (
|
||||
<tr className="font-bold text-xl text-dark-gray dark:text-white whitespace-nowrap">
|
||||
|
||||
@@ -8,6 +8,7 @@ import localImgLoad from "../../lib/localImgLoad";
|
||||
|
||||
|
||||
export default function MyPendingJobTable({ MyJobList, className }) {
|
||||
|
||||
let [jobPopout, setJobPopout] = useState({ show: false, data: {} }); // STATE TO HOLD THE VALUE OF THE ALERT DETAILS AND DETERMINE WHEN TO SHOW
|
||||
|
||||
const [currentPage, setCurrentPage] = useState(0);
|
||||
@@ -45,6 +46,7 @@ export default function MyPendingJobTable({ MyJobList, className }) {
|
||||
value?.currency_code,
|
||||
value?.currency
|
||||
);
|
||||
let image = `${MyJobList.session_image_server}${localStorage.getItem('session_token')}/job/${value.job_uid}`
|
||||
return (
|
||||
<tr
|
||||
key={index}
|
||||
@@ -54,7 +56,7 @@ export default function MyPendingJobTable({ MyJobList, className }) {
|
||||
<div className="flex space-x-2 items-center w-full">
|
||||
<div className="w-[60px] h-[60px] p-2 bg-alice-blue rounded-full overflow-hidden flex justify-center items-center">
|
||||
<img
|
||||
src={localImgLoad(`images/taskbanners/${value.banner || "default.jpg"}`)}
|
||||
src={image}
|
||||
alt="data"
|
||||
className="w-full h-full rounded-full"
|
||||
/>
|
||||
|
||||
@@ -10,7 +10,7 @@ import localImgLoad from "../../lib/localImgLoad";
|
||||
const noTasksBg = require("../../assets/images/no-task-background.jpg");
|
||||
const noFamilyTasksBg = require("../../assets/images/family-no-task-background.jpg");
|
||||
|
||||
export default function MyJobTable({ className, ActiveJobList, Account }) {
|
||||
export default function MyJobTable({ className, ActiveJobList, Account, imageServer }) {
|
||||
let navigate = useNavigate();
|
||||
let { pathname } = useLocation();
|
||||
|
||||
@@ -55,7 +55,7 @@ export default function MyJobTable({ className, ActiveJobList, Account }) {
|
||||
{!ActiveJobList?.data.length && accountType && (
|
||||
<div className="absolute inset-0 bg-black opacity-30"></div>
|
||||
)}
|
||||
{ActiveJobList.loading ?
|
||||
{ActiveJobList?.loading ?
|
||||
<div className="w-full h-[520px] flex items-center justify-center">
|
||||
<LoadingSpinner size="16" color="sky-blue" />
|
||||
</div>
|
||||
@@ -71,6 +71,7 @@ export default function MyJobTable({ className, ActiveJobList, Account }) {
|
||||
task?.currency_code,
|
||||
task?.currency
|
||||
);
|
||||
let image = `${imageServer}${localStorage.getItem('session_token')}/job/${task.origin_job_uid}`
|
||||
return (
|
||||
<div
|
||||
className="bg-white dark:bg-dark-white border-b dark:border-[#5356fb29] w-full flex justify-between items-center hover:bg-gray-50"
|
||||
@@ -80,7 +81,7 @@ export default function MyJobTable({ className, ActiveJobList, Account }) {
|
||||
<div className="flex space-x-2 items-center">
|
||||
<div className="w-full min-w-[60px] max-w-[60px] flex-[0.1] h-[60px] rounded-full overflow-hidden flex justify-center items-center">
|
||||
<img
|
||||
src={localImgLoad(`images/taskbanners/${task?.banner}`)}
|
||||
src={image}
|
||||
alt="data"
|
||||
className="w-full h-full"
|
||||
/>
|
||||
|
||||
@@ -4,7 +4,7 @@ import Icons from "../Helpers/Icons";
|
||||
import SliderCom from "../Helpers/SliderCom";
|
||||
import FamilyOfferJobPopout from "../jobPopout/FamilyOfferJobPopout";
|
||||
|
||||
export default function MyOffersFamilyTable({ className, familyOffers }) {
|
||||
export default function MyOffersFamilyTable({ className, familyOffers, image_server }) {
|
||||
let [offerPopout, setOfferPopout] = useState({ show: false, data: {} }); // STATE TO HOLD THE VALUE OF THE ALERT DETAILS AND DETERMINE WHEN TO SHOW
|
||||
const settings = {
|
||||
arrows: false,
|
||||
@@ -117,6 +117,7 @@ export default function MyOffersFamilyTable({ className, familyOffers }) {
|
||||
key={item.id}
|
||||
datas={item}
|
||||
setOfferPopout={setOfferPopout}
|
||||
image_server={image_server}
|
||||
/>
|
||||
);
|
||||
})}
|
||||
|
||||
@@ -122,7 +122,7 @@ export default function MyOffersTable({ className, MyActiveOffersList }) {
|
||||
MyActiveOffersList?.result_list?.length > 0 &&
|
||||
MyActiveOffersList.result_list.map((item) => {
|
||||
return (
|
||||
<OfferCard key={item.id} datas={item} setOfferPopout={setOfferPopout} />
|
||||
<OfferCard key={item.id} datas={item} setOfferPopout={setOfferPopout} image_server={MyActiveOffersList.session_image_server} />
|
||||
)
|
||||
})}
|
||||
</SliderCom>
|
||||
|
||||
@@ -94,7 +94,7 @@ export default function MyTasks({
|
||||
</button>
|
||||
}
|
||||
</div>
|
||||
<MyJobTable ActiveJobList={ActiveJobList} Account={userDetails} />
|
||||
<MyJobTable ActiveJobList={ActiveJobList} Account={userDetails} imageServer={MyActiveOffersList.session_image_server} />
|
||||
</div>
|
||||
</div>
|
||||
</Layout>
|
||||
|
||||
@@ -44,7 +44,7 @@ export default function MyWaitingJobTable({ MyJobList, className }) {
|
||||
value?.currency_code,
|
||||
value?.currency
|
||||
);
|
||||
let image = value.banner ? value.banner : 'default.jpg'
|
||||
let image = `${MyJobList.session_image_server}${localStorage.getItem('session_token')}/job/${value.job_uid}`
|
||||
return (
|
||||
<tr
|
||||
key={index}
|
||||
@@ -52,9 +52,9 @@ export default function MyWaitingJobTable({ MyJobList, className }) {
|
||||
>
|
||||
<td className=" py-4">
|
||||
<div className="flex space-x-2 items-center w-full">
|
||||
<div className="w-[60px] h-[60px] p-2 bg-alice-blue rounded-full overflow-hidden flex justify-center items-center">
|
||||
<div className="max-w-[60px] max-h-[60px] min-w-[60px] min-h-[60px] p-2 bg-alice-blue rounded-full overflow-hidden flex justify-center items-center">
|
||||
<img
|
||||
src={localImgLoad(`images/taskbanners/${image}`)}
|
||||
src={image}
|
||||
alt="data"
|
||||
className="w-full h-full rounded-full"
|
||||
/>
|
||||
|
||||
@@ -64,17 +64,21 @@ const initialValues = {
|
||||
};
|
||||
|
||||
function AddFundDollars(props) {
|
||||
let MaxNoOfCards = process.env.REACT_APP_MAX_CREDIT_CARDS // HOLDS THE VALUE OF THE MAX NUMBER OF CARDS USER CAN ADD
|
||||
|
||||
let MaxNoOfCards = process.env.REACT_APP_MAX_CREDIT_CARDS; // HOLDS THE VALUE OF THE MAX NUMBER OF CARDS USER CAN ADD
|
||||
|
||||
const apiCall = new usersService();
|
||||
let countryWallet = props.walletItem.country;
|
||||
const [tab, setTab] = useState("previous");
|
||||
const [selectedOption, setSelectedOption] = useState("previous");
|
||||
const { userDetails } = useSelector((state) => state?.userDetails);
|
||||
const [prevCardDetails, setPrevCardDetails] = useState({});
|
||||
const [payListCards, setPayListCards] = useState({ loading: true, data: [] });
|
||||
const [cardIcons, setCardIcons] = useState("atm-card");
|
||||
const [prevCardError, setPrevCardError] = useState("");
|
||||
|
||||
const handleOptionChange = (event) => {
|
||||
setSelectedOption(event.target.value);
|
||||
};
|
||||
|
||||
const { firstname, lastname } = userDetails;
|
||||
|
||||
// Handling Card Icons
|
||||
@@ -144,7 +148,12 @@ function AddFundDollars(props) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (tab === "previous") {
|
||||
let stateData = {
|
||||
amount: Number(props.input) * 100,
|
||||
currency: props.walletItem?.code,
|
||||
};
|
||||
|
||||
if (selectedOption === "previous") {
|
||||
// To check if card is empty
|
||||
if (Object.keys(prevCardDetails).length === 0) {
|
||||
setPrevCardError("No card selected!");
|
||||
@@ -158,10 +167,26 @@ function AddFundDollars(props) {
|
||||
show: { awaitConfirm: { loader: true } },
|
||||
}));
|
||||
|
||||
let stateData = {
|
||||
amount: Number(props.input) * 100,
|
||||
currency: props.walletItem?.code,
|
||||
};
|
||||
// Extracting card_uid from the previous card details
|
||||
const paymentCardValue = prevCardDetails["payment-card"];
|
||||
|
||||
if (paymentCardValue) {
|
||||
try {
|
||||
const paymentCardObject = JSON.parse(paymentCardValue);
|
||||
stateData = {
|
||||
...stateData,
|
||||
card_uid: paymentCardObject.card_uid,
|
||||
};
|
||||
} catch (error) {
|
||||
console.error("Error parsing JSON:", error);
|
||||
}
|
||||
} else {
|
||||
// For the new card details
|
||||
stateData = {
|
||||
...stateData,
|
||||
card_uid: "",
|
||||
};
|
||||
}
|
||||
|
||||
try {
|
||||
const res = await apiCall.getStartCredit(stateData);
|
||||
@@ -171,12 +196,11 @@ function AddFundDollars(props) {
|
||||
}
|
||||
|
||||
const _response = res.data;
|
||||
stateData.amount = Number(props.input);
|
||||
stateData.card =
|
||||
tab === "previous"
|
||||
selectedOption === "previous"
|
||||
? prevCardDetails["payment-card"]
|
||||
: { ...values, cvv: values.cvv };
|
||||
stateData.cardType = tab === "previous" ? "prev" : "new";
|
||||
stateData.cardType = selectedOption === "previous" ? "prev" : "new";
|
||||
stateData = { ...stateData, ..._response };
|
||||
|
||||
setTimeout(() => {
|
||||
@@ -224,36 +248,45 @@ function AddFundDollars(props) {
|
||||
</h1>
|
||||
<div className="my-1 flex items-center gap-2">
|
||||
<label
|
||||
onClick={() => setTab("previous")}
|
||||
htmlFor="previous"
|
||||
className="cursor-pointer flex items-center gap-1"
|
||||
>
|
||||
<input
|
||||
type="radio"
|
||||
id="previous"
|
||||
value="previous"
|
||||
name="card-option"
|
||||
checked={tab === "previous"}
|
||||
onChange={handleOptionChange}
|
||||
checked={selectedOption === "previous"}
|
||||
className={`p-2 text-lg font-bold text-slate-600 dark:text-white border pointer-events-none w-7 h-7 ${
|
||||
tab == "previous" ? "" : ""
|
||||
selectedOption == "previous" ? "" : ""
|
||||
} tracking-wide transition duration-200`}
|
||||
/>
|
||||
Previous Cards
|
||||
</label>
|
||||
<label
|
||||
onClick={() => setTab("new")}
|
||||
htmlFor="new"
|
||||
className={`cursor-pointer flex items-center gap-1 ${payListCards.data.length >= MaxNoOfCards ? 'pointer-events-none':''}`}
|
||||
className={`cursor-pointer flex items-center gap-1 ${
|
||||
payListCards.data.length >= MaxNoOfCards
|
||||
? "pointer-events-none"
|
||||
: ""
|
||||
}`}
|
||||
>
|
||||
<input
|
||||
id="new"
|
||||
type="radio"
|
||||
name="card-option"
|
||||
checked={tab === "new"}
|
||||
value="new"
|
||||
onChange={handleOptionChange}
|
||||
checked={selectedOption === "new"}
|
||||
className={`p-2 text-lg font-bold text-slate-600 dark:text-white border pointer-events-none w-7 h-7 ${
|
||||
tab == "new" ? "" : ""
|
||||
selectedOption == "new" ? "" : ""
|
||||
} tracking-wide transition duration-200`}
|
||||
/>
|
||||
Add New Card {payListCards.data.length >= MaxNoOfCards && <span className="text-[14px] text-red-500">Max Reached</span>}
|
||||
Add New Card{" "}
|
||||
{payListCards.data.length >= MaxNoOfCards && (
|
||||
<span className="text-[14px] text-red-500">Max Reached</span>
|
||||
)}
|
||||
</label>
|
||||
</div>
|
||||
</form>
|
||||
@@ -261,8 +294,8 @@ function AddFundDollars(props) {
|
||||
<hr />
|
||||
{/* END OF switch button */}
|
||||
|
||||
{/* previous tab */}
|
||||
{tab === "previous" && (
|
||||
{/* previous selectedOption */}
|
||||
{selectedOption === "previous" && (
|
||||
<div className="p-4 previous-details w-full min-h-[16.5rem] flex flex-col">
|
||||
{payListCards.loading ? (
|
||||
<LoadingSpinner size="10" color="sky-blue" />
|
||||
@@ -278,10 +311,11 @@ function AddFundDollars(props) {
|
||||
{currentPreviousCards.map((item, index) => (
|
||||
<option
|
||||
key={index}
|
||||
className={index !== 0 && "border-t-2"}
|
||||
className={index !== 0 ? "border-t-2" : undefined}
|
||||
value={JSON.stringify(item)}
|
||||
title={`${item.description} Card\nBank **************${item.digits}`}
|
||||
>
|
||||
<div className="my-2 flex items-center gap-5">
|
||||
{/* <div className="my-2 flex items-center gap-5">
|
||||
<div className="card-details">
|
||||
<h1 className="text-lg font-bold text-dark-gray dark:text-white tracking-wide">
|
||||
{item.description} Card
|
||||
@@ -290,7 +324,8 @@ function AddFundDollars(props) {
|
||||
Bank **************{item.digits}
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</div> */}
|
||||
{item.description} Card - Bank **************{item.digits}
|
||||
</option>
|
||||
))}
|
||||
</select>
|
||||
@@ -300,7 +335,7 @@ function AddFundDollars(props) {
|
||||
No Previous Card Found!
|
||||
</p>
|
||||
<button
|
||||
onClick={() => setTab("new")}
|
||||
onClick={() => setSelectedOption("new")}
|
||||
type="button"
|
||||
className="my-5 px-2 py-1 h-11 flex justify-center items-center btn-gradient text-base rounded-full text-white"
|
||||
>
|
||||
@@ -314,13 +349,13 @@ function AddFundDollars(props) {
|
||||
</div>
|
||||
)}
|
||||
|
||||
{tab === "new" && (
|
||||
{selectedOption === "new" && (
|
||||
<div className="new-details w-full max-h-[22rem]">
|
||||
{payListCards.loading ?
|
||||
{payListCards.loading ? (
|
||||
<div className="pt-10 flex w-full h-full justify-center items-center">
|
||||
<LoadingSpinner size='10' color='sky-blue' />
|
||||
<LoadingSpinner size="10" color="sky-blue" />
|
||||
</div>
|
||||
:payListCards.data.length < MaxNoOfCards ?
|
||||
) : payListCards.data.length < MaxNoOfCards ? (
|
||||
<div className="w-full flex flex-col justify-between">
|
||||
<Formik
|
||||
initialValues={initialValues}
|
||||
@@ -379,7 +414,8 @@ function AddFundDollars(props) {
|
||||
*
|
||||
</span>
|
||||
<span className="text-[12px] text-red-500 ml-1">
|
||||
{props.errors.expirationMonth && "**"}
|
||||
{props.errors.expirationMonth &&
|
||||
"**"}
|
||||
</span>
|
||||
</label>
|
||||
</div>
|
||||
@@ -431,7 +467,8 @@ function AddFundDollars(props) {
|
||||
*
|
||||
</span>
|
||||
<span className="text-[12px] text-red-500 italic">
|
||||
{props.errors.expirationYear && "**"}
|
||||
{props.errors.expirationYear &&
|
||||
"**"}
|
||||
</span>
|
||||
</label>
|
||||
</div>
|
||||
@@ -560,14 +597,12 @@ function AddFundDollars(props) {
|
||||
}}
|
||||
</Formik>
|
||||
</div>
|
||||
:
|
||||
null
|
||||
}
|
||||
) : null}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
|
||||
{tab == "previous" && (
|
||||
{selectedOption == "previous" && (
|
||||
<div className="md:py-8 add-fund-btn flex justify-end items-center gap-2 py-4">
|
||||
<button
|
||||
className="px-4 py-1 h-11 max-w-[100px] w-full flex justify-center bg-[#f5a430] text-black items-center text-base rounded-full"
|
||||
|
||||
@@ -71,6 +71,7 @@ function AddFundPop({
|
||||
// Prepare state data for API call
|
||||
let stateData = {
|
||||
amount: Number(input) * 100,
|
||||
card_uid: "", //added card_uid as empty string
|
||||
currency: walletItem?.code,
|
||||
};
|
||||
|
||||
|
||||
@@ -80,14 +80,16 @@ function CompleteConfirmCredit({ onClose, confirmCredit }) {
|
||||
</span>
|
||||
</div>
|
||||
|
||||
<div className="flex items-center gap-8">
|
||||
<h1 className="text-xl font-bold text-dark-gray dark:text-white tracking-tighter my-1">
|
||||
Confirmation Number
|
||||
</h1>
|
||||
<span className="text-xl font-bold text-dark-gray dark:text-white tracking-tighter my-1">
|
||||
{data?.confirmation}
|
||||
</span>
|
||||
</div>
|
||||
{isSuccess && (
|
||||
<div className="flex items-center gap-8">
|
||||
<h1 className="text-xl font-bold text-dark-gray dark:text-white tracking-tighter my-1">
|
||||
Confirmation Number
|
||||
</h1>
|
||||
<span className="text-xl font-bold text-dark-gray dark:text-white tracking-tighter my-1">
|
||||
{data?.confirmation}
|
||||
</span>
|
||||
</div>
|
||||
)}
|
||||
</>
|
||||
)}
|
||||
</div>
|
||||
|
||||
@@ -45,7 +45,7 @@ function ThePaymentText({ value, type }) {
|
||||
* @returns {JSX.Element} - The rendered component.
|
||||
*/
|
||||
function AmountSection({ currency, amount, country }) {
|
||||
const formattedAmount = amount?.toFixed(2);
|
||||
const formattedAmount = (+amount * 0.01)?.toFixed(2);
|
||||
const gapClassName = country === "US" ? "gap-14" : "gap-4";
|
||||
|
||||
return (
|
||||
@@ -87,7 +87,7 @@ function TransactionFeeSection({ currency, fee, country }) {
|
||||
*/
|
||||
function TotalSection({ currency, amount, fee, country }) {
|
||||
const total = Number(amount) + Number(fee);
|
||||
const formattedTotal = total?.toFixed(2);
|
||||
const formattedTotal = (total * 0.01)?.toFixed(2);
|
||||
|
||||
const gap = country === "US" ? "gap-[8rem]" : "gap-[6.3rem]";
|
||||
|
||||
@@ -130,7 +130,7 @@ function ConfirmAddFund({
|
||||
public_key: __confirmData?.flutterwave_key,
|
||||
tx_ref: __confirmData?.credit_reference,
|
||||
currency: "NGN",
|
||||
amount: Number(__confirmData.amount),
|
||||
amount: Number(__confirmData.amount) * 0.01,
|
||||
payment_options: "card,mobilemoney,ussd",
|
||||
customer: {
|
||||
email: userDetails.email,
|
||||
@@ -224,7 +224,7 @@ function ConfirmAddFund({
|
||||
|
||||
// Create request data object with required parameters for making the payment
|
||||
const reqData = {
|
||||
amount: amount * 100,
|
||||
amount: amount,
|
||||
card_uid,
|
||||
credit_reference,
|
||||
currency,
|
||||
@@ -289,7 +289,7 @@ function ConfirmAddFund({
|
||||
|
||||
// Prepare request data
|
||||
const reqData = {
|
||||
amount: amount * 100,
|
||||
amount: amount,
|
||||
cardnumber: cardNum.replace(/\s/g, ""),
|
||||
credit_reference,
|
||||
cvc: cvv,
|
||||
|
||||
@@ -170,9 +170,9 @@ function ConfirmNairaWithdraw({
|
||||
viewBox="0 0 24 24"
|
||||
fill="none"
|
||||
stroke="green"
|
||||
stroke-width="2"
|
||||
stroke-linecap="round"
|
||||
stroke-linejoin="round"
|
||||
strokeWidth="2"
|
||||
strokeLinecap="round"
|
||||
strokeLinejoin="round"
|
||||
className="feather feather-check-circle"
|
||||
>
|
||||
<path d="M22 11.08V12a10 10 0 1 1-5.93-9.14"></path>
|
||||
@@ -186,9 +186,9 @@ function ConfirmNairaWithdraw({
|
||||
width="100"
|
||||
height="100"
|
||||
stroke="red"
|
||||
stroke-width="2"
|
||||
stroke-linecap="round"
|
||||
stroke-linejoin="round"
|
||||
strokeWidth="2"
|
||||
strokeLinecap="round"
|
||||
strokeLinejoin="round"
|
||||
className="feather feather-x-circle"
|
||||
>
|
||||
<circle cx="12" cy="12" r="10"></circle>
|
||||
|
||||
@@ -17,7 +17,7 @@ const CreditPopup = ({ details, onClose, situation, walletItem }) => {
|
||||
|
||||
const getTitle = () => {
|
||||
if (confirmCredit?.show?.acceptConfirm?.state) {
|
||||
if (confirmCredit?.data?.internal_return < 0) {
|
||||
if (confirmCredit?.data?.internal_return <= 0) {
|
||||
return "Credit Unsuccessful";
|
||||
} else {
|
||||
return "Credit Add Completed";
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
import { useSelector } from "react-redux";
|
||||
import LoadingSpinner from "../Spinners/LoadingSpinner";
|
||||
import WalletItemCard from "./WalletItemCard";
|
||||
import WalletItemCardFamily from "./WalletItemCardFamily";
|
||||
|
||||
/**
|
||||
* Renders a list of wallet items or a loading spinner depending on the state of the `wallet` object.
|
||||
@@ -7,9 +9,27 @@ import WalletItemCard from "./WalletItemCard";
|
||||
export default function WalletBox({ wallet, payment, countries }) {
|
||||
const { loading, data } = wallet;
|
||||
|
||||
const { userDetails } = useSelector((state) => state.userDetails);
|
||||
const accountType = userDetails?.account_type === "FAMILY";
|
||||
|
||||
return (
|
||||
<div className="my-wallet-wrapper w-full mb-10">
|
||||
<div className="main-wrapper w-full">
|
||||
{accountType ?
|
||||
<div className="balance-inquery w-auto grid sm:grid-cols-2 lg:grid-cols-2 xl:grid-cols-[repeat(auto-fill,_minmax(354px,_1fr))] min-[1440px]:grid-cols-[repeat(auto-fill,_minmax(415px,_1fr))] gap-5 mb-11 h-auto">
|
||||
{loading ? (
|
||||
<div className="w-full h-full flex items-center justify-center">
|
||||
<LoadingSpinner size="16" color="sky-blue" />
|
||||
</div>
|
||||
) : (
|
||||
data.length > 0 && data.map((item) => (
|
||||
<div key={item.wallet_uid} className="lg:w-full h-full mb-10 lg:mb-0">
|
||||
<WalletItemCardFamily walletItem={item} payment={payment} countries={countries} />
|
||||
</div>
|
||||
))
|
||||
)}
|
||||
</div>
|
||||
:
|
||||
<div className="balance-inquery w-auto grid sm:grid-cols-2 lg:grid-cols-2 xl:grid-cols-[repeat(auto-fill,_minmax(354px,_1fr))] min-[1440px]:grid-cols-[repeat(auto-fill,_minmax(415px,_1fr))] gap-5 mb-11 h-auto">
|
||||
{loading ? (
|
||||
<div className="w-full h-full flex items-center justify-center">
|
||||
@@ -23,6 +43,7 @@ export default function WalletBox({ wallet, payment, countries }) {
|
||||
))
|
||||
)}
|
||||
</div>
|
||||
}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
|
||||
@@ -16,45 +16,36 @@ function PurchasesTable({purchase}) {
|
||||
|
||||
return (
|
||||
<div className='flex flex-col justify-between min-h-[500px]'>
|
||||
<table className="wallet-activity w-full table-auto border-collapse text-left">
|
||||
<thead className='border-b-2'>
|
||||
<tr className='text-slate-600'>
|
||||
<th className="p-2">Trx.</th>
|
||||
<th className="p-2">Amount</th>
|
||||
<th className="p-2">Fee</th>
|
||||
</tr>
|
||||
</thead>
|
||||
{purchase.data.length ?
|
||||
(
|
||||
<tbody>
|
||||
{currentPurchase.map((item, index) => (
|
||||
<tr key={index} className='text-slate-500'>
|
||||
<td className="p-2">{item.added_date}<br />
|
||||
<b>{item.confirmation} </b>
|
||||
</td>
|
||||
<td className="p-2">{item.amount}</td>
|
||||
<td className="p-2">{item.fee}</td>
|
||||
</tr>
|
||||
))}
|
||||
</tbody>
|
||||
)
|
||||
<table className="wallet-activity w-full table-auto border-collapse text-left">
|
||||
<thead className='border-b-2'>
|
||||
<tr className='text-slate-600'>
|
||||
<th className="p-2">Trx.</th>
|
||||
<th className="p-2">Amount</th>
|
||||
<th className="p-2">Fee</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{currentPurchase.map((item, index) => (
|
||||
<tr key={index} className='text-slate-500'>
|
||||
<td className="p-2">{item.added_date}<br />
|
||||
<b>{item.confirmation} </b>
|
||||
</td>
|
||||
<td className="p-2">{item.amount}</td>
|
||||
<td className="p-2">{item.fee}</td>
|
||||
</tr>
|
||||
))}
|
||||
</tbody>
|
||||
</table>
|
||||
:purchase.error ?
|
||||
<div className="p-2 text-slate-500 flex flex-col grow justify-center items-center">
|
||||
<span>Opps! an error occurred. Please try again!</span>
|
||||
</div>
|
||||
:
|
||||
purchase.error ?
|
||||
(
|
||||
<tbody>
|
||||
<tr className='text-slate-500'>
|
||||
<td className="p-2" colSpan={4}>Opps! an error occurred. Please try again!</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
)
|
||||
:
|
||||
<tbody>
|
||||
<tr className='text-slate-500'>
|
||||
<td className="p-2" colSpan={4}>No Purchase History Found!</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
<div className="p-2 text-slate-500 flex flex-col grow justify-center items-center">
|
||||
<span>No Purchase History Found!</span>
|
||||
</div>
|
||||
}
|
||||
</table>
|
||||
|
||||
{/* PAGINATION BUTTON */}
|
||||
<PaginatedList onClick={handlePagination} prev={currentPage == 0 ? true : false} next={currentPage+Number(process.env.REACT_APP_ITEM_PER_PAGE) >= purchase?.data?.length ? true : false} data={purchase?.data} start={indexOfFirstItem} stop={indexOfLastItem} />
|
||||
|
||||
@@ -19,16 +19,16 @@ function RecentActivityTable({ payment }) {
|
||||
|
||||
return (
|
||||
<div className="flex flex-col justify-between min-h-[500px]">
|
||||
<table className="wallet-activity w-full table-auto border-collapse text-left">
|
||||
<thead className="border-b-2">
|
||||
<tr className="text-slate-600">
|
||||
<th className="p-2">Date</th>
|
||||
<th className="p-4">Trx.</th>
|
||||
<th className="p-2">Amnt./Fee</th>
|
||||
<th className="p-2">Status</th>
|
||||
</tr>
|
||||
</thead>
|
||||
{payment?.data?.length > 0 ? (
|
||||
{payment?.data?.length > 0 ?
|
||||
<table className="wallet-activity w-full table-auto border-collapse text-left">
|
||||
<thead className="border-b-2">
|
||||
<tr className="text-slate-600">
|
||||
<th className="p-2">Date</th>
|
||||
<th className="p-4">Trx.</th>
|
||||
<th className="p-2">Amnt./Fee</th>
|
||||
<th className="p-2">Status</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{currentActivity.map((item, index) => (
|
||||
<tr key={index} className="text-slate-500">
|
||||
@@ -46,24 +46,16 @@ function RecentActivityTable({ payment }) {
|
||||
</tr>
|
||||
))}
|
||||
</tbody>
|
||||
) : payment?.error ? (
|
||||
<tbody>
|
||||
<tr className="text-slate-500">
|
||||
<td className="p-2" colSpan={4}>
|
||||
Opps! an error occurred. Please try again!
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
) : (
|
||||
<tbody>
|
||||
<tr className="text-slate-500">
|
||||
<td className="p-2" colSpan={4}>
|
||||
No Payment History Found!
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
)}
|
||||
</table>
|
||||
</table>
|
||||
:payment.error ?
|
||||
<div className="p-2 text-slate-500 flex flex-col grow justify-center items-center">
|
||||
<span>Opps! an error occurred. Please try again!</span>
|
||||
</div>
|
||||
:
|
||||
<div className="p-2 text-slate-500 flex flex-col grow justify-center items-center">
|
||||
<span>No Payment History Found!</span>
|
||||
</div>
|
||||
}
|
||||
|
||||
{/* PAGINATION BUTTON */}
|
||||
<PaginatedList
|
||||
|
||||
@@ -11,8 +11,7 @@ import WalletAction from "./WalletAction";
|
||||
* Renders a card displaying information about a wallet item.
|
||||
*/
|
||||
export default function WalletItemCard({ walletItem, payment, countries }) {
|
||||
const { userDetails } = useSelector((state) => state.userDetails);
|
||||
const accountType = userDetails?.account_type === "FAMILY";
|
||||
|
||||
const dispatch = useDispatch();
|
||||
const [creditPopup, setCreditPopup] = useState({ show: false, data: {} });
|
||||
|
||||
@@ -91,13 +90,11 @@ export default function WalletItemCard({ walletItem, payment, countries }) {
|
||||
|
||||
<div className="my-2 w-full h-[1px] bg-white"></div>
|
||||
|
||||
{!accountType && (
|
||||
<WalletAction
|
||||
walletItem={{ ...walletItem, walletCountry: currentWalletCurrency }}
|
||||
payment={payment}
|
||||
openPopUp={openPopUp}
|
||||
/>
|
||||
)}
|
||||
<WalletAction
|
||||
walletItem={{ ...walletItem, walletCountry: currentWalletCurrency }}
|
||||
payment={payment}
|
||||
openPopUp={openPopUp}
|
||||
/>
|
||||
</div>
|
||||
|
||||
{creditPopup.show && (
|
||||
|
||||
@@ -0,0 +1,97 @@
|
||||
import React, { useState } from "react";
|
||||
import { useDispatch, useSelector } from "react-redux";
|
||||
import background from "../../assets/images/bg-sky-blue.jpg"; //shape/balance-bg.svg";
|
||||
import localImgLoad from "../../lib/localImgLoad";
|
||||
import { tableReload } from "../../store/TableReloads";
|
||||
import { PriceFormatter } from "../Helpers/PriceFormatter";
|
||||
import CreditPopup from "./Popup/CreditPopup";
|
||||
import WalletAction from "./WalletAction";
|
||||
|
||||
/**
|
||||
* Renders a card displaying information about a wallet item.
|
||||
*/
|
||||
export default function WalletItemCardFamily({ walletItem, payment, countries }) {
|
||||
|
||||
const dispatch = useDispatch();
|
||||
const [creditPopup, setCreditPopup] = useState({ show: false, data: {} });
|
||||
|
||||
/**
|
||||
* Opens the credit popup.
|
||||
* @param {Object} value - The value object.
|
||||
*/
|
||||
const openPopUp = (value) => {
|
||||
setCreditPopup({
|
||||
show: true,
|
||||
data: { ...value },
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* Closes the credit popup and dispatches a table reload action.
|
||||
*/
|
||||
const closePopUp = () => {
|
||||
setCreditPopup({ show: false, data: {} });
|
||||
dispatch(tableReload({ type: "WALLETTABLE" }));
|
||||
};
|
||||
|
||||
const currentWalletCurrency = countries
|
||||
.map((country) => country)
|
||||
.filter((country) => country[0] === walletItem.country);
|
||||
|
||||
const image = walletItem.code
|
||||
? `${walletItem.code.toLowerCase()}.svg`
|
||||
: "default.png";
|
||||
|
||||
return (
|
||||
<>
|
||||
<div
|
||||
className="current-balance-widget w-full h-full rounded-2xl overflow-hidden flex flex-col items-center gap-2 p-8 justify-between"
|
||||
style={{
|
||||
background: `url(${background}) 0% 0% / cover no-repeat`,
|
||||
}}
|
||||
>
|
||||
<div className="wallet w-full flex justify-between items-center gap-3">
|
||||
<div className="min-w-[100px] min-h-[100px] max-w-min md:max-w-[150px] max-h-min md:max-h-[150px] rounded-full bg-[#e3e3e3] flex justify-center items-center">
|
||||
<img
|
||||
src={localImgLoad(`images/currency/${image}`)}
|
||||
className="w-full h-full"
|
||||
alt="currency-icon"
|
||||
/>
|
||||
</div>
|
||||
<div className="balance w-full mt-2 flex justify-center">
|
||||
<div className="">
|
||||
<p className="text-base sm:text-lg text-white opacity-[70%] tracking-wide mb-2 sm:mb-6">
|
||||
Current Balance
|
||||
</p>
|
||||
<p className="text-[44px] lg:text-[62px] font-bold text-white tracking-wide leading-10 xxs:scale-100 lg:scale-100 xl:scale-125">
|
||||
{PriceFormatter(
|
||||
walletItem.amount * 0.01,
|
||||
walletItem.code,
|
||||
undefined,
|
||||
"text-[2rem]"
|
||||
)}
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="my-2 w-full h-[1px] bg-white"></div>
|
||||
|
||||
{/* <WalletAction
|
||||
walletItem={{ ...walletItem, walletCountry: currentWalletCurrency }}
|
||||
payment={payment}
|
||||
openPopUp={openPopUp}
|
||||
/> */}
|
||||
</div>
|
||||
|
||||
{creditPopup.show && (
|
||||
<CreditPopup
|
||||
details={creditPopup.data}
|
||||
walletItem={walletItem}
|
||||
onClose={closePopUp}
|
||||
situation={openPopUp}
|
||||
/>
|
||||
)}
|
||||
</>
|
||||
);
|
||||
}
|
||||
@@ -6,8 +6,11 @@ import CommonHead from "../UserHeader/CommonHead";
|
||||
import usersService from "../../services/UsersService";
|
||||
import LoadingSpinner from "../Spinners/LoadingSpinner";
|
||||
import OthersInterestedTable from "./OthersInterestedTable";
|
||||
import { useDispatch } from "react-redux";
|
||||
import { tableReload } from "../../store/TableReloads";
|
||||
|
||||
export default function ManageInterestOffer(props) {
|
||||
const dispatch = useDispatch()
|
||||
const navigate = useNavigate()
|
||||
const apiCall = new usersService()
|
||||
|
||||
@@ -89,6 +92,7 @@ export default function ManageInterestOffer(props) {
|
||||
setRedirectTime(prev => prev - 1)
|
||||
}, 1000);
|
||||
setRequestStatus({loading: false, status: true, message: `Offer ${name}ed`, processType: ''})
|
||||
dispatch(tableReload({ type: "WALLETTABLE" }));
|
||||
setTimeout(()=>{
|
||||
navigate('/offer-interest', {replace: true})
|
||||
clearInterval(intervalTime)
|
||||
|
||||
@@ -36,6 +36,18 @@ export default function Header({ logoutModalHandler, sidebarHandler }) {
|
||||
const { notifications } = useSelector((state) => state?.notifications); // NOTIFICATION STORE
|
||||
const { walletDetails } = useSelector((state) => state?.walletDetails); // WALLET STORE
|
||||
|
||||
const image = localStorage.getItem("session_token")
|
||||
? userDetails.account_type === "FAMILY"
|
||||
? `${userDetails.session_image_server}${localStorage.getItem(
|
||||
"session_token"
|
||||
)}/family/${sessionStorage.getItem("family_uid")}`
|
||||
: `${userDetails.session_image_server}${localStorage.getItem(
|
||||
"session_token"
|
||||
)}/profile/${userDetails.uid}`
|
||||
: "";
|
||||
|
||||
// 9308RDR122
|
||||
|
||||
const handlerBalance = () => {
|
||||
setbalanceValue.toggle();
|
||||
if (notificationDropdown) {
|
||||
@@ -91,7 +103,7 @@ export default function Header({ logoutModalHandler, sidebarHandler }) {
|
||||
// User Profile
|
||||
let { firstname, lastname, email, profile_pic_url } = userDetails;
|
||||
let userEmail = email?.split("@")[0];
|
||||
const userProfileImage = profile_pic_url || DEFAULT_PROFILE_IMAGE;
|
||||
const userProfileImage = image || DEFAULT_PROFILE_IMAGE;
|
||||
|
||||
return (
|
||||
<>
|
||||
|
||||
@@ -23,6 +23,7 @@ export default function Layout({ children }) {
|
||||
localStorage.removeItem("session_token");
|
||||
localStorage.removeItem("member_id");
|
||||
localStorage.removeItem("uid");
|
||||
sessionStorage.removeItem("family_uid");
|
||||
// localStorage.clear();
|
||||
// toast.success("Come Back Soon", {
|
||||
// icon: `🙂`,
|
||||
|
||||
@@ -8,13 +8,14 @@ import {
|
||||
import DarkModeContext from "../Contexts/DarkModeContext";
|
||||
import Icons from "../Helpers/Icons";
|
||||
|
||||
|
||||
export default function Sidebar({
|
||||
sidebar,
|
||||
action,
|
||||
logoutModalHandler,
|
||||
myJobList,
|
||||
}) {
|
||||
const darkMode = useContext(DarkModeContext);
|
||||
const darkMode = useContext(DarkModeContext);
|
||||
|
||||
let { userDetails } = useSelector((state) => state.userDetails);
|
||||
//const jobLists = getJobList(); // pass from upper - we need in a lot of places
|
||||
|
||||
@@ -63,6 +63,7 @@ export default function Resources(props) {
|
||||
loading: false,
|
||||
msg: "success",
|
||||
data: res?.result_list,
|
||||
image:res?.session_image_server
|
||||
}));
|
||||
} catch (error) {
|
||||
setUploadedFiles((prev) => ({
|
||||
|
||||
@@ -13,7 +13,6 @@ export default function MyUploadedFiles({ uploadedFiles }) {
|
||||
indexOfFirstItem,
|
||||
indexOfLastItem
|
||||
);
|
||||
|
||||
const handlePagination = (e) => {
|
||||
handlePagingFunc(e, setCurrentPage);
|
||||
};
|
||||
@@ -45,10 +44,11 @@ export default function MyUploadedFiles({ uploadedFiles }) {
|
||||
currentFiles.map((value, idx) => {
|
||||
let addedDate = value?.added?.split(" ")[0];
|
||||
let formattedSize = formatFileSize(value?.file_size);
|
||||
let imageLink = `${uploadedFiles?.image}${localStorage.getItem('session_token')}/myfile/${value.file_uid}`
|
||||
return (
|
||||
<tr
|
||||
key={value?.file_uid}
|
||||
className="bg-white dark:bg-dark-white border-b dark:border-[#5356fb29] hover:bg-gray-50"
|
||||
className="bg-white dark:bg-dark-white border-b dark:border-[#5356fb29] hover:bg-gray-50"
|
||||
>
|
||||
<td className=" py-4">
|
||||
<div className="flex space-x-2 items-center w-full">
|
||||
@@ -102,17 +102,17 @@ export default function MyUploadedFiles({ uploadedFiles }) {
|
||||
</td>
|
||||
<td className="text-right py-4 px-2">
|
||||
<div className="flex justify-center items-center">
|
||||
<button
|
||||
type="button"
|
||||
// onClick={() => {
|
||||
// navigate("/manage-active-job", {
|
||||
// state: { ...value, pathname },
|
||||
// });
|
||||
// }}
|
||||
className="w-20 h-11 flex justify-center items-center btn-gradient text-base rounded-full text-white"
|
||||
<a
|
||||
href={imageLink}
|
||||
title="download"
|
||||
// className="w-20 h-11 flex justify-center items-center btn-gradient text-base rounded-full text-white"
|
||||
>
|
||||
View
|
||||
</button>
|
||||
<img
|
||||
src={localImgLoad('images/icons/download-arrow.svg')}
|
||||
alt='download-link'
|
||||
className="w-auto h-6 flex justify-center items-center"
|
||||
/>
|
||||
</a>
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import React, { useEffect, useState } from "react";
|
||||
import React, { useEffect, useRef, useState } from "react";
|
||||
import { useSelector } from "react-redux";
|
||||
import { useNavigate } from "react-router-dom";
|
||||
import usersService from "../../../services/UsersService";
|
||||
@@ -6,6 +6,9 @@ import Icons from "../../Helpers/Icons";
|
||||
import InputCom from "../../Helpers/Inputs/InputCom";
|
||||
import LoadingSpinner from "../../Spinners/LoadingSpinner";
|
||||
|
||||
import cover from "../../../assets/images/profile-info-cover.png";
|
||||
import profileImage from "../../../assets/images/profile.jpg";
|
||||
|
||||
import { Form, Formik } from "formik";
|
||||
import * as Yup from "yup";
|
||||
|
||||
@@ -43,18 +46,15 @@ export default function PersonalInfoTab({
|
||||
frstNmeHndlr,
|
||||
lstNmeHndlr,
|
||||
dscrphn,
|
||||
profileImg,
|
||||
coverImg,
|
||||
profileImgInput,
|
||||
browseProfileImg,
|
||||
profileImgChangHandler,
|
||||
coverImgInput,
|
||||
browseCoverImg,
|
||||
coverImgChangHandler,
|
||||
uploadStatus
|
||||
}) {
|
||||
let { userDetails } = useSelector((state) => state.userDetails);
|
||||
|
||||
const image = localStorage.getItem("session_token")
|
||||
? `${userDetails.session_image_server}${localStorage.getItem(
|
||||
"session_token"
|
||||
)}/profile/${userDetails.uid}`
|
||||
: "";
|
||||
|
||||
const apiCall = new usersService();
|
||||
|
||||
let navigate = useNavigate();
|
||||
@@ -85,6 +85,125 @@ export default function PersonalInfoTab({
|
||||
status: false,
|
||||
});
|
||||
|
||||
const [profileImg, setProfileImg] = useState(
|
||||
userDetails?.session_image_server ? image : profileImage
|
||||
);
|
||||
|
||||
let [uploadStatus, setUploadStatus] = useState({
|
||||
loading: false,
|
||||
status: false,
|
||||
message: "",
|
||||
}); // HOLDS STATE FOR UPLOAD PROFILE PICTURE STATUS
|
||||
|
||||
const [coverImg, setCoverImg] = useState(cover);
|
||||
|
||||
// profile img
|
||||
const profileImgInput = useRef(null);
|
||||
const browseProfileImg = () => {
|
||||
profileImgInput.current.click();
|
||||
};
|
||||
|
||||
const profileImgChangHandler = (e) => {
|
||||
setUploadStatus({ loading: false, status: false, message: "" });
|
||||
const acceptedFormat = ["jpeg", "jpg", "png", "bmp", "gif"]; // ARRAY OF SUPPORTED FORMATS
|
||||
const uploadedFile = e.target.files[0]; //UPLOADED FILE
|
||||
|
||||
const fileFormat = uploadedFile?.type?.split("/")[1]?.toLowerCase();
|
||||
const MAX_FILE_SIZE = 5 * 1048576; // 5MB
|
||||
|
||||
if (!acceptedFormat.includes(fileFormat)) {
|
||||
//CHECKING FOR CORRECT UPLOAD FORMAT
|
||||
const msg = `Please select ${acceptedFormat
|
||||
.slice(0, -1)
|
||||
.join(", ")} or ${acceptedFormat.slice(-1)}`;
|
||||
setUploadStatus({ loading: false, status: false, message: msg });
|
||||
return setTimeout(() => {
|
||||
profileImgInput.current.value = ""; // clear the input
|
||||
setUploadStatus({ loading: false, status: false, message: "" });
|
||||
}, 5000);
|
||||
}
|
||||
|
||||
if (uploadedFile.size > MAX_FILE_SIZE) {
|
||||
// CHECKING FOR CORRECT FILE SIZE
|
||||
setUploadStatus({
|
||||
loading: false,
|
||||
status: false,
|
||||
message: "File must not exceed 5MB",
|
||||
});
|
||||
return setTimeout(() => {
|
||||
profileImgInput.current.value = ""; // clear the input
|
||||
setUploadStatus({ loading: false, status: false, message: "" });
|
||||
}, 5000);
|
||||
}
|
||||
|
||||
if (e.target.value !== "") {
|
||||
const imgReader = new FileReader();
|
||||
imgReader.onload = (event) => {
|
||||
let base64Img = imgReader.result.split(",")[1];
|
||||
let reqData = {
|
||||
// PAYLOAD FOR API CALL
|
||||
file_name: uploadedFile?.name,
|
||||
file_size: uploadedFile?.size,
|
||||
file_type: uploadedFile?.type?.split("/")[0]?.toLowerCase(),
|
||||
file_data: base64Img,
|
||||
msg_type: "FILE",
|
||||
action: 11300,
|
||||
};
|
||||
|
||||
setUploadStatus({
|
||||
loading: true,
|
||||
status: false,
|
||||
message: "Loading...",
|
||||
});
|
||||
apiCall
|
||||
.sendFiles(reqData)
|
||||
.then((res) => {
|
||||
if (res.status != 200 || res.data.internal_return < 0) {
|
||||
return setUploadStatus({
|
||||
loading: false,
|
||||
status: false,
|
||||
message: "Something went wrong, try again",
|
||||
});
|
||||
}
|
||||
setUploadStatus({
|
||||
loading: false,
|
||||
status: true,
|
||||
message: "Uploaded successfully",
|
||||
});
|
||||
setProfileImg(event.target.result);
|
||||
})
|
||||
.catch((error) => {
|
||||
setUploadStatus({
|
||||
loading: false,
|
||||
status: false,
|
||||
message: "Network error, try again",
|
||||
});
|
||||
})
|
||||
.finally(() => {
|
||||
setTimeout(() => {
|
||||
setUploadStatus({ loading: false, status: false, message: "" });
|
||||
}, 5000);
|
||||
});
|
||||
};
|
||||
imgReader.readAsDataURL(uploadedFile);
|
||||
}
|
||||
};
|
||||
|
||||
// cover img
|
||||
const coverImgInput = useRef(null);
|
||||
const browseCoverImg = () => {
|
||||
coverImgInput.current.click();
|
||||
};
|
||||
const coverImgChangHandler = (e) => {
|
||||
if (e.target.value !== "") {
|
||||
const imgReader = new FileReader();
|
||||
imgReader.onload = (event) => {
|
||||
setCoverImg(event.target.result);
|
||||
};
|
||||
imgReader.readAsDataURL(e.target.files[0]);
|
||||
}
|
||||
};
|
||||
|
||||
const handleUpdateUser = (values, helpers) => {
|
||||
setRequestState({ message: "", loading: true, status: false });
|
||||
|
||||
@@ -363,65 +482,77 @@ export default function PersonalInfoTab({
|
||||
</div>
|
||||
</div>
|
||||
{/* {process.env.REACT_APP_SHOW_UPLOAD_PROFILE_PICTURE != 0 && */}
|
||||
<div className="w-[232px] mb-10">
|
||||
<div className="update-profile w-full mb-9">
|
||||
<h1 className="text-xl tracking-wide font-bold text-dark-gray dark:text-white flex items-center mb-2">
|
||||
Update Profile
|
||||
<span className="ml-1">
|
||||
<Icons name="block-question" />
|
||||
</span>
|
||||
</h1>
|
||||
<p className="text-base text-thin-light-gray mb-5">
|
||||
Profile of at least Size
|
||||
<span className="ml-1 text-dark-gray dark:text-white">
|
||||
300x300
|
||||
</span>
|
||||
. Gifs work too.
|
||||
<span className="ml-1 text-dark-gray dark:text-white">
|
||||
Max 5mb
|
||||
</span>
|
||||
.
|
||||
</p>
|
||||
<div className="flex justify-center">
|
||||
<div className="w-full relative">
|
||||
<img
|
||||
src={profileImg}
|
||||
alt="profile"
|
||||
className="sm:w-[198px] sm:h-[198px] w-[120px] h-[120px] rounded-full overflow-hidden object-contain object-center"
|
||||
/>
|
||||
<input
|
||||
ref={profileImgInput}
|
||||
onChange={(e) => profileImgChangHandler(e)}
|
||||
type="file"
|
||||
className="hidden"
|
||||
/>
|
||||
<div
|
||||
onClick={browseProfileImg}
|
||||
className="w-[32px] h-[32px] absolute bottom-7 sm:right-10 right-[105px] hover:bg-pink bg-dark-gray rounded-full cursor-pointer"
|
||||
<div className="w-[232px] mb-10">
|
||||
<div className="update-profile w-full mb-9">
|
||||
<h1 className="text-xl tracking-wide font-bold text-dark-gray dark:text-white flex items-center mb-2">
|
||||
Update Profile
|
||||
<span className="ml-1">
|
||||
<Icons name="block-question" />
|
||||
</span>
|
||||
</h1>
|
||||
<p className="text-base text-thin-light-gray mb-5">
|
||||
Profile of at least Size
|
||||
<span className="ml-1 text-dark-gray dark:text-white">
|
||||
200x200
|
||||
</span>
|
||||
. Gifs work too.
|
||||
<span className="ml-1 text-dark-gray dark:text-white">
|
||||
Max 5mb
|
||||
</span>
|
||||
.
|
||||
</p>
|
||||
<div className="flex justify-center">
|
||||
<div className="w-[200px] h-[200px] relative">
|
||||
<img
|
||||
src={profileImg}
|
||||
alt="profile"
|
||||
className="sm:w-[198px] sm:h-[198px] w-full h-full rounded-full overflow-hidden object-cover object-center"
|
||||
/>
|
||||
<input
|
||||
ref={profileImgInput}
|
||||
onChange={(e) => profileImgChangHandler(e)}
|
||||
type="file"
|
||||
className="hidden"
|
||||
/>
|
||||
<div
|
||||
onClick={browseProfileImg}
|
||||
className="w-[32px] h-[32px] absolute bottom-4 right-4 hover:bg-pink bg-dark-gray rounded-full cursor-pointer"
|
||||
>
|
||||
<svg
|
||||
width="32"
|
||||
height="32"
|
||||
viewBox="0 0 32 32"
|
||||
fill="none"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
>
|
||||
<svg
|
||||
width="32"
|
||||
height="32"
|
||||
viewBox="0 0 32 32"
|
||||
fill="none"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
>
|
||||
<path
|
||||
d="M16.5147 11.5C17.7284 12.7137 18.9234 13.9087 20.1296 15.115C19.9798 15.2611 19.8187 15.4109 19.6651 15.5683C17.4699 17.7635 15.271 19.9587 13.0758 22.1539C12.9334 22.2962 12.7948 22.4386 12.6524 22.5735C12.6187 22.6034 12.5663 22.6296 12.5213 22.6296C11.3788 22.6334 10.2362 22.6297 9.09365 22.6334C9.01498 22.6334 9 22.6034 9 22.536C9 21.4009 9 20.2621 9.00375 19.1271C9.00375 19.0746 9.02997 19.0109 9.06368 18.9772C10.4123 17.6249 11.7609 16.2763 13.1095 14.9277C14.2295 13.8076 15.3459 12.6913 16.466 11.5712C16.4884 11.5487 16.4997 11.5187 16.5147 11.5Z"
|
||||
fill="white"
|
||||
/>
|
||||
<path
|
||||
d="M20.9499 14.2904C19.7436 13.0842 18.5449 11.8854 17.3499 10.6904C17.5634 10.4694 17.7844 10.2446 18.0054 10.0199C18.2639 9.76139 18.5261 9.50291 18.7884 9.24443C19.118 8.91852 19.5713 8.91852 19.8972 9.24443C20.7251 10.0611 21.5492 10.8815 22.3771 11.6981C22.6993 12.0165 22.7105 12.4698 22.3996 12.792C21.9238 13.2865 21.4443 13.7772 20.9686 14.2717C20.9648 14.2792 20.9536 14.2867 20.9499 14.2904Z"
|
||||
fill="white"
|
||||
/>
|
||||
</svg>
|
||||
</div>
|
||||
<path
|
||||
d="M16.5147 11.5C17.7284 12.7137 18.9234 13.9087 20.1296 15.115C19.9798 15.2611 19.8187 15.4109 19.6651 15.5683C17.4699 17.7635 15.271 19.9587 13.0758 22.1539C12.9334 22.2962 12.7948 22.4386 12.6524 22.5735C12.6187 22.6034 12.5663 22.6296 12.5213 22.6296C11.3788 22.6334 10.2362 22.6297 9.09365 22.6334C9.01498 22.6334 9 22.6034 9 22.536C9 21.4009 9 20.2621 9.00375 19.1271C9.00375 19.0746 9.02997 19.0109 9.06368 18.9772C10.4123 17.6249 11.7609 16.2763 13.1095 14.9277C14.2295 13.8076 15.3459 12.6913 16.466 11.5712C16.4884 11.5487 16.4997 11.5187 16.5147 11.5Z"
|
||||
fill="white"
|
||||
/>
|
||||
<path
|
||||
d="M20.9499 14.2904C19.7436 13.0842 18.5449 11.8854 17.3499 10.6904C17.5634 10.4694 17.7844 10.2446 18.0054 10.0199C18.2639 9.76139 18.5261 9.50291 18.7884 9.24443C19.118 8.91852 19.5713 8.91852 19.8972 9.24443C20.7251 10.0611 21.5492 10.8815 22.3771 11.6981C22.6993 12.0165 22.7105 12.4698 22.3996 12.792C21.9238 13.2865 21.4443 13.7772 20.9686 14.2717C20.9648 14.2792 20.9536 14.2867 20.9499 14.2904Z"
|
||||
fill="white"
|
||||
/>
|
||||
</svg>
|
||||
</div>
|
||||
</div>
|
||||
{uploadStatus.message && !uploadStatus.loading && <p className={`text-center ${uploadStatus.status ? 'text-green-500':'text-red-500'}`}>{uploadStatus.message}</p>}
|
||||
{uploadStatus.loading && <p className="text-center">{uploadStatus.message}</p>}
|
||||
</div>
|
||||
{uploadStatus.message && !uploadStatus.loading && (
|
||||
<p
|
||||
className={`text-center ${
|
||||
uploadStatus.status
|
||||
? "text-green-500"
|
||||
: "text-red-500"
|
||||
}`}
|
||||
>
|
||||
{uploadStatus.message}
|
||||
</p>
|
||||
)}
|
||||
{uploadStatus.loading && (
|
||||
<p className="text-center">{uploadStatus.message}</p>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
{/* } */}
|
||||
</div>
|
||||
|
||||
|
||||
+115
-194
@@ -6,8 +6,6 @@ import React, {
|
||||
useState,
|
||||
} from "react";
|
||||
import { useSelector } from "react-redux";
|
||||
import cover from "../../assets/images/profile-info-cover.png";
|
||||
import profile from "../../assets/images/profile.jpg";
|
||||
import usersService from "../../services/UsersService";
|
||||
import Icons from "../Helpers/Icons";
|
||||
import Layout from "../Partials/Layout";
|
||||
@@ -26,161 +24,8 @@ import RecipientAccountTab from "./Tabs/RecipientAccountTab";
|
||||
export default function Settings({ faq }) {
|
||||
const apiCall = new usersService();
|
||||
const { userDetails } = useSelector((state) => state?.userDetails);
|
||||
const [profileImg, setProfileImg] = useState(
|
||||
userDetails?.profile_pic_url ? userDetails.profile_pic_url : profile
|
||||
);
|
||||
let [uploadStatus, setUploadStatus] = useState({loading: false, status: false, message:''}) // HOLDS STATE FOR UPLOAD PROFILE PICTURE STATUS
|
||||
const [coverImg, setCoverImg] = useState(cover);
|
||||
const [reloadCardList, setReloadCardList] = useState(false); // STATE TO DETERMINE WHEN CARD LIST RELOADS. EG: WHEN USER DELETES A CARD
|
||||
|
||||
// profile img
|
||||
const profileImgInput = useRef(null);
|
||||
const browseProfileImg = () => {
|
||||
profileImgInput.current.click();
|
||||
};
|
||||
const profileImgChangHandler = (e) => {
|
||||
// if (e.target.value !== "") {
|
||||
// const imgReader = new FileReader();
|
||||
// imgReader.onload = (event) => {
|
||||
// setProfileImg(event.target.result);
|
||||
// };
|
||||
// imgReader.readAsDataURL(e.target.files[0]);
|
||||
// }
|
||||
setUploadStatus({loading: false, status: false, message:''})
|
||||
let acceptedFormat = ["jpeg", "jpg", "png", "bmp", "gif"] // ARRAY OF SUPPORTED FORMATS
|
||||
let uploadedFile = e.target.files[0] //UPLOADED FILE
|
||||
|
||||
if(!acceptedFormat.includes(uploadedFile?.type?.split("/")[1]?.toLowerCase())){ //CHECKING FOR CORRECT UPLOAD FORMAT
|
||||
let msg = 'Please select '
|
||||
for(let i=0; i<=acceptedFormat.length-1; i++){
|
||||
if(i == acceptedFormat.length-1){
|
||||
msg+=`or ${acceptedFormat[i]}`
|
||||
}else{
|
||||
msg+=`${acceptedFormat[i]}, `
|
||||
}
|
||||
}
|
||||
setUploadStatus({loading: false, status: false, message:msg})
|
||||
return setTimeout(()=>{
|
||||
profileImgInput.current.value = '' // clear the input
|
||||
setUploadStatus({loading: false, status: false, message:''})
|
||||
},5000)
|
||||
}
|
||||
|
||||
if(uploadedFile.size > 5*1048576){ // CHECKING FOR CORRECT FILE SIZE
|
||||
setUploadStatus({loading: false, status: false, message:'File must not exceed 5MB'})
|
||||
return setTimeout(()=>{
|
||||
profileImgInput.current.value = '' // clear the input
|
||||
setUploadStatus({loading: false, status: false, message:''})
|
||||
},5000)
|
||||
}
|
||||
|
||||
if (e.target.value !== "") {
|
||||
const imgReader = new FileReader();
|
||||
imgReader.onload = (event) => {
|
||||
let reqData = { // PAYLOAD FOR API CALL
|
||||
file_name: uploadedFile?.name,
|
||||
file_size: uploadedFile?.size,
|
||||
file_type: uploadedFile?.type?.split("/")[0]?.toLowerCase(),
|
||||
file_data: event?.target?.result,
|
||||
msg_type: 'FILE',
|
||||
action: 'WRENCHBOARD_PICTURE_PROFILE',
|
||||
// action: 11307
|
||||
}
|
||||
setUploadStatus({loading: true, status: false, message:'Loading...'})
|
||||
apiCall.sendFiles(reqData).then(res=>{
|
||||
if(res.status != 200 || res.data.internal_return < 0){
|
||||
return setUploadStatus({loading: false, status: false, message: 'Something went wrong, try again'})
|
||||
}
|
||||
setUploadStatus({loading: false, status: true, message: 'Uploaded successfully'})
|
||||
setProfileImg(event.target.result);
|
||||
}).catch(error=>{
|
||||
setUploadStatus({loading: false, status: false, message: 'Network error, try again'})
|
||||
}).finally(()=>{
|
||||
setTimeout(()=>{
|
||||
setUploadStatus({loading: false, status: false, message: ''})
|
||||
},5000)
|
||||
})
|
||||
};
|
||||
imgReader.readAsDataURL(e.target.files[0]);
|
||||
}
|
||||
};
|
||||
// cover img
|
||||
const coverImgInput = useRef(null);
|
||||
const browseCoverImg = () => {
|
||||
coverImgInput.current.click();
|
||||
};
|
||||
const coverImgChangHandler = (e) => {
|
||||
if (e.target.value !== "") {
|
||||
const imgReader = new FileReader();
|
||||
imgReader.onload = (event) => {
|
||||
setCoverImg(event.target.result);
|
||||
};
|
||||
imgReader.readAsDataURL(e.target.files[0]);
|
||||
}
|
||||
};
|
||||
|
||||
// Tabs Handling
|
||||
const tabs = [
|
||||
{
|
||||
id: 1,
|
||||
name: "personal",
|
||||
title: "Edit Profile",
|
||||
iconName: "people-hover",
|
||||
},
|
||||
{
|
||||
id: 2,
|
||||
name: "payment",
|
||||
title: "Payment Cards",
|
||||
iconName: "bank-card",
|
||||
},
|
||||
{
|
||||
id: 3,
|
||||
name: "recipients-account",
|
||||
title: "Recipients Account",
|
||||
iconName: "bank-card",
|
||||
},
|
||||
{
|
||||
id: 4,
|
||||
name: "notification",
|
||||
title: "Notification Setting",
|
||||
iconName: "notification-setting",
|
||||
},
|
||||
{
|
||||
id: 5,
|
||||
name: "login_activity",
|
||||
title: "Login Activity",
|
||||
iconName: "login-activity",
|
||||
},
|
||||
{
|
||||
id: 6,
|
||||
name: "password",
|
||||
title: "Change Password",
|
||||
iconName: "password-hover",
|
||||
},
|
||||
{
|
||||
id: 7,
|
||||
name: "faq",
|
||||
title: "FAQ",
|
||||
iconName: "block-question",
|
||||
},
|
||||
{
|
||||
id: 8,
|
||||
name: "privacy",
|
||||
title: "Privacy Policy",
|
||||
iconName: "page-right",
|
||||
},
|
||||
{
|
||||
id: 9,
|
||||
name: "terms",
|
||||
title: "Terms and Conditions",
|
||||
iconName: "page-right",
|
||||
},
|
||||
];
|
||||
const [tab, setTab] = useState(tabs[0].name);
|
||||
const tabHandler = (value) => {
|
||||
setTab(value);
|
||||
};
|
||||
|
||||
// Recipient Account
|
||||
const [recipientAccount, setRecipientAccount] = useState({
|
||||
state: false,
|
||||
@@ -223,6 +68,120 @@ export default function Settings({ faq }) {
|
||||
getRecipientAccount();
|
||||
}, [reloadCardList]);
|
||||
|
||||
// Tabs Handling
|
||||
const tabs = [
|
||||
{
|
||||
id: 1,
|
||||
name: "personal",
|
||||
title: "Edit Profile",
|
||||
iconName: "people-hover",
|
||||
},
|
||||
{
|
||||
id: 2,
|
||||
name: "payment",
|
||||
title: "Payment Cards",
|
||||
iconName: "bank-card",
|
||||
},
|
||||
{
|
||||
id: 3,
|
||||
name: "recipients_account",
|
||||
title: "Recipients Account",
|
||||
iconName: "bank-card",
|
||||
},
|
||||
{
|
||||
id: 4,
|
||||
name: "notification",
|
||||
title: "Notification Setting",
|
||||
iconName: "notification-setting",
|
||||
},
|
||||
{
|
||||
id: 5,
|
||||
name: "login_activity",
|
||||
title: "Login Activity",
|
||||
iconName: "login-activity",
|
||||
},
|
||||
{
|
||||
id: 6,
|
||||
name: "password",
|
||||
title: "Change Password",
|
||||
iconName: "password-hover",
|
||||
},
|
||||
{
|
||||
id: 7,
|
||||
name: "faq",
|
||||
title: "FAQ",
|
||||
iconName: "block-question",
|
||||
},
|
||||
{
|
||||
id: 8,
|
||||
name: "privacy",
|
||||
title: "Privacy Policy",
|
||||
iconName: "page-right",
|
||||
},
|
||||
{
|
||||
id: 9,
|
||||
name: "terms",
|
||||
title: "Terms and Conditions",
|
||||
iconName: "page-right",
|
||||
},
|
||||
];
|
||||
|
||||
const [tab, setTab] = useState(() => {
|
||||
// Retrieve the active tab from local storage or use the default tab
|
||||
return localStorage.getItem("activeTab") || tabs[0].name;
|
||||
});
|
||||
|
||||
const tabHandler = (value) => {
|
||||
setTab(value);
|
||||
};
|
||||
|
||||
// Update local storage when the tab changes
|
||||
useEffect(() => {
|
||||
localStorage.setItem("activeTab", tab);
|
||||
}, [tab]);
|
||||
|
||||
const tabComponents = {
|
||||
personal: (
|
||||
<div className="tab-item">
|
||||
<PersonalInfoTab />
|
||||
</div>
|
||||
),
|
||||
payment: (
|
||||
<div className="tab-item">
|
||||
<PaymentMathodsTab />
|
||||
</div>
|
||||
),
|
||||
recipients_account: (
|
||||
<div className="tab-item">
|
||||
<RecipientAccountTab
|
||||
recipientAccount={recipientAccount}
|
||||
setRecipientAccount={setRecipientAccount}
|
||||
setReloadCardList={setReloadCardList}
|
||||
/>
|
||||
</div>
|
||||
),
|
||||
notification: (
|
||||
<div className="tab-item">
|
||||
<NotificationSettingTab />
|
||||
</div>
|
||||
),
|
||||
login_activity: <LoginActivityTab />,
|
||||
password: <ChangePasswordTab />,
|
||||
faq: <FaqTab datas={faq} />,
|
||||
privacy: <PrivacyPolicyTab />,
|
||||
terms: <TermsConditionTab />,
|
||||
};
|
||||
|
||||
// Default tab component
|
||||
const defaultTabComponent = (
|
||||
<div className="tab-item">
|
||||
<PersonalInfoTab />
|
||||
</div>
|
||||
);
|
||||
|
||||
// Selected tab component based on the current 'tab'
|
||||
const selectedTabComponent = tabComponents[tab] || defaultTabComponent;
|
||||
|
||||
return (
|
||||
<>
|
||||
<Layout>
|
||||
@@ -263,45 +222,7 @@ export default function Settings({ faq }) {
|
||||
</div>
|
||||
<div className="w-[1px] bg-[#E3E4FE] dark:bg-[#a7a9b533] mr-10"></div>
|
||||
<div className="content-body-items flex-1">
|
||||
{tab === "personal" && (
|
||||
<div className="tab-item">
|
||||
<PersonalInfoTab
|
||||
profileImg={profileImg}
|
||||
coverImg={coverImg}
|
||||
browseProfileImg={browseProfileImg}
|
||||
profileImgInput={profileImgInput}
|
||||
profileImgChangHandler={profileImgChangHandler}
|
||||
coverImgChangHandler={coverImgChangHandler}
|
||||
browseCoverImg={browseCoverImg}
|
||||
coverImgInput={coverImgInput}
|
||||
uploadStatus={uploadStatus}
|
||||
/>
|
||||
</div>
|
||||
)}
|
||||
{tab === "payment" && (
|
||||
<div className="tab-item">
|
||||
<PaymentMathodsTab />
|
||||
</div>
|
||||
)}
|
||||
{tab === "recipients-account" && (
|
||||
<div className="tab-item">
|
||||
<RecipientAccountTab
|
||||
recipientAccount={recipientAccount}
|
||||
setRecipientAccount={setRecipientAccount}
|
||||
setReloadCardList={setReloadCardList}
|
||||
/>
|
||||
</div>
|
||||
)}
|
||||
{tab === "notification" && (
|
||||
<div className="tab-item">
|
||||
<NotificationSettingTab />
|
||||
</div>
|
||||
)}
|
||||
{tab === "login_activity" && <LoginActivityTab />}
|
||||
{tab === "password" && <ChangePasswordTab />}
|
||||
{tab === "faq" && <FaqTab datas={faq} />}
|
||||
{tab === "privacy" && <PrivacyPolicyTab />}
|
||||
{tab === "terms" && <TermsConditionTab />}
|
||||
{selectedTabComponent}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -157,7 +157,7 @@ export default function ProductUploadField({
|
||||
fieldClass="px-6"
|
||||
type="text"
|
||||
name="name"
|
||||
placeholder="RaidParty Fighters"
|
||||
placeholder="Item Name"
|
||||
inputHandler={inh}
|
||||
value={datas.itemName}
|
||||
/>
|
||||
@@ -186,7 +186,7 @@ export default function ProductUploadField({
|
||||
<textarea
|
||||
value={datas.description}
|
||||
onChange={(e) => dscrphn(e)}
|
||||
placeholder="provide a detailed description of your item."
|
||||
placeholder="Enter detail description"
|
||||
rows="7"
|
||||
className="w-full h-full px-7 py-4 border border-light-purple dark:border-[#5356fb29] rounded-[20px] text-dark-gray dark:text-white bg-[#FAFAFA] dark:bg-[#11131F] focus:ring-0 focus:outline-none"
|
||||
/>
|
||||
|
||||
@@ -144,14 +144,14 @@ export default function UploadProduct({uploadTypes}) {
|
||||
}
|
||||
|
||||
let reqData = { // PAYLOAD FOR API CALL
|
||||
file_name: selectedFile.substring(0,21).replace(/ /gi, ""),//selectedFile.replace(/[ -]/gi, ""),
|
||||
// file_name: selectedFile.substring(0,21).replace(/ /gi, ""),
|
||||
file_name: `myfile.${imgDetails?.type?.split('/')[1]}`,
|
||||
file_size: imgDetails.size,
|
||||
file_type: imgDetails.type,
|
||||
file_data: img.file,
|
||||
file_data: img.file?.split(",")[1],
|
||||
title: itemName,
|
||||
description: description,
|
||||
msg_type: 'FILE',
|
||||
// action: 'WRENCHBOARD_RESOURCE_MYFILES',
|
||||
action: 11307
|
||||
}
|
||||
|
||||
@@ -602,7 +602,10 @@ export default function UploadProduct({uploadTypes}) {
|
||||
|
||||
//FUNCTIONS to check if file upload type is valid
|
||||
const isValidFile = (file, supportedFile=[]) => {
|
||||
let fileType = file.type.split("/")[1];
|
||||
let fileType = file.type.split("/")[1].toLowerCase();
|
||||
if(fileType=='jpg' || fileType=='jpeg'){ //forcing both JPG and JPEG TO RETURN JPG AS FILE TYPE
|
||||
fileType = 'jpg'
|
||||
}
|
||||
let valid = supportedFile.filter(item => (
|
||||
item.name.toLowerCase() == fileType.toLowerCase()
|
||||
))
|
||||
@@ -615,7 +618,10 @@ const isValidFile = (file, supportedFile=[]) => {
|
||||
|
||||
//FUNCTIONS TO CHECK IF FILE SIZE IS VALID
|
||||
const isValidFileSize = (file, supportedFile=[]) => {
|
||||
let fileType = file.type.split("/")[1];
|
||||
let fileType = file.type.split("/")[1].toLowerCase();
|
||||
if(fileType=='jpg' || fileType=='jpeg'){ //forcing both JPG and JPEG TO RETURN JPG AS FILE TYPE
|
||||
fileType = 'jpg'
|
||||
}
|
||||
let fileSize = file.size;
|
||||
let valid = supportedFile.filter(item => (
|
||||
item.name.toLowerCase() == fileType.toLowerCase()
|
||||
@@ -627,6 +633,6 @@ const isValidFileSize = (file, supportedFile=[]) => {
|
||||
return {status: false, message: `File must not exceed ${valid[0].max_size_mb}MB`}
|
||||
}
|
||||
}else{
|
||||
return false
|
||||
return {status: false, message: `Cannot read file size, try again`}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { Field, Form, Formik } from "formik";
|
||||
import React, { useCallback, useMemo, useState } from "react";
|
||||
import { useDispatch } from "react-redux";
|
||||
import { useDispatch, useSelector } from "react-redux";
|
||||
import { useNavigate } from "react-router-dom";
|
||||
import * as Yup from "yup";
|
||||
import usersService from "../../services/UsersService";
|
||||
@@ -51,6 +51,20 @@ const EditJobPopOut = ({
|
||||
categories,
|
||||
}) => {
|
||||
const dispatch = useDispatch();
|
||||
const { userDetails } = useSelector((state) => state.userDetails);
|
||||
const { walletDetails } = useSelector((state) => state.walletDetails);
|
||||
|
||||
const uploadedImage = `${
|
||||
userDetails.session_image_server
|
||||
}${localStorage.getItem("session_token")}/job/${details?.job_uid}`;
|
||||
|
||||
const [taskImage, setTaskImage] = useState(uploadedImage);
|
||||
|
||||
let [uploadStatus, setUploadStatus] = useState({
|
||||
loading: false,
|
||||
status: false,
|
||||
message: "",
|
||||
}); // HOLDS STATE FOR UPLOAD PROFILE PICTURE STATUS
|
||||
|
||||
let [requestStatus, setRequestStatus] = useState({
|
||||
loading: false,
|
||||
@@ -105,6 +119,101 @@ const EditJobPopOut = ({
|
||||
[jobApi, navigate, onClose, details]
|
||||
);
|
||||
|
||||
const taskImgChangeHandler = (e) => {
|
||||
setUploadStatus({ loading: false, status: false, message: "" });
|
||||
let acceptedFormat = ["jpeg", "jpg", "png", "bmp", "gif"]; // ARRAY OF SUPPORTED FORMATS
|
||||
let uploadedFile = e.target.files[0]; //UPLOADED FILE
|
||||
|
||||
const fileFormat = uploadedFile?.type?.split("/")[1]?.toLowerCase();
|
||||
if (!acceptedFormat.includes(fileFormat)) {
|
||||
//CHECKING FOR CORRECT UPLOAD FORMAT
|
||||
const msg = `Please select ${acceptedFormat
|
||||
.slice(0, -1)
|
||||
.join(", ")} or ${acceptedFormat.slice(-1)}`;
|
||||
setUploadStatus({ loading: false, status: false, message: msg });
|
||||
return setTimeout(() => {
|
||||
// profileImgInput.current.value = '' // clear the input
|
||||
setUploadStatus({ loading: false, status: false, message: "" });
|
||||
}, 5000);
|
||||
}
|
||||
|
||||
if (uploadedFile.size > 5 * 1048576) {
|
||||
// CHECKING FOR CORRECT FILE SIZE
|
||||
setUploadStatus({
|
||||
loading: false,
|
||||
status: false,
|
||||
message: "File must not exceed 5MB",
|
||||
});
|
||||
return setTimeout(() => {
|
||||
// profileImgInput.current.value = '' // clear the input
|
||||
setUploadStatus({ loading: false, status: false, message: "" });
|
||||
}, 5000);
|
||||
}
|
||||
|
||||
if (e.target.value !== "") {
|
||||
const imgReader = new FileReader();
|
||||
imgReader.onload = (event) => {
|
||||
let base64Img = imgReader.result.split(",")[1];
|
||||
let reqData = {
|
||||
// PAYLOAD FOR API CALL
|
||||
job_uid: details?.job_uid,
|
||||
file_name: uploadedFile?.name.slice(0, 19),
|
||||
file_size: uploadedFile?.size,
|
||||
file_type: uploadedFile?.type?.split("/")[0]?.toLowerCase(),
|
||||
file_data: base64Img,
|
||||
msg_type: "FILE",
|
||||
action: 11303,
|
||||
};
|
||||
setUploadStatus({
|
||||
loading: true,
|
||||
status: false,
|
||||
message: "Loading...",
|
||||
});
|
||||
jobApi
|
||||
.sendFiles(reqData)
|
||||
.then((res) => {
|
||||
if (res.status != 200 || res.data.internal_return < 0) {
|
||||
return setUploadStatus({
|
||||
loading: false,
|
||||
status: false,
|
||||
message: "Something went wrong, try again",
|
||||
});
|
||||
}
|
||||
setUploadStatus({
|
||||
loading: false,
|
||||
status: true,
|
||||
message: "Uploaded successfully",
|
||||
});
|
||||
setTaskImage(event.target.result);
|
||||
setTimeout(() => {
|
||||
dispatch(tableReload({ type: "JOBTABLE" }));
|
||||
navigate("/myjobs", { replace: true });
|
||||
onClose();
|
||||
}, 1000);
|
||||
})
|
||||
.catch((error) => {
|
||||
setUploadStatus({
|
||||
loading: false,
|
||||
status: false,
|
||||
message: "Network error, try again",
|
||||
});
|
||||
})
|
||||
.finally(() => {
|
||||
setTimeout(() => {
|
||||
setUploadStatus({ loading: false, status: false, message: "" });
|
||||
}, 5000);
|
||||
});
|
||||
};
|
||||
imgReader.readAsDataURL(e.target.files[0]);
|
||||
}
|
||||
};
|
||||
|
||||
// Check if the user is using iOS
|
||||
const isIOS = /MacIntel|MacPPC/.test(navigator.platform) && !window.MSStream;
|
||||
|
||||
// Check if the user is using Windows
|
||||
const isWindows = /Windows/.test(navigator.userAgent);
|
||||
|
||||
return (
|
||||
<ModalCom action={onClose} situation={situation} className="edit-popup">
|
||||
<div className="logout-modal-wrapper lg:w-[600px] h-full lg:h-auto bg-white dark:bg-dark-white lg:rounded-2xl">
|
||||
@@ -138,7 +247,7 @@ const EditJobPopOut = ({
|
||||
</svg>
|
||||
</button>
|
||||
</div>
|
||||
<div className="logout-modal-body w-full flex flex-col items-center px-10 py-8">
|
||||
<div className="logout-modal-body w-full flex flex-col items-center px-10 pb-8 pt-2">
|
||||
<Formik
|
||||
initialValues={initialValues}
|
||||
validationSchema={validationSchema}
|
||||
@@ -258,19 +367,20 @@ const EditJobPopOut = ({
|
||||
role="group"
|
||||
aria-labelledby="checked-group"
|
||||
>
|
||||
{Object.entries(categories).map(([key, value]) => (
|
||||
<label
|
||||
key={key}
|
||||
className="flex gap-1 w-full items-center"
|
||||
>
|
||||
<Field
|
||||
type="checkbox"
|
||||
name="category"
|
||||
value={key}
|
||||
/>
|
||||
<span className="text-[13.975px]">{value}</span>
|
||||
</label>
|
||||
))}
|
||||
{categories &&
|
||||
Object.entries(categories)?.map(([key, value]) => (
|
||||
<label
|
||||
key={key}
|
||||
className="flex gap-1 w-full items-center"
|
||||
>
|
||||
<Field
|
||||
type="checkbox"
|
||||
name="category"
|
||||
value={key}
|
||||
/>
|
||||
<span className="text-[13.975px]">{value}</span>
|
||||
</label>
|
||||
))}
|
||||
<span className="h-5 text-sm italic text-[#cf3917]">
|
||||
{props.errors.category &&
|
||||
props.touched.category &&
|
||||
@@ -279,43 +389,77 @@ const EditJobPopOut = ({
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="field w-full mb-6">
|
||||
<div
|
||||
className={`flex items-center justify-between mb-2.5`}
|
||||
>
|
||||
<label
|
||||
className="input-label text-[#181c32] dark:text-white text-[13.975px] leading-[20.9625px] font-semibold block"
|
||||
htmlFor="timeline_days"
|
||||
>
|
||||
Timeline
|
||||
<span className="text-green-700 text-sm tracking-wide">
|
||||
- Expected duration of this task
|
||||
</span>
|
||||
</label>
|
||||
</div>
|
||||
|
||||
<Field
|
||||
component="select"
|
||||
name="timeline_days"
|
||||
className={`input-field p-2 mt-3 rounded-md placeholder:text-base text-dark-gray dark:text-white w-full h-10 bg-slate-100 dark:bg-[#11131F] focus:ring-0 focus:outline-none ${
|
||||
props.errors.timeline_days &&
|
||||
props.touched.timeline_days
|
||||
? "border-[#ff0a0a63] shadow-red-500 border-[0.5px] animate-shake"
|
||||
: "border border-[#f5f8fa] dark:border-[#5e6278]"
|
||||
}`}
|
||||
value={props.values.timeline_days}
|
||||
>
|
||||
<option value="">Select Duration</option>
|
||||
{publicArray.map(({ name, duration }, idx) => (
|
||||
<option
|
||||
className="text-slate-500 text-lg"
|
||||
value={duration}
|
||||
<div className="w-full flex items-center gap-2 mb-2">
|
||||
{/* FOR TASK IMAGE */}
|
||||
<div className="w-1/2 relative max-h-[130px] min-h-[130px]">
|
||||
<input
|
||||
id="task_image"
|
||||
className="hidden"
|
||||
type="file"
|
||||
accept="image/*"
|
||||
onChange={taskImgChangeHandler}
|
||||
/>
|
||||
{taskImage ? (
|
||||
<div className="w-full absolute -top-5">
|
||||
<img
|
||||
src={taskImage}
|
||||
className="max-h-[150px] min-h-[150px] w-full object-cover"
|
||||
alt="uploaded task"
|
||||
/>
|
||||
<span
|
||||
onClick={() => setTaskImage("")}
|
||||
className="p-2 absolute text-sm top-1/2 left-1/2 -translate-x-1/2 -translate-y-1/2 bg-white/80 hover:bg-white hover:shadow-md transition-all duration-500 cursor-pointer text-slate-800"
|
||||
>
|
||||
Remove Image
|
||||
</span>
|
||||
</div>
|
||||
) : (
|
||||
<label
|
||||
className="absolute -top-5 h-[150px] w-full flex flex-col justify-center items-center bg-slate-100 dark:bg-[#11131F] cursor-pointer input-label text-[#181c32] dark:text-white text-[13.975px] leading-[20.9625px] font-semibold"
|
||||
htmlFor="task_image"
|
||||
>
|
||||
{name}
|
||||
</option>
|
||||
))}
|
||||
</Field>
|
||||
Select Task Image
|
||||
</label>
|
||||
)}
|
||||
</div>
|
||||
{/* END OF TASK IMAGE */}
|
||||
|
||||
<div className="field w-1/2">
|
||||
<div className={`flex items-center justify-between`}>
|
||||
<label
|
||||
className="w-full input-label text-[#181c32] dark:text-white text-[13.975px] leading-[20.9625px] font-semibold flex flex-col"
|
||||
htmlFor="timeline_days"
|
||||
>
|
||||
Timeline -
|
||||
<span className="w-full text-center text-green-700 text-sm tracking-wide">
|
||||
Expected duration of this task
|
||||
</span>
|
||||
</label>
|
||||
</div>
|
||||
|
||||
<Field
|
||||
component="select"
|
||||
name="timeline_days"
|
||||
className={`input-field p-2 mt-3 rounded-md placeholder:text-base text-dark-gray dark:text-white w-full h-10 bg-slate-100 dark:bg-[#11131F] focus:ring-0 focus:outline-none ${
|
||||
props.errors.timeline_days &&
|
||||
props.touched.timeline_days
|
||||
? "border-[#ff0a0a63] shadow-red-500 border-[0.5px] animate-shake"
|
||||
: "border border-[#f5f8fa] dark:border-[#5e6278]"
|
||||
}`}
|
||||
value={props.values.timeline_days}
|
||||
>
|
||||
<option value="">Select Duration</option>
|
||||
{publicArray.map(({ name, duration }, idx) => (
|
||||
<option
|
||||
key={duration}
|
||||
className="text-slate-500 text-lg"
|
||||
value={duration}
|
||||
>
|
||||
{name}
|
||||
</option>
|
||||
))}
|
||||
</Field>
|
||||
</div>
|
||||
</div>
|
||||
{/* inputs ends here */}
|
||||
</div>
|
||||
@@ -341,6 +485,24 @@ const EditJobPopOut = ({
|
||||
))}
|
||||
{/* End of error or success display */}
|
||||
|
||||
{/* DISPLAYS TASK IMAGE UPLOADING STATUS */}
|
||||
<div className="w-full">
|
||||
{uploadStatus.message && !uploadStatus.loading && (
|
||||
<p
|
||||
className={`text-center ${
|
||||
uploadStatus.status
|
||||
? "text-green-500"
|
||||
: "text-red-500"
|
||||
}`}
|
||||
>
|
||||
{uploadStatus.message}
|
||||
</p>
|
||||
)}
|
||||
{uploadStatus.loading && (
|
||||
<p className="text-center">{uploadStatus.message}</p>
|
||||
)}
|
||||
</div>
|
||||
|
||||
<div className="w-full border-t border-light-purple dark:border-[#5356fb29] flex justify-end items-center">
|
||||
<div className="flex items-center space-x-4 mr-2 mt-2">
|
||||
{requestStatus.loading ? (
|
||||
@@ -350,6 +512,9 @@ const EditJobPopOut = ({
|
||||
type="submit"
|
||||
className="w-[120px] h-[40px] flex justify-center items-center btn-gradient text-base rounded-full text-white"
|
||||
// className='w-20 h-11 flex justify-center items-center btn-gradient text-base rounded-full text-white'
|
||||
disabled={
|
||||
requestStatus.loading || uploadStatus.loading
|
||||
}
|
||||
>
|
||||
Save
|
||||
</button>
|
||||
|
||||
@@ -101,6 +101,8 @@ function FamilyOfferJobPopout({ details, onClose, situation }) {
|
||||
});
|
||||
};
|
||||
|
||||
console.log(details)
|
||||
|
||||
return (
|
||||
<ModalCom action={onClose} situation={situation}>
|
||||
<div className="logout-modal-wrapper lw-[90%] md:w-[768px] h-full lg:h-auto bg-white dark:bg-dark-white lg:rounded-2xl overflow-y-auto">
|
||||
@@ -138,7 +140,7 @@ function FamilyOfferJobPopout({ details, onClose, situation }) {
|
||||
<div className="p-4 w-full md:w-3/4 md:border-r-2">
|
||||
<div className="flex gap-2">
|
||||
<div className="image-wrapper w-32">
|
||||
<img className="w-full h-auto" src={localImgLoad(`images/taskbanners/${details.banner}`)} alt='Banner-Image' />
|
||||
<img className="w-full h-auto" src={details?.image} alt='banner' />
|
||||
</div>
|
||||
<div className="details-wrapper">
|
||||
<p className="text-lg my-5 font-semibold text-slate-900 tracking-wide">
|
||||
|
||||
@@ -1,11 +1,13 @@
|
||||
import { Field, Form, Formik } from "formik";
|
||||
import React, { useCallback, useEffect, useMemo, useState } from "react";
|
||||
import * as Yup from "yup";
|
||||
import SiteService from "../../services/SiteService";
|
||||
import usersService from "../../services/UsersService";
|
||||
import InputCom from "../Helpers/Inputs/InputCom/index";
|
||||
import ModalCom from "../Helpers/ModalCom";
|
||||
import LoadingSpinner from "../Spinners/LoadingSpinner";
|
||||
import Detail from "./popoutcomponent/Detail";
|
||||
import { tableReload } from "../../store/TableReloads";
|
||||
import { useDispatch } from "react-redux";
|
||||
|
||||
const validationSchema = Yup.object().shape({
|
||||
family: Yup.string().required("This is required "),
|
||||
@@ -21,6 +23,8 @@ const validationSchema = Yup.object().shape({
|
||||
});
|
||||
|
||||
function JobListPopout({ details, onClose, situation }) {
|
||||
const dispatch = useDispatch()
|
||||
|
||||
const [familyList, setFamilyList] = useState([]);
|
||||
let [loader, setLoader] = useState({
|
||||
member: false,
|
||||
@@ -32,7 +36,7 @@ function JobListPopout({ details, onClose, situation }) {
|
||||
},
|
||||
});
|
||||
|
||||
const apiCall = useMemo(() => new SiteService(), []);
|
||||
const apiCall = useMemo(() => new usersService(), []);
|
||||
|
||||
// member listing
|
||||
const memberList = useCallback(async () => {
|
||||
@@ -159,6 +163,7 @@ function JobListPopout({ details, onClose, situation }) {
|
||||
const res = await apiCall.assignJobTask(reqData);
|
||||
let { data } = await res;
|
||||
setLoader({ jobFields: false });
|
||||
dispatch(tableReload({ type: "JOBTABLE" }));
|
||||
onClose();
|
||||
throw new Response(data);
|
||||
} catch (error) {
|
||||
|
||||
@@ -133,9 +133,9 @@ function OfferJobPopout({ details, onClose, situation }) {
|
||||
</svg>
|
||||
</button>
|
||||
</div>
|
||||
<div className="md:flex bg-white rounded-lg shadow-lg">
|
||||
<div className="md:flex bg-white dark:bg-dark-white rounded-lg shadow-lg">
|
||||
<div className="p-4 w-full md:w-3/4 md:border-r-2">
|
||||
<p className="text-lg my-5 font-semibold text-slate-900 tracking-wide">
|
||||
<p className="text-lg my-5 font-semibold text-slate-900 dark:text-white tracking-wide">
|
||||
{details.title}
|
||||
</p>
|
||||
|
||||
|
||||
@@ -24,6 +24,7 @@ function PendingJobsPopout({ details, onClose, situation }) {
|
||||
const [pendingJobLoader, setPendingJobLoader] = useState({
|
||||
extend: false,
|
||||
offer: false,
|
||||
cancel: false,
|
||||
});
|
||||
|
||||
let [requestMessage, setRequestMessage] = useState({
|
||||
@@ -81,6 +82,29 @@ function PendingJobsPopout({ details, onClose, situation }) {
|
||||
setRequestMessage({ status: false, message: "" });
|
||||
}, 3000);
|
||||
});
|
||||
} else if (name == "cancel") {
|
||||
// RUNS THIS IF JOB OFFER IS CANCELLED
|
||||
reqData = { ...pendingData, reason: "cancel", offer_result: 3333 };
|
||||
setPendingJobLoader({ cancel: true });
|
||||
apiCall
|
||||
.pendingCancelOffer(reqData)
|
||||
.then((res) => {
|
||||
setRequestMessage({ status: true, message: res.data.status });
|
||||
setTimeout(() => {
|
||||
setPendingJobLoader({ cancel: false });
|
||||
setRequestMessage({ status: false, message: "" });
|
||||
onClose();
|
||||
dispatch(tableReload({ type: "PENDINGTABLE" }));
|
||||
dispatch(tableReload({ type: "JOBTABLE" }));
|
||||
}, 4000);
|
||||
})
|
||||
.catch((error) => {
|
||||
setRequestMessage("Try Again");
|
||||
setTimeout(() => {
|
||||
setPendingJobLoader({ cancel: false });
|
||||
setRequestMessage({ status: false, message: "" });
|
||||
}, 3000);
|
||||
});
|
||||
} else return;
|
||||
// try {
|
||||
// if (name === "extend") {
|
||||
@@ -274,9 +298,16 @@ function PendingJobsPopout({ details, onClose, situation }) {
|
||||
<div className="mt-10 md:mt-32 md:flex md:justify-center">
|
||||
<button
|
||||
className="px-2 h-11 flex justify-center items-center btn-gradient text-base rounded-full text-white"
|
||||
onClick={onClose}
|
||||
onClick={handlePendingJobsBtn}
|
||||
name="cancel"
|
||||
>
|
||||
Cancel Offer
|
||||
{pendingJobLoader.cancel ? (
|
||||
<div className="w-[96px] flex justify-center items-center h-full">
|
||||
<LoadingSpinner size={5} color="sky-blue" />
|
||||
</div>
|
||||
) : (
|
||||
"Cancel Offer"
|
||||
)}
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -304,6 +304,12 @@ input[type="text"][dir="rtl"] {
|
||||
transform: rotate(360deg);
|
||||
}
|
||||
}
|
||||
|
||||
.my-custom-bg-class{
|
||||
background: url("./assets/images/wrench-page-notfound.jpg") center/cover;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
/* TODO: =================================default end================================ */
|
||||
/* TODO: =================================update password================================ */
|
||||
.content-wrapper.thankyou-section {
|
||||
|
||||
@@ -11,6 +11,7 @@ import { updateNotifications } from "../store/notifications";
|
||||
import { updateUserJobList } from "../store/userJobList";
|
||||
import { updateWalletDetails } from "../store/walletDetails";
|
||||
import { formattedDate } from "../lib";
|
||||
import { tableReload } from "../store/TableReloads";
|
||||
|
||||
const AuthRoute = ({ redirectPath = "/login", children }) => {
|
||||
const apiCall = useMemo(() => new usersService(), []);
|
||||
@@ -20,10 +21,12 @@ const AuthRoute = ({ redirectPath = "/login", children }) => {
|
||||
const [loadProfileDetails, setLoadProfileDetails] = useState([]);
|
||||
const navigate = useNavigate();
|
||||
|
||||
const { jobListTable, walletTable } = useSelector((state) => state.tableReload);
|
||||
const { jobListTable, walletTable } = useSelector(
|
||||
(state) => state.tableReload
|
||||
);
|
||||
|
||||
const {
|
||||
userDetails: { username, uid, session },
|
||||
userDetails: { username, uid, session},
|
||||
} = useSelector((state) => state?.userDetails); // CHECKS IF USER Details are avaliable, to determine if user is active
|
||||
|
||||
let loggedIn = username && session && uid ? true : false; // variable to determine if user is logged in
|
||||
@@ -34,6 +37,7 @@ const AuthRoute = ({ redirectPath = "/login", children }) => {
|
||||
localStorage.removeItem("uid");
|
||||
localStorage.removeItem("member_id");
|
||||
localStorage.removeItem("session_token");
|
||||
sessionStorage.removeItem("family_uid");
|
||||
navigate("/login", { replace: true }); // redirects user to login page after session expires
|
||||
};
|
||||
|
||||
@@ -180,13 +184,15 @@ const AuthRoute = ({ redirectPath = "/login", children }) => {
|
||||
|
||||
useEffect(() => {
|
||||
const getMyWalletList = async () => {
|
||||
dispatch(updateWalletDetails({ loading: true, data:[] }));
|
||||
dispatch(updateWalletDetails({ loading: true, data: [] }));
|
||||
try {
|
||||
const res = await apiCall.getUserWallets();
|
||||
console.log("wallet - >", res.data);
|
||||
dispatch(updateWalletDetails({ loading: false, data:res.data?.result_list }));
|
||||
dispatch(
|
||||
updateWalletDetails({ loading: false, data: res.data?.result_list })
|
||||
);
|
||||
} catch (error) {
|
||||
dispatch(updateWalletDetails({ loading: false, data:[] }));
|
||||
dispatch(updateWalletDetails({ loading: false, data: [] }));
|
||||
console.log("Error getting mode");
|
||||
}
|
||||
};
|
||||
@@ -204,7 +210,7 @@ const AuthRoute = ({ redirectPath = "/login", children }) => {
|
||||
}
|
||||
};
|
||||
getMarketActiveJobList();
|
||||
}, [apiCall, dispatch]);
|
||||
}, [apiCall, dispatch, jobListTable]);
|
||||
|
||||
//FUNCTION TO GET COMMON HEAD DATA
|
||||
useEffect(() => {
|
||||
|
||||
@@ -19,6 +19,16 @@ class usersService {
|
||||
return this.postAuxEnd("/completesignuplink", reqData);
|
||||
}
|
||||
|
||||
assignJobTask(reqData) {
|
||||
var postData = {
|
||||
uid: localStorage.getItem("uid"),
|
||||
member_id: localStorage.getItem("member_id"),
|
||||
sessionid: localStorage.getItem("session_token"),
|
||||
...reqData,
|
||||
};
|
||||
return this.postAuxEnd("/assigntask", postData);
|
||||
}
|
||||
|
||||
// FUNCTION TO GET USER CURRENT TASK DUE TIME
|
||||
getHomeDate() {
|
||||
var postData = {
|
||||
@@ -30,12 +40,12 @@ class usersService {
|
||||
return this.postAuxEnd("/dashdata", postData);
|
||||
}
|
||||
|
||||
getRecentActivities(){
|
||||
getRecentActivities() {
|
||||
var postData = {
|
||||
uid: localStorage.getItem("uid"),
|
||||
member_id: localStorage.getItem("member_id"),
|
||||
sessionid: localStorage.getItem("session_token"),
|
||||
action: 11202
|
||||
action: 11202,
|
||||
};
|
||||
return this.postAuxEnd("/recentactivities", postData);
|
||||
}
|
||||
@@ -368,7 +378,7 @@ class usersService {
|
||||
page: 0,
|
||||
offset: 0,
|
||||
limit: 100,
|
||||
allstatus: 0
|
||||
allstatus: 0,
|
||||
};
|
||||
return this.postAuxEnd("/activetaskslist", postData);
|
||||
}
|
||||
@@ -496,6 +506,19 @@ class usersService {
|
||||
return this.postAuxEnd("/purchasehx", postData);
|
||||
}
|
||||
|
||||
// API FUNCTION TO GET FAMILY REWARD HISTORY
|
||||
getFamilyRewardHx() {
|
||||
var postData = {
|
||||
uid: localStorage.getItem("uid"),
|
||||
member_id: localStorage.getItem("member_id"),
|
||||
sessionid: localStorage.getItem("session_token"),
|
||||
offset: 1,
|
||||
limit: 20,
|
||||
action: 22011,
|
||||
};
|
||||
return this.postAuxEnd("/familyrewardhx", postData);
|
||||
}
|
||||
|
||||
// API FUNCTION TO GET PAYMENT HISTORY
|
||||
getPaymentHx() {
|
||||
var postData = {
|
||||
@@ -578,13 +601,14 @@ class usersService {
|
||||
return this.postAuxEnd("/familyadd", postData);
|
||||
}
|
||||
|
||||
getFamilyUpdate() {
|
||||
getFamilyUpdate(reqdata) {
|
||||
var postData = {
|
||||
uuid: localStorage.getItem("uid"),
|
||||
member_id: localStorage.getItem("member_id"),
|
||||
sessionid: localStorage.getItem("session_token"),
|
||||
page: 0,
|
||||
limit: 100,
|
||||
...reqdata,
|
||||
};
|
||||
return this.postAuxEnd("/familyupdate", postData);
|
||||
}
|
||||
@@ -768,6 +792,17 @@ class usersService {
|
||||
return this.postAuxEnd("/pendingjobsendtome", postData);
|
||||
}
|
||||
|
||||
pendingCancelOffer(reqData) {
|
||||
var postData = {
|
||||
uid: localStorage.getItem("uid"),
|
||||
member_id: localStorage.getItem("member_id"),
|
||||
sessionid: localStorage.getItem("session_token"),
|
||||
action: 13043,
|
||||
...reqData,
|
||||
};
|
||||
return this.postAuxEnd("/pendingjobcancel", postData);
|
||||
}
|
||||
|
||||
// FUNCTION TO GET ACTIVE JOB MESSAGE LIST
|
||||
activeJobMesList(reqData) {
|
||||
var postData = {
|
||||
@@ -803,11 +838,11 @@ class usersService {
|
||||
action: 14010,
|
||||
...reqData,
|
||||
};
|
||||
|
||||
const formData = new FormData();
|
||||
for (let data in postData) {
|
||||
formData.append(data, postData[data]);
|
||||
}
|
||||
// return this.postAuxEnd("/uploads", formData);
|
||||
|
||||
return this.postAuxEnd("/uploads", postData);
|
||||
}
|
||||
@@ -1064,18 +1099,51 @@ class usersService {
|
||||
return this.postAuxEnd("/blogdata", postData);
|
||||
}
|
||||
|
||||
// FUNCTION TO CANCEL TASK OR SEND REMINDER BY FAMILY MEMBER
|
||||
suggestStatus(reqData) {
|
||||
var postData = {
|
||||
uid: localStorage.getItem("uid"),
|
||||
member_id: localStorage.getItem("member_id"),
|
||||
sessionid: localStorage.getItem("session_token"),
|
||||
action: 22026,
|
||||
...reqData,
|
||||
};
|
||||
return this.postAuxEnd("/suggeststatus", postData);
|
||||
}
|
||||
|
||||
// FUNCTION TO CANCEL TASK OR SEND REMINDER BY FAMILY MEMBER
|
||||
suggestStatus(reqData) {
|
||||
var postData = {
|
||||
uid: localStorage.getItem("uid"),
|
||||
member_id: localStorage.getItem("member_id"),
|
||||
sessionid: localStorage.getItem("session_token"),
|
||||
action: 22026,
|
||||
...reqData,
|
||||
};
|
||||
return this.postAuxEnd("/suggeststatus", postData);
|
||||
}
|
||||
// FUNCTION TO GET FAMILY WALLET
|
||||
getFamilyWallet(reqData) {
|
||||
var postData = {
|
||||
uid: localStorage.getItem("uid"),
|
||||
member_id: localStorage.getItem("member_id"),
|
||||
sessionid: localStorage.getItem("session_token"),
|
||||
action: 22012,
|
||||
...reqData,
|
||||
};
|
||||
return this.postAuxEnd("/familywallet", postData);
|
||||
}
|
||||
|
||||
// FUNCTION TO START FAMILY TRANSFER
|
||||
familyTransferStart(reqData) {
|
||||
var postData = {
|
||||
uid: localStorage.getItem("uid"),
|
||||
member_id: localStorage.getItem("member_id"),
|
||||
sessionid: localStorage.getItem("session_token"),
|
||||
...reqData,
|
||||
};
|
||||
return this.postAuxEnd("/familytransferstart", postData);
|
||||
}
|
||||
|
||||
// FUNCTION TO PERFORM FAMILY TRANSFER
|
||||
familyTransfer(reqData) {
|
||||
var postData = {
|
||||
uid: localStorage.getItem("uid"),
|
||||
member_id: localStorage.getItem("member_id"),
|
||||
sessionid: localStorage.getItem("session_token"),
|
||||
...reqData,
|
||||
};
|
||||
return this.postAuxEnd("/familytransfer", postData);
|
||||
}
|
||||
|
||||
/*
|
||||
- 20:27:30.118 FLOG_MAX [757411]: REQ_STRING(username)
|
||||
@@ -1189,6 +1257,10 @@ class usersService {
|
||||
console.log(response);
|
||||
// res = response;
|
||||
console.log("~~~~~~~ Toks2 POST ~~~~~~~~");
|
||||
if (response.data.internal_return == "-9999") {
|
||||
localStorage.clear();
|
||||
window.location.href = `/login?sessionExpired=true`;
|
||||
}
|
||||
return response;
|
||||
})
|
||||
.catch((error) => {
|
||||
|
||||
@@ -6,4 +6,4 @@ export default function FamilyManagePage() {
|
||||
<FamilyManage />
|
||||
</>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,9 @@
|
||||
import FamilySettings from "../components/FamilyAcc/FamilySettings";
|
||||
|
||||
export default function FamilySettingsPage() {
|
||||
return (
|
||||
<>
|
||||
<FamilySettings />
|
||||
</>
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
import React from 'react'
|
||||
|
||||
import Lnd from '../components/Lnd/Lnd'
|
||||
|
||||
function LndPage() {
|
||||
return (
|
||||
<>
|
||||
<Lnd />
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
||||
export default LndPage
|
||||
@@ -39,6 +39,7 @@ function ManageActiveJobs() {
|
||||
loading: false,
|
||||
error: false,
|
||||
data: res.data.result_list,
|
||||
image: res.data.session_image_server
|
||||
});
|
||||
})
|
||||
.catch((error) => {
|
||||
|
||||
@@ -22,7 +22,7 @@ export default function MyPendingJobsPage() {
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
getMyJobList();
|
||||
getMyJobList();
|
||||
}, [pendingListTable]);
|
||||
|
||||
// debugger;
|
||||
|
||||
Reference in New Issue
Block a user