Compare commits

...

145 Commits

Author SHA1 Message Date
victorAnumudu 36d8c70fd9 refactored some components and seperated their API calls 2024-03-27 23:17:58 +01:00
tokslaw 23443a4677 Merge branch 'family-media-task' of WrenchBoard/Users-Wrench into master 2024-03-27 17:42:54 +00:00
victorAnumudu 16e5656422 rendered video component for media task type 2024-03-27 18:39:04 +01:00
ameye 0e42668285 Merge branch 'media-task-api' of WrenchBoard/Users-Wrench into master 2024-03-27 12:42:23 +00:00
victorAnumudu 66bd8cf6ec added api for assign media task 2024-03-27 13:38:17 +01:00
ameye 3d7ad25517 Merge branch 'media-task' of WrenchBoard/Users-Wrench into master 2024-03-27 11:40:30 +00:00
victorAnumudu 2d5c828089 added inputs validation 2024-03-27 12:20:01 +01:00
victorAnumudu e39a8358f7 initial commit 2024-03-27 09:44:30 +01:00
tokslaw e9c57550a2 Merge branch 'assign-media-tab' of WrenchBoard/Users-Wrench into master 2024-03-26 19:21:02 +00:00
victorAnumudu 27f87ee92d added media tab on assign task to kid 2024-03-26 20:16:53 +01:00
ameye edf23352ef Merge branch 'error-msg' of WrenchBoard/Users-Wrench into master 2024-03-26 11:56:31 +00:00
victorAnumudu 002e2fead4 added error message object 2024-03-26 12:51:59 +01:00
ameye 160c302417 Merge branch 'my-page-link' of WrenchBoard/Users-Wrench into master 2024-03-25 16:46:49 +00:00
victorAnumudu 44ae966cc1 removed my page link from header nav 2024-03-25 17:17:08 +01:00
ameye f3b977c624 Merge branch 'package-lock-revert' of WrenchBoard/Users-Wrench into master 2024-03-25 14:25:14 +00:00
victorAnumudu 0c99e416ea reverted package-lock.json 2024-03-25 15:23:37 +01:00
tokslaw fd531d7d10 Merge branch 'assign-job-popout-style' of WrenchBoard/Users-Wrench into master 2024-03-25 11:47:25 +00:00
victorAnumudu ac027a3228 changed font weight 2024-03-25 12:45:55 +01:00
victorAnumudu 001492eeb5 changed price to reward 2024-03-25 12:40:01 +01:00
victorAnumudu 72fe2cad5f changed assign new job popout style 2024-03-25 12:36:10 +01:00
ameye eac693eba6 Merge branch 'pagination-item' of WrenchBoard/Users-Wrench into master 2024-03-24 19:25:43 +00:00
victorAnumudu d3b1462fe3 increased number of item to show in pagination 2024-03-24 15:57:21 +01:00
ameye 748546641d Merge branch 'market-pagination' of WrenchBoard/Users-Wrench into master 2024-03-24 12:35:26 +00:00
victorAnumudu 48a50fd47c added pagination for market list 2024-03-23 20:06:30 +01:00
tokslaw e87cb95b43 Merge branch 'offer-interest-refresh-fixed' of WrenchBoard/Users-Wrench into master 2024-03-22 17:09:35 +00:00
victorAnumudu 7638d68a7d merged branch with master, offer interest refresh fixed 2024-03-22 16:20:38 +01:00
victorAnumudu 3541363f9f added dummy pagination 2024-03-22 16:18:19 +01:00
victorAnumudu 30ce6a7d6e initial commit 2024-03-22 15:07:13 +01:00
ameye 115366672a Merge branch 'return-link' of WrenchBoard/Users-Wrench into master 2024-03-22 13:16:19 +00:00
victorAnumudu 3cd8b6e574 added link to family page 2024-03-22 12:45:34 +01:00
ameye 9850cdd392 Merge branch 'offer-interest-refresh' of WrenchBoard/Users-Wrench into master 2024-03-22 10:30:33 +00:00
victorAnumudu 6d98141c39 offer interest refresh started 2024-03-22 10:40:57 +01:00
victorAnumudu 5fe5ccbd4d merge with master 2024-03-22 07:46:17 +01:00
victorAnumudu 76c0994eb0 initial commit 2024-03-21 22:00:31 +01:00
ameye a1bc6db381 Merge branch 'others-interested-offer' of WrenchBoard/Users-Wrench into master 2024-03-21 20:03:22 +00:00
victorAnumudu 6f5d72e033 merge master 2024-03-21 20:33:37 +01:00
ameye 762de4c23e Merge branch 'market-job-event' of WrenchBoard/Users-Wrench into master 2024-03-21 17:13:37 +00:00
victorAnumudu 2a8b7ba6ec updated marketjob-addded to marketjob-added 2024-03-21 18:05:48 +01:00
victorAnumudu 50e44dab43 initial commit 2024-03-21 17:37:57 +01:00
ameye 7649a90c47 Merge branch 'job-interest-modal' of WrenchBoard/Users-Wrench into master 2024-03-21 15:30:37 +00:00
victorAnumudu 3c2c46e293 added footer style to modal 2024-03-21 16:27:52 +01:00
ameye 8dc634d900 Merge branch 'modal-footer-style-start' of WrenchBoard/Users-Wrench into master 2024-03-21 14:10:51 +00:00
victorAnumudu 3a9cb4667e added some custom style for modal footer 2024-03-21 15:08:26 +01:00
ameye 5f4b032f68 Merge branch 'home-banners-dashboard' of WrenchBoard/Users-Wrench into master 2024-03-21 13:17:35 +00:00
Ebube 8ab3e9ae50 Changed the Name of link 2024-03-21 14:15:28 +01:00
ameye 01b5fba75b Merge branch 'home-banners-dashboard' of WrenchBoard/Users-Wrench into master 2024-03-21 13:09:19 +00:00
Ebube 66b8c96592 fixed typo on link path 2024-03-21 14:04:34 +01:00
ameye 616352e1ac Merge branch 'home-banners-dashboard' of WrenchBoard/Users-Wrench into master 2024-03-21 12:53:43 +00:00
Ebube acc4417835 commit issue fix 2024-03-21 13:51:49 +01:00
Ebube 7da693f298 fix commit issue 2024-03-21 13:49:22 +01:00
ameye b0423c665c Merge branch 'popout-fix' of WrenchBoard/Users-Wrench into master 2024-03-21 12:44:43 +00:00
victorAnumudu 7089b8f14b fixed popout header style 2024-03-21 13:38:01 +01:00
ameye 2c2e2b0ca5 Merge branch 'home-banners-dashboard' of WrenchBoard/Users-Wrench into master 2024-03-21 12:33:20 +00:00
Ebube ba3dd91d81 Merge branch 'master' of https://gitlab.chiefsoft.net/WrenchBoard/Users-Wrench into home-banners-dashboard 2024-03-21 13:28:42 +01:00
Ebube 3f04c9f9f8 added items to right sidebar 2024-03-21 13:28:03 +01:00
ameye 781f5cc5a6 Merge branch 'add-group-email' of WrenchBoard/Users-Wrench into master 2024-03-21 11:33:48 +00:00
ameye 58097d8b57 Merge branch 'home-banners-dashboard' of WrenchBoard/Users-Wrench into master 2024-03-21 11:33:41 +00:00
victorAnumudu 630ccefa51 Made the email input longer 2024-03-21 12:20:20 +01:00
Ebube ed9f2dec0e fixed link location 2024-03-21 12:01:32 +01:00
Ebube d2ed29c6a6 fixed link location 2024-03-21 11:53:58 +01:00
ameye 4e81156d72 Merge branch 'parent-kid-wallet' of WrenchBoard/Users-Wrench into master 2024-03-21 09:50:41 +00:00
ameye e402c02c1f Merge branch 'assign-task-image' of WrenchBoard/Users-Wrench into master 2024-03-21 09:50:34 +00:00
victorAnumudu 5907b5d872 parent kids wallet bug fixed 2024-03-20 21:04:49 +01:00
victorAnumudu 042cc4110c initial commit 2024-03-20 19:16:16 +01:00
victorAnumudu 944fd134f6 added image to assign task popout 2024-03-20 18:02:47 +01:00
tokslaw 5e6b208513 Merge branch 'family-wallet-refresh' of WrenchBoard/Users-Wrench into master 2024-03-20 13:32:48 +00:00
victorAnumudu 762601a5c3 made kid wallet refresh when parent transfers cash 2024-03-20 13:44:14 +01:00
victorAnumudu 1e28a0e15b made kid wallet refresh when parent transfers cash 2024-03-20 13:43:23 +01:00
ameye a910ab177f Merge branch 'modal-change' of WrenchBoard/Users-Wrench into master 2024-03-19 23:33:19 +00:00
victorAnumudu 4e741f587c added color to modal and made the labels same font 2024-03-19 22:42:52 +01:00
tokslaw f779529cc6 Merge branch 'kid-accepts-task' of WrenchBoard/Users-Wrench into master 2024-03-19 20:17:00 +00:00
victorAnumudu 44cedf2f65 Made parent family task list refresh when child accepts task 2024-03-19 20:27:29 +01:00
ameye fbcba191b0 Merge branch 'rounded-select' of WrenchBoard/Users-Wrench into master 2024-03-19 14:51:37 +00:00
victorAnumudu c64d372193 assign job rounded select tag 2024-03-19 14:33:42 +01:00
ameye abff42e0a8 Merge branch 'parent-assign-job-bug' of WrenchBoard/Users-Wrench into master 2024-03-19 12:13:06 +00:00
victorAnumudu d5c342a57a made socket to trigger for refresh for only the child the parent assigns a job to 2024-03-19 13:09:06 +01:00
victorAnumudu 60d6629526 made socket to trigger for refresh for only the child the parent assigns a job to 2024-03-19 11:45:18 +01:00
tokslaw b1fbf89f10 Merge branch 'parent-assign-job' of WrenchBoard/Users-Wrench into master 2024-03-18 21:02:02 +00:00
victorAnumudu ae346d5ac5 parent assign job triggers update in child account 2024-03-18 21:38:41 +01:00
victorAnumudu 75b5102766 Merge master into parent-assign-job 2024-03-18 19:31:01 +01:00
victorAnumudu 9b12ffe0cd initial commit 2024-03-18 19:29:35 +01:00
tokslaw 408165e718 Merge branch 'assign-modal' of WrenchBoard/Users-Wrench into master 2024-03-18 15:35:33 +00:00
victorAnumudu f7a0594447 added bg color to modal head 2024-03-18 16:00:45 +01:00
ameye c733b006fb Merge branch 'assign-job-label' of WrenchBoard/Users-Wrench into master 2024-03-18 13:05:13 +00:00
victorAnumudu 5a6b60578e clean up 2024-03-18 13:44:56 +01:00
victorAnumudu cdd998235e made assign job labels same style 2024-03-18 13:42:58 +01:00
ameye 6e9efd7f43 Merge branch 'wallet-family-api' of WrenchBoard/Users-Wrench into master 2024-03-18 09:46:18 +00:00
ameye 07e9520774 Merge branch 'home-banners-dashboard' of WrenchBoard/Users-Wrench into master 2024-03-18 09:46:04 +00:00
victorAnumudu c1600a2a13 consumed family wallet API 2024-03-17 23:47:35 +01:00
Ebube 58e1745ac5 Merge branch 'master' of https://gitlab.chiefsoft.net/WrenchBoard/Users-Wrench into home-banners-dashboard 2024-03-17 19:47:27 +01:00
Ebube 35db4fe536 fixed underlying issues 2024-03-17 19:46:38 +01:00
ameye dfdbf404a5 Merge branch 'socket-market-refresh' of WrenchBoard/Users-Wrench into master 2024-03-14 00:52:28 +00:00
victorAnumudu 985afa3c7b clean up 2024-03-13 21:02:37 +01:00
victorAnumudu 70d82d89b3 added socket event to listen to job market post 2024-03-13 19:56:13 +01:00
ameye 7f5eccb3b7 Merge branch 'home-banners-dashboard' of WrenchBoard/Users-Wrench into master 2024-03-13 07:24:29 +00:00
Ebube 6729b780bd Increased Font Size 2024-03-13 03:27:56 +01:00
ameye 06224f121a Merge branch 'home-banners-dashboard' of WrenchBoard/Users-Wrench into master 2024-03-13 00:52:26 +00:00
Ebube dc91649114 Merge branch 'master' of https://gitlab.chiefsoft.net/WrenchBoard/Users-Wrench into home-banners-dashboard 2024-03-13 00:01:38 +01:00
Ebube 22f6eb436d Completed the flow 2024-03-13 00:00:58 +01:00
CHIEFSOFT\ameye f2ab0cd6c9 added host 2024-03-12 15:59:13 -04:00
CHIEFSOFT\ameye a2819786ae 76.209.103.227 2024-03-12 13:48:01 -04:00
CHIEFSOFT\ameye 6cd92f6372 Merge branch 'master' of https://gitlab.chiefsoft.net/WrenchBoard/Users-Wrench 2024-03-12 12:35:04 -04:00
CHIEFSOFT\ameye 7173901c9b socker in coposer 2024-03-12 12:34:47 -04:00
ameye 380d014964 Merge branch 'socket-context' of WrenchBoard/Users-Wrench into master 2024-03-12 15:52:32 +00:00
victorAnumudu 9032b36b13 Merge master into socket-context 2024-03-12 16:32:59 +01:00
victorAnumudu f291382786 added a socket context for real time communication 2024-03-12 16:31:08 +01:00
ameye 69dcee859d Merge branch 'home-banners-dashboard' of WrenchBoard/Users-Wrench into master 2024-03-12 15:01:02 +00:00
Ebube fe295dc775 Merge branch 'master' of https://gitlab.chiefsoft.net/WrenchBoard/Users-Wrench into home-banners-dashboard 2024-03-12 15:51:44 +01:00
Ebube 044c8edf7d Suggest task flow pt1 2024-03-12 15:50:47 +01:00
CHIEFSOFT\ameye c22cffd167 added socket package 2024-03-12 02:04:10 -04:00
ameye 6d77bfa1b0 Merge branch 'home-banners-dashboard' of WrenchBoard/Users-Wrench into master 2024-03-11 19:55:16 +00:00
Ebube 60cc6f375e Fixed font size and added name to waitlist tab 2024-03-11 20:31:11 +01:00
ameye e6684c56fd Merge branch 'home-banners-dashboard' of WrenchBoard/Users-Wrench into master 2024-03-11 17:20:31 +00:00
Ebube 4f675b30ef Added assign popup dropdown, fixed the right api for my task page 2024-03-11 18:15:31 +01:00
ameye 00c7f65092 Merge branch 'home-banners-dashboard' of WrenchBoard/Users-Wrench into master 2024-03-10 18:17:56 +00:00
Ebube 9a978b1913 Filtered pending task with family uid and added text to waiting tab 2024-03-10 19:03:16 +01:00
ameye 23f8346734 Merge branch 'family-double-wallet' of WrenchBoard/Users-Wrench into master 2024-03-09 22:05:33 +00:00
victorAnumudu 585632c1e1 implemented family wallet with multiple wallet 2024-03-09 22:55:02 +01:00
ameye 11ee1195c2 Merge branch 'home-banners-dashboard' of WrenchBoard/Users-Wrench into master 2024-03-09 18:18:42 +00:00
ameye c44f456cc1 Merge branch 'pending-task-title' of WrenchBoard/Users-Wrench into master 2024-03-09 18:18:32 +00:00
Ebube b769e4a4ba Fixed the pendingfamilyData 2024-03-09 18:58:25 +01:00
victorAnumudu a6ff06e2b4 added breadcrumb for no pending task 2024-03-09 16:04:04 +01:00
victorAnumudu 0f801b2408 removed double Title from page 2024-03-09 15:49:41 +01:00
ameye c26f2b725a Merge branch 'family-wallet-cleanup' of WrenchBoard/Users-Wrench into master 2024-03-09 12:10:22 +00:00
victorAnumudu ee94cbc92c undefined variable fixed 2024-03-09 08:19:33 +01:00
victorAnumudu dccbe76c5b removed unwanted signup country API 2024-03-09 08:13:52 +01:00
ameye 735d13b440 Merge branch 'family-redeem-options' of WrenchBoard/Users-Wrench into master 2024-03-08 20:57:58 +00:00
victorAnumudu 7f83cd5cc6 added family wallet redeem options 2024-03-08 21:50:42 +01:00
ameye 71ee75072d Merge branch 'home-banners-dashboard' of WrenchBoard/Users-Wrench into master 2024-03-08 13:58:02 +00:00
ameye 265f2b7655 Merge branch 'family-wallet-font' of WrenchBoard/Users-Wrench into master 2024-03-08 13:57:53 +00:00
Ebube 4d264fa18e Changed btn for suggested task components 2024-03-08 14:07:00 +01:00
victorAnumudu cd6ab8b504 wallet font adjusted 2024-03-08 12:20:37 +01:00
ameye 8c81073443 Merge branch 'home-banners-dashboard' of WrenchBoard/Users-Wrench into master 2024-03-08 10:41:47 +00:00
Ebube 14566de037 . 2024-03-08 04:32:19 +01:00
Ebube 69d711eddc Fixed suggested task bug 2024-03-08 03:47:57 +01:00
Ebube 159623eb69 Merge branch 'master' of https://gitlab.chiefsoft.net/WrenchBoard/Users-Wrench into home-banners-dashboard 2024-03-08 02:13:00 +01:00
Ebube da26d5c24a Task page bug fix 2024-03-08 02:12:35 +01:00
ameye 40850b7342 Merge branch 'home-banners-dashboard' of WrenchBoard/Users-Wrench into master 2024-03-08 00:38:54 +00:00
Ebube 28098169b9 . 2024-03-07 23:47:15 +01:00
Ebube 2ff6ed777f Fixed bug of tasks and chores page 2024-03-07 23:44:30 +01:00
ameye 723a2a09ab Merge branch 'new-family-wallet' of WrenchBoard/Users-Wrench into master 2024-03-07 20:00:49 +00:00
victorAnumudu 68482c7956 added new family wallet page 2024-03-07 20:25:58 +01:00
ameye 225e3ae34c Merge branch 'home-banners-dashboard' of WrenchBoard/Users-Wrench into master 2024-03-07 16:55:41 +00:00
Ebube 251d5abf10 Added family waiting data 2024-03-07 17:34:28 +01:00
ameye 22314a61c5 Merge branch 'adding-breadcrumb' of WrenchBoard/Users-Wrench into master 2024-03-07 16:01:30 +00:00
81 changed files with 3533 additions and 1345 deletions
+7 -1
View File
@@ -15,6 +15,9 @@ REACT_APP_APPSITE="https://myfitapp.mermsemr.com"
REACT_APP_AUX_ENDPOINT="https://apigate.lotus.g1.wrenchboard.com/en/wrench/api/v1"
REACT_APP_USERS_ENDPOINT="https://apigate.lotus.g1.wrenchboard.com/en/wrench/api/v1"
#SOCKETS ENDS
REACT_APP_PRIMARY_SOCKET="https://socket-dev.wrenchboard.com"
#"https://devapi.mermsemr.com/en/desktop/api/v2/myfituser"
REACT_APP_SESSION_EXPIRE_MINUTES=600000
@@ -111,4 +114,7 @@ REACT_APP_SHOW_NEW_FAMILY_DASH=1
REACT_APP_SHOW_ACCOUNT_DASH=1
# Displays the slider banners
REACT_APP_SHOW_SLIDER_BANNERS=0
REACT_APP_SHOW_SLIDER_BANNERS=0
# FOR MEDIA LINK
REACT_APP_MEDIA_LINK='https://dev-media.wrenchboard.com '
+7 -1
View File
@@ -15,6 +15,9 @@ REACT_APP_APPSITE="https://myfitapp.mermsemr.com"
REACT_APP_AUX_ENDPOINT="https://apigate.lotus.g1.wrenchboard.com/en/wrench/api/v1"
REACT_APP_USERS_ENDPOINT="https://apigate.lotus.g1.wrenchboard.com/en/wrench/api/v1"
#SOCKETS ENDS
REACT_APP_PRIMARY_SOCKET="https://socket-dev.wrenchboard.com"
#"https://devapi.mermsemr.com/en/desktop/api/v2/myfituser"
REACT_APP_SESSION_EXPIRE_MINUTES=600000
@@ -79,4 +82,7 @@ REACT_APP_SHOW_NEW_FAMILY_DASH=1
REACT_APP_SHOW_ACCOUNT_DASH=1
# Displays the slider banners
REACT_APP_SHOW_SLIDER_BANNERS=0
REACT_APP_SHOW_SLIDER_BANNERS=0
# FOR MEDIA LINK
REACT_APP_MEDIA_LINK='https://dev-media.wrenchboard.com '
+7 -1
View File
@@ -15,6 +15,9 @@ REACT_APP_APPSITE="https://myfitapp.mermsemr.com"
REACT_APP_AUX_ENDPOINT="https://apigate.orion.g1.wrenchboard.com/en/wrench/api/v1"
REACT_APP_USERS_ENDPOINT="https://apigate.orion.g1.wrenchboard.com/en/wrench/api/v1"
#SOCKETS ENDS
REACT_APP_PRIMARY_SOCKET="https://socket.wrenchboard.com"
#"https://devapi.mermsemr.com/en/desktop/api/v2/myfituser"
REACT_APP_SESSION_EXPIRE_MINUTES=600000
@@ -85,4 +88,7 @@ REACT_APP_SHOW_NEW_FAMILY_DASH=1
REACT_APP_SHOW_ACCOUNT_DASH=1
# Displays the slider banners
REACT_APP_SHOW_SLIDER_BANNERS=0
REACT_APP_SHOW_SLIDER_BANNERS=0
# FOR MEDIA LINK
REACT_APP_MEDIA_LINK='https://media.wrenchboard.com '
+10 -1
View File
@@ -21,5 +21,14 @@
"emmet.triggerExpansionOnTab": true,
"emmet.includeLanguages": {
"javascript": "javascriptreact"
}
},
"cSpell.words": [
"completesignuplink",
"MOBILEUSER",
"MYFILES",
"mynotifications",
"PASSWORDRESET",
"TRANSFERSTART",
"WRENCHBOARD"
]
}
+3 -2
View File
@@ -21,9 +21,10 @@ services:
- backend.wrenchboard.api.live:10.10.33.15
- backend.wrenchboard.api.test:10.10.33.15
- apigate.lotus.g1.wrenchboard.com:10.10.33.15
- apigate.nebula.g1.wrenchboard.com:10.10.33.15
- apigate.orion.g1.wrenchboard.com:10.10.33.15
# #- backend.wrenchboard.api.live:172.31.4.27
# #- backend.wrenchboard.api.test:10.20.30.27
- socket-dev.wrenchboard.com:10.10.33.15
- socket.wrenchboard.com:10.10.33.15
- apigateway.wrenchboard.app.dev.fluxtra.net:10.20.30.19
- apigateway.wrenchboard.app.lotus.fluxtra.net:172.31.4.19
environment:
+1
View File
@@ -30,6 +30,7 @@
"react-to-print": "^2.14.12",
"react-toastify": "^9.0.1",
"redux": "^4.2.0",
"socket.io-client": "^4.4.1",
"slick-carousel": "^1.8.1",
"web-vitals": "^1.0.1",
"yup": "^1.1.1"
+11 -8
View File
@@ -2,19 +2,22 @@ import { Navigate, useLocation } from "react-router-dom";
import Routers from "./Routers";
import Toaster from "./components/Helpers/Toaster";
import Default from "./components/Partials/Default";
import SocketIOContextProvider from "./components/Contexts/SocketIOContext";
function App() {
const { pathname } = useLocation();
return (
<Default>
<>
{pathname.startsWith("/@") ? (
<Navigate to="/app" replace={true} />
) : (
<Routers />
)}
<Toaster />
</>
<SocketIOContextProvider>
<>
{pathname.startsWith("/@") ? (
<Navigate to="/app" replace={true} />
) : (
<Routers />
)}
<Toaster />
</>
</SocketIOContextProvider>
</Default>
);
}
+13 -13
View File
@@ -89,9 +89,9 @@ function AddJob({ popUpHandler, categories }) {
<div className="field w-full mb-6 xl:mb-0">
<label
htmlFor="country"
className="input-label text-[#181c32] dark:text-white text-[13.975px] leading-[20.9625px] font-semibold flex item-center gap-1"
className="job-label job-label-flex"
>
Currency
<span>Currency</span>
{props.errors.country && props.touched.country && (
<span className="text-[12px] text-red-500">
{props.errors.country}
@@ -136,9 +136,9 @@ function AddJob({ popUpHandler, categories }) {
{/* Price */}
<div className="field w-full">
<InputCom
fieldClass="px-6 text-right"
fieldClass="px-6 text-right flex"
label="Price"
labelClass="tracking-wide"
labelClass=""
type="number"
name="price"
placeholder="0"
@@ -159,7 +159,7 @@ function AddJob({ popUpHandler, categories }) {
<InputCom
fieldClass="px-6"
label="Title"
labelClass="tracking-wide"
labelClass=""
type="text"
name="title"
value={props.values.title}
@@ -178,7 +178,7 @@ function AddJob({ popUpHandler, categories }) {
<InputCom
fieldClass="px-6"
label="Description"
labelClass="tracking-wide"
labelClass=""
type="text"
name="description"
value={props.values.description}
@@ -197,7 +197,7 @@ function AddJob({ popUpHandler, categories }) {
<div className="sm:w-[60%] 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"
className="job-label job-label-flex"
>
Job Delivery Details
{props.errors.job_detail &&
@@ -220,13 +220,13 @@ function AddJob({ popUpHandler, categories }) {
</div>
<div className="sm:w-[35%] w-full">
<div
<label
htmlFor="Job Categories"
className='className="input-label text-[#181c32] dark:text-white text-[13.975px] leading-[20.9625px] font-semibold block"'
className='job-label'
id="checked-group"
>
Categories
</div>
</label>
<div
className="sm:flex-col flex flex-wrap px-3 mt-3"
role="group"
@@ -266,7 +266,7 @@ function AddJob({ popUpHandler, categories }) {
<div className="field w-full mb-[5px]">
<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"
className="job-label"
htmlFor="timeline_days"
>
Timeline
@@ -328,10 +328,10 @@ function AddJob({ popUpHandler, categories }) {
<div className="flex items-center space-x-4 mr-9">
<button
type="button"
className="text-18 text-light-red tracking-wide "
className="text-18 tracking-wide h-11 flex justify-center items-center border border-light-red text-base rounded-full text-light-red cursor-pointer"
>
<span
className="border-b dark:border-[#5356fb29] border-light-red"
className="px-2"
onClick={popUpHandler}
>
{" "}
+2 -1
View File
@@ -145,8 +145,9 @@ export default function Login() {
localStorage.setItem("member_id", `${res.data.member_id}`);
localStorage.setItem("uid", `${res.data.uid}`);
localStorage.setItem("session_token", `${res.data.session}`);
if (name === "family") {
if (res.data?.account_type == "FAMILY") {
sessionStorage.setItem("family_uid", res.data?.family_uid);
sessionStorage.setItem("parent_uid", res.data?.parent_uid);
}
// localStorage.setItem("session", `${res.data.session}`);
dispatch(updateUserDetails({ ...res.data }));
+20 -17
View File
@@ -1,22 +1,25 @@
import React from 'react'
import { Link } from 'react-router-dom'
import React from "react";
import { Link } from "react-router-dom";
export default function CustomBreadcrumb({title, breadcrumb}) {
export default function CustomBreadcrumb({ title, breadcrumb }) {
return (
<div className="">
<h3 className="text-26 font-bold text-dark-gray dark:text-white">{title}</h3>
<nav aria-label="breadcrumb" className='text-base text-dark-gray dark:text-white flex items-center'>
{breadcrumb.map((value, index) => (
<p key={index}>
<Link className={`${value.active ? 'opacity-60' : ''}`} to={value.link}>{value.title}</Link>
{index != breadcrumb.length - 1 ?
<span className='px-1'>/</span>
:
null
}
</p>
))}
</nav>
<h3 className="text-26 font-bold text-dark-gray dark:text-white">
{title}
</h3>
<nav
aria-label="breadcrumb"
className="text-base text-dark-gray dark:text-white flex items-center"
>
{breadcrumb.map((value, index) => (
<React.Fragment key={index}>
<Link className={value.active ? "opacity-60" : ""} to={value.link}>
{value.title}
</Link>
{index !== breadcrumb.length - 1 && <span className="px-1">\</span>}
</React.Fragment>
))}
</nav>
</div>
)
);
}
+10 -2
View File
@@ -1,7 +1,10 @@
import { Suspense, lazy } from "react";
import localImgLoad from "../../lib/localImgLoad";
import CountDown from "../Helpers/CountDown";
import { PriceFormatter } from "../Helpers/PriceFormatter";
const VideoElement = lazy(() => import("../VideoCom/VideoElement")); // LAZY IMPORTING VIDEO COMPONENT
export default function OfferCard({
datas,
hidden = false,
@@ -19,9 +22,13 @@ export default function OfferCard({
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">
{/* thumbnail */}
<div className="w-full h-40">
{/* thumbnail image */}
{/* thumbnail image/video */}
{datas.job_type == "MEDIA" ?
<Suspense fallback={<p>Loading...</p>}>
<VideoElement videoId={datas?.media_uid} />
</Suspense>
:
<div
className="thumbnail w-full h-full rounded-xl overflow-hidden px-4 pt-4"
style={{
@@ -30,6 +37,7 @@ export default function OfferCard({
>
{hidden && <div className="flex justify-center"></div>}
</div>
}
</div>
{/* details */}
<div className="details">
+116
View File
@@ -0,0 +1,116 @@
import React, { createContext, useContext, useEffect, useState } from "react";
import { tableReload } from "../../store/TableReloads";
import { useDispatch, useSelector } from "react-redux";
import io from "socket.io-client";
let SocketIOContext = createContext({})
export default function SocketIOContextProvider({children}) {
const {userDetails} = useSelector((state) => state?.userDetails); // CHECKS IF USER UID, to determine if user is active
const dispatch = useDispatch()
const socket = io.connect(process.env.REACT_APP_PRIMARY_SOCKET);
// //Room State
// const [room, setRoom] = useState("");
// // Messages States
// const [message, setMessage] = useState("");
const [socketMsgReceived, setSocketMsgReceived] = useState("");
const joinRoom = (room) => {
if (room !== "") {
socket.emit("join_room", room);
}
};
const sendMessage = (message, room) => {
if(message && room){
socket.emit("send_message", { message, room });
}
};
const marketUpdate = (message, room) => {
if(message && room){
socket.emit("marketjob_added", { message, room });
}
};
const parentAssignJobToKid = (message, room) => {
if(message && room){
socket.emit("family", { message:{...message}, room });
}
};
const sendJobInterestToOwner = (message, room) => {
if(message && room){
socket.emit("marketjob", { message:{...message}, room });
}
};
useEffect(() => {
socket.on("receive_message", (data) => {
// setSocketMsgReceived(data.message);
dispatch(tableReload({type:'CHATMESSAGELIST'})) // dispatches to update chat message sending from owner to worker and vice versa
});
socket.on("received_refreshmarket_jobs", (data) => {
// setSocketMsgReceived(data.message);
dispatch(tableReload({type:'MARKETTABLELIST'})) // dispatches to update market list on full account
});
socket.on("family_actions", (data) => {
// setSocketMsgReceived(data.message);
let user_uid = userDetails.account_type == 'FULL' ? userDetails.uid : sessionStorage.getItem('family_uid') // gets user UID
let {message} = data
if(message.action == "REFRESH_OFFER" && message.family_uid == user_uid && message.audience == "MEMBER"){ // for refreshing child account when parent assigns a job
dispatch(tableReload({type:'FAMILYOFFERLIST'})) // dispatches to update family pending/offer list on family side
}
if(message.action == "REFRESH_TASK" && message.audience == "PARENT"){ // for refreshing parent account when child accepts or rejects a job
dispatch(tableReload({type:'PARENTFAMILYTASKLIST'})) // dispatches to update parent family task list on parent side
}
if(message.action == "REFRESH_WALLET" && message.family_uid == user_uid && message.audience == "MEMBER"){ // for refreshing child wallet account when parent sends money to kid
dispatch(tableReload({type:'WALLETTABLE'})) // dispatches to update wallet balance on family side
}
// console.log('DATA', data)
});
socket.on("marketjob_actions", (data) => { // Triggers refresh on owner side, when somebody sends/shows interest in a job
let user_uid = userDetails.account_type == 'FULL' ? userDetails.uid : sessionStorage.getItem('family_uid') // gets user UID
let {message} = data
if(message.action == "REFRESH_OFFERS" && message.audience == "MERCHANT" && message.market_uid == user_uid){ // for refreshing job owner offer interest list when any worker sends interest
dispatch(tableReload({type:'OFFERINTERESTLISTRELOAD'}))
}
console.log('data', data)
});
}, [socket]);
let values = {
socket,
sendMessage,
joinRoom,
setSocketMsgReceived,
marketUpdate,
parentAssignJobToKid,
sendJobInterestToOwner,
socketMsgReceived,
// room,
// setRoom,
// message,
// setMessage,
}
return (
<SocketIOContext.Provider value={values}>
{children}
</SocketIOContext.Provider>
)
}
export const SocketValues = () => {
return useContext(SocketIOContext)
}
+18 -16
View File
@@ -29,14 +29,15 @@ const AccountDashboard = ({ className, bannerList }) => {
props;
return (
<TopBanner
btn={short_button_text}
image={image}
title={short_title}
desc={short_description}
link_path={link_path}
key={idx}
/>
<div key={idx}>
<TopBanner
btn={short_button_text}
image={image}
title={short_title}
desc={short_description}
link_path={link_path}
/>
</div>
);
})}
</div>
@@ -48,14 +49,15 @@ const AccountDashboard = ({ className, bannerList }) => {
props;
return (
<LowerBanner
btn={short_button_text}
image={image}
title={short_title}
desc={short_description}
link_path={link_path}
key={idx}
/>
<div key={idx}>
<LowerBanner
btn={short_button_text}
image={image}
title={short_title}
desc={short_description}
link_path={link_path}
/>
</div>
);
})}
</div>
+29 -17
View File
@@ -1,16 +1,11 @@
import React, { Suspense, useMemo, useState } from "react";
import React, { Suspense } from "react";
import { Link } from "react-router-dom";
import usersService from "../../services/UsersService";
import CustomBreadcrumb from "../Breadcrumb/CustomBreadcrumb";
import Layout from "../Partials/Layout";
import LoadingSpinner from "../Spinners/LoadingSpinner";
import FamilyTableNew from "./FamilyTableNew";
export default function FamilyActivities() {
const [familyList, setFamilyList] = useState({});
const [loader, setLoader] = useState(false);
const apiCall = useMemo(() => new usersService(), []);
return (
<Layout>
{/*<CommonHead />*/}
@@ -19,11 +14,26 @@ export default function FamilyActivities() {
{/* 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={``}>Tasks & Chores</span>
</h1>
<CustomBreadcrumb
title={"Tasks & Chores"}
breadcrumb={[
{ link: "/", title: "Home" },
{
link: "/acc-family",
title: "Family Account",
},
{
link: "/acc-family/activities",
title: "Tasks & Chores",
active: true,
},
]}
/>
</div>
<Link className="flex items-center gap-2" to="/acc-family">
<Link
className="item-content relative text-[18px] transition-all duration-300 ease-in-out bg-[#76a5df] text-white font-medium dark:text-white h-12 px-2 flex items-center gap-2 rounded-md shadow-sm justify-center cursor-pointer dark:bg-[linear-gradient(134.38deg,#f539f8_0%,#c342f9_43.55%,#5356fb_104.51%)]"
to="/acc-family"
>
<svg
xmlns="http://www.w3.org/2000/svg"
fill="none"
@@ -42,12 +52,14 @@ export default function FamilyActivities() {
<span>Family</span>
</Link>
</div>
<Suspense fallback={<LoadingSpinner color="sky-blue" size="16" />}>
<FamilyTableNew
familyList={familyList?.result_list}
loader={loader}
imageServer={familyList?.session_image_server}
/>
<Suspense
fallback={
<div className="bg-white">
<LoadingSpinner color="sky-blue" size="16" height='h-[30rem]' />
</div>
}
>
<FamilyTableNew />
</Suspense>
</div>
</div>
+37 -2
View File
@@ -1,8 +1,9 @@
import React, { useEffect, useState } from "react";
import { useLocation, useNavigate } from "react-router-dom";
import { Link, useLocation, useNavigate } from "react-router-dom";
import Layout from "../Partials/Layout";
import LoadingSpinner from "../Spinners/LoadingSpinner";
import FamilyManageTabs from "./FamilyManageTabs";
import CustomBreadcrumb from "../Breadcrumb/CustomBreadcrumb";
export default function FamilyManage() {
const [selectTab, setValue] = useState("today");
@@ -34,7 +35,7 @@ export default function FamilyManage() {
<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="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
@@ -50,6 +51,40 @@ export default function FamilyManage() {
className="relative"
></div>
</div>
</div> */}
<div className="w-full mb-5 flex justify-between items-center">
<div className="">
<CustomBreadcrumb
title = {'Manage Family'}
breadcrumb={
[
{ link: "/", title: "Home" },
{ link: "/manage-family", title: "Manage Family", active: true},
]
}
/>
</div>
<Link
className="item-content relative text-[18px] transition-all duration-300 ease-in-out bg-[#76a5df] text-white font-medium dark:text-white h-12 px-2 flex items-center gap-2 rounded-md shadow-sm justify-center cursor-pointer dark:bg-[linear-gradient(134.38deg,#f539f8_0%,#c342f9_43.55%,#5356fb_104.51%)]"
to="/acc-family"
>
<svg
xmlns="http://www.w3.org/2000/svg"
fill="none"
viewBox="0 0 24 24"
strokeWidth={1.5}
stroke="currentColor"
className="w-4 h-4"
>
<path
strokeLinecap="round"
strokeLinejoin="round"
d="M10.5 19.5 3 12m0 0 7.5-7.5M3 12h18"
/>
</svg>
<span>Family</span>
</Link>
</div>
<FamilyManageTabs accountDetails={accountDetails} />
</div>
+103 -101
View File
@@ -14,6 +14,7 @@ import LoadingSpinner from "../Spinners/LoadingSpinner";
import AssignTaskPopout from "./FamilyPopout/AssignTaskPopout";
import FamilyWallet from "./Tabs/FamilyWallet";
import { apiConst } from "../../lib/apiConst";
import { useSelector } from "react-redux";
// Lazy Imports for components
const FamilyWaitlist = lazy(() => import("./Tabs/FamilyWaitlist"));
@@ -29,43 +30,32 @@ export default function FamilyManageTabs({
listReload,
loader,
}) {
const { jobListTable, pendingListTable, parentFamilyTaskList } = useSelector((state) => state.tableReload); // TABLE RELOAD TRIGGERS
// Initial state for family details
const initialDetailState = {
loading: false,
data: null,
data: [],
};
// console.log('accountDetails',accountDetails)
// State for family details, tasks, waitlist, and pending
const [details, setDetails] = useState({
familyDetails: { ...initialDetailState },
familyTasks: { ...initialDetailState },
familyWaitList: { ...initialDetailState },
familyPending: { ...initialDetailState },
});
// Function to reset family details, tasks, waitlist, and pending
const resetDetails = () => {
setDetails({
familyDetails: { ...initialDetailState },
familyTasks: { ...initialDetailState },
familyWaitList: { ...initialDetailState },
familyPending: { ...initialDetailState },
});
};
// State for family details, tasks, waitlist, and pending
let [familyDetails, setFamilyDetails] = useState({loading: false, data: {}})
let [familyTasks, setFamilyTasks] = useState({...initialDetailState})
let [familyWaitList, setFamilyWaitList] = useState({...initialDetailState})
let [familyPending, setFamilyPending] = useState({...initialDetailState})
const [updatePage, setUpdatePage] = useState(false) // State to determine when to update the page
// State for family task data
const [familyTask, setFamilyTask] = useState({ loading: false, data: [] });
// State for list of created jobs by FULL USER
const [jobList, setJobList] = useState({ loading: false, data: [] });
// State for active task
// State for active/selected job
const [activeTask, setActiveTask] = useState({ id: 0, data: {} });
// State for error messages
const [errMsg, setErrMsg] = useState("");
// State for family task popout
const [familyTaskPopout, setFamilyTaskPopout] = useState(false);
const [assignTaskPopout, setAssignTaskPopout] = useState(false);
let [uploadStatus, setUploadStatus] = useState({loading: false, status: false, message:''}) // HOLDS STATE FOR UPLOAD PROFILE PICTURE STATUS
@@ -79,8 +69,8 @@ export default function FamilyManageTabs({
const apiCall = useMemo(() => new usersService(), []);
// Function to handle toggling the family task popout
const familyPopUpHandler = () => {
setFamilyTaskPopout((prev) => !prev);
const familyAssignPopUpHandler = () => {
setAssignTaskPopout((prev) => !prev);
};
// Function to trigger a click on the hidden profile image input
@@ -177,43 +167,40 @@ export default function FamilyManageTabs({
Tasks: (
<FamilyTasks
className={className}
loader={details.familyTasks.loading}
familyData={details.familyTasks.data}
familyData={familyTasks}
accountDetails={accountDetails}
/>
),
Waiting: (
<FamilyWaitlist
familyData={details.familyWaitList.data}
familyData={familyWaitList}
accountDetails={accountDetails}
loader={details.familyWaitList.loading}
setUpdatePage={setUpdatePage}
/>
),
Pending: (
<FamilyPending
familyData={details.familyPending.data}
familyData={familyPending}
accountDetails={accountDetails}
loader={details.familyPending.loading}
setUpdatePage={setUpdatePage}
/>
),
Account: (
<FamilyAccount
familyData={details.familyDetails.data}
familyData={familyDetails}
myRef={accountRef}
loader={details.familyDetails.loading}
handlePrint={useHandlePrint}
/>
),
Profile: <FamilyProfile familyData={details.familyDetails.data} />,
wallet: <FamilyWallet familyData={details.familyDetails.data} />,
Profile: <FamilyProfile familyData={familyDetails.data} />,
wallet: <FamilyWallet familyData={familyDetails.data} />,
};
// Default tab component
const defaultTabComponent = (
<FamilyTasks
className={className}
loader={details.familyTasks.loading}
familyData={details.familyTasks.data}
familyData={familyTasks}
accountDetails={accountDetails}
/>
);
@@ -221,67 +208,90 @@ export default function FamilyManageTabs({
// Selected tab component based on the current 'tab'
const selectedTabComponent = tabComponents[tab] || defaultTabComponent;
// Effect to manage family details and related data
// Effect to manage family details
useEffect(() => {
const manageFamily = async () => {
setFamilyDetails({loading:true, data: {}})
try {
resetDetails();
setDetails({
familyDetails: { loading: true },
familyTasks: { loading: true },
familyWaitList: { loading: true },
familyPending: { loading: true },
});
const { family_uid } = accountDetails;
const reqData = { family_uid };
const [familyRes, tasksRes, familyWaitRes, familyPending] =
await Promise.all([
apiCall.ManageFamily(reqData),
apiCall.ManageTasks(reqData),
apiCall.ManageFamilyWaitlist(),
apiCall.ManageFamilyPending(),
]);
const familyData = familyRes.data;
const tasksData = tasksRes.data;
const familyWaitData = familyWaitRes.data;
const familyPendingData = familyPending.data;
// Function to check for errors in data
const checkDataError = (data) => data?.internal_return < 0;
if (
checkDataError(familyData) ||
checkDataError(tasksData) ||
checkDataError(familyWaitData) ||
checkDataError(familyPendingData)
) {
return;
const response = await apiCall.ManageFamily(reqData)
if(response.status != 200 || !response?.data){
return setFamilyDetails({loading:false, data: {}})
}
setDetails({
familyDetails: { loading: false, data: familyData },
familyTasks: { loading: false, data: tasksData },
familyWaitList: { loading: false, data: familyWaitData },
familyPending: { loading: false, data: familyPendingData },
});
setFamilyDetails({loading:false, data: response?.data})
} catch (error) {
resetDetails();
setErrMsg("An error occurred");
throw new Error(error);
setFamilyDetails({loading:false, data: {}})
}
};
// Invoke the manageFamily function when the component mounts
manageFamily();
}, []);
// Effect to manage active family task details
useEffect(() => {
const manageTasks = async () => {
setFamilyTasks({loading:true, data: []})
try {
const { family_uid } = accountDetails;
const reqData = { family_uid };
const response = await apiCall.ManageTasks(reqData)
if(response.status != 200 || !response?.data){
return setFamilyTasks({loading:false, data: []})
}
setFamilyTasks({loading:false, data: response?.data})
} catch (error) {
throw new Error(error);
setFamilyTasks({loading:false, data: []})
}
};
// Invoke the manageFamily function when the component mounts
manageTasks();
}, [updatePage, parentFamilyTaskList]);
// Effect to manage family wait task details
useEffect(() => {
const manageFamilyWaitlist = async () => {
setFamilyWaitList({loading:true, data: []})
try {
const response = await apiCall.ManageFamilyWaitlist()
if(response.status != 200 || !response?.data){
return setFamilyWaitList({loading:false, data: []})
}
setFamilyWaitList({loading:false, data: response?.data})
} catch (error) {
throw new Error(error);
setFamilyWaitList({loading:false, data: []})
}
};
// Invoke the manageFamily function when the component mounts
manageFamilyWaitlist();
}, [updatePage]);
// Effect to manage family tasks
// Effect to manage family pending task details
useEffect(() => {
const manageFamilyPending = async () => {
setFamilyPending({loading:true, data: []})
try {
const response = await apiCall.ManageFamilyPending()
if(response.status != 200 || !response?.data){
return setFamilyPending({loading:false, data: []})
}
setFamilyPending({loading:false, data: response?.data})
} catch (error) {
throw new Error(error);
setFamilyPending({loading:false, data: []})
}
};
// Invoke the manageFamily function when the component mounts
manageFamilyPending();
}, [updatePage, pendingListTable]);
// Effect to get all parent job list
useEffect(() => {
let checkFamilyTask = true;
const reqData = {
limit: 30,
offset: 0,
@@ -289,12 +299,11 @@ export default function FamilyManageTabs({
action: apiConst.WRENCHBOARD_PICTURE_FAMMEMBER,
};
if (checkFamilyTask) {
setFamilyTask({ loading: true });
setJobList({ loading: true });
apiCall
.getMyJobList(reqData)
.then((res) => {
setFamilyTask({ loading: false, data: res?.data?.result_list });
setJobList({ loading: false, data: res?.data?.result_list });
if (res?.data?.result_list?.length) {
setActiveTask((prev) => ({
...prev,
@@ -303,15 +312,9 @@ export default function FamilyManageTabs({
}
})
.catch((err) => {
setFamilyTask({ loading: false, data: [] });
setJobList({ loading: false, data: [] });
console.log("Error", err);
});
}
// Cleanup function to prevent memory leaks
return () => {
checkFamilyTask = false;
};
}, []);
return (
@@ -394,7 +397,7 @@ export default function FamilyManageTabs({
</ul>
<button
type="button"
onClick={familyPopUpHandler}
onClick={familyAssignPopUpHandler}
className="p-1 my-1 w-[100px] flex justify-center items-center btn-gradient text-base rounded-full text-white"
>
Add task
@@ -416,15 +419,14 @@ export default function FamilyManageTabs({
</Suspense>
</div>
{familyTaskPopout && (
{assignTaskPopout && (
<AssignTaskPopout
action={familyPopUpHandler}
situation={familyTaskPopout}
familyTask={familyTask}
setFamilyTask={setFamilyTask}
action={familyAssignPopUpHandler}
situation={assignTaskPopout}
jobList={jobList}
setActiveTask={setActiveTask}
activeTask={activeTask}
familyDetailsData={details.familyDetails.data}
familyDetailsData={familyDetails.data}
setUpdatePage={setUpdatePage}
/>
)}
+20 -17
View File
@@ -4,39 +4,29 @@ import MyOffersFamilyTable from '../MyTasks/MyOffersFamilyTable'
import LoadingSpinner from '../Spinners/LoadingSpinner';
import usersService from '../../services/UsersService';
import CustomBreadcrumb from '../Breadcrumb/CustomBreadcrumb';
import { useSelector } from 'react-redux';
export default function FamilyPendingOffer() {
const userApi = new usersService();
const {familyOfferList} = useSelector((state) => state.tableReload)
const [myOffersList, setMyOffersList] = useState({loading: true, data: []});
const getMyOffersList = async () => {
try {
const res = await userApi.getOffersList();
setMyOffersList({loading:false, data:res.data});
console.log('SAME', res.data)
} catch (error) {
setMyOffersList({loading:false, data:[]});
console.log("Error getting offers", error);
}
};
useEffect(()=>{
getMyOffersList()
},[])
},[familyOfferList])
return (
<Layout>
<div className="mb-5">
<CustomBreadcrumb
title = {'Ready to Start'}
breadcrumb={
[
{ link: "/", title: "Home" },
{ link: "/pending", title: "Pending", active: true},
]
}
/>
</div>
{myOffersList.loading ?
<div className='w-full flex justify-center items-center rounded-2xl bg-white'>
<LoadingSpinner size='10' color='sky-blue' height='h-[20rem]' />
@@ -49,9 +39,22 @@ export default function FamilyPendingOffer() {
className="mb-10"
/>
:
<div className='w-full h-[30rem] bg-white dark:bg-dark-white flex justify-center items-center rounded-2xl'>
<p className='text-black dark:text-white'>No Record Found!</p>
</div>
<>
<div className="mb-6">
<CustomBreadcrumb
title = {'Ready to Start'}
breadcrumb={
[
{ link: "/", title: "Home" },
{ link: "/pending", title: "Pending", active: true},
]
}
/>
</div>
<div className='w-full h-[30rem] bg-white dark:bg-dark-white flex justify-center items-center rounded-2xl'>
<p className='text-black dark:text-white'>No Record Found!</p>
</div>
</>
}
</Layout>
)
@@ -0,0 +1,340 @@
import React, { useEffect, useState, lazy, Suspense } from 'react'
import LoadingSpinner from '../../Spinners/LoadingSpinner'
import { NewTasks } from './forms'
import { PriceFormatter } from '../../Helpers/PriceFormatter'
import { useSelector } from 'react-redux';
import { InputCom } from '../../AddJob/settings';
import * as Yup from "yup";
import { Form, Formik } from "formik";
// To get the validation schema
const validationSchema = Yup.object().shape({
currency: Yup.string()
.min(1, "Minimum 3 characters")
.max(25, "Maximum 25 characters")
.required("required"),
amount: Yup.number()
.typeError("Invalid number")
.min(1, "Must be greater than 0")
.test("no-e", "Invalid number", (value) => {
if (value && /\d+e/.test(value)) {
return false;
}
return true;
})
.required("required"),
job_description: Yup.string()
.min(3, "Minimum 3 characters")
.max(499, "Maximum 499 characters")
.required("required"),
timeline_days: Yup.number()
.typeError("you must specify a number")
.min(1, "Must be greater than 0")
.required("required"),
});
const VideoElement = lazy(() => import("../../VideoCom/VideoElement")); // LAZY IMPORTING VIDEO COMPONENT
export default function AssignMediaTask({
commonMedia,
requestStatus,
setRequestStatus,
assignMediaTask,
activeMedia,
handleActiveMedia,
closeModal,
family_uid
}) {
// For form initial values
const initialValues = {
// initial values for formik
currency: "",
amount: "",
job_description: "",
timeline_days: "",
media_uid: activeMedia.uid,
family_uid: family_uid,
media_type: "COMMON"
};
const {userDetails} = useSelector((state) => state?.userDetails); // CHECKS IF USER Details are avaliable, to determine if user is active
const { walletDetails } = useSelector((state) => state?.walletDetails); // WALLET STORE
// let imageSrc = (localStorage.getItem("session_token")
// ? `${userDetails?.session_image_server}${localStorage.getItem("session_token")}/job/${activeMedia.uid}` : ""); // FOR GETTING JOB IMAGE
return (
<>
{commonMedia?.loading ? (
<div className="h-[30rem] w-full flex justify-center items-center">
<LoadingSpinner color="sky-blue" size="16" />
</div>
) : (
<Formik
initialValues={initialValues}
validationSchema={validationSchema}
onSubmit={(values, helpers)=>{assignMediaTask(values, helpers)}}
>
{(props) => {
return (
<Form>
<>
<div
className={`job-action-modal-body w-full min-h-[450px] max-h-[450px] overflow-y-auto md:grid md:grid-cols-2`}
>
<div className="p-4 pt-0">
<div className="p-4 w-full min-h-[400px] max-h-[400px] overflow-y-auto bg-slate-100 rounded-md dark:bg-[#11131f] dark:text-white">
{commonMedia?.data?.length ? (
commonMedia?.data?.map((item, index) => (
<div
key={item.uid}
className="mb-2 flex justify-start items-center gap-2 text-sky-blue text-base cursor-pointer"
onClick={() => handleActiveMedia(item)}
>
<input
type="radio"
name="media-list"
checked={activeMedia?.uid == item?.uid}
onChange={() =>
handleActiveMedia(item)
}
className="w-[15px] h-[15px] cursor-pointer"
/>
<p className="w-full text-dark-gray dark:text-white tracking-wide">
{item?.title}
</p>
</div>
))
) : (
<p className="p-8 text-lg text-dark-gray dark:text-white tracking-wide text-center cursor-default">
No Media found!
</p>
)}
</div>
</div>
{/*Right Hand Side for details && Task Type === select */}
<>
{commonMedia?.data?.length > 0 ? (
<div className="p-4 py-0 h-full">
<div className="w-full">
<div className="mb-3 w-full">
<label className="job-label">
Description
</label>
<p className="p-1 text-sm text-slate-900 dark:text-white">
{activeMedia?.description}
</p>
</div>
<div className="my-3 w-full flex items-center justify-center">
<div className="w-full max-w-xs h-28 rounded-2xl flex items-center justify-center">
<Suspense fallback={<p>Loading...</p>}>
<VideoElement videoId={activeMedia?.uid} />
</Suspense>
</div>
</div>
<div className="grid grid-cols-2 gap-3">
{/* Price */}
<div className="field w-full">
<label htmlFor="price" className="job-label flex gap-1">
Price
<span className='text-red-500 text-[10px]'>{props.errors.amount && props.touched.amount && props.errors.amount}</span>
</label>
<InputCom
fieldClass="px-6 text-right"
// label="Price"
// labelClass="tracking-wide"
inputBg="bg-slate-100"
type="number"
name="amount"
placeholder="0"
value={props.values.amount}
inputHandler={props.handleChange}
// error={props.errors.price && props.touched.price && props.errors.price}
/>
</div>
{/* Currency */}
<div className="field w-full">
<label
htmlFor="currency"
className="job-label flex gap-1"
>
Currency
{props.errors.currency && props.touched.currency && <span className="text-[10px] text-red-500">{props.errors.currency}</span>}
</label>
<select
id="currency"
name="currency"
value={props.values.currency}
className={`input-field w-full h-[42px] flex items-center px-2 mt-2 rounded-full placeholder:text-base text-dark-gray dark:text-white bg-slate-100 dark:bg-[#11131F] focus:ring-0 focus:outline-none`}
onChange={props.handleChange}
>
{walletDetails?.loading ? (
<option className="text-slate-500 text-[13.975px]" value="">
Loading...
</option>
) : walletDetails.data.length ? (
<>
<option className="text-slate-500 text-[13.975px]" value="">
Currency
</option>
{walletDetails.data?.map((item, index) => (
<option
key={index}
className="text-slate-500 text-lg"
value={item?.country}
>
{item?.code}
</option>
))}
</>
) : (
<option className="text-slate-500 text-lg" value="">
No Options Found!
</option>
)}
</select>
</div>
</div>
{/* Duration */}
<div className="field w-full">
<label
htmlFor="timeline_days"
className="job-label flex gap-1"
>
Timeline
{props.errors.timeline_days && props.touched.timeline_days && <span className="text-[12px] text-red-500">{props.errors.timeline_days}</span>}
</label>
<select
id="timeline_days"
name="timeline_days"
value={props.values.timeline_days}
className={`input-field w-full h-[42px] flex items-center px-2 mt-2 rounded-full placeholder:text-base text-dark-gray dark:text-white bg-slate-100 dark:bg-[#11131F] focus:ring-0 focus:outline-none`}
onChange={props.handleChange}
>
{publicArray.length && (
<>
<option className="text-slate-500 text-[13.975px]" value="">
Duration
</option>
{publicArray.map(({ name, duration }, idx) => (
<option
key={idx}
className="text-slate-500 text-[13.975px]"
value={duration}
>
{name}
</option>
))}
</>
)}
</select>
</div>
{/* Delivery Detail */}
<div className="my-3">
<label className="w-full job-label flex gap-1">
Delivery Detail
{props.errors.job_description && props.touched.job_description && <span className="text-[12px] text-red-500">{props.errors.job_description}</span>}
</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"
style={{ resize: "none" }}
value={props.values.job_description}
onChange={props.handleChange}
name='job_description'
/>
</div>
</div>
</div>
) : (
<></>
)}
</>
</div>
{/* BTN */}
<div className="modal-footer-wrapper">
{/* error or success display */}
<div className="w-auto h-auto flex items-center">
{requestStatus.message != "" &&
(!requestStatus.status ? (
<div
className={`relative p-2 text-[#912741] bg-[#fcd9e2] border-[#fbc6d3] rounded-[0.475rem] text-md font-light leading-[19.5px] text-[13px] self-start`}
>
{requestStatus.message}
</div>
) : (
requestStatus.status && (
<div
className={`relative p-2 text-green-700 bg-slate-200 border-slate-800 mb-4 rounded-[0.475rem] text-md font-light leading-[19.5px] text-[13px]`}
>
{requestStatus.message}
</div>
)
))}
</div>
{/* End of error or success display */}
<div className="w-auto h-auto flex items-center gap-20">
<button
disabled={requestStatus.loading}
onClick={()=>closeModal()}
type="button"
className="custom-btn border-gradient"
>
<span className="text-gradient">Close</span>
</button>
<div className="">
{requestStatus.loading ? (
<LoadingSpinner color="sky-blue" size="8" />
) : (
<button
type="submit"
disabled={requestStatus.loading}
// onClick={assignFamilyTask}
className="custom-btn btn-gradient text-white"
>
Assign
</button>
)
}
</div>
</div>
</div>
</>
</Form>
)
}
}
</Formik>
)
}
</>
)
}
const publicArray = [
{ duration: 1, name: "1 day" },
{ duration: 2, name: "2 days" },
{ duration: 3, name: "3 days" },
{ duration: 4, name: "4 days" },
{ duration: 5, name: "5 days" },
{ duration: 6, name: "6 days" },
{ duration: 7, name: "1 week" },
{ duration: 14, name: "2 weeks" },
{ duration: 21, name: "3 weeks" },
{ duration: 28, name: "4 weeks" },
];
@@ -0,0 +1,263 @@
import React from 'react'
import LoadingSpinner from '../../Spinners/LoadingSpinner'
import { NewTasks } from './forms'
import { PriceFormatter } from '../../Helpers/PriceFormatter'
import { useSelector } from 'react-redux';
export default function AssignPrevNewTask({
jobList,
requestStatus,
assignFamilyTask,
taskType,
switchTaskType,
formState,
setFormState,
activeTask,
handleActiveTask,
closeModal
}) {
const {userDetails} = useSelector((state) => state?.userDetails); // CHECKS IF USER Details are avaliable, to determine if user is active
let imageSrc = (localStorage.getItem("session_token")
? `${userDetails?.session_image_server}${localStorage.getItem("session_token")}/job/${activeTask.data.job_uid}` : ""); // FOR GETTING JOB IMAGE
return (
<>
{jobList?.loading ? (
<div className="h-[30rem] w-full flex justify-center items-center">
<LoadingSpinner color="sky-blue" size="16" />
</div>
) : (
<>
<div
className={`job-action-modal-body w-full min-h-[450px] max-h-[450px] overflow-y-auto md:grid ${
taskType !== "new" ? "md:grid-cols-2" : "md:grid-cols-1"
}`}
>
<div className="p-4 pt-0">
<div className="mb-2 w-full flex items-center gap-4">
<div className="flex items-center gap-2 text-sky-blue text-base">
<input
type="radio"
name="task-type"
value="select"
className="w-[20px] h-[20px] cursor-pointer"
checked={taskType == "select"}
onChange={switchTaskType}
/>
<span className="text-lg tracking-wide font-semibold">Previous Task</span>
</div>
<div className="flex items-center gap-2 text-sky-blue text-base">
<input
type="radio"
name="task-type"
value="new"
className="w-[20px] h-[20px] cursor-pointer"
checked={taskType == "new"}
onChange={switchTaskType}
/>
<span className="text-lg tracking-wide font-semibold">New Task</span>
</div>
</div>
{/* Task Type === select */}
{taskType == "select" && (
<div className="p-4 w-full h-[400px] overflow-y-auto bg-slate-100 rounded-md dark:bg-[#11131f] dark:text-white">
{jobList?.data?.length ? (
jobList?.data?.map((item, index) => (
<div
key={item.job_uid}
className="mb-2 flex justify-start items-center gap-2 text-sky-blue text-base cursor-pointer"
onClick={() => handleActiveTask(item.job_uid, item)}
>
<input
type="radio"
name="task-list"
checked={
activeTask.id == item.job_uid ||
(activeTask.id == index && true)
}
onChange={() =>
handleActiveTask(item.job_uid, item)
}
className="w-[15px] h-[15px] cursor-pointer"
/>
<p className="w-full text-dark-gray dark:text-white tracking-wide">
{item?.title}
</p>
</div>
))
) : (
<p className="p-8 text-lg text-dark-gray dark:text-white tracking-wide text-center cursor-default">
No Task found!
</p>
)}
</div>
)}
{taskType == "new" && (
<div className="p-4 w-full h-full">
<NewTasks
formState={formState}
setFormState={setFormState}
/>
</div>
)}
</div>
{/*Right Hand Side for details && Task Type === select */}
{taskType == "select" && (
<>
{jobList?.data?.length > 0 ? (
<div className="p-4 pt-0 h-full">
<div className="w-full">
<p className="text-lg font-bold text-dark-gray dark:text-white tracking-wide border-b-2">
{activeTask?.data?.title}
</p>
{/* <div className="my-3">
<Detail
label="Description"
value={activeTask?.data?.description}
/>
</div> */}
<div className="my-3 w-full">
<label className="job-label">
Description
</label>
<p className="p-1 text-sm text-slate-900 dark:text-white">
{activeTask?.data?.description}
</p>
</div>
<div className="grid grid-cols-2">
<div className="w-full">
<div className="my-3 w-full flex items-center gap-1">
<label className="job-label">
Reward
</label>
<p className="p-1 text-sm text-slate-900 dark:text-white">
{PriceFormatter(
activeTask?.data?.price * 0.01,
activeTask?.data?.currency,
activeTask?.data?.curreny_code
)}
</p>
</div>
<div className="my-3 w-full flex items-center gap-1">
<label className="job-label">
Timeline
</label>
<p className="p-1 text-sm text-slate-900 dark:text-white">{`${activeTask?.data?.timeline_days} day(s)`}</p>
</div>
</div>
<div className="w-full flex items-center justify-center">
<div className="w-28 h-28 rounded-2xl flex items-center justify-center">
<img
className="w-full h-auto"
loading="lazy"
src={imageSrc}
alt='job image'
/>
</div>
</div>
</div>
{/* Dummy, no value found for created! thus commented*/}
{/* <div className="my-3 sm:flex items-center">
<Detail
label="Created"
value={`Dummy, no value found for created!`}
/>
</div> */}
<div className="my-3">
<label className="w-full job-label">
Delivery Detail
</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"
style={{ resize: "none" }}
value={activeTask?.data?.job_detail}
readOnly
// onChange={handleInputChange}
/>
</div>
</div>
</div>
) : (
<></>
)}
</>
)}
</div>
{/* BTN */}
<div className="modal-footer-wrapper">
{/* error or success display */}
<div className="w-auto h-auto flex items-center">
{requestStatus.message != "" &&
(!requestStatus.status ? (
<div
className={`relative p-2 text-[#912741] bg-[#fcd9e2] border-[#fbc6d3] rounded-[0.475rem] text-md font-light leading-[19.5px] text-[13px] self-start`}
>
{requestStatus.message}
</div>
) : (
requestStatus.status && (
<div
className={`relative p-2 text-green-700 bg-slate-200 border-slate-800 mb-4 rounded-[0.475rem] text-md font-light leading-[19.5px] text-[13px]`}
>
{requestStatus.message}
</div>
)
))}
</div>
{/* End of error or success display */}
<div className="w-auto h-auto flex items-center gap-20">
<button
disabled={requestStatus.loading}
onClick={()=>closeModal()}
type="button"
className="custom-btn border-gradient"
>
<span className="text-gradient">Close</span>
</button>
<div className="">
{requestStatus.loading ? (
<LoadingSpinner color="sky-blue" size="8" />
) : (
<button
type="button"
disabled={requestStatus.loading}
onClick={assignFamilyTask}
className="custom-btn btn-gradient text-white"
>
Assign
</button>
)
// : (
// <button
// type="button"
// disabled={requestStatus.loading}
// onClick={assignFamilyTask}
// className="px-1 w-40 h-11 flex justify-center items-center btn-gradient text-base rounded-full text-white cursor-pointer"
// >
// {details
// ? `Assign task to ${details?.firstname}`
// : familyDetailsData
// ? `Assign task to ${familyDetailsData.firstname}`
// : "Assign"}
// </button>
// )
}
</div>
</div>
</div>
</>
)
}
</>
)
}
@@ -1,424 +1,410 @@
import React, { useState } from "react";
import React, { Suspense, useEffect, useState } from "react";
import { useDispatch, useSelector } from "react-redux";
import { useLocation } from "react-router-dom";
import usersService from "../../../services/UsersService";
import { tableReload } from "../../../store/TableReloads";
import ModalCom from "../../Helpers/ModalCom";
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";
import { SocketValues } from "../../Contexts/SocketIOContext";
import { errorMsg } from "../../../lib/errorMsg";
const AssignTaskPopout = React.memo(
({
action,
details,
situation,
familyDetailsData,
familyTask,
activeTask,
setActiveTask,
setUpdatePage
}) => {
const apiCall = new usersService();
import AssignPrevNewTask from "./AssignPrevNewTask";
import AssignMediaTask from "./AssignMediaTask";
const dispatch = useDispatch()
const AssignTaskPopout = ({
action,
details,
situation,
familyDetailsData,
jobList,
activeTask,
setActiveTask,
setUpdatePage,
}) => {
const {parentAssignJobToKid} = SocketValues()
let [requestStatus, setRequestStatus] = useState({
loading: false,
status: false,
message: "",
}); // HOLDS RESPONSE FOR SENDING API REQUEST
const apiCall = new usersService();
let [taskType, setTaskType] = useState(details ? "new" : "select"); // SWITCHES BTW SELECT TASK AND NEW TASK
let { pathname, state } = useLocation();
const switchTaskType = ({ target: { value } }) => {
// FUNCTION TO CHANGE SELECTED ACTIVE TASK
setTaskType(value);
};
const {userDetails} = useSelector((state) => state?.userDetails); // CHECKS IF USER Details are avaliable, to determine if user is active
const handleActiveTask = (id = 0, data = {}) => {
// FUNCTION TO CHANGE SELECTED ACTIVE TASK
setActiveTask({ id, data });
};
const [selectedFamilyUid, setSelectedFamilyUid] = useState('');
// New Task
const [formState, setFormState] = useState({
// Initialize form state with desired fields
banner: details?.banner || "default.jpg",
country: details?.country || "",
price: details?.price || "",
title: details?.title || "",
description: details?.description || "",
job_detail: details?.job_detail || "",
timeline_days: details?.timeline_days || "",
category: details?.category || "",
});
const handleFamChange = (event) => {
setSelectedFamilyUid(event.target.value);
};
const assignFamilyTask = () => {
setRequestStatus({ loading: true, status: false, message: "" });
let reqData = {};
if (taskType == "select") {
// RUNS HERE IF TASK TYPE IS SELECT
if (!Object.keys(activeTask.data).length) {
const dispatch = useDispatch();
const getFamilySession = JSON.parse(sessionStorage.getItem("family_list"));
const familyList = getFamilySession?.map((member) => (
<option key={member?.family_uid} value={member?.family_uid}>
{member?.firstname} {member?.lastname}
</option>
));
let [requestStatus, setRequestStatus] = useState({
loading: false,
status: false,
message: "",
}); // HOLDS RESPONSE FOR SENDING API REQUEST
let [commonMedia, setCommonMedia] = useState({loading: true, data: [], image: ''}) // HOLDS COMMON MEDIA DATA
let [activeMedia, setActiveMedia] = useState({}) // HOLDS ACTIVE COMMON MEDIA DATA
const handleActiveMedia = (data = {}) => {
// FUNCTION TO CHANGE SELECTED ACTIVE MEDIA
setActiveMedia({...data});
};
let [assignType, setAssignType] = useState("task"); // SWITCHES BTW TASK AND MEDIA ASSIGNMENT
const switchAssignType = ({ target: { name } }) => {
// FUNCTION TO CHANGE ASSIGN TASK TYPE
setAssignType(name);
};
let [taskType, setTaskType] = useState(details ? "new" : "select"); // SWITCHES BTW SELECT TASK AND NEW TASK
const switchTaskType = ({ target: { value } }) => {
// FUNCTION TO CHANGE SELECTED ACTIVE TASK
setTaskType(value);
};
const handleActiveTask = (id = 0, data = {}) => {
// FUNCTION TO CHANGE SELECTED ACTIVE TASK
setActiveTask({ id, data });
};
// New Task
const [formState, setFormState] = useState({
// Initialize form state with desired fields
banner: details?.banner || "default.jpg",
country: details?.country || "",
price: details?.price || "",
title: details?.title || "",
description: details?.description || "",
job_detail: details?.job_detail || "",
timeline_days: details?.timeline_days || "",
category: details?.category || "",
});
const assignFamilyTask = () => {
setRequestStatus({ loading: true, status: false, message: "" });
if(!selectedFamilyUid){ // If no family found, throw error
setRequestStatus({ loading: false, status: false, message: "Please Select a Kid" });
return setTimeout(() => {
setRequestStatus({ loading: false, status: false, message: "" });
}, 3000);
}
let reqData = {};
if (taskType == "select" && assignType == 'task') {
// RUNS HERE IF TASK TYPE IS SELECT
if (!Object.keys(activeTask.data).length) {
setRequestStatus({
loading: false,
status: false,
message: "No Task is selected",
});
return setTimeout(() => {
setRequestStatus({ loading: false, status: false, message: "" });
}, 3000);
}
reqData = {
// API PAYLOADS
job_id: activeTask.data?.job_id,
job_uid: activeTask.data?.job_uid,
family_uid: selectedFamilyUid
? selectedFamilyUid
: familyDetailsData?.uid || details?.family_uid,
job_description: activeTask.data?.description,
assign_mode: 110011,
};
}
if (taskType === "new" && assignType == 'task') {
const {
banner,
category,
country,
description,
job_detail,
price,
timeline_days,
title,
} = formState;
const requiredFields = {
banner,
// category,
currency: country,
description,
"job detail": job_detail,
price,
timeline: timeline_days,
title,
};
for (let field in requiredFields) {
if (requiredFields[field] == "") {
// let currencyErrMsg = field == "country" && "currency"
setRequestStatus({
loading: false,
status: false,
message: "No Task is seleted",
message: `${
field[0].toUpperCase() + field.slice(1).toLowerCase()
} is empty`,
});
return setTimeout(() => {
setRequestStatus({ loading: false, status: false, message: "" });
}, 3000);
}
reqData = {
// API PAYLOADS
job_id: activeTask.data?.job_id,
job_uid: activeTask.data?.job_uid,
family_uid: familyDetailsData?.uid || details?.family_uid,
job_description: activeTask.data?.description,
assign_mode: 110011,
};
}
if (taskType === "new") {
const {
banner,
category,
country,
description,
job_detail,
price,
timeline_days,
title,
} = formState;
reqData = {
banner,
category,
country,
description,
job_detail,
price: price * 100,
timeline_days,
title,
assign_mode: 110055,
family_uid: selectedFamilyUid
? selectedFamilyUid
: familyDetailsData?.uid || details?.family_uid,
};
}
const requiredFields = {
banner,
// category,
currency:country,
description,
'job detail':job_detail,
price,
timeline:timeline_days,
title,
};
apiCall
.assignFamilyTask(reqData)
.then((res) => {
if (res.status != 200 || res.data.internal_return < 0) {
for (let field in requiredFields) {
if (requiredFields[field] == "") {
// let currencyErrMsg = field == "country" && "currency"
setRequestStatus({
loading: false,
status: false,
message: `${field[0].toUpperCase()+field.slice(1).toLowerCase()} is empty`,
});
return setTimeout(() => {
setRequestStatus({ loading: false, status: false, message: "" });
}, 3000);
}
let error = errorMsg[res?.data?.error_msg] || ''
setRequestStatus({
loading: false,
status: false,
message: error? error : "failed to assign task",
});
return setTimeout(() => {
setRequestStatus({ loading: false, status: false, message: "" });
}, 5000);
}
if (res.status === 200) {
reqData = {
banner,
category,
country,
description,
job_detail,
price: price * 100,
timeline_days,
title,
assign_mode: 110055,
family_uid: details?.family_uid || familyDetailsData?.uid,
};
}
apiCall
.assignFamilyTask(reqData)
.then((res) => {
if (res.status != 200 || res.data.internal_return < 0) {
setRequestStatus({
loading: false,
status: false,
message: "failed to assign task",
});
return setTimeout(() => {
setRequestStatus({ loading: false, status: false, message: "" });
}, 5000);
}
setRequestStatus({
loading: false,
status: true,
message: "action successful",
message: "Task Assigned successfully",
});
setUpdatePage(prev => !prev) // Updates family task page by calling the useeffect hook
setUpdatePage(prev => !prev); // Updates family task page by calling the useeffect hook
dispatch(tableReload({ type: "WALLETTABLE" })); // RELOADS USER WALLET
//SENDS MESSAGE TO SOCKET TO UPDATE CHILD ACCOUNT
// message, room
let socketMsg = {
"audience": "MEMBER",
"action": "REFRESH_OFFER",
"family_uid": reqData.family_uid,
}
let socketRoom = `FAMILY-${userDetails.uid}`
parentAssignJobToKid(socketMsg, socketRoom) //SENDS MESSAGE TO SOCKET TO UPDATE CHILD ACCOUNT
setTimeout(() => {
setRequestStatus({ loading: false, status: false, message: "" });
action(); // FUNCTION THAT CLOSES THE MODAL BOX
}, 5000);
})
.catch((err) => {
setRequestStatus({
loading: false,
status: false,
message: "An Error occured, try again",
});
setTimeout(() => {
setRequestStatus({ loading: false, status: false, message: "" });
}, 5000);
}
})
.catch((err) => {
setRequestStatus({
loading: false,
status: false,
message: "An Error occured, try again",
});
};
return (
<>
<ModalCom
action={action}
situation={situation}
>
<div className="w-11/12 lg:w-[700px] bg-white dark:bg-dark-white lg:rounded-2xl overflow-y-auto">
<div className="w-full flex items-center justify-between lg:px-10 lg:py-8 px-[30px] py-[23px] border-b dark:border-[#5356fb29] border-light-purple">
<h1 className="text-26 font-bold text-dark-gray dark:text-white tracking-wide">
Assign task to{" "}
{familyDetailsData?.firstname || details?.firstName}
</h1>
<button
type="button"
className="text-[#374557] dark:text-red-500"
onClick={action}
setTimeout(() => {
setRequestStatus({ loading: false, status: false, message: "" });
}, 5000);
});
};
const closeModal = () => { // FOR CLOSING ASSIGN TASK MODAL
action()
}
const assignMediaTask = (values, helpers) => { // FUNCTION TO HANDLE ASSIGNING MEDIA TASK
setRequestStatus({ loading: true, status: false, message: "" });
if(!selectedFamilyUid){ // If no family found, throw error
setRequestStatus({ loading: false, status: false, message: "Please Select a Kid" });
return setTimeout(() => {
setRequestStatus({ loading: false, status: false, message: "" });
}, 3000);
}
let reqData = {...values, assign_mode:'110012', family_uid:selectedFamilyUid, media_uid:activeMedia.uid, amount:values.amount * 100}
apiCall.parentAssignMediaTask(reqData).then(res => { // API CALL TO ASSIGN MEDIA TASK
if(res.status != 200 || res.data.internal_return < 0){
setRequestStatus({ loading: false, status: false, message: "Failed to Assign Task" });
return setTimeout(()=>{
setRequestStatus({ loading: false, status: false, message: "" });
}, 3000)
}
setRequestStatus({ loading: false, status: true, message: "Task Assigned Successfully" });
return setTimeout(()=>{
setRequestStatus({ loading: false, status: false, message: "" });
closeModal() // FOR CLOSING ASSIGN MODAL
}, 3000)
}).catch(err => {
setRequestStatus({ loading: false, status: false, message: "Failed, something went wrong. Try Again" });
return setTimeout(()=>{
setRequestStatus({ loading: false, status: false, message: "" });
}, 3000)
})
}
useEffect(()=>{ // effect to update family UID when components mounts
if(familyDetailsData?.uid){
setSelectedFamilyUid(familyDetailsData?.uid)
}else if(details?.family_uid){
setSelectedFamilyUid(details?.family_uid)
}else{
setSelectedFamilyUid('')
}
},[])
useEffect(()=>{
apiCall.getParentCommonMedia().then((res)=>{
// console.log('RESPONSE', res)
setCommonMedia({loading: false, data: res?.data?.result, image: ''})
setActiveMedia(res?.data?.result[0])
}).catch(err => {
setCommonMedia({loading: false, data: [], image: ''})
})
},[])
return (
<>
<ModalCom action={action} situation={situation}>
<div className="w-11/12 lg:w-[700px] bg-white dark:bg-dark-white lg:rounded-2xl overflow-y-auto">
<div className="modal-header-con">
<h1 className="modal-title">
{details ? (
` Assign ${details?.firstname}'s Task`
) : familyDetailsData ? (
` Assign ${familyDetailsData.firstname}'s Task`
) : (
<div className="flex items-center gap-2">
<span className="text-black">Assign task to{" "}</span>
<div className="w-[270px] h-[40px] flex items-center">
<select
name=""
id=""
className="text-lg text-black/80 px-2 tracking-wide font-semibold transition-all cursor-pointer bg-white focus:outline-none border border-gray-200 rounded-full w-full h-full"
onChange={handleFamChange}
value={selectedFamilyUid}
>
<option value="" className="">
Select a kid
</option>
{familyList}
</select>
</div>
</div>
)}
</h1>
<button
type="button"
className="modal-close-btn"
onClick={action}
>
<svg
width="36"
height="36"
viewBox="0 0 36 36"
fill="none"
className="fill-current"
xmlns="http://www.w3.org/2000/svg"
>
<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>
<path
d="M36 16.16C36 17.4399 36 18.7199 36 20.0001C35.7911 20.0709 35.8636 20.2554 35.8385 20.4001C34.5321 27.9453 30.246 32.9248 22.9603 35.2822C21.9006 35.6251 20.7753 35.7657 19.6802 35.9997C18.4003 35.9997 17.1204 35.9997 15.8401 35.9997C15.5896 35.7086 15.2189 35.7732 14.9034 35.7093C7.77231 34.2621 3.08728 30.0725 0.769671 23.187C0.435002 22.1926 0.445997 21.1199 0 20.1599C0 18.7198 0 17.2798 0 15.8398C0.291376 15.6195 0.214408 15.2656 0.270759 14.9808C1.71321 7.69774 6.02611 2.99691 13.0428 0.700951C14.0118 0.383805 15.0509 0.386897 15.9999 0C17.2265 0 18.4532 0 19.6799 0C19.7156 0.124041 19.8125 0.136067 19.9225 0.146719C27.3 0.868973 33.5322 6.21922 35.3801 13.427C35.6121 14.3313 35.7945 15.2484 36 16.16ZM33.011 18.0787C33.0433 9.77105 26.3423 3.00309 18.077 2.9945C9.78479 2.98626 3.00344 9.658 2.98523 17.8426C2.96667 26.1633 9.58859 32.9601 17.7602 33.0079C26.197 33.0577 32.9787 26.4186 33.011 18.0787Z"
fill=""
fillOpacity="0.6"
/>
<path
d="M15.9309 18.023C13.9329 16.037 12.007 14.1207 10.0787 12.2072C9.60071 11.733 9.26398 11.2162 9.51996 10.506C9.945 9.32677 11.1954 9.0811 12.1437 10.0174C13.9067 11.7585 15.6766 13.494 17.385 15.2879C17.9108 15.8401 18.1633 15.7487 18.6375 15.258C20.3586 13.4761 22.1199 11.7327 23.8822 9.99096C24.8175 9.06632 26.1095 9.33639 26.4967 10.517C26.7286 11.2241 26.3919 11.7413 25.9133 12.2178C24.1757 13.9472 22.4477 15.6855 20.7104 17.4148C20.5228 17.6018 20.2964 17.7495 20.0466 17.9485C22.0831 19.974 24.0372 21.8992 25.9689 23.8468C26.9262 24.8119 26.6489 26.1101 25.4336 26.4987C24.712 26.7292 24.2131 26.3441 23.7455 25.8757C21.9945 24.1227 20.2232 22.3892 18.5045 20.6049C18.0698 20.1534 17.8716 20.2269 17.4802 20.6282C15.732 22.4215 13.9493 24.1807 12.1777 25.951C11.7022 26.4262 11.193 26.7471 10.4738 26.4537C9.31345 25.9798 9.06881 24.8398 9.98589 23.8952C11.285 22.5576 12.6138 21.2484 13.9387 19.9355C14.5792 19.3005 15.2399 18.6852 15.9309 18.023Z"
fill="#"
fillOpacity="0.6"
/>
</svg>
</button>
</div>
<div className="modal-body">
<div className="px-4 py-2 w-full flex items-center gap-4">
<button
name='task'
className={`py-1 px-2 font-medium bg-transparent border border-purple text-purple transition-all rounded-md duration-300 ${assignType=='task' && 'bg-yellow-500'}`}
onClick={switchAssignType}
disabled={requestStatus.loading}
>
Task
</button>
<button
name='media'
className={`py-1 px-2 font-medium bg-transparent border border-purple text-purple transition-all rounded-md duration-300 ${assignType=='media' && 'bg-yellow-500'}`}
onClick={switchAssignType}
disabled={requestStatus.loading}
>
Media
</button>
</div>
{familyTask?.loading ? (
<div className="h-[100px] w-full flex justify-center items-center">
<LoadingSpinner color="sky-blue" size="16" />
</div>
) : (
<>
<div
className={`job-action-modal-body w-full md:grid ${
taskType !== "new" ? "md:grid-cols-2" : "md:grid-cols-1"
}`}
>
<div className="p-4">
<div className="mb-2 w-full flex items-center gap-4">
<div className="flex items-center gap-2 text-sky-blue text-base">
<input
type="radio"
name="task-type"
value="select"
className="w-[20px] h-[20px] cursor-pointer"
checked={taskType == "select"}
onChange={switchTaskType}
/>
<span>Select Task</span>
</div>
<div className="flex items-center gap-2 text-sky-blue text-base">
<input
type="radio"
name="task-type"
value="new"
className="w-[20px] h-[20px] cursor-pointer"
checked={taskType == "new"}
onChange={switchTaskType}
/>
<span>New Task</span>
</div>
</div>
{/* Task Type === select */}
{taskType == "select" && (
<div className="p-4 w-full h-[400px] overflow-y-auto bg-slate-100 rounded-md dark:bg-[#11131f] dark:text-white">
{familyTask?.data?.length ? (
familyTask?.data?.map((item, index) => (
<div
key={item.job_uid}
className="mb-2 flex justify-start items-center gap-2 text-sky-blue text-base cursor-pointer"
onClick={() =>
handleActiveTask(item.job_uid, item)
}
>
<input
type="radio"
name="task-list"
checked={
activeTask.id == item.job_uid ||
(activeTask.id == index && true)
}
onChange={() =>
handleActiveTask(item.job_uid, item)
}
className="w-[15px] h-[15px] cursor-pointer"
/>
<p className="w-full text-dark-gray dark:text-white tracking-wide">
{item?.title}
</p>
</div>
))
) : (
<p className="p-8 text-lg text-dark-gray dark:text-white tracking-wide text-center cursor-default">
No Task found!
</p>
)}
</div>
)}
{taskType == "new" && (
<div className="p-4 w-full h-[400px]">
<NewTasks
formState={formState}
setFormState={setFormState}
/>
</div>
)}
</div>
{/*Right Hand Side for details && Task Type === select */}
{taskType == "select" && (
<>
{familyTask?.data?.length > 0 ? (
<div className="p-4">
<div className="w-full">
<p className="text-lg font-bold text-dark-gray dark:text-white tracking-wide border-b-2">
{activeTask?.data?.title}
</p>
<div className="my-3">
<Detail
label="Description"
value={activeTask?.data?.description}
/>
</div>
<div className="flex items-center">
<div className="my-3 w-full flex items-center gap-1">
<label className="text-slate-900 dark:text-white tracking-wide font-semibold">
Price
</label>
<p className="p-1 text-sm text-slate-900 dark:text-white">
{PriceFormatter(
activeTask?.data?.price * 0.01,
activeTask?.data?.currency,
activeTask?.data?.curreny_code
)}
</p>
</div>
<div className="my-3 w-full flex items-center gap-1">
<label className="text-slate-900 dark:text-white tracking-wide font-semibold">
Timeline
</label>
<p className="p-1 text-sm text-slate-900 dark:text-white">{`${activeTask?.data?.timeline_days} day(s)`}</p>
</div>
</div>
<div className="my-3 sm:flex items-center">
<Detail
label="Created"
value={`Dummy, no value found for created!`}
/>
</div>
<div className="my-3">
<label className="w-full text-slate-900 dark:text-white tracking-wide font-semibold">
Delivery Detail
</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"
style={{ resize: "none" }}
value={activeTask?.data?.job_detail}
readOnly
// onChange={handleInputChange}
/>
</div>
</div>
</div>
) : (
<></>
)}
</>
)}
</div>
{/* BTN */}
<div className="py-2 px-4 border-t-2 flex justify-between items-center">
{/* error or success display */}
<div className="w-auto h-auto flex items-center">
{requestStatus.message != "" &&
(!requestStatus.status ? (
<div
className={`relative p-2 text-[#912741] bg-[#fcd9e2] border-[#fbc6d3] rounded-[0.475rem] text-md font-light leading-[19.5px] text-[13px] self-start`}
>
{requestStatus.message}
</div>
) : (
requestStatus.status && (
<div
className={`relative p-2 text-green-700 bg-slate-200 border-slate-800 mb-4 rounded-[0.475rem] text-md font-light leading-[19.5px] text-[13px]`}
>
{requestStatus.message}
</div>
)
))}
</div>
{/* End of error or success display */}
<div className="w-auto h-auto flex items-center gap-3">
<button
disabled={requestStatus.loading}
onClick={action}
type="button"
className="w-20 h-11 flex justify-center items-center border-gradient text-base rounded-full text-white cursor-pointer"
>
<span className="text-gradient">Close</span>
</button>
<div className="">
{requestStatus.loading ? (
<LoadingSpinner color="sky-blue" size="8" />
) : taskType == "select" ? (
<button
type="button"
disabled={requestStatus.loading}
onClick={assignFamilyTask}
className="px-1 w-20 h-11 flex justify-center items-center btn-gradient text-base rounded-full text-white cursor-pointer"
>
Assign
</button>
) : (
<button
type="button"
disabled={requestStatus.loading}
onClick={assignFamilyTask}
className="px-1 w-40 h-11 flex justify-center items-center btn-gradient text-base rounded-full text-white cursor-pointer"
>
{`Assign to ${
familyDetailsData?.firstname || details?.firstName
}`}
</button>
)}
</div>
</div>
</div>
</>
)}
<div className="">
{assignType == 'task' ?
<AssignPrevNewTask
jobList={jobList}
requestStatus={requestStatus}
assignFamilyTask={assignFamilyTask}
taskType={taskType}
switchTaskType={switchTaskType}
formState={formState}
setFormState={setFormState}
activeTask={activeTask}
handleActiveTask={handleActiveTask}
closeModal={closeModal}
/>
:
<AssignMediaTask
commonMedia={commonMedia}
requestStatus={requestStatus}
assignMediaTask={assignMediaTask}
activeMedia={activeMedia}
handleActiveMedia={handleActiveMedia}
closeModal={closeModal}
family_uid = {selectedFamilyUid}
/>
}
</div>
</div>
</ModalCom>
</>
);
}
);
</div>
</ModalCom>
</>
);
};
export default AssignTaskPopout;
@@ -1,13 +1,17 @@
import React, { useEffect, useState } from "react";
import usersService from "../../../../services/UsersService";
import InputCom from "../../../Helpers/Inputs/InputCom";
import { useSelector } from "react-redux";
export default function NewTasks({ formState, setFormState }) {
let [currency, setCurrency] = useState({
loading: true,
status: false,
data: null,
});
const { walletDetails } = useSelector((state) => state?.walletDetails); // WALLET STORE
// let [currency, setCurrency] = useState({
// loading: true,
// status: false,
// data: null,
// });
const selectImage = require(`../../../../assets/images/taskbanners/${
formState.banner || "default.jpg"
@@ -15,25 +19,25 @@ export default function NewTasks({ formState, setFormState }) {
const ApiCall = new usersService();
// FUNCTION TO GET Currency
const getUserCurrency = () => {
setCurrency((prev) => ({ ...prev, loading: true }));
ApiCall.getUserWallets()
.then((res) => {
if (res.data.internal_return < 0) {
setCurrency({ loading: false, status: true, data: [] });
return;
}
// const getUserCurrency = () => {
// setCurrency((prev) => ({ ...prev, loading: true }));
// ApiCall.getUserWallets()
// .then((res) => {
// if (res.data.internal_return < 0) {
// setCurrency({ loading: false, status: true, data: [] });
// return;
// }
setCurrency({
loading: false,
status: true,
data: res.data.result_list,
});
})
.catch((err) => {
setCurrency({ loading: false, status: false, data: [] });
});
};
// setCurrency({
// loading: false,
// status: true,
// data: res.data.result_list,
// });
// })
// .catch((err) => {
// setCurrency({ loading: false, status: false, data: [] });
// });
// };
const handleInputChange = (event) => {
const { name, value } = event.target;
@@ -43,20 +47,100 @@ export default function NewTasks({ formState, setFormState }) {
}));
};
useEffect(() => {
getUserCurrency();
}, []);
// useEffect(() => {
// getUserCurrency();
// }, []);
return (
<form className="w-full flex justify-between items-start">
<div className="flex flex-col gap-3 max-w-[77%]">
<div className="w-full block sm:grid grid-cols-3 gap-2">
<div className="w-full flex flex-col gap-2 col-span-2">
{/* Title */}
<div className="field w-full">
<label htmlFor="title" className="job-label">Title</label>
<InputCom
fieldClass="px-6"
// label="Title"
// labelClass="tracking-wide"
inputBg="bg-slate-100"
type="text"
name="title"
value={formState.title}
inputHandler={handleInputChange}
// blurHandler={props.handleBlur}
// error={props.errors.title && props.touched.title && props.errors.title}
/>
</div>
{/* Description */}
{/* <div className="field w-full mb-[5px]">
<label htmlFor="description" className="job-label">Description</label>
<InputCom
fieldClass="px-6"
// label="Description"
// labelClass="tracking-wide"
inputBg="bg-slate-100"
type="text"
name="description"
value={formState.description}
inputHandler={handleInputChange}
// blurHandler={props.handleBlur}
// error={props.errors.description && props.touched.description && props.errors.description}
/>
</div> */}
<div className="field flex flex-col sm:flex-row w-full gap-2">
<div className="w-full">
<label
htmlFor="description"
className='job-label'
>
Description
</label>
<textarea
id="description"
rows="5"
className={`input-field px-3 py-2 placeholder:text-base text-dark-gray dark:text-white w-full h-[100px] bg-slate-100 dark:bg-[#11131F] focus:ring-0 focus:outline-[#dce4e9] rounded-[10px]`}
style={{ resize: "none" }}
name="description"
value={formState.description}
onChange={handleInputChange}
// onBlur={props.handleBlur}
/>
</div>
</div>
{/* Details */}
<div className="field flex flex-col sm:flex-row w-full gap-2">
<div className="w-full">
<label
htmlFor="Job Delivery Details"
className='job-label'
>
Job Delivery Details
{/* {props.errors.job_detail && props.touched.job_detail && <span className="text-[12px] text-red-500">{props.errors.job_detail}</span>} */}
</label>
<textarea
id="Job Delivery Details"
rows="5"
className={`input-field px-3 py-2 placeholder:text-base text-dark-gray dark:text-white w-full h-[100px] bg-slate-100 dark:bg-[#11131F] focus:ring-0 focus:outline-[#dce4e9] rounded-[10px]`}
style={{ resize: "none" }}
name="job_detail"
value={formState.job_detail}
onChange={handleInputChange}
// onBlur={props.handleBlur}
/>
</div>
</div>
</div>
{/* inputs starts here */}
<div className="grid md:grid-cols-3 grid-cols-1 gap-6 mb-[5px]">
<div className="w-full flex flex-col gap-2">
{/* Currency */}
<div className="field w-full mb-6 xl:mb-0">
<div className="field w-full">
<label
htmlFor="country"
className="input-label text-[#181c32] dark:text-white text-[13.975px] leading-[20.9625px] font-semibold flex item-center gap-1"
className="job-label"
>
Currency
{/* {props.errors.country && props.touched.country && <span className="text-[12px] text-red-500">{props.errors.country}</span>} */}
@@ -65,26 +149,26 @@ export default function NewTasks({ formState, setFormState }) {
id="country"
name="country"
value={formState.country}
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`}
className={`input-field w-full h-[42px] flex items-center px-2 mt-2 rounded-full placeholder:text-base text-dark-gray dark:text-white bg-slate-100 dark:bg-[#11131F] focus:ring-0 focus:outline-none`}
onChange={handleInputChange}
// onBlur={props.handleBlur}
>
{currency?.loading ? (
{walletDetails?.loading ? (
<option className="text-slate-500 text-[13.975px]" value="">
Loading...
</option>
) : currency.data.length ? (
) : walletDetails.data.length ? (
<>
<option className="text-slate-500 text-[13.975px]" value="">
Currency
</option>
{currency.data?.map((item, index) => (
{walletDetails.data?.map((item, index) => (
<option
key={index}
className="text-slate-500 text-lg"
value={item?.country}
>
{item?.description}
{item?.code}
</option>
))}
</>
@@ -98,10 +182,11 @@ export default function NewTasks({ formState, setFormState }) {
{/* Price */}
<div className="field w-full">
<label htmlFor="price" className="job-label">Price</label>
<InputCom
fieldClass="px-6 text-right"
label="Price"
labelClass="tracking-wide"
// label="Price"
// labelClass="tracking-wide"
inputBg="bg-slate-100"
type="number"
name="price"
@@ -114,10 +199,10 @@ export default function NewTasks({ formState, setFormState }) {
</div>
{/* Duration */}
<div className="field w-full mb-6 xl:mb-0">
<div className="field w-full">
<label
htmlFor="timeline_days"
className="input-label text-[#181c32] dark:text-white text-[13.975px] leading-[20.9625px] font-semibold flex item-center gap-1"
className="job-label"
>
Timeline
{/* {props.errors.country && props.touched.country && <span className="text-[12px] text-red-500">{props.errors.country}</span>} */}
@@ -126,7 +211,7 @@ export default function NewTasks({ formState, setFormState }) {
id="timeline_days"
name="timeline_days"
value={formState.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`}
className={`input-field w-full h-[42px] flex items-center px-2 mt-2 rounded-full placeholder:text-base text-dark-gray dark:text-white bg-slate-100 dark:bg-[#11131F] focus:ring-0 focus:outline-none`}
onChange={handleInputChange}
// onBlur={props.handleBlur}
>
@@ -137,6 +222,7 @@ export default function NewTasks({ formState, setFormState }) {
</option>
{publicArray.map(({ name, duration }, idx) => (
<option
key={idx}
className="text-slate-500 text-[13.975px]"
value={duration}
>
@@ -147,72 +233,19 @@ export default function NewTasks({ formState, setFormState }) {
)}
</select>
</div>
</div>
{/* Title */}
<div className="field w-full mb-[5px]">
<InputCom
fieldClass="px-6"
label="Title"
labelClass="tracking-wide"
inputBg="bg-slate-100"
type="text"
name="title"
value={formState.title}
inputHandler={handleInputChange}
// blurHandler={props.handleBlur}
// error={props.errors.title && props.touched.title && props.errors.title}
/>
</div>
{/* Description */}
<div className="field w-full mb-[5px]">
<InputCom
fieldClass="px-6"
label="Description"
labelClass="tracking-wide"
inputBg="bg-slate-100"
type="text"
name="description"
value={formState.description}
inputHandler={handleInputChange}
// blurHandler={props.handleBlur}
// error={props.errors.description && props.touched.description && props.errors.description}
/>
</div>
{/* Details */}
<div className="field flex flex-col sm:flex-row w-full mb-[5px] gap-2">
{/* Banner Image */}
<div className="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'
>
Job Delivery Details
{/* {props.errors.job_detail && props.touched.job_detail && <span className="text-[12px] text-red-500">{props.errors.job_detail}</span>} */}
</label>
<textarea
id="Job Delivery Details"
rows="5"
className={`input-field px-3 py-2 placeholder:text-base text-dark-gray dark:text-white w-full h-[100px] bg-slate-100 dark:bg-[#11131F] focus:ring-0 focus:outline-[#dce4e9] rounded-[10px]`}
style={{ resize: "none" }}
name="job_detail"
value={formState.job_detail}
onChange={handleInputChange}
// onBlur={props.handleBlur}
/>
<div className="h-32 w-full">
<img
src={selectImage}
alt="task_banner_img"
className="w-full h-full object-contain"
/>
</div>
</div>
</div>
</div>
{/* Banner Image */}
<div className="max-w-[20%] w-full">
<div className="h-32 w-full">
<img
src={selectImage}
alt="task_banner_img"
className="w-full h-full object-contain"
/>
</div>
</div>
</form>
);
@@ -1,11 +1,12 @@
import React, { useEffect, useState } from "react";
import { Link, useLocation } from "react-router-dom";
import CustomBreadcrumb from "../../Breadcrumb/CustomBreadcrumb";
import Icons from "../../Helpers/Icons";
import Layout from "../../Partials/Layout";
import { AddFamily, FamilyBanner, Relatives } from "./Tabs";
const FamilySettings = () => {
let {state} = useLocation()
let { state } = useLocation();
const tabs = [
{
id: 1,
@@ -58,15 +59,42 @@ const FamilySettings = () => {
{/* 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>
<CustomBreadcrumb
title={"Family Settings"}
breadcrumb={[
{ link: "/", title: "Home" },
{
link: "/acc-family",
title: "Family Account",
},
{
link: "/familysettings",
title: "Family Settings",
active: true,
},
]}
/>
</div>
<Link to="/acc-family" className="flex gap-2 items-center text-dark-gray dark:text-white">
<svg className="w-5 h-5 rtl:rotate-180" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" strokeWidth="1.5" stroke="currentColor">
<path strokeLinecap="round" strokeLinejoin="round" d="M6.75 15.75L3 12m0 0l3.75-3.75M3 12h18" />
<Link
className="item-content relative text-[18px] transition-all duration-300 ease-in-out bg-[#76a5df] text-white font-medium dark:text-white h-12 px-2 flex items-center gap-2 rounded-md shadow-sm justify-center cursor-pointer dark:bg-[linear-gradient(134.38deg,#f539f8_0%,#c342f9_43.55%,#5356fb_104.51%)]"
to="/acc-family"
>
<svg
xmlns="http://www.w3.org/2000/svg"
fill="none"
viewBox="0 0 24 24"
strokeWidth={1.5}
stroke="currentColor"
className="w-4 h-4"
>
<path
strokeLinecap="round"
strokeLinejoin="round"
d="M10.5 19.5 3 12m0 0 7.5-7.5M3 12h18"
/>
</svg>
Family
<span>Family</span>
</Link>
</div>
+241 -304
View File
@@ -1,314 +1,251 @@
import React, {
Suspense,
lazy,
useEffect,
useMemo,
useRef,
useState,
} from "react";
import { useReactToPrint } from "react-to-print";
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";
import { apiConst } from "../../lib/apiConst";
// Lazy Imports for components
const FamilyWaitlist = lazy(() => import("./Tabs/FamilyWaitlist"));
const FamilyAccount = lazy(() => import("./Tabs/FamilyAccount"));
const FamilyProfile = lazy(() => import("./Tabs/FamilyProfile"));
const FamilyTasks = lazy(() => import("./Tabs/FamilyTasks"));
const FamilyPending = lazy(() => import("./Tabs/FamilyPending"));
export default function FamilyTableNew({
className,
accountDetails,
listReload,
loader,
}) {
// Initial state for family details
const initialDetailState = {
loading: false,
data: null,
};
// console.log('accountDetails',accountDetails)
// State for family details, tasks, waitlist, and pending
const [details, setDetails] = useState({
familyDetails: { ...initialDetailState },
familyTasks: { ...initialDetailState },
familyWaitList: { ...initialDetailState },
familyPending: { ...initialDetailState },
});
// Function to reset family details, tasks, waitlist, and pending
const resetDetails = () => {
setDetails({
familyDetails: { ...initialDetailState },
familyTasks: { ...initialDetailState },
familyWaitList: { ...initialDetailState },
familyPending: { ...initialDetailState },
});
};
const [updatePage, setUpdatePage] = useState(false) // State to determine when to update the page
// State for family task data
const [familyTask, setFamilyTask] = useState({ loading: false, data: [] });
// State for active task
const [activeTask, setActiveTask] = useState({ id: 0, data: {} });
// State for error messages
const [errMsg, setErrMsg] = useState("");
// State for family task popout
const [familyTaskPopout, setFamilyTaskPopout] = useState(false);
// Create an instance of the usersService class
const apiCall = useMemo(() => new usersService(), []);
// Function to handle toggling the family task popout
const familyPopUpHandler = () => {
setFamilyTaskPopout((prev) => !prev);
};
// Ref for the account section
const accountRef = useRef();
// React-to-Print hook for handling printing
const useHandlePrint = useReactToPrint({
content: () => accountRef.current,
});
// Array of tab names
const tabs = [
{ id: 1, name: "Tasks" },
{ id: 2, name: "Waiting" },
{ id: 3, name: "Pending" },
];
// State for the currently selected tab
const [tab, setTab] = useState(tabs[0].name);
// Function to handle tab changes
const tabHandler = (value) => {
setTab(value);
};
// Object that maps tab names to their corresponding components
const tabComponents = {
Tasks: (
<FamilyTasks
className={className}
loader={details.familyTasks.loading}
familyData={details.familyTasks.data}
accountDetails={accountDetails}
/>
),
Waiting: (
<FamilyWaitlist
familyData={details.familyWaitList.data}
accountDetails={accountDetails}
loader={details.familyWaitList.loading}
/>
),
Pending: (
<FamilyPending
familyData={details.familyPending.data}
accountDetails={accountDetails}
loader={details.familyPending.loading}
/>
),
Account: (
<FamilyAccount
familyData={details.familyDetails.data}
myRef={accountRef}
loader={details.familyDetails.loading}
handlePrint={useHandlePrint}
/>
),
Profile: <FamilyProfile familyData={details.familyDetails.data} />,
wallet: <FamilyWallet familyData={details.familyDetails.data} />,
};
// Default tab component
const defaultTabComponent = (
import React, { Suspense, lazy, useEffect, useMemo, useState } from "react";
import { useLocation } from "react-router-dom";
import { apiConst } from "../../lib/apiConst";
import usersService from "../../services/UsersService";
import LoadingSpinner from "../Spinners/LoadingSpinner";
import AssignTaskPopout from "./FamilyPopout/AssignTaskPopout";
import { useSelector } from "react-redux";
// Lazy Imports for components
const FamilyWaitlist = lazy(() => import("./Tabs/FamilyNewWaitlist"));
const FamilyTasks = lazy(() => import("./Tabs/FamilyNewTasks"));
const FamilyPending = lazy(() => import("./Tabs/FamilyNewPending"));
export default function FamilyTableNew() {
const { jobListTable, pendingListTable, parentFamilyTaskList } = useSelector((state) => state.tableReload); // TABLE RELOAD TRIGGERS
let { pathname } = useLocation();
// Initial state for family details
const initialDetailState = {
loading: false,
data: [],
};
// State for family details, tasks, waitlist, and pending
let [familyTasks, setFamilyTasks] = useState({...initialDetailState})
let [familyPending, setFamilyPending] = useState({...initialDetailState})
let [familyWaitList, setFamilyWaitList] = useState({...initialDetailState})
const [updatePage, setUpdatePage] = useState(false); // State to determine when to update the page
// State for list of created jobs by FULL USER
const [jobList, setJobList] = useState({ loading: false, data: [] });
// State for active/selected job
const [activeTask, setActiveTask] = useState({ id: 0, data: {} });
// State for family task popout
const [assignTaskPopout, setAssignTaskPopout] = useState(false);
// Create an instance of the usersService class
const apiCall = useMemo(() => new usersService(), []);
// Function to handle toggling the family task popout
const familyAssignPopUpHandler = () => {
setAssignTaskPopout((prev) => !prev);
};
// Array of tab names
const tabs = [
{ id: 1, name: "Tasks" },
{ id: 2, name: "Waiting" },
{ id: 3, name: "Pending" },
];
// State for the currently selected tab
const [tab, setTab] = useState(tabs[0].name);
// Function to handle tab changes
const tabHandler = (value) => {
setTab(value);
};
// Object that maps tab names to their corresponding components
const tabComponents = {
Tasks: (
<FamilyTasks
className={className}
loader={details.familyTasks.loading}
familyData={details.familyTasks.data}
accountDetails={accountDetails}
familyData={familyTasks}
action={familyAssignPopUpHandler}
/>
);
// Selected tab component based on the current 'tab'
const selectedTabComponent = tabComponents[tab] || defaultTabComponent;
// Effect to manage family details and related data
useEffect(() => {
const manageFamily = async () => {
try {
resetDetails();
setDetails({
familyDetails: { loading: true },
familyTasks: { loading: true },
familyWaitList: { loading: true },
familyPending: { loading: true },
});
const { family_uid } = accountDetails;
const reqData = { family_uid };
const [familyRes, tasksRes, familyWaitRes, familyPending] =
await Promise.all([
apiCall.ManageFamily(reqData),
apiCall.ManageTasks(reqData),
apiCall.ManageFamilyWaitlist(),
apiCall.ManageFamilyPending(),
]);
const familyData = familyRes.data;
const tasksData = tasksRes.data;
const familyWaitData = familyWaitRes.data;
const familyPendingData = familyPending.data;
// Function to check for errors in data
const checkDataError = (data) => data?.internal_return < 0;
if (
checkDataError(familyData) ||
checkDataError(tasksData) ||
checkDataError(familyWaitData) ||
checkDataError(familyPendingData)
) {
return;
}
setDetails({
familyDetails: { loading: false, data: familyData },
familyTasks: { loading: false, data: tasksData },
familyWaitList: { loading: false, data: familyWaitData },
familyPending: { loading: false, data: familyPendingData },
});
} catch (error) {
resetDetails();
setErrMsg("An error occurred");
throw new Error(error);
),
Waiting: (
<FamilyWaitlist
familyData={familyWaitList}
setUpdatePage={setUpdatePage}
/>
),
Pending: (
<FamilyPending
familyData={familyPending}
/>
),
};
const defaultTabComponent = tabComponents.Tasks;
// Selected tab component based on the current 'tab'
const selectedTabComponent = tabComponents[tab] || defaultTabComponent;
// Effect to manage active family task details
useEffect(() => {
const manageTasks = async () => {
setFamilyTasks({loading:true, data: []})
try {
const response = await apiCall.getMyActiveJobList()
if(response.status != 200 || !response?.data){
return setFamilyTasks({loading:false, data: []})
}
};
// Invoke the manageFamily function when the component mounts
manageFamily();
}, [updatePage]);
// Effect to manage family tasks
useEffect(() => {
let checkFamilyTask = true;
const reqData = {
limit: 30,
offset: 0,
job_type: "FAMILY",
action: apiConst.WRENCHBOARD_PICTURE_FAMMEMBER,
};
if (checkFamilyTask) {
setFamilyTask({ loading: true });
apiCall
.getMyJobList(reqData)
.then((res) => {
setFamilyTask({ loading: false, data: res?.data?.result_list });
if (res?.data?.result_list?.length) {
setActiveTask((prev) => ({
...prev,
data: res?.data?.result_list[0],
}));
}
})
.catch((err) => {
setFamilyTask({ loading: false, data: [] });
console.log("Error", err);
});
setFamilyTasks({loading:false, data: response?.data})
} catch (error) {
throw new Error(error);
setFamilyTasks({loading:false, data: []})
}
// Cleanup function to prevent memory leaks
return () => {
checkFamilyTask = false;
};
}, []);
return (
<div
className={`w-full bg-white dark:bg-dark-white overflow-y-auto rounded-2xl section-shadow h-full ${
className || ""
}`}
>
<div className="relative w-full sm:rounded-lg overflow-x-auto">
<Suspense
fallback={
<div className="h-full min-h-[609px] w-full overflow-hidden flex justify-center items-center">
<LoadingSpinner size="16" color="sky-blue" />
</div>
}
>
<div className="w-full h-full text-sm text-left text-gray-500 dark:text-gray-400 relative grid grid-cols-3 min-h-[575px]">
<div className="col-span-3 h-full w-full">
<div className="flex flex-col w-full">
<div className="w-full pr-8 flex items-center gap-1 border-b border-b-[#FAFAF]">
<ul className="flex gap-2 items-center w-full">
{tabs.map(({ name, id }) => (
<li
onClick={() => tabHandler(name)}
className={`p-4 flex hover:text-purple transition-all ease-in-out items-center cursor-pointer overflow-hidden text-xl relative top-[2px] ${
tab === name
? "text-purple border-r"
: "text-thin-light-gray"
}`}
key={id}
>
<h1>{name}</h1>
</li>
))}
</ul>
<button
type="button"
onClick={familyPopUpHandler}
className="p-1 my-1 w-[100px] flex justify-center items-center btn-gradient text-base rounded-full text-white"
>
Add task
</button>
</div>
<div className="flex-[0.9] h-full">
<div className="h-full relative overflow-y-auto">
<Suspense
fallback={<LoadingSpinner size="16" color="sky-blue" />}
};
// Invoke the manageFamily function when the component mounts
manageTasks();
}, [updatePage, parentFamilyTaskList]);
// Effect to manage family wait task details
useEffect(() => {
const manageFamilyWaitlist = async () => {
setFamilyWaitList({loading:true, data: []})
try {
const response = await apiCall.ManageFamilyWaitlist()
if(response.status != 200 || !response?.data){
return setFamilyWaitList({loading:false, data: []})
}
setFamilyWaitList({loading:false, data: response?.data})
} catch (error) {
throw new Error(error);
setFamilyWaitList({loading:false, data: []})
}
};
// Invoke the manageFamily function when the component mounts
manageFamilyWaitlist();
}, [updatePage]);
// Effect to manage family pending task details
useEffect(() => {
const manageFamilyPending = async () => {
setFamilyPending({loading:true, data: []})
try {
const response = await apiCall.ManageFamilyPending()
if(response.status != 200 || !response?.data){
return setFamilyPending({loading:false, data: []})
}
setFamilyPending({loading:false, data: response?.data})
} catch (error) {
throw new Error(error);
setFamilyPending({loading:false, data: []})
}
};
// Invoke the manageFamily function when the component mounts
manageFamilyPending();
}, [updatePage, pendingListTable]);
// Effect to get all parent job list
useEffect(() => {
const reqData = {
limit: 30,
offset: 0,
job_type: "FAMILY",
action: apiConst.WRENCHBOARD_PICTURE_FAMMEMBER,
};
setJobList({ loading: true });
apiCall
.getMyJobList(reqData)
.then((res) => {
setJobList({ loading: false, data: res?.data?.result_list });
if (res?.data?.result_list?.length) {
setActiveTask((prev) => ({
...prev,
data: res?.data?.result_list[0],
}));
}
})
.catch((err) => {
setJobList({ loading: false, data: [] });
console.log("Error", err);
});
}, []);
// console.log(updatePage);
return (
<div
className={`w-full bg-white dark:bg-dark-white overflow-y-auto rounded-2xl section-shadow h-full`}
>
<div className="relative w-full sm:rounded-lg overflow-x-auto">
<Suspense
fallback={
<div className="h-full min-h-[609px] w-full overflow-hidden flex justify-center items-center">
<LoadingSpinner size="16" color="sky-blue" />
</div>
}
>
<div className="w-full h-full text-sm text-left text-gray-500 dark:text-gray-400 relative grid grid-cols-3 min-h-[575px]">
<div className="col-span-3 h-full w-full">
<div className="flex flex-col w-full">
<div className="w-full pr-8 flex items-center gap-1 border-b border-b-[#FAFAF]">
<ul className="flex gap-2 items-center w-full">
{tabs.map(({ name, id }) => (
<li
onClick={() => tabHandler(name)}
className={`p-4 flex hover:text-purple transition-all ease-in-out items-center cursor-pointer overflow-hidden text-xl relative top-[2px] ${
tab === name
? "text-purple border-r"
: "text-thin-light-gray"
}`}
key={id}
>
{selectedTabComponent}
</Suspense>
</div>
<h1>{name}</h1>
</li>
))}
</ul>
<button
type="button"
onClick={familyAssignPopUpHandler}
className="p-1 my-1 w-[100px] flex justify-center items-center btn-gradient text-base rounded-full text-white"
>
Add task
</button>
</div>
<div className="flex-[0.9] h-full">
<div className="h-full relative overflow-y-auto">
<Suspense
fallback={<LoadingSpinner size="16" color="sky-blue" />}
>
{selectedTabComponent}
</Suspense>
</div>
</div>
</div>
</div>
</Suspense>
</div>
{familyTaskPopout && (
<AssignTaskPopout
action={familyPopUpHandler}
situation={familyTaskPopout}
familyTask={familyTask}
setFamilyTask={setFamilyTask}
setActiveTask={setActiveTask}
activeTask={activeTask}
familyDetailsData={details.familyDetails.data}
setUpdatePage={setUpdatePage}
/>
)}
</div>
</Suspense>
</div>
);
}
{assignTaskPopout && (
<AssignTaskPopout
action={familyAssignPopUpHandler}
situation={assignTaskPopout}
jobList={jobList}
setActiveTask={setActiveTask}
activeTask={activeTask}
familyDetailsData={''}
setUpdatePage={setUpdatePage}
/>
)}
</div>
);
}
@@ -3,7 +3,7 @@ import QRCode from "react-qr-code";
import { useSelector } from "react-redux";
import LoadingSpinner from "../../Spinners/LoadingSpinner";
const FamilyAccount = forwardRef(({ familyData, myRef, handlePrint, loader }, ref) => {
const FamilyAccount = forwardRef(({ familyData, myRef, handlePrint}, ref) => {
const { userDetails } = useSelector((state) => state.userDetails);
return (
<div
@@ -16,13 +16,13 @@ const FamilyAccount = forwardRef(({ familyData, myRef, handlePrint, loader }, re
<h2 className="font-bold text-lg tracking-wide line-clamp-1 text-dark-gray dark:text-white capitalize">
Username:{" "}
<span className="ml-2 normal-case">
{familyData?.username ? familyData?.username : "please wait..."}
{familyData?.data?.username ? familyData?.data?.username : "please wait..."}
</span>
</h2>
<h2 className="font-bold text-lg tracking-wide line-clamp-1 text-dark-gray dark:text-white capitalize">
Pin:{" "}
<span className="ml-2 normal-case">
{familyData?.pin ? familyData?.pin : "please wait..."}
{familyData?.data?.pin ? familyData?.data?.pin : "please wait..."}
</span>
</h2>
</div>
@@ -35,7 +35,7 @@ const FamilyAccount = forwardRef(({ familyData, myRef, handlePrint, loader }, re
<p className="text-xl tracking-wide mb-[15px] text-center font-bold text-dark-gray dark:text-white">
Scan the code from mobile app
</p>
{loader ?
{familyData.loading ?
<div className="w-full">
<LoadingSpinner size='8' color='sky-blue' />
</div>
@@ -44,7 +44,7 @@ const FamilyAccount = forwardRef(({ familyData, myRef, handlePrint, loader }, re
size={256}
style={{ height: "auto", maxWidth: "100%", width: "100%" }}
// value={`https://www.google.com`}
value={`${userDetails?.uid}@${familyData?.username}@${familyData?.uid}`}
value={`${userDetails?.uid}@${familyData?.data?.username}@${familyData?.data?.uid}`}
viewBox={`0 0 256 256`}
/>
}
@@ -0,0 +1,161 @@
import { useMemo, useState } from "react";
import { PriceFormatter } from "../../Helpers/PriceFormatter";
import { PaginatedList, handlePagingFunc } from "../../Pagination";
import PendingJobsPopout from "../../jobPopout/PendingJobsPopout";
import LoadingSpinner from "../../Spinners/LoadingSpinner";
export default function FamilyPending({ familyData }) {
let [jobPopout, setJobPopout] = useState({ show: false, data: {} }); // STATE TO HOLD THE VALUE OF THE ALERT DETAILS AND DETERMINE WHEN TO SHOW
let filteredFamilyData = useMemo(
() => familyData?.data?.result_list?.filter((data) => data.family_uid !== ""),
[familyData?.data?.result_list]
);
const [currentPage, setCurrentPage] = useState(0);
const itemsPerPage = Number(process.env.REACT_APP_ITEM_PER_PAGE);
const indexOfFirstItem = Number(currentPage);
const indexOfLastItem =
Number(indexOfFirstItem) + Number(process.env.REACT_APP_ITEM_PER_PAGE);
const currentPendingTasks = filteredFamilyData?.slice(
indexOfFirstItem,
indexOfLastItem
);
const handlePagination = (e) => {
handlePagingFunc(e, setCurrentPage);
};
return (
<div
className={`update-table w-full p-3 bg-white dark:bg-dark-white overflow-hidden rounded-2xl section-shadow lg:min-h-[538px]`}
>
{familyData.loading ?
(<div className="w-full h-full flex justify-center items-center">
<LoadingSpinner size={16} color="sky-blue" height='h-[30rem]' />
</div>
):
<>
{familyData?.data?.result_list && (
<div className="relative w-full overflow-x-auto sm:rounded-lg flex flex-col justify-between h-full">
<table className="w-full text-sm text-left text-gray-500 dark:text-gray-400">
<tbody>
{
<>
{currentPendingTasks.length > 0 ? (
currentPendingTasks.map((value, index) => {
let deliveryDate = value?.expire?.split(" ")[0];
let thePrice = PriceFormatter(
value?.price * 0.01,
value?.currency_code,
value?.currency
);
let image = `${familyData?.data?.session_image_server}${localStorage.getItem(
"session_token"
)}/job/${value.job_uid}`;
return (
<tr
key={index}
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">
<div className="w-[60px] h-[60px] p-2 bg-alice-blue rounded-full overflow-hidden flex justify-center items-center">
<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">
{value.title}
</h1>
<div>{value.description}</div>
<span className="text-sm text-thin-light-gray flex items-start gap-1">
Price:{" "}
<span className="text-purple">
{thePrice}
</span>
</span>
<div className="flex items-center gap-4">
<span className="text-sm text-thin-light-gray">
Duration:{" "}
<span className="text-purple">
{" "}
{value.timeline_days} day(s)
</span>
</span>
<span className="text-sm text-thin-light-gray">
Expire:{" "}
<span className="text-purple">
{" "}
{deliveryDate}
</span>
</span>
<span className="text-sm text-thin-light-gray">
Sent to:{" "}
<span className="text-purple">
{" "}
{value.job_to}
</span>
</span>
</div>
</div>
</div>
</td>
<td className="text-right py-4 px-2 flex justify-end items-center">
<button
type="button"
onClick={() => {
setJobPopout({ show: true, data: value });
}}
className="w-20 h-11 flex justify-center items-center btn-gradient text-base rounded-full text-white"
>
View
</button>
</td>
</tr>
);
})
) : (
<tr>
<td className="font-bold text-xl text-dark-gray dark:text-white whitespace-nowrap text-center py-4 px-2">
No Pending Task!
</td>
</tr>
)}
</>
}
</tbody>
</table>
{/* PAGINATION BUTTON */}
<PaginatedList
onClick={handlePagination}
prev={currentPage == 0}
next={currentPage + itemsPerPage >= filteredFamilyData.length}
data={filteredFamilyData}
start={indexOfFirstItem}
stop={indexOfLastItem}
/>
{/* END OF PAGINATION BUTTON */}
</div>
)}
</>
}
{/* Active Job Popout */}
{jobPopout.show && (
<PendingJobsPopout
details={jobPopout.data}
onClose={() => {
setJobPopout({ show: false, data: {} });
}}
situation={jobPopout.show}
/>
)}
{/* End of Active Job Popout */}
</div>
);
}
@@ -0,0 +1,169 @@
import React, { useMemo, useState } from "react";
import { useLocation, useNavigate } from "react-router-dom";
import Icons from "../../Helpers/Icons";
import { PriceFormatter } from "../../Helpers/PriceFormatter";
import { handlePagingFunc } from "../../Pagination/HandlePagination";
import PaginatedList from "../../Pagination/PaginatedList";
import LoadingSpinner from "../../Spinners/LoadingSpinner";
export default function FamilyNewTasks({
familyData,
className,
action,
}) {
let navigate = useNavigate();
let { pathname } = useLocation();
// ...
let filteredFamilyData = useMemo(
() => familyData?.data?.result_list?.filter((data) => data.family_uid !== ""),
[familyData?.data?.result_list]
);
const [currentPage, setCurrentPage] = useState(0);
const indexOfFirstItem = Number(currentPage);
const indexOfLastItem =
Number(indexOfFirstItem) + Number(process.env.REACT_APP_ITEM_PER_PAGE);
const currentTasks = filteredFamilyData?.slice(
indexOfFirstItem,
indexOfLastItem
);
const handlePagination = (e) => handlePagingFunc(e, setCurrentPage);
return (
<div
className={`update-table w-full bg-white dark:bg-dark-white h-full lg:min-h-[538px] overflow-hidden rounded-2xl section-shadow p-3 ${
familyData?.data?.result_list?.length <= 0 && "flex items-center justify-center"
}`}
>
{familyData.loading ? (
<div className="w-full h-full flex justify-center items-center">
<LoadingSpinner size={16} color="sky-blue" height='h-[30rem]' />
</div>
) : (
<>
{familyData?.data?.result_list && (
<div className="relative w-full overflow-x-auto sm:rounded-lg flex flex-col justify-between h-full">
<table className="w-full text-sm text-left text-gray-500 dark:text-gray-400">
<tbody>
{
<>
{familyData?.data?.result_list?.length <= 0 ? (
<tr>
<td
colSpan="2"
className="text-center py-4 font-bold text-xl text-dark-gray dark:text-white whitespace-nowrap flex items-center justify-center gap-1"
>
You currently have no active tasks{" "}
<span
className="text-blue-400 cursor-pointer hover:underline transition duration-150"
onClick={action}
>
add new
</span>
</td>
</tr>
) : (
currentTasks.map((value, index) => {
// find due date
const dueDate = value?.delivery_date.split(" ")[0];
// the price
let thePrice = PriceFormatter(
value?.price * 0.01,
value?.currency_code,
value?.currency
);
let image = `${familyData?.data?.result_list}${localStorage.getItem(
"session_token"
)}/job/${value.job_uid}`;
return (
<tr
key={index}
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">
<div className="w-[60px] h-[60px] p-2 bg-alice-blue rounded-full overflow-hidden flex justify-center items-center">
<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">
{value.title}
</h1>
<div className="flex flex-col sm:flex-row items-start gap-1 md:gap-4 md:items-center">
<span className="text-sm text-thin-light-gray flex flex-start gap-1">
Price:{" "}
<span className="text-purple">
{thePrice}
</span>
</span>
<span className="text-sm text-thin-light-gray">
Duration:{" "}
<span className="text-purple">
{" "}
{value.timeline_days} day(s)
</span>
</span>
<span className="text-sm text-thin-light-gray">
Due Date:{" "}
<span className="text-purple">
{" "}
{dueDate}
</span>
</span>
</div>
</div>
</div>
</td>
<td className="text-right py-4 px-2">
<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"
>
<Icons name="right-arrow" />
</button>
</td>
</tr>
);
})
)}
</>
}
</tbody>
</table>
{/* PAGINATION BUTTON */}
<PaginatedList
onClick={handlePagination}
prev={currentPage == 0 ? true : false}
next={
currentPage + Number(process.env.REACT_APP_ITEM_PER_PAGE) >=
filteredFamilyData?.length
? true
: false
}
data={filteredFamilyData}
start={indexOfFirstItem}
stop={indexOfLastItem}
/>
{/* END OF PAGINATION BUTTON */}
</div>
)}
</>
)}
</div>
);
}
@@ -0,0 +1,152 @@
import { useState } from "react";
import SuggestTask from "../../FamilyPopup/SuggestTask";
import { PaginatedList, handlePagingFunc } from "../../Pagination";
import LoadingSpinner from "../../Spinners/LoadingSpinner";
import AssignTaskPopout from "../FamilyPopout/AssignTaskPopout";
import Icons from "../../Helpers/Icons";
const FamilyNewWaitlist = ({
familyData,
className,
setUpdatePage
}) => {
const [popUp, setPopUp] = useState({ show: false, data: {} });
const [continueTaskPopup, setContinueTaskPopup] = useState({
show: false,
data: {},
});
const [currentPage, setCurrentPage] = useState(0);
const itemsPerPage = Number(process.env.REACT_APP_ITEM_PER_PAGE);
const indexOfFirstItem = currentPage;
const indexOfLastItem = currentPage + itemsPerPage;
const currentTask = familyData?.data?.result_list?.slice(
indexOfFirstItem,
indexOfLastItem
);
const handlePagination = (e) => handlePagingFunc(e, setCurrentPage);
const openPopUp = (value) => {
setPopUp({ show: true, data: { ...value } });
};
const closePopUp = () => {
setPopUp({ show: false, data: {} });
};
const openContinueTaskPopup = (value) => {
setContinueTaskPopup({ show: true, data: { ...value } });
};
const closeContinueTaskPopup = () => {
setContinueTaskPopup({ show: false, data: {} });
};
console.log("Check this >>",continueTaskPopup)
return (
<div
className={`update-table w-full bg-white dark:bg-dark-white h-full lg:min-h-[538px] p-3 overflow-hidden rounded-2xl section-shadow ${
className || ""
}`}
>
{familyData.loading ? (
<div className="w-full h-full flex justify-center items-center">
<LoadingSpinner size={16} color="sky-blue" height='h-[30rem]' />
</div>
) : (
<>
{familyData?.data?.result_list && (
<div className="relative w-full overflow-x-auto sm:rounded-lg flex flex-col justify-between h-full">
<table className="w-full text-sm text-left text-gray-500 dark:text-gray-400">
<tbody>
{currentTask.map((value) => {
const addedDate = value?.added.split(" ")[0];
const selectedImage = require(`../../../assets/images/family/${
value?.banner || "default.jpg"
}`);
// console.log("VALUE", value);
// let image = `${familyData?.data?.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"
key={value.uid}
>
<td className="py-4">
<div className="w-full flex justify-between items-center">
<div className="account-name flex space-x-4 items-center">
<div className="icon w-14 h-14 flex justify-center items-center">
<img
src={selectedImage}
alt="task_img"
className="w-full object-cover"
/>
</div>
<div className="">
<p className="text-xl font-bold text-dark-gray dark:text-white mb-2 capitalize line-clamp-1">
{value.title}
</p>
<p className="text-sm text-thin-light-gray font-medium">
{value.description}
</p>
</div>
</div>
<div className="px-2 flex flex-col items-center justify-center">
<p className="text-sm font-bold text-dark-gray dark:text-white">
{addedDate}
</p>
<p className="text-xs py-1.5 w-[70px] cursor-default tracking-wide rounded-full bg-gold text-white flex justify-center items-center">
{value.firstname}
</p>
</div>
</div>
</td>
<td className="text-right py-4 px-2 flex justify-end">
<button
onClick={() => openPopUp(value)}
className="w-20 h-11 flex justify-center items-center btn-gradient text-base rounded-full text-white"
>
{/* View */}
<Icons name="right-arrow" />
</button>
</td>
</tr>
);
})}
</tbody>
</table>
<PaginatedList
onClick={handlePagination}
prev={currentPage === 0}
next={currentPage + itemsPerPage >= familyData?.data?.result_list?.length}
data={familyData?.data?.result_list}
start={indexOfFirstItem}
stop={indexOfLastItem}
/>
</div>
)}
</>
)}
{popUp.show && (
<SuggestTask
details={popUp.data}
onClose={closePopUp}
continuePopupData={openContinueTaskPopup}
situation={popUp.show}
/>
)}
{continueTaskPopup.show && (
<AssignTaskPopout
details={continueTaskPopup.data}
action={closeContinueTaskPopup}
situation={continueTaskPopup.show}
setUpdatePage={setUpdatePage}
/>
)}
</div>
);
};
export default FamilyNewWaitlist;
@@ -3,21 +3,23 @@ import localImgLoad from "../../../lib/localImgLoad";
import { PriceFormatter } from "../../Helpers/PriceFormatter";
import { PaginatedList, handlePagingFunc } from "../../Pagination";
import PendingJobsPopout from "../../jobPopout/PendingJobsPopout";
import LoadingSpinner from "../../Spinners/LoadingSpinner";
export default function FamilyPending({
familyData,
className,
accountDetails,
loader,
setUpdatePage,
}) {
let [jobPopout, setJobPopout] = useState({ show: false, data: {} }); // STATE TO HOLD THE VALUE OF THE ALERT DETAILS AND DETERMINE WHEN TO SHOW
let filteredFamilyData = useMemo(
() =>
familyData?.result_list?.filter(
familyData?.data?.result_list?.filter(
(data) => data?.family_uid === accountDetails?.family_uid
),
[accountDetails?.family_uid, familyData?.result_list]
[accountDetails?.family_uid, familyData?.data?.result_list]
);
const [currentPage, setCurrentPage] = useState(0);
@@ -40,6 +42,12 @@ export default function FamilyPending({
className || ""
}`}
>
{familyData.loading ?
(<div className="w-full h-full flex justify-center items-center">
<LoadingSpinner size={16} color="sky-blue" height='h-[30rem]' />
</div>
):
<>
{filteredFamilyData && (
<div className="relative w-full overflow-x-auto sm:rounded-lg flex flex-col justify-between h-full">
<table className="w-full text-sm text-left text-gray-500 dark:text-gray-400">
@@ -54,7 +62,7 @@ export default function FamilyPending({
value?.currency_code,
value?.currency
);
let image = `${familyData.session_image_server}${localStorage.getItem('session_token')}/job/${value.job_uid}`
let image = `${familyData.data.session_image_server}${localStorage.getItem('session_token')}/job/${value.job_uid}`
return (
<tr
key={index}
@@ -144,6 +152,8 @@ export default function FamilyPending({
{/* END OF PAGINATION BUTTON */}
</div>
)}
</>
}
{/* Active Job Popout */}
{jobPopout.show && (
@@ -151,6 +161,7 @@ export default function FamilyPending({
details={jobPopout.data}
onClose={() => {
setJobPopout({ show: false, data: {} });
setUpdatePage(prev => !prev);
}}
situation={jobPopout.show}
/>
+13 -14
View File
@@ -10,7 +10,6 @@ import LoadingSpinner from "../../Spinners/LoadingSpinner";
export default function FamilyTasks({
familyData,
className,
loader,
accountDetails,
}) {
let navigate = useNavigate();
@@ -19,17 +18,17 @@ export default function FamilyTasks({
// ...
const filteredFamilyData = useMemo(
() =>
familyData?.result_list?.filter(
familyData?.data?.result_list?.filter(
(data) => data?.family_uid === accountDetails?.family_uid
),
[familyData, accountDetails]
[familyData?.data, accountDetails]
);
const [currentPage, setCurrentPage] = useState(0);
const indexOfFirstItem = Number(currentPage);
const indexOfLastItem =
Number(indexOfFirstItem) + Number(process.env.REACT_APP_ITEM_PER_PAGE);
const currentTask = familyData?.result_list.slice(
const currentTask = familyData?.data?.result_list?.slice(
indexOfFirstItem,
indexOfLastItem
);
@@ -43,21 +42,21 @@ export default function FamilyTasks({
className || ""
}`}
>
{loader ? (
<div className="w-full h-full flex justify-center items-center lg:min-h-[470px]">
<LoadingSpinner size={16} color="sky-blue" />
{familyData.loading ? (
<div className="w-full h-full flex justify-center items-center">
<LoadingSpinner size={16} color="sky-blue" height='h-[30rem]' />
</div>
) : (
<>
{familyData && familyData?.result_list && (
{familyData?.data && familyData?.data?.result_list && (
<div className="relative w-full overflow-x-auto sm:rounded-lg flex flex-col justify-between h-full">
<table className="w-full text-sm text-left text-gray-500 dark:text-gray-400">
<tbody>
{
<>
{familyData &&
familyData?.result_list &&
familyData.result_list.length > 0 ? (
{familyData?.data &&
familyData?.data?.result_list &&
familyData?.data.result_list.length > 0 ? (
currentTask.map((value, index) => {
// find due date
const dueDate = value?.delivery_date.split(" ")[0];
@@ -67,7 +66,7 @@ export default function FamilyTasks({
value?.currency_code,
value?.currency
);
let image = `${familyData.session_image_server}${localStorage.getItem('session_token')}/job/${value.job_uid}`
let image = `${familyData?.data?.session_image_server}${localStorage.getItem('session_token')}/job/${value.job_uid}`
return (
<tr
key={index}
@@ -149,11 +148,11 @@ export default function FamilyTasks({
prev={currentPage == 0 ? true : false}
next={
currentPage + Number(process.env.REACT_APP_ITEM_PER_PAGE) >=
familyData?.result_list.length
familyData?.data?.result_list.length
? true
: false
}
data={familyData?.result_list}
data={familyData?.data?.result_list}
start={indexOfFirstItem}
stop={indexOfLastItem}
/>
@@ -5,7 +5,7 @@ import LoadingSpinner from "../../Spinners/LoadingSpinner";
import AssignTaskPopout from "../FamilyPopout/AssignTaskPopout";
const FamilyWaitlist = memo(
({ familyData, className, accountDetails, loader }) => {
({ familyData, className, accountDetails, setUpdatePage }) => {
const [popUp, setPopUp] = useState({ show: false, data: {} });
const [continueTaskPopup, setContinueTaskPopup] = useState({
show: false,
@@ -13,10 +13,10 @@ const FamilyWaitlist = memo(
});
const filteredFamilyData = useMemo(
() =>
familyData?.result_list?.filter(
familyData?.data?.result_list?.filter(
(data) => data?.family_uid === accountDetails?.family_uid
),
[familyData, accountDetails]
[familyData.data, accountDetails]
);
const [currentPage, setCurrentPage] = useState(0);
@@ -52,9 +52,9 @@ const FamilyWaitlist = memo(
className || ""
}`}
>
{loader ? (
<div className="w-full h-full flex justify-center items-center lg:min-h-[470px]">
<LoadingSpinner size={16} color="sky-blue" />
{familyData.loading ? (
<div className="w-full h-full flex justify-center items-center">
<LoadingSpinner size={16} color="sky-blue" height='h-[30rem]' />
</div>
) : (
<>
@@ -67,8 +67,7 @@ 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}`
// let image = `${familyData.data.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"
@@ -144,6 +143,7 @@ const FamilyWaitlist = memo(
details={continueTaskPopup.data}
action={closeContinueTaskPopup}
situation={continueTaskPopup.show}
setUpdatePage={setUpdatePage}
/>
)}
</div>
@@ -15,7 +15,7 @@ function FamilyWallet({familyData}) {
useEffect(()=>{
setFamilyWallet({loading:true, data: []})
apiUrl.getFamilyWallet({family_uid:familyData?.uid}).then(res => {
apiUrl.getKidWallets({family_uid:familyData?.uid}).then(res => {
setFamilyWallet({loading:false, data: res?.data?.result_list || []})
}).catch(error => {
setFamilyWallet({loading:false, data: []})
@@ -9,8 +9,9 @@ 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";
import { useDispatch, useSelector } from "react-redux";
import { apiConst } from "../../../../lib/apiConst";
import { SocketValues } from "../../../Contexts/SocketIOContext";
const validationSchema = Yup.object().shape({
// amount: Yup.string()
@@ -30,6 +31,11 @@ const validationSchema = Yup.object().shape({
});
function FamilyAddFundPopout({ action, situation, wallet, familyData }) {
const {userDetails} = useSelector((state) => state?.userDetails); // Gets User Detail
const { parentAssignJobToKid } = SocketValues() // socket emit event from FULL account
const dispatch = useDispatch();
const apiUrl = new usersService();
@@ -54,6 +60,7 @@ function FamilyAddFundPopout({ action, situation, wallet, familyData }) {
};
// 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
@@ -132,6 +139,17 @@ function FamilyAddFundPopout({ action, situation, wallet, familyData }) {
status: true,
message: "Transfer Successful",
});
//SENDS MESSAGE TO SOCKET TO UPDATE CHILD ACCOUNT
// message, room
let socketMsg = {
"audience": "MEMBER",
"action": "REFRESH_WALLET",
"family_uid": reqData.family_uid,
}
let socketRoom = `FAMILY-${userDetails.uid}`
parentAssignJobToKid(socketMsg, socketRoom) //SENDS MESSAGE TO SOCKET TO UPDATE CHILD ACCOUNT
setTimeout(() => {
setRequestStatus({ loading: false, status: false, message: "" });
dispatch(tableReload({ type: "WALLETTABLE" })); // UPDATES PARENT WALLET ACCOUNT
@@ -170,13 +188,13 @@ function FamilyAddFundPopout({ action, situation, wallet, familyData }) {
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">
<div className="modal-header-con">
<h1 className="modal-title">
Add Fund
</h1>
<button
type="button"
className="text-[#374557] dark:text-red-500"
className="modal-close-btn"
onClick={action}
>
<svg
@@ -271,7 +289,7 @@ function FamilyAddFundPopout({ action, situation, wallet, familyData }) {
<div className="field w-full mb-[0.5rem]">
<div className="w-full">
<label
htmlFor="Job Delivery Details"
htmlFor="job-label"
className="input-label text-[#181c32] dark:text-white text-[13.975px] leading-[20.9625px] font-semibold flex items-center gap-1"
>
Comment
+14 -13
View File
@@ -109,6 +109,7 @@ export default function FamilyAcc() {
if (data?.internal_return >= 0 && data?.status === "OK") {
const { result_list, session_image_server } = data;
setFamilyList({ result_list, session_image_server });
sessionStorage.setItem("family_list", JSON.stringify(result_list))
setLoader(false);
} else {
return;
@@ -145,7 +146,7 @@ export default function FamilyAcc() {
breadcrumb={
[
{ link: "/", title: "Home" },
{ link: "/acc-family", title: "Family-acc", active: true},
{ link: "/acc-family", title: "Family Account", active: true},
]
}
/>
@@ -164,13 +165,13 @@ export default function FamilyAcc() {
</h1>
</div>
<div className="flex gap-2 items-center">
<Link to="/acc-family/activities" className={`nav-item flex items-center `}>
<div className="flex gap-4 items-center">
<Link state={familyList} to="/acc-family/activities" className={`nav-item flex items-center rounded-md shadow-sm justify-center cursor-pointer dark:bg-[linear-gradient(134.38deg,#f539f8_0%,#c342f9_43.55%,#5356fb_104.51%)] bg-[#76a5df] text-white px-2 gap-2`}>
<span className="item-icon group-hover:bg-purple group-hover:text-white w-8 h-8 flex justify-center items-center transition-all duration-300 ease-in-out bg-light-purple dark:bg-dark-light-purple rounded-full text-dark-gray dark:text-white dark:text-lighter-gray">
<Icons name="pending-job" />
</span>
<span
className={`item-content relative group-hover:text-purple text-[18px] transition-all duration-300 ease-in-out text-lighter-gray font-medium`}
className={`item-content relative text-[18px] transition-all duration-300 ease-in-out font-medium dark:text-white h-12 flex items-center`}
>
Activities
</span>
@@ -178,7 +179,7 @@ export default function FamilyAcc() {
<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%)]"
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%)] bg-[#76a5df] text-white"
>
<svg
xmlns="http://www.w3.org/2000/svg"
@@ -247,13 +248,13 @@ const FamilyForm = ({
}) => {
return (
<div className="logout-modal-wrapper w-11/12 lg:w-[460px] 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:px-10 lg:py-8 px-[30px] py-[23px] border-b dark:border-[#5356fb29] border-light-purple dark:border-[#5356fb29] ">
<h1 className="text-26 font-bold text-dark-gray dark:text-white tracking-wide">
<div className="modal-header-con">
<h1 className="modal-title">
Add Members
</h1>
<button
type="button"
className="text-[#374557] dark:text-red-500"
className="modal-close-btn"
onClick={popUpHandler}
>
<CloseIcon />
@@ -266,8 +267,8 @@ const FamilyForm = ({
name="first_name"
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]"
labelClass="flex-[0.4] mb-0"
inputClass="flex-[0.6] input-curve lg border border-[#dce4e9]"
fieldClass="px-2"
value={first_name}
inputHandler={inputHandler}
@@ -278,8 +279,8 @@ const FamilyForm = ({
name="last_name"
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]"
labelClass="flex-[0.4] mb-0"
inputClass="flex-[0.6] input-curve lg border border-[#dce4e9]"
fieldClass="px-2"
value={last_name}
inputHandler={inputHandler}
@@ -288,7 +289,7 @@ const FamilyForm = ({
{/* Age dropdown */}
<div className="">
<label
className="input-label text-[#181c32] dark:text-white text-[15px] font-semibold"
className="job-label"
htmlFor="age-selection"
>
Birthday: (Year/Month)
+98 -51
View File
@@ -1,10 +1,9 @@
import { Form, Formik } from "formik";
import { useState } from "react";
import { useLocation } from "react-router-dom";
import ModalCom from "../Helpers/ModalCom";
import { Form, Formik } from "formik";
import InputCom from "../Helpers/Inputs/InputCom";
import usersService from "../../services/UsersService";
import Icons from "../Helpers/Icons";
import InputCom from "../Helpers/Inputs/InputCom";
import ModalCom from "../Helpers/ModalCom";
const DEFAULT_IMAGE = require("../../assets/images/family/default.jpg");
const SuggestTask = ({ details, onClose, situation, continuePopupData }) => {
@@ -51,26 +50,50 @@ const SuggestTask = ({ details, onClose, situation, continuePopupData }) => {
};
const handleParentSuggestion = (values) => {
if (suggestedNextStep == "Send Task") {
let firstName = state?.firstname;
let family_uid = state?.family_uid;
continuePopupData({ ...details, firstName, family_uid });
if (suggestedNextStep === "Send Task") {
let firstname = state?.firstname || details?.firstname;
let family_uid = state?.family_uid || details?.family_uid;
continuePopupData({
...details,
firstname,
family_uid,
});
}
onClose();
};
const isActivitiesPage = pathname === "/acc-family/activities";
const isManageFamilyPage = pathname === "/manage-family";
const getButtonText = () => {
if (isActivitiesPage) {
return suggestedNextStep === "Send Task" ? "Continue" : "Complete";
} else {
if (!isManageFamilyPage) {
if (submitTask.loading) return "Submitting Task";
if (submitTask.state === "success") return "Task Submitted";
if (submitTask.state === "bad") return "An Error Occurred";
return "Send to Parents";
} else {
return suggestedNextStep === "Send Task" ? "Continue" : "Complete";
}
}
};
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">
<div className="logout-modal-header w-full flex items-center justify-between lg:p-6 px-[30px] py-[23px] border-b dark:border-[#5356fb29] border-light-purple">
<h1 className="text-base md:text-lg font-bold text-dark-gray dark:text-white tracking-wide">
{pathname === "/manage-family"
<div className="logout-modal-wrapper lw-[90%] md:w-[48rem] min-h-[500px] bg-white dark:bg-dark-white lg:rounded-2xl">
<div className="modal-header-con">
<h1 className="modal-title">
{isManageFamilyPage
? `${state?.firstname}'s Suggested Task`
: isActivitiesPage
? `${details?.firstname}'s Suggested Task`
: "Suggest to Parent"}
</h1>
<button
type="button"
className="text-[#374557] dark:text-red-500"
className="modal-close-btn"
onClick={onClose}
>
<svg
@@ -97,19 +120,21 @@ const SuggestTask = ({ details, onClose, situation, continuePopupData }) => {
<Formik
initialValues={initialValues}
onSubmit={
pathname !== "/manage-family"
? handleSuggestedTask
: handleParentSuggestion
isActivitiesPage
? handleParentSuggestion
: isManageFamilyPage
? handleParentSuggestion
: handleSuggestedTask
}
>
{(props) => {
return (
<Form>
<div className="p-5 w-full bg-white rounded-md flex justify-between">
<Form className="h-[33.875rem] flex flex-col">
<div className="px-5 w-full bg-white rounded-md flex justify-between items-center h-full">
{/* Image Section */}
<div className="p-4 w-full md:w-2/4 md:border-r-2">
<div className="p-4 w-full md:w-2/4 md:border-r-2 h-full flex items-center">
<div
className="w-full h-[236px] p-6 bg-gray-400 rounded-xl overflow-hidden"
className="w-full h-[14.75rem] p-6 bg-gray-400 rounded-xl overflow-hidden"
style={{
background: `url(${selectedImage}) center / contain no-repeat`,
}}
@@ -117,17 +142,22 @@ const SuggestTask = ({ details, onClose, situation, continuePopupData }) => {
</div>
{/* ACTION SECTION */}
<div className="p-4 w-full md:w-2/4 h-full">
<div className="p-4 w-full md:w-2/4 h-full flex flex-col justify-between">
{/* Title */}
<div className="field w-full mb-[15px]">
<div className="field w-full mb-[.9375rem]">
<InputCom
fieldClass={
pathname === "/manage-family" ? "px-2" : "px-6"
pathname === "/manage-family" ||
pathname === "/acc-family/activities"
? "px-2"
: "px-6"
}
label="Title"
labalClass="text-[1.125rem]"
labelClass="tracking-wide"
inputBg={
pathname === "/manage-family"
pathname === "/manage-family" ||
pathname === "/acc-family/activities"
? "bg-white"
: "bg-slate-100"
}
@@ -147,15 +177,15 @@ const SuggestTask = ({ details, onClose, situation, continuePopupData }) => {
</div>
{/* Description */}
<div className="field w-full mb-[5px]">
<div className="w-full mb-[.3125rem]">
<label
htmlFor="description"
className='className="input-label text-[#181c32] dark:text-white text-[13.975px] leading-[20.9625px] font-semibold flex items-center gap-1'
className='job-label'
>
Description
{props.errors.description &&
props.touched.description && (
<span className="text-[12px] text-red-500">
<span className="text-[.75rem] text-red-500">
{props.errors.description}
</span>
)}
@@ -164,9 +194,10 @@ const SuggestTask = ({ details, onClose, situation, continuePopupData }) => {
id="description"
rows="5"
className={`input-field pt-2 placeholder:text-base text-dark-gray dark:text-white w-full ${
pathname === "/manage-family"
? "px-2 h-[110px]"
: "bg-slate-100 px-3 dark:bg-[#11131F] focus:ring-0 focus:outline-[#dce4e9] rounded-[10px] h-[130px]"
pathname === "/manage-family" ||
pathname === "/acc-family/activities"
? "px-2 h-[6.875rem]"
: "bg-slate-100 px-3 dark:bg-[#11131F] focus:ring-0 focus:outline-[#dce4e9] rounded-[.625rem] h-[8.125rem]"
}`}
style={{ resize: "none" }}
name="description"
@@ -177,8 +208,9 @@ const SuggestTask = ({ details, onClose, situation, continuePopupData }) => {
</div>
{/* Radio buttons for family */}
{pathname === "/manage-family" ? (
<div className="h-[20px] w-full border-t dark:border-[#5356fb29] border-light-purple relative">
{pathname === "/manage-family" ||
pathname === "/acc-family/activities" ? (
<div className="h-[3.75rem] w-full border-t dark:border-[#5356fb29] border-light-purple relative">
<div id="my-radio-group" className="sr-only">
Parent suggested next step
</div>
@@ -196,7 +228,7 @@ const SuggestTask = ({ details, onClose, situation, continuePopupData }) => {
role="group"
key={idx}
htmlFor={`parent-suggested-${idx}`}
className={`transition duration-150 ease-in-out parent-suggest group cursor-pointer`}
className={`transition duration-150 ease-in-out parent-suggest group cursor-pointer flex items-center`}
onClick={() => setSuggestedNextStep(title)}
>
<input
@@ -205,19 +237,19 @@ const SuggestTask = ({ details, onClose, situation, continuePopupData }) => {
value={title}
checked={suggestedNextStep === title}
onChange={switchNextStep}
className={`transition duration-150 ease-in-out parent-suggest pointer-events-none`}
className={`transition duration-150 ease-in-out parent-suggest pointer-events-none w-[1.125rem] h-[1.125rem]`}
/>
<span
onClick={() => setSuggestedNextStep(title)}
id={`parent-suggested-${idx}`}
name="parent-suggested"
className={`ml-1 ${
title == "Not Now"
title === "Not Now"
? "text-red-500"
: title == "Duplicate"
: title === "Duplicate"
? "text-purple"
: "text-black"
} font-semibold`}
} font-semibold text-[1.125rem]`}
>
{title}
</span>
@@ -229,7 +261,7 @@ const SuggestTask = ({ details, onClose, situation, continuePopupData }) => {
</div>
</div>
<div className="w-full h-[70px] border-t border-light-purple dark:border-[#5356fb29] flex justify-end items-center">
<div className="w-full h-[4.375rem] border-t border-light-purple dark:border-[#5356fb29] flex justify-end items-center">
<div className="flex items-center space-x-4 mr-9">
<button
type="button"
@@ -243,19 +275,9 @@ const SuggestTask = ({ details, onClose, situation, continuePopupData }) => {
disabled={props.isSubmitting}
className="text-white primary-gradient text-18 tracking-wide px-4 py-3 rounded-full transition duration-150 ease-in-out flex items-center"
>
{pathname !== "/manage-family" ? (
{/* {pathname === "/acc-family/activities" ? (
<>
{submitTask.loading
? "Submitting Task"
: submitTask.state == "success"
? "Task Submitted"
: submitTask.state == "bad"
? "An Error Occurred"
: "Send to Parents"}
</>
) : (
<>
{suggestedNextStep == "Send Task" ? (
{suggestedNextStep === "Send Task" ? (
<>
Continue <Icons name="chevron-right" />
</>
@@ -263,7 +285,32 @@ const SuggestTask = ({ details, onClose, situation, continuePopupData }) => {
"Complete"
)}
</>
)}
) : (
<>
{pathname !== "/manage-family" ? (
<>
{submitTask.loading
? "Submitting Task"
: submitTask.state === "success"
? "Task Submitted"
: submitTask.state === "bad"
? "An Error Occurred"
: "Send to Parents"}
</>
) : (
<>
{suggestedNextStep === "Send Task" ? (
<>
Continue <Icons name="chevron-right" />
</>
) : (
"Complete"
)}
</>
)}
</>
)} */}
{getButtonText()}
</button>
</div>
</div>
+1 -1
View File
@@ -7,7 +7,7 @@ function DataIteration(props) {
{datas &&
datas?.length >= endLength &&
datas?.slice(startLength, endLength)
.map((value) => children({ datas: value }))}
.map((value, index) => children({ datas: value, index }))}
</>
);
}
@@ -54,7 +54,7 @@ export default function InputCom({
<div className={`flex items-center justify-between mb-2.5 ${labelClass}`}>
{label && (
<label
className={`input-label text-[#181c32] text-[13.975px] leading-[20.9625px] font-semibold flex items-center gap-1 ${labalClass}`}
className={`job-label ${error && 'job-label-flex'} ${labalClass}`}
htmlFor={name}
>
{label}
+5 -5
View File
@@ -89,13 +89,13 @@ export default function AddGroup({ action, situation, setUpdateList }) {
return (
<ModalCom action={action} situation={situation}>
<div className="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">
<div className="modal-header-con">
<h1 className="modal-title">
Add Group
</h1>
<button
type="button"
className="text-[#374557] dark:text-red-500"
className="modal-close-btn"
onClick={action}
>
<svg
@@ -139,9 +139,9 @@ export default function AddGroup({ action, situation, setUpdateList }) {
<button
onClick={action}
type="button"
className="text-base text-light-red tracking-wide "
className="w-[152px] h-[46px] flex justify-center items-center rounded-full text-base text-light-red tracking-wide border border-light-red"
>
<span className="border-b dark:border-[#5356fb29] border-light-red">
<span className="">
Cancel
</span>
</button>
+3 -3
View File
@@ -21,13 +21,13 @@ export default function DeleteMember({action, situation, details}) {
situation={situation}
>
<div className="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">
<div className="modal-header-con">
<h1 className="modal-title">
Remove Member
</h1>
<button
type="button"
className="text-[#374557] dark:text-red-500"
className="modal-close-btn"
onClick={action}
>
<svg
+66 -12
View File
@@ -24,12 +24,6 @@ export default function MemberList({
const handleFieldsChange = ({ target: { name, value } }) => {
setFields((prev) => ({ ...prev, [name]: value }));
// let error = requestState?.errors?.indexOf(name) //// checks if the input field was in error array and removes it when the input changes
// if(error >= 0){
// let oldErrorArr = requestState.errors
// let newErrorArr = oldErrorArr.splice(error, 1)
// setRequestState(prev => ({...prev, errors:oldErrorArr}))
// }
if (value == "") {
setRequestState({
@@ -89,6 +83,63 @@ export default function MemberList({
// return
// }
if(fields.firstname.length > 25){ // checks if firstname length is more than 25
setRequestState({
loading: false,
status: false,
message: "Firstname must not be more than 25 characters",
data: [],
errors: [],
});
return setTimeout(() => {
setRequestState({
loading: false,
status: false,
message: "",
data: [],
errors: [],
});
}, 3000);
}
if(fields.lastname.length > 25){ // checks if lastname length is more than 25
setRequestState({
loading: false,
status: false,
message: "Lastname must not be more than 25 characters",
data: [],
errors: [],
});
return setTimeout(() => {
setRequestState({
loading: false,
status: false,
message: "",
data: [],
errors: [],
});
}, 3000);
}
if(fields.email.length > 45){ // checks if email length is more than 45
setRequestState({
loading: false,
status: false,
message: "Email must not be more than 45 characters",
data: [],
errors: [],
});
return setTimeout(() => {
setRequestState({
loading: false,
status: false,
message: "",
data: [],
errors: [],
});
}, 3000);
}
//checks if email is a valid email address
let regEx = /^[^0-9][a-zA-Z0-9._%+-]+@[a-zA-Z]+(\.[a-zA-Z]+)+$/;
if (!EmailValidator(fields.email)) {
@@ -201,7 +252,7 @@ export default function MemberList({
{selectedGroup?.name}
</h1>
<div className="w-full flex flex-col-reverse lg:flex-col">
<div className="py-3 w-full">
<div className="relative py-3 w-full">
<div className="relative grid grid-cols-1 sm:grid-cols-2 gap-2 place-content-center">
<div className="input-item">
<InputCom
@@ -229,13 +280,15 @@ export default function MemberList({
// iconName="message"
/>
</div>
<div className="input-item w-full">
</div>
<div className="sm:flex gap-2 items-center">
<div className="input-item my-2 w-full sm:w-9/12">
<InputCom
labelClass="tracking-wider"
fieldClass="sm:px-6 px-2"
value={fields.email}
inputHandler={handleFieldsChange}
inputClass="xl:w-[16rem] 2xl:w-full"
inputClass=""
placeholder="Email"
// label="Email"
name="email"
@@ -243,7 +296,7 @@ export default function MemberList({
// iconName="message"
/>
</div>
<div className="flex justify-end items-end">
<div className="flex justify-end items-end w-full sm:w-3/12">
{requestState.loading ? (
<LoadingSpinner size="8" color="sky-blue" />
) : (
@@ -266,10 +319,11 @@ export default function MemberList({
</button>
)}
</div>
</div>
<div className="absolute top-full w-full my-1 text-center">
{!requestState.loading && requestState.message && (
<p
className={`text-lg absolute -bottom-7 left-0 ${
className={`text-base ${
requestState.status ? "text-green-500" : "text-red-500"
}`}
>
+78 -3
View File
@@ -4,6 +4,8 @@ import ListView from "../../assets/images/list-view.png";
import AvailableJobsCard from "../Cards/AvailableJobsCard";
import DataIteration from "../Helpers/DataIteration";
import SelectBox from "../Helpers/SelectBox";
import NewPaginatedList from "../Pagination/NewPaginatedList";
import InfiniteScroll from "../infiniteScroll/InfiniteScroll";
export default function MainSection({
className,
@@ -95,7 +97,9 @@ export default function MainSection({
</div>
{/* end of contentDisplay toggler */}
</div>
<div className="filter-navigate-content w-full min-h-screen">
{/* OLD MARKET JOB LISTING */}
{/* <div className="filter-navigate-content w-full min-h-screen">
<div
className={
contentDisplay == "grid"
@@ -108,8 +112,8 @@ export default function MainSection({
startLength={process.env.REACT_APP_ZERO_STATE}
endLength={products?.length}
>
{({ datas }) => (
<div key={datas.job_uid}>
{({ datas, index }) => (
<div key={datas.job_uid+index}>
<AvailableJobsCard
contentDisplay={contentDisplay}
image_server={image_server}
@@ -119,7 +123,78 @@ export default function MainSection({
)}
</DataIteration>
</div>
</div> */}
{/* END OF OLD MARKET JOB LISTING */}
{products?.length ?
<NewPaginatedList
data={products}
itemsPerPage={9}
filterItem=''
tableTitle=''
>
{
({data})=>(
<div className="filter-navigate-content w-full min-h-[600px]">
<div
className={
contentDisplay == "grid"
? "grid lg:grid-cols-3 sm:grid-cols-2 gap-[30px]"
: "w-full"
}
>
{
data.map((datum, index) => (
<div key={datum.job_uid+index}>
<AvailableJobsCard
contentDisplay={contentDisplay}
image_server={image_server}
datas={datum}
/>
</div>
))
}
</div>
</div>
)
}
</NewPaginatedList>
// <InfiniteScroll
// allData={products}
// addItemBy={3}
// intialItemsToShow={9}
// >
// {
// ({dataToShow})=>(
// <div className="filter-navigate-content w-full min-h-[600px]">
// <div
// className={
// contentDisplay == "grid"
// ? "grid lg:grid-cols-3 sm:grid-cols-2 gap-[30px]"
// : "w-full"
// }
// >
// {
// dataToShow.map((datum, index) => (
// <div key={datum.job_uid+index}>
// <AvailableJobsCard
// contentDisplay={contentDisplay}
// image_server={image_server}
// datas={datum}
// />
// </div>
// ))
// }
// </div>
// </div>
// )
// }
// </InfiniteScroll>
:
<div className="w-full h-[40rem] bg-white dark:bg-dark-white flex justify-center items-center">
No Jobs Found!
</div>
}
</div>
</div>
);
@@ -1,11 +1,27 @@
import { useMemo, useState } from "react";
import { useEffect, useMemo, useState } from "react";
import { toast } from "react-toastify";
import usersService from "../../../services/UsersService";
import ModalCom from "../../Helpers/ModalCom";
import { PriceFormatter } from "../../Helpers/PriceFormatter";
import LoadingSpinner from "../../Spinners/LoadingSpinner";
import { SocketValues } from "../../Contexts/SocketIOContext";
const MarketPopUp = ({ details, onClose, situation, marketInt }) => {
let {sendJobInterestToOwner} = SocketValues() // function to emit job interest request
const emitOfferInterest = () => {
let message = {
"audience": "MERCHANT",
"action": "REFRESH_OFFERS",
"offer_code": details?.offer_code,
"offer_uid": details?.offer_uid,
"job_uid": details?.job_uid,
"market_uid": details?.market_uid
}
let room = `INTEREST-${details?.market_uid}`
sendJobInterestToOwner(message, room)
}
const [textValue, setTextValue] = useState("");
const [errMsg, setErrMsg] = useState({
market: false,
@@ -88,7 +104,7 @@ const MarketPopUp = ({ details, onClose, situation, marketInt }) => {
state: true,
});
}
emitOfferInterest() // FUNCTIONS TO EMIT EVENT INDICATING SOMEONE SENDS AN INTEREST IN YOUR JOB
setTimeout(() => setManageInt({ msg: "" }), 3000);
} catch (error) {
throw new Error(error);
@@ -118,9 +134,9 @@ const MarketPopUp = ({ details, onClose, situation, marketInt }) => {
return (
<ModalCom action={onClose} situation={situation}>
<div className="logout-modal-wrapper w-11/12 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">
<div className="w-11/12 md:w-[650px] bg-white dark:bg-dark-white lg:rounded-2xl overflow-y-auto">
<div className="modal-header-con">
<h1 className="modal-title">
{details.offer_code}
</h1>
<CloseIcon onClose={onClose} />
@@ -128,7 +144,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="max-h-[240px] h-full">
<div className="min-h-[240px]">
<h2 className="font-semibold text-slate-900 dark:text-white tracking-wide">
{details?.title}
</h2>
@@ -153,7 +169,7 @@ const MarketPopUp = ({ details, onClose, situation, marketInt }) => {
},
].map(({ name, content, danger }, idx) => (
<div className={`my-3 md:flex items-center`} key={idx}>
<label className="w-full md:w-[19%] tracking-wide font-semibold whitespace-pre-wrap">
<label className="job-label w-full md:w-[19%]">
{name}
</label>
<div
@@ -198,7 +214,7 @@ const MarketPopUp = ({ details, onClose, situation, marketInt }) => {
<hr />
<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 font-semibold">
<label className="job-label w-full">
If you have any questions about this task:
</label>
<textarea
@@ -218,7 +234,7 @@ const MarketPopUp = ({ details, onClose, situation, marketInt }) => {
{errMsg.market && "Something went wrong"}
</span>
<button
className="self-end w-[150px] h-[48px] rounded-full text-base bg-yellow-500 text-white"
className="custom-btn self-end bg-yellow-500 text-white"
name="market-message"
onClick={MarketDetail}
>
@@ -235,7 +251,7 @@ const MarketPopUp = ({ details, onClose, situation, marketInt }) => {
<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">
<p className="job-label w-full">
Interested in the task?
</p>
<hr />
@@ -270,33 +286,39 @@ const MarketPopUp = ({ details, onClose, situation, marketInt }) => {
<div className="text-slate-900">
<p className="flex items-center tracking-wide">
Interest: <b className="ml-1">{details.interest_count}</b>
<span className="job-label">Interest: </span> <b className="ml-1">{details.interest_count}</b>
</p>
<hr />
<p className="my-1">Expire: {details.expire}</p>
<p className="my-1 flex flex-col">
<span className="job-label">Expire: </span>
<span> {new Date(details.expire).toLocaleString()} </span>
</p>
</div>
</div>
<button
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}
>
Cancel
</button>
</div>
{/* END OF ACTION SECTION */}
</div>
<div className="modal-footer-wrapper">
<button
className="custom-btn bg-transparent border border-red-500 text-red-500 ml-auto"
name="cancel"
onClick={onClose}
>
Cancel
</button>
</div>
</div>
</ModalCom>
);
};
export default MarketPopUp;
const CloseIcon = ({ onClose }) => (
<button
type="button"
className="text-[#374557] dark:text-red-500"
className="modal-close-btn"
onClick={onClose}
>
<svg
+7
View File
@@ -3,6 +3,7 @@ import Layout from "../Partials/Layout";
import CommonHead from "../UserHeader/CommonHead";
import MainSection from "./MainSection";
import CustomBreadcrumb from "../Breadcrumb/CustomBreadcrumb";
import LoadingSpinner from "../Spinners/LoadingSpinner";
export default function MarketPlace({ commonHeadData }) {
let { jobLists } = useSelector((state) => state.jobLists);
@@ -25,12 +26,18 @@ export default function MarketPlace({ commonHeadData }) {
}
/>
</div>
{jobLists.loading ?
<div className="w-full flex justify-center items-center bg-white dark:bg-dark-white">
<LoadingSpinner size='20' height='h-[40rem]' />
</div>
:
<MainSection
marketPlaceProduct={marketData}
categories={categories}
image_server={image_server}
className="mb-10"
/>
}
</Layout>
</>
);
+15 -3
View File
@@ -11,8 +11,11 @@ import IndexJobActions from "./JobActions/IndexJobActions";
import usersService from "../../services/UsersService";
import { PriceFormatter } from "../Helpers/PriceFormatter";
import { SocketValues } from "../Contexts/SocketIOContext";
function ActiveJobs(props) {
let {sendMessage, joinRoom} = SocketValues() // destructures 'SEND MESSAGE' and 'JOIN ROOM' FUNCTIONS FROM SOCKET
const ApiCall = new usersService();
const navigate = useNavigate();
@@ -139,6 +142,9 @@ function ActiveJobs(props) {
status: true,
message: "Message Sent Successfully",
});
// function to trigger socket to emit 'send_message'
sendMessage(messageToSend, `${props.details.contract}-${props.details.contract_uid}`)
props.reloadActiveJobList((prev) => !prev); // MAKES ACTIVE JOB MESSAGE LIST TO RELOAD
setMessageToSend(""); // SENDS MESSAGE TO SEND BACK TO EMPTY STRINGS
})
@@ -255,6 +261,12 @@ function ActiveJobs(props) {
props.details?.currency
);
useEffect(()=>{
// calls function to add user to a room
joinRoom(`${props.details.contract}-${props.details.contract_uid}`)
},[props.details.contract, props.details.contract_uid])
return (
<Layout>
<div className="py-[20px] bg-white dark:bg-black dark:text-white px-4 rounded-2xl shadow-md md:flex justify-between items-start gap-16">
@@ -534,13 +546,13 @@ function ActiveJobs(props) {
{/* MESSAGE SECTION */}
<div className="w-full lg:w-1/2">
<div className="flex justify-between items-center gap-5">
<p className="w-full text-lg font-bold text-dark-gray dark:text-white tracking-wide flex items-center gap-2">
<div className="flex justify-between items-center gap-5 justify-between">
<p className="w-full text-lg font-bold text-dark-gray dark:text-white tracking-wide flex items-center gap-2 justify-between">
<span>Message</span>
<button
type="button"
onClick={popUpHandler}
className="text-[12px] tracking-wider text-emerald-600 dark:text-emerald-300"
className="text-[12px] tracking-wider text-gray-400 dark:text-slate-400"
>
View all
</button>
+14 -2
View File
@@ -4,6 +4,7 @@ import Layout from "../Partials/Layout";
import MyJobTable from "./MyJobTable";
import CommonHead from "../UserHeader/CommonHead";
import AddJobPage from "../../views/AddJobPage";
import CustomBreadcrumb from "../Breadcrumb/CustomBreadcrumb";
export default function MyJobs(props) {
let { state } = useLocation();
@@ -32,8 +33,8 @@ export default function MyJobs(props) {
<div className="notification-page w-full mb-10">
<div className="notification-wrapper w-full">
{/* heading */}
<div className="sm:flex items-center mb-6">
<div className="mb-5 sm:mb-0">
<div className="sm:flex items-center mb-2">
<div className="w-full">
<h1 className="text-26 font-bold flex items-center space-x-1 text-dark-gray dark:text-white gap-2">
<span>My Jobs</span>
@@ -46,6 +47,17 @@ export default function MyJobs(props) {
</h1>
</div>
</div>
<div className="mb-5">
<CustomBreadcrumb
// title = 'My Jobs'
breadcrumb={
[
{ link: "/", title: "Home" },
{ link: "/myjobs", title: "My Jobs", active: true},
]
}
/>
</div>
<MyJobTable MyJobList={props.MyJobList} />
</div>
</div>
+13 -1
View File
@@ -3,6 +3,7 @@ import OfferCard from "../Cards/OfferCard";
import Icons from "../Helpers/Icons";
import SliderCom from "../Helpers/SliderCom";
import FamilyOfferJobPopout from "../jobPopout/FamilyOfferJobPopout";
import CustomBreadcrumb from "../Breadcrumb/CustomBreadcrumb";
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
@@ -60,10 +61,21 @@ export default function MyOffersFamilyTable({ className, familyOffers, image_ser
>
{/* heading */}
<div className="flex justify-between items-center mb-6">
<div>
{/* <div>
<h1 className="text-26 font-bold text-dark-gray dark:text-white">
Ready to Start?
</h1>
</div> */}
<div className="">
<CustomBreadcrumb
title = {'Ready to Start'}
breadcrumb={
[
{ link: "/", title: "Home" },
{ link: "/pending", title: "Pending", active: true},
]
}
/>
</div>
<div className="slider-btns flex space-x-3">
<button onClick={nextHandler} type="button">
+92 -16
View File
@@ -2,33 +2,109 @@ import { useSelector } from "react-redux";
import LoadingSpinner from "../Spinners/LoadingSpinner";
import WalletItemCard from "./WalletItemCard";
import WalletItemCardFamily from "./WalletItemCardFamily";
import { useEffect, useState } from "react";
import { PriceFormatter } from "../Helpers/PriceFormatter";
import SearchCom from "../Helpers/SearchCom";
import { localImgLoad } from "../../lib";
import background from "../../assets/images/bg-sky-blue.jpg";
import FamilyWalletRedeemOptions from "./FamilyWalletRedeemOptions";
/**
* Renders a list of wallet items or a loading spinner depending on the state of the `wallet` object.
*/
export default function FamilyWalletBox({ wallet, payment, countries }) {
const { loading, data } = wallet;
export default function FamilyWalletBox({ wallet, payment }) {
// const { loading, data } = wallet;
// const { userDetails } = useSelector((state) => state.userDetails);
// const accountType = userDetails?.account_type === "FAMILY";
const [selectedWallet, setSelectedWallet] = useState('')
const { userDetails } = useSelector((state) => state.userDetails);
const accountType = userDetails?.account_type === "FAMILY";
const [activeWalletBtn, setActiveWalletBtn] = useState('')
const handleChangeWallet = ({target:{name}}) => { // FUNCTION TO SWITCH WALLET IF USER HAS MORE THAN TWO WALLETS
const currentWalletSelected = wallet?.data?.filter((item) => item.code == name);
setSelectedWallet(currentWalletSelected[0])
setActiveWalletBtn(name)
// console.log(name, currentWalletSelected)
}
const image = selectedWallet?.code
? `${selectedWallet?.code.toLowerCase()}.svg`
: "default.png";
useEffect(()=>{
setSelectedWallet(wallet.data[0])
setActiveWalletBtn(wallet?.data[0]?.code)
},[wallet])
return (
<div className="my-wallet-wrapper w-full mb-10">
<div className="main-wrapper w-full">
<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 bg-white">
<LoadingSpinner size="16" color="sky-blue" height='h-[30rem]' />
<div className="w-full">
<div className="my-wallet-wrapper w-full">
<div className="main-wrapper w-full mb-10">
<div className="w-full mb-10 sm:grid grid-cols-2 gap-4">
<div className="w-full mb-4 sm:mb-0 rounded-2xl bg-white dark:bg-dark-white overflow-hidden">
{wallet?.loading ?
<div className="w-full h-full flex items-center justify-center bg-white">
<LoadingSpinner size="16" color="sky-blue" height='min-h-[240px]' />
</div>
: wallet?.data.length > 0 ?
<>
{wallet?.data?.length > 1 &&
<div className="wal-selection px-5 py-2 text-black dark:text-white flex items-center gap-2">
{wallet?.data?.map(item =>(
<button
className={`py-0.5 px-1 mb-1 rounded-lg border border-orange-500 ${activeWalletBtn == item?.code && 'bg-orange-500'}`}
key={item?.wallet_uid}
name={item?.code}
onClick={handleChangeWallet}
>
{item?.description}
</button>
))}
</div>
}
<div className="p-5 bg-white-opacity min-h-[240px]"
style={{
background: `url(${background}) 0% 0% / cover no-repeat`,
}}
>
{/* image */}
<div className="min-w-[100px] min-h-[100px] max-w-min md:max-w-[100px] max-h-min md:max-h-[100px] rounded-full bg-[#e3e3e3] flex justify-center items-center">
<img
src={localImgLoad(`images/currency/${image}`)}
className="w-full h-full"
alt="currency-icon"
/>
</div>
<p className="text-base sm:text-lg text-white opacity-[70%] tracking-wide my-3">Current Balance</p>
<p className="text-[44px] lg:text-[62px] font-bold text-white tracking-wide leading-10">
{PriceFormatter(selectedWallet?.amount/100, selectedWallet?.code, undefined, "text-[2rem]")}
</p>
</div>
</>
:
<div className="w-full h-full flex justify-center items-center rounded-2xl bg-white">
<p>No Wallet Record Found</p>
</div>
}
</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 className="p-5 w-full rounded-2xl bg-white dark:bg-dark-white text-black dark:text-white h-full min-h-[240px] max-h-96">
<h1 className="text-xl font-bold text-black dark:text-white">Recent Activities</h1>
</div>
))
)}
</div>
</div>
<div className="w-full">
<FamilyWalletRedeemOptions />
</div>
</div>
</div>
);
}
// data.length>0 && data.map((item) => (
// <div key={item.wallet_uid} className="w-full h-full mb-10 ">
// {/* <WalletItemCardFamily walletItem={item} payment={payment} countries={countries} /> */}
// </div>
// ))
@@ -6,9 +6,12 @@ import LoadingSpinner from "../Spinners/LoadingSpinner";
import CustomBreadcrumb from "../Breadcrumb/CustomBreadcrumb";
const FamilyWalletBox = lazy(() => import("./FamilyWalletBox"));
const FamilyWallet = () => {
const FamilyWalletCon = () => {
const apiCall = new usersService();
const { walletDetails } = useSelector((state) => state?.walletDetails); // WALLET STORE
// const { walletDetails } = useSelector((state) => state?.walletDetails); // WALLET STORE
const {userDetails} = useSelector((state) => state?.userDetails); // GETS USER INFO
const { walletTable } = useSelector((state) => state.tableReload);
const [paymentHistory, setPaymentHistory] = useState({
@@ -16,12 +19,20 @@ const FamilyWallet = () => {
data: [],
});
const [allCountries, setAllCountries] = useState({
// STATE TO HOLD LIST OF COUNTRIES
const [familyWalletBal, setFamilyWalletBal] = useState({
loading: true,
data: [],
data: []
});
const getFamilyWalletBal = () => {
setFamilyWalletBal({loading:true, data: []})
apiCall.getFamilyWallet({family_uid: userDetails.uid}).then(res => {
setFamilyWalletBal({loading:false, data: res?.data?.result_list})
}).catch(error => {
setFamilyWalletBal({loading:false, data: []})
})
};
const getPaymentHistory = () => {
apiCall
.getPaymentHx()
@@ -30,6 +41,7 @@ const FamilyWallet = () => {
setPaymentHistory({ loading: false, data: [] });
} else {
setPaymentHistory({ loading: false, data: res.data?.result_list });
// console.log('Hist', res.data?.result_list)
}
})
.catch(() => {
@@ -37,37 +49,11 @@ const FamilyWallet = () => {
});
};
// FUNCTION TO GET COUNTRIES
const getCountry = () => {
apiCall
.getSignupCountryData()
.then((res) => {
if (res?.data?.internal_return < 0) {
setAllCountries((prev) => ({ loading: false, data: [] }));
return;
}
setAllCountries((prev) => ({
loading: false,
data: res?.data?.result_list,
}));
})
.catch((error) => {
setAllCountries((prev) => ({ loading: false, data: [] }));
});
};
useEffect(() => {
getCountry();
getPaymentHistory();
getFamilyWalletBal()
}, [walletTable]);
console.log(
"Testing all country: ",
allCountries,
"Testing wallet: ",
walletDetails
);
return (
<Layout>
<div className='mb-4'>
@@ -81,15 +67,18 @@ const FamilyWallet = () => {
}
/>
</div>
<Suspense fallback={<LoadingSpinner size="16" color="sky-blue" />}>
<Suspense fallback={
<div className="bg-white rounded-2xl">
<LoadingSpinner size="16" color="sky-blue" height='h-[30rem]' />
</div>
}>
<FamilyWalletBox
wallet={walletDetails}
wallet={familyWalletBal}
payment={paymentHistory}
countries={allCountries.data}
/>
</Suspense>
</Layout>
);
};
export default FamilyWallet;
export default FamilyWalletCon;
@@ -0,0 +1,82 @@
import React, { useEffect, useState } from 'react'
import SearchCom from '../Helpers/SearchCom'
import LoadingSpinner from '../Spinners/LoadingSpinner';
import { Link } from 'react-router-dom';
import { useSelector } from 'react-redux';
export default function FamilyWalletRedeemOptions() {
const { familyWalletRedeemOptList } = useSelector((state) => state.familyWalletRedeemOptList); // FAMILY WALLET REDDEM OPTIONS LIST
const [filteredRedeemData, setFilteredRedeemData] = useState({value: '', data:{}}) // State to hold filtered redeem banner option
const handleFilterRedeemData = ({target}) => {
// thiskey01
let filterWord = target.value
let filteredData = {}
if(!filterWord){
filteredData = {...familyWalletRedeemOptList?.data}
}else{
let matchedData = Object.keys(familyWalletRedeemOptList?.data)?.filter(item => (item.toLowerCase().startsWith(filterWord.toLowerCase())))
filteredData = matchedData.map(item => familyWalletRedeemOptList.data[item])
}
setFilteredRedeemData({value:target.value, data: {...filteredData}})
}
useEffect(()=>{
setFilteredRedeemData(prev => ({...prev, data:{...familyWalletRedeemOptList?.data}}))
}, [familyWalletRedeemOptList.image])
return (
<div className="w-full">
<div className="w-full sm:flex items-center gap-4">
<h1 className="text-2xl font-bold text-black dark:text-white">Redeem Options</h1>
<div className="sm:w-1/2 w-full sm:pr-20 pr-0 mb-5 sm:mb-0">
<SearchCom
placeholder='Search...'
value={filteredRedeemData.value}
handleSearch={handleFilterRedeemData}
/>
</div>
</div>
{/* redeem options */}
{familyWalletRedeemOptList.loading ?
<div className='mt-5 w-full h-[20rem] rounded-2xl bg-white dark:bg-dark-white text-black dark:text-white flex justify-center items-center'>
<LoadingSpinner size='10' color='sky-blue' height='h-[30rem]' />
</div>
: familyWalletRedeemOptList?.data && Object.keys(familyWalletRedeemOptList?.data)?.length > 0 ?
Object.keys(filteredRedeemData?.data)?.length ?
<div className="mt-5 grid sm:grid-cols-2 lg:grid-cols-3 xxl:grid-cols-4 gap-4">
{ Object.keys(filteredRedeemData?.data)?.map((item)=>{
// text, image, description, action
let newData = filteredRedeemData?.data[item].banner
let bgImage = `url(${newData?.image})`
return (
<Link to={''} key={item}>
<div className={`group relative h-60 text-black dark:text-white rounded-2xl shadow-lg hover:shadow-md transition-all duration-300 overflow-hidden`}
style={{
// background: `#ffffff ${bgImage} no-repeat center center / cover`
}}
>
<img src={newData?.image} alt='Redeem Image' className='w-full h-full bg-cover rounded-2xl group-hover:scale-110 transition-all duration-300' />
<div className='absolute bottom-0 mb-1 left-1/2 -translate-x-1/2 p-2 bg-white/80 rounded-2xl w-[90%] flex flex-col justify-center items-center gap-2'>
<h1 className='text-lg font-bold text-[#083e21]'>{newData.text}</h1>
</div>
</div>
</Link>
)
})}
</div>
:
<div className='mt-5 w-full h-[20rem] rounded-2xl bg-white dark:bg-dark-white text-black dark:text-white flex justify-center items-center'>
<p>Search Item not Found!</p>
</div>
:
<div className='mt-5 w-full h-[20rem] rounded-2xl bg-white dark:bg-dark-white text-black dark:text-white flex justify-center items-center'>
<p>No Redeem Options Found!</p>
</div>
}
</div>
)
}
@@ -4,7 +4,6 @@ import background from "../../assets/images/bg-sky-blue.jpg"; //shape/balance-bg
import localImgLoad from "../../lib/localImgLoad";
import { tableReload } from "../../store/TableReloads";
import { PriceFormatter } from "../Helpers/PriceFormatter";
import CreditPopup from "./Popup/CreditPopup";
import WalletAction from "./WalletAction";
/**
@@ -13,26 +12,15 @@ import WalletAction from "./WalletAction";
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?.filter((country) => country.code === walletItem.country);
@@ -43,10 +31,10 @@ export default function WalletItemCardFamily({ walletItem, payment, countries })
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`,
}}
className="current-balance-widget w-full h-full rounded-2xl overflow-hidden flex flex-col items-center gap-2 p-8 justify-between bg-family-header-bg"
// 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">
@@ -72,24 +60,7 @@ export default function WalletItemCardFamily({ walletItem, payment, countries })
</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}
/>
)}
</>
);
}
@@ -28,6 +28,7 @@ export default function OffersInterestTable({offerInterestList, className}) {
handlePagingFunc(e, setCurrentPage);
};
let imgServer = offerInterestList?.imgServer // FOR RENDERING IMAGE FROM SERVER
return (
<div
className={`update-table w-full my-8 p-8 bg-white dark:bg-dark-white rounded-2xl section-shadow min-h-[520px] ${
@@ -54,14 +55,19 @@ export default function OffersInterestTable({offerInterestList, className}) {
</thead>
<tbody className="h-full">
{currentOfferInterestList?.map((item, index) => {
let image = item.banner ? item.banner : 'default.jpg'
// let image = item.banner ? item.banner : 'default.jpg'
const image = localStorage.getItem("session_token")
? `${imgServer}${localStorage.getItem("session_token")}/job/${
item.job_uid
}`
: "";
return (
<tr key={index} 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">
<div className="w-[60px] h-[60px] p-2 bg-alice-blue rounded-full overflow-hidden flex justify-center items-center">
<div className="min-w-[60px] max-w-[60px] min-h-[60px] max-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"
/>
@@ -90,7 +96,7 @@ export default function OffersInterestTable({offerInterestList, className}) {
<button
onClick={() => {
navigate("/manage-offer", {
state: { ...item, pathname },
state: { ...item, pathname, jobImage:image },
});
}}
type="button"
@@ -56,13 +56,18 @@ export default function OthersInterestTable({othersInterestedList, className}) {
</thead>
<tbody className="h-full">
{currentOthersInterestedList?.map((item, index) => {
const image = localStorage.getItem("session_token")
? `${othersInterestedList.imageServer}${localStorage.getItem("session_token")}/job/${
item.job_uid
}`
: "";
return (
<tr key={index} 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">
<div className="min-w-[60px] min-h-[60px] rounded-full overflow-hidden flex justify-center items-center">
<img
src={dataImage1}
src={image}
alt="data"
className="w-full h-full"
/>
+1
View File
@@ -9,6 +9,7 @@ export default function OffersInterest(props) {
const filterHandler = (value) => {
setValue(value);
};
return (
<Layout>
<CommonHead
@@ -0,0 +1,124 @@
import { useEffect, useState } from "react";
const data1 = [];
export default function NewPaginatedList({
data = data1,
itemsPerPage = 5,
filterItem,
tableTitle,
children,
}) {
const [searchTerm, setSearchTerm] = useState("");
const [filteredData, setFilteredData] = useState(data);
const [currentPage, setCurrentPage] = useState(0);
const [newData, setNewData] = useState([]);
const numberOfSelection = itemsPerPage;
const handlePrev = () => {
if (currentPage != 0) {
setCurrentPage((prev) => prev - numberOfSelection);
}
};
const handleNext = () => {
if (currentPage < data.length) {
setCurrentPage((prev) => prev + numberOfSelection);
}
};
const handleSearch = ({ target: { value } }, name) => {
setSearchTerm(value);
let newFilteredData = data.filter((item) =>
item[name].toLowerCase().startsWith(value.toLowerCase())
);
setFilteredData(newFilteredData);
setCurrentPage(0);
};
useEffect(() => {
setNewData(
filteredData?.slice(currentPage, numberOfSelection + currentPage)
);
}, [currentPage, filteredData]);
useEffect(()=>{
setCurrentPage(0)
},[itemsPerPage])
return (
<div className="w-full">
<h1 className="text-2xl mb-5 font-semibold">{tableTitle}</h1>
{data.length > 0 && filterItem && (
<div className="mb-10 flex justify-end items-center gap-2">
{filterItem.map((item, index) => (
<label
key={index}
className="flex flex-col sm:flex-row items-center gap-2 text-slate-600 dark:text-slate-100 transition-all duration-500"
>
Search by {item[0].toUpperCase() + item.slice(1)}
<input
name={item}
type="text"
className="py-1 px-2 text-sm min-w-[100px] text-black dark:text-white bg-white dark:bg-slate-800 rounded-full border-0 outline-none ring-1 ring-slate-300 dark:ring-white transition-all duration-500"
value={searchTerm}
onChange={(e) => {
handleSearch(e, item);
}}
/>
</label>
))}
</div>
)}
{children({ data: newData })}
{/* show prev and next button if data exist */}
{data.length > 0 && (
<div className="mt-10 w-full flex gap-4 justify-center items-center">
<button
onClick={handlePrev}
className={`w-12 h-12 rounded-full flex justify-center items-center transition-all duration-300 ${
currentPage == 0
? "text-slate-400 border-slate-400 dark:text-slate-400 dark:border-slate-400 pointer-events-none"
: "text-slate-600 border-slate-600 dark:text-white dark:border-white"
}`}
>
&lt;
</button>
{data.length && data.map((item, index)=>{
if(index%itemsPerPage == 0 && index >= currentPage && index <= currentPage+itemsPerPage){
return (
<button
key={index}
onClick={handleNext}
className={`w-12 h-12 rounded-full flex justify-center items-center border transition-all duration-300 ${
currentPage != index
? "text-slate-400 border-slate-400 dark:text-slate-400 dark:border-slate-400"
: "text-slate-600 border-slate-600 dark:text-white dark:border-white pointer-events-none"
}`}
>
{index/itemsPerPage +1}
</button>
)
}
})}
<button
onClick={handleNext}
className={`w-12 h-12 rounded-full flex justify-center items-center transition-all duration-300 ${
currentPage + numberOfSelection >= data.length
? "text-slate-400 border-slate-400 dark:text-slate-400 dark:border-slate-400 pointer-events-none"
: "text-slate-600 border-slate-600 dark:text-white dark:border-white"
}`}
>
&gt;
</button>
</div>
)}
</div>
);
}
+1 -1
View File
@@ -238,7 +238,7 @@ export default function Header({ logoutModalHandler, sidebarHandler }) {
{/* balance */}
{/* My Page Button */}
{userDetails.account_type === "FULL" ? <PageButton /> : null}
{/* {userDetails.account_type === "FULL" ? <PageButton /> : null} */}
{/*<div className="lg:hidden block"></div>*/}
<WalletHeader
+5 -8
View File
@@ -20,10 +20,7 @@ export default function Layout({ children }) {
};
const navigate = useNavigate();
const logOut = () => {
localStorage.removeItem("session_token");
localStorage.removeItem("member_id");
localStorage.removeItem("uid");
sessionStorage.removeItem("family_uid");
sessionStorage.clear();
localStorage.clear();
// toast.success("Come Back Soon", {
// icon: `🙂`,
@@ -88,11 +85,11 @@ export default function Layout({ children }) {
{logoutModal && (
<ModalCom action={logoutModalHandler} situation={logoutModal}>
<div className="logout-modal-wrapper w-11/12 sm:w-[460px] 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 dark:border-[#5356fb29] border-light-purple dark:border-[#5356fb29] ">
<h1 className="text-26 font-bold text-dark-gray dark:text-white tracking-wide">
<div className="modal-header-con">
<h1 className="modal-title">
Confirm
</h1>
<button type="button" className="text-[#374557] dark:text-red-500" onClick={logoutModalHandler}>
<button type="button" className="modal-close-btn" onClick={logoutModalHandler}>
<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" />
@@ -112,7 +109,7 @@ export default function Layout({ children }) {
Are you sure you want to Logout of your WrenchBoard account?
</p>
</div>
<div className="flex space-x-2.5">
<div className="modal-footer-wrapper flex justify-center items-center gap-5">
<button onClick={logOut} type="button" className="text-white primary-gradient text-18 tracking-wide px-4 py-3 rounded-full">
{`Yes ${process.env.REACT_APP_LOGOUT_TEXT}`}
</button>
+13 -7
View File
@@ -89,7 +89,9 @@ export default function MobileSidebar({
<ul className="flex flex-col space-y-6">
{/* Using mini component reduces the bulk amount of html */}
<ListItem
title= {userDetails?.account_type == "FULL" ? "Dashboard" : "Home"}
title={
userDetails?.account_type == "FULL" ? "Dashboard" : "Home"
}
route="/"
sidebar={sidebar}
iconName="new-dashboard"
@@ -190,17 +192,21 @@ export default function MobileSidebar({
<div className="items">
<ul className="flex flex-col space-y-6">
{[
{ name: "List", path: "/myjobs", iconName: "job-list" },
{
name: "List",
path: "/myjobs",
iconName: "job-list",
},
// {
// name: "Waiting",
// path: "/pend-interest",
// iconName: "pending-job",
// },
{
name: "Offers",
path: "/my-offers",
iconName: "pending-job",
},
{
name: "Waiting",
path: "/pend-interest",
iconName: "pending-job",
},
{
name: "Active",
path: "/my-active-jobs",
+63 -11
View File
@@ -4,6 +4,7 @@ import { NavLink } from "react-router-dom";
//import SideStatistics from "./SideStatistics";
import { localImgLoad } from "../../lib";
import DarkModeContext from "../Contexts/DarkModeContext";
import Icons from "../Helpers/Icons";
export default function RightSideBar({ myJobList }) {
const filterDatas = ["Last 15 days", "Last Month", "Last 6 month"];
@@ -39,7 +40,7 @@ export default function RightSideBar({ myJobList }) {
return (
<>
<div className="right-sidebar-wrapper overflow-y-scroll overflow-style-none 2xl:fixed h-full 2xl:pb-96">
<div className="top-platform bg-white dark:bg-dark-white rounded-2xl p-8 2xl:w-[268px] w-full 2xl:mb-10 2xl:border-none border ">
<div className="top-platform bg-white dark:bg-dark-white rounded-2xl p-8 2xl:w-[268px] w-full 2xl:mb-6 2xl:border-none border ">
{/* heading */}
<div className="heading flex justify-between items-center mb-3.5">
<h3 className="text-xl font-bold text-dark-gray dark:text-white">
@@ -48,10 +49,10 @@ export default function RightSideBar({ myJobList }) {
<div></div>
</div>
<div className="platform-list">
<div className="platform-list flex flex-col gap-y-2.5">
{userDetails && userDetails?.account_type !== "FAMILY" && (
<>
<div className="item flex space-x-3 items-center mb-4">
<div className="item flex space-x-3 items-center">
{/* image */}
<div className="w-8 h-8 rounded-full">
<svg
@@ -81,7 +82,20 @@ export default function RightSideBar({ myJobList }) {
{/* action */}
</div>
<div className="item flex space-x-3 items-center mb-4">
<div className="item flex space-x-3 items-center">
{/* image */}
<div className="w-8 h-8 rounded-full flex items-center justify-center">
<Icons name="pending-job" />
</div>
{/* name */}
<div>
<p className="text-thin-light-gray text-base font-medium">
<NavLink to="/pend-interest">Waiting</NavLink>
</p>
</div>
{/* action */}
</div>
<div className="item flex space-x-3 items-center">
{/* image */}
<div className="w-8 h-8 rounded-full">
<svg
@@ -115,10 +129,29 @@ export default function RightSideBar({ myJobList }) {
</p>
</div>
</div>
<div className="item flex space-x-3 items-center">
{/* image */}
<div className="w-8 h-8 rounded-full bg-sky-500/50 p-1 flex justify-center items-center">
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 68 50" id="Page">
<path
fillRule="evenodd"
d="M0 13V2a2 2 0 0 1 2-2h64a2 2 0 0 1 2 2v11H0Zm0 4v31a2 2 0 0 0 2 2h20V17H0Zm26 33h40a2 2 0 0 0 2-2V17H26v33Z"
fill="#ffffff"
className="color000000 svgShape"
></path>
</svg>
</div>
{/* name */}
<div>
<p className="text-thin-light-gray text-base font-medium">
<NavLink to="/yourpage">My Page</NavLink>
</p>
</div>
</div>
</>
)}
<div className="item flex space-x-3 items-center mb-4">
<div className="item flex space-x-3 items-center">
{/* image */}
<div className="w-8 h-8 rounded-full">
<svg
@@ -158,7 +191,7 @@ export default function RightSideBar({ myJobList }) {
{/* dark mode */}
<div
onClick={darkMode.handleThemeSwitch}
className="item flex space-x-5 items-center mb-4 cursor-pointer"
className="item flex space-x-5 items-center cursor-pointer"
>
<span className="dark:text-dark-gray text-white">
{darkMode.theme === "light" ? (
@@ -235,6 +268,7 @@ export default function RightSideBar({ myJobList }) {
/>
</svg>
)}
</span>
<p className="text-thin-light-gray text-base font-medium">
{darkMode.theme === "light" ? "Dark" : "Light"} Mode
@@ -255,8 +289,8 @@ export default function RightSideBar({ myJobList }) {
<div></div>
</div>
<div className="platform-list">
<div className="px-8 item flex space-x-3 items-center mb-4">
<div className="platform-list flex flex-col gap-y-2.5">
<div className="px-8 item flex space-x-3 items-center">
{/* image */}
<div className="w-8 h-8 p-[4px] rounded-full">
<img
@@ -274,7 +308,25 @@ export default function RightSideBar({ myJobList }) {
{/* action */}
</div>
<div className="px-8 item flex space-x-3 items-center mb-4">
<div className="px-8 item flex space-x-3 items-center">
{/* image */}
<div className="w-8 h-8 p-[4px] rounded-full">
<img
src={localImgLoad("images/icons/job_active.svg")}
className="w-full h-full"
alt="Active Task"
/>
</div>
{/* name */}
<div>
<p className="text-thin-light-gray text-base font-medium">
<NavLink to="/offer-interest">Offers Interest</NavLink>
</p>
</div>
{/* action */}
</div>
<div className="px-8 item flex space-x-3 items-center">
{/* image */}
<div className="w-8 h-8 p-[4px] rounded-full">
<img
@@ -291,7 +343,7 @@ export default function RightSideBar({ myJobList }) {
</div>
</div>
<div className="px-8 item flex space-x-3 items-center mb-4">
<div className="px-8 item flex space-x-3 items-center">
{/* image */}
<div className="w-8 h-8 p-[4px] rounded-full">
<img
@@ -311,7 +363,7 @@ export default function RightSideBar({ myJobList }) {
{/* Line */}
<div className="my-4 mx-auto w-10/12 h-[2px] bg-slate-500 dark:bg-white rounded-full"></div>
<div className="px-8 item flex space-x-3 items-center mb-4">
<div className="px-8 item flex space-x-3 items-center">
{/* image */}
<div className="w-8 h-8 p-[4px] rounded-full">
<img
+5 -5
View File
@@ -216,11 +216,11 @@ export default function Sidebar({
path: "/myjobs",
iconName: "job-list",
},
{
name: "Waiting",
path: "/pend-interest",
iconName: "pending-job",
},
// {
// name: "Waiting",
// path: "/pend-interest",
// iconName: "pending-job",
// },
{
name: "Offers",
path: "/my-offers",
+22
View File
@@ -0,0 +1,22 @@
import React, { useEffect, useRef } from 'react'
export default function VideoElement({videoId}) {
let videoRef = useRef(null)
useEffect(()=>{
if(videoRef.current){
videoRef.current.pause()
videoRef.current.removeAttribute('src')
videoRef.current.load()
}
},[videoId])
return (
<video ref={videoRef} className='w-full h-full' controls>
<source src={`https://dev-media.wrenchboard.com/videos/${videoId}`} type='video/mp4'></source>
Your browser does not support the video tag.
</video>
)
}
@@ -0,0 +1,62 @@
import React, { useEffect, useState } from 'react'
import LoadingSpinner from '../Spinners/LoadingSpinner'
export default function InfiniteScroll({
allData=data,
addItemBy=12,
intialItemsToShow=39,
children
}) {
let [currentPage, setCurrentPage] = useState(intialItemsToShow)
let [loading, setLoading] = useState(false)
let dataToShow = allData.slice(0, currentPage)
// let [winScr, setWinScr] = useState(0)
const customScroll = ()=>{
let wi = window.innerHeight + window.scrollY
// let scrollContainer = document.querySelector('.scroll-container').scrollHeight
let scrollBody = document.body.offsetHeight
if(wi >= scrollBody && allData.length > currentPage){
setLoading(true)
setTimeout(()=>{
setCurrentPage(prev => prev+addItemBy)
setLoading(false)
},2000)
}
// else if(window.scrollY < winScr && currentPage > intialItemsToShow){
// setCurrentPage(prev => prev-addItemBy)
// window.scrollTo(0,wi)
// setWinScr(window.scrollY)
// }
// else if(winScr == 0 && currentPage > intialItemsToShow){
// setCurrentPage(prev => intialItemsToShow)
// window.scrollTo(0,0)
// setWinScr(window.scrollY)
// }
}
useEffect(()=>{
window.addEventListener('scroll', customScroll)
return () => {
window.removeEventListener('scroll', customScroll)
}
},[currentPage])
return (
<div className='w-full'>
{children({ dataToShow })}
{loading &&
<div className='w-full my-2 p-2 flex justify-center items-center'>
<LoadingSpinner size='8' />
</div>
}
</div>
)
}
// let data = new Array(100).fill(0).map((_,i) => i )
+5 -5
View File
@@ -64,13 +64,13 @@ function DeleteJobPopout({ details, onClose, situation }) {
return (
<ModalCom action={onClose} situation={situation}>
<div className="logout-modal-wrapper lg:w-[600px] 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">
<div className="modal-header-con">
<h1 className="modal-title">
Delete Job
</h1>
<button
type="button"
className="text-[#374557] dark:text-red-500"
className="modal-close-btn"
onClick={onClose}
>
<svg
@@ -119,10 +119,10 @@ function DeleteJobPopout({ details, onClose, situation }) {
{details.title}
</p>
<p className="text-lg tracking-wide text-dark-gray dark:text-white flex items-start gap-1">
Price: {details.thePrice}
<span className="job-label">Price: </span>{details.thePrice}
</p>
<p className="text-lg tracking-wide text-dark-gray dark:text-white">
Duration: {details.timeline_days} day(s)
<span className="job-label">Duration: </span>{details.timeline_days} day(s)
</p>
</div>
<div className="flex space-x-2.5">
+7 -7
View File
@@ -212,13 +212,13 @@ const EditJobPopOut = ({
return (
<ModalCom action={onClose} situation={situation}>
<div className="logout-modal-wrapper w-11/12 lg:w-[600px] 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">
<div className="modal-header-con">
<h1 className="modal-title">
Edit Job
</h1>
<button
type="button"
className="text-[#374557] dark:text-red-500"
className="modal-close-btn"
onClick={onClose}
>
<svg
@@ -328,8 +328,8 @@ const EditJobPopOut = ({
<div className="field flex flex-col sm:flex-row w-full mb-[5px] gap-2">
<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 block"'
htmlFor="job-label"
className='job-label'
>
Job Delivery Details
</label>
@@ -352,7 +352,7 @@ const EditJobPopOut = ({
<div className="sm:w-[35%] w-full">
<div
htmlFor="Job Categories"
className='className="input-label text-[#181c32] dark:text-white text-[13.975px] leading-[20.9625px] font-semibold block"'
className='job-label'
id="checked-group"
>
Categories
@@ -422,7 +422,7 @@ const EditJobPopOut = ({
<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"
className="job-label flex flex-col"
htmlFor="timeline_days"
>
Timeline -
@@ -10,7 +10,12 @@ import localImgLoad from "../../lib/localImgLoad";
import { tableReload } from "../../store/TableReloads";
import { useDispatch } from "react-redux";
import { SocketValues } from "../Contexts/SocketIOContext";
function FamilyOfferJobPopout({ details, onClose, situation }) {
const {parentAssignJobToKid} = SocketValues()
const apiUrl = new usersService();
const navigate = useNavigate();
const dispatch = useDispatch();
@@ -69,6 +74,18 @@ function FamilyOfferJobPopout({ details, onClose, situation }) {
message: `Offer ${name}ed Successfully`,
trigger: "",
});
// trigger socket event to refresh parent side
//SENDS MESSAGE TO SOCKET TO UPDATE PARENT ACCOUNT WHEN CHILD ACCEPTS OR REJECTS A JOB ASSIGNED BY PARENT
// message, room
let socketMsg = {
"audience": "PARENT",
"action": "REFRESH_TASK",
"family_uid": sessionStorage.getItem('family_uid'),
}
let socketRoom = `FAMILY-${sessionStorage.getItem('parent_uid')}`
parentAssignJobToKid(socketMsg, socketRoom) //SENDS MESSAGE TO SOCKET TO UPDATE CHILD ACCOUNT
// end of socket event trigger
setTimeout(() => {
onClose();
dispatch(tableReload({ type: "MYTASKTABLE" }));
@@ -106,13 +123,13 @@ function FamilyOfferJobPopout({ details, onClose, situation }) {
return (
<ModalCom action={onClose} situation={situation}>
<div className="logout-modal-wrapper w-[90%] md:w-[768px] 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] border-b dark:border-[#5356fb29] border-light-purple">
<h1 className="text-26 font-bold text-dark-gray dark:text-white tracking-wide">
<div className="modal-header-con">
<h1 className="modal-title">
Start Task
</h1>
<button
type="button"
className="text-[#374557] dark:text-red-500"
className="modal-close-btn"
onClick={onClose}
>
<svg
@@ -136,7 +153,7 @@ function FamilyOfferJobPopout({ details, onClose, situation }) {
</svg>
</button>
</div>
<div className="md:flex bg-white rounded-lg shadow-lg">
<div className="md:flex bg-white rounded-lg">
<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">
@@ -244,12 +261,12 @@ function FamilyOfferJobPopout({ details, onClose, situation }) {
</div>
{/* close button */}
<div className="p-6 flex justify-end">
<div className="modal-footer-wrapper flex justify-end">
<button
onClick={onClose}
disabled={requestStatus.loading}
type="button"
className="border-gradient text-18 tracking-wide px-2 py-2 rounded-full"
className="custom-btn border-gradient"
>
<span className="text-gradient">Cancel</span>
</button>
+16 -10
View File
@@ -8,6 +8,7 @@ import InputCom from "../Helpers/Inputs/InputCom/index";
import ModalCom from "../Helpers/ModalCom";
import LoadingSpinner from "../Spinners/LoadingSpinner";
import Detail from "./popoutcomponent/Detail";
import { SocketValues } from "../Contexts/SocketIOContext";
const validationSchema = Yup.object().shape({
family: Yup.string().required("This is required "),
@@ -29,6 +30,9 @@ function JobListPopout({
openWallet,
setWalletItem,
}) {
let {marketUpdate} = SocketValues() // destructures 'SEND MESSAGE' and 'JOIN ROOM' FUNCTIONS FROM SOCKET
const [selectedTab, setSelectedTab] = useState("public");
const tabs = ["public", "individual", "group"];
@@ -201,7 +205,9 @@ function JobListPopout({
setRequestStatus({ message: "", status: false });
}, 3000);
}
dispatch(tableReload({ type: "JOBTABLE" }));
marketUpdate('market', 'full-markets-jobs') // sends an event to the socket to update market lists
dispatch(tableReload({ type: "JOBTABLE" })); // reloads my job page
dispatch(tableReload({ type: "MARKETTABLELIST" })); // reloads market page
setRequestStatus({ message: data?.status_msg ? data?.status_msg : "Offer Assigned Successful", status: true });
setTimeout(() => {
setLoader({ jobFields: false });
@@ -224,6 +230,12 @@ function JobListPopout({
members: [],
});
const DetailsSection = ({ label, value }) => (
<div className="my-3 md:flex">
<Detail label={label} value={value} />
</div>
);
// FUNCTION TO POPULATE USER GROUP LIST
useEffect(() => {
// setGroupList({loading: true, groups: [], members: []})
@@ -250,12 +262,6 @@ function JobListPopout({
});
}, []);
const DetailsSection = ({ label, value }) => (
<div className="my-3 md:flex">
<Detail label={label} value={value} />
</div>
);
const DetailsComponent = () => {
const detailsArray = [
{ label: "Description", value: details.description },
@@ -297,13 +303,13 @@ function JobListPopout({
return (
<ModalCom action={onClose} situation={situation} className="">
<div className="logout-modal-wrapper w-[90%] md:w-[768px] 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] border-b dark:border-[#5356fb29] border-light-purple">
<h1 className="text-base md:text-lg font-bold text-dark-gray dark:text-white tracking-wide">
<div className="modal-header-con">
<h1 className="modal-title">
{details.title}
</h1>
<button
type="button"
className="text-[#374557] dark:text-red-500"
className="modal-close-btn"
onClick={onClose}
>
<svg
+6 -6
View File
@@ -103,13 +103,13 @@ function OfferJobPopout({ details, onClose, situation }) {
return (
<ModalCom action={onClose} situation={situation}>
<div className="logout-modal-wrapper w-[90%] md:w-[768px] 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] border-b dark:border-[#5356fb29] border-light-purple">
<h1 className="text-26 font-bold text-dark-gray dark:text-white tracking-wide">
<div className="modal-header-con">
<h1 className="modal-title">
Start Task
</h1>
<button
type="button"
className="text-[#374557] dark:text-red-500"
className="modal-close-btn"
onClick={onClose}
>
<svg
@@ -133,7 +133,7 @@ function OfferJobPopout({ details, onClose, situation }) {
</svg>
</button>
</div>
<div className="md:flex bg-white dark:bg-dark-white rounded-lg shadow-lg">
<div className="md:flex bg-white dark:bg-dark-white rounded-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 dark:text-white tracking-wide">
{details.title}
@@ -234,12 +234,12 @@ function OfferJobPopout({ details, onClose, situation }) {
</div>
{/* close button */}
<div className="p-6 flex justify-end">
<div className="modal-footer-wrapper flex justify-end">
<button
onClick={onClose}
disabled={requestStatus.loading}
type="button"
className="border-gradient text-18 tracking-wide px-2 py-2 rounded-full"
className="custom-btn border-gradient"
>
<span className="text-gradient">Cancel</span>
</button>
+17 -13
View File
@@ -9,6 +9,7 @@ import Detail from "./popoutcomponent/Detail";
import { useDispatch } from "react-redux";
import { tableReload } from "../../store/TableReloads";
import { NewDateTimeFormatter } from "../../lib/NewDateTimeFormatter";
const showSuccessToast = (message) => {
toast.success(message, {
@@ -70,7 +71,9 @@ function PendingJobsPopout({ details, onClose, situation }) {
.pendingJobSendTome(reqData)
.then((res) => {
setRequestMessage({ status: true, message: res.data.status });
dispatch(tableReload({ type: "PENDINGTABLE" }));
setTimeout(() => {
onClose()
setPendingJobLoader({ extend: false, offer: false });
setRequestMessage({ status: false, message: "" });
}, 4000);
@@ -135,13 +138,13 @@ function PendingJobsPopout({ details, onClose, situation }) {
return (
<ModalCom action={onClose} situation={situation}>
<div className="logout-modal-wrapper w-[90%] md:w-[768px] 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] border-b dark:border-[#5356fb29] border-light-purple">
<h1 className="text-26 font-bold text-dark-gray dark:text-white tracking-wide">
<div className="modal-header-con">
<h1 className="modal-title">
Manage Pending Item
</h1>
<button
type="button"
className="text-[#374557] dark:text-red-500"
className="modal-close-btn"
onClick={onClose}
>
<svg
@@ -170,7 +173,7 @@ function PendingJobsPopout({ details, onClose, situation }) {
<p className="text-base font-semibold text-slate-900 dark:text-white tracking-wide">
{details.title}
</p>
<div className="my-2 p-2 flex justify-start items-center space-x-2 bg-red-100 border-2 border-red-300">
<div className="my-2 p-2 flex justify-start items-center space-x-2 bg-red-100 border-2 border-red-300 rounded-2xl">
<span className="w-8 h-8 text-center text-sm rounded-full flex justify-center items-center text-red-300 bg-yellow-100">
!
</span>
@@ -184,12 +187,12 @@ function PendingJobsPopout({ details, onClose, situation }) {
</div>
{/* INPUT SECTION */}
<div className="my-2 md:flex">
{/* <div className="my-2 md:flex">
<Detail
label="Date Added"
value={details.offer_added || "default"}
/>
</div>
</div> */}
<div className="my-2 md:flex">
<Detail label="Description" value={details.description} />
@@ -198,12 +201,13 @@ function PendingJobsPopout({ details, onClose, situation }) {
<div className="my-2 md:flex">
<Detail
label="Offer Expire"
value={
details.expire &&
`${details.expire.split(" ")[0]} ${
details.expire.split(" ")[1].split(".")[0]
}`
}
// value={
// details.expire &&
// `${details.expire.split(" ")[0]} ${
// details.expire.split(" ")[1].split(".")[0]
// }`
// }
value={NewDateTimeFormatter(new Date(details.expire))}
/>
</div>
@@ -236,7 +240,7 @@ function PendingJobsPopout({ details, onClose, situation }) {
{/* ACTION SECTION */}
<div className="p-4 w-full md:w-1/4 h-full">
<p className="mb-6 text-sm dark:text-white">Actions</p>
<p className="job-label mb-6 dark:text-white">Actions</p>
<div className="mb-3">
<p className="px-2 py-1 text-sm bg-slate-100">
@@ -3,7 +3,7 @@ import React from 'react'
function Detail({label, value, bg,}) {
return (
<>
<label className='w-full md:w-1/4 text-slate-900 dark:text-white tracking-wide font-semibold'>{label}</label>
<label className='job-label w-full md:w-1/4'>{label}</label>
<p className={`p-1 w-full md:w-3/4 text-sm text-slate-900 dark:text-white ${bg ? bg : null}`}>{value}</p>
</>
)
+32
View File
@@ -152,6 +152,38 @@
--toastify-color-success: #f539f8;
}
@layer components{
.job-label{
@apply text-base text-slate-900 dark:text-white tracking-wide font-semibold
}
.job-label-flex{
@apply flex items-center gap-2
}
/* STYLES FOR MODAL */
/* Modal Header */
.modal-header-con{
@apply w-full flex items-center justify-between lg:px-10 lg:py-8 px-[30px] py-[23px] bg-sky-blue/50 border-b dark:border-[#5356fb29] border-light-purple
}
.modal-title{
@apply text-2xl leading-8 font-bold text-dark-gray dark:text-white tracking-wide flex items-center
}
.modal-close-btn{
@apply text-[#000] dark:text-red-500
}
/* modal footer */
.modal-footer-wrapper{
@apply py-2 px-4 border-t-2 flex justify-between items-center
}
/* END OF STYLES FOR MODAL BOX */
/* STYLES FOR BUTTON */
.custom-btn {
@apply px-2 min-w-[80px] h-11 flex justify-center items-center text-base rounded-full cursor-pointer
}
}
/* ===================== EXTRA ===================== */
.bottomMargin {
margin-bottom: 15px;
+17
View File
@@ -0,0 +1,17 @@
export function NewDateTimeFormatter(isoDateString, addHour = true) {
const date = new Date(isoDateString);
if (addHour) {
date.setTime(date.getTime() + 1 * 60 * 60 * 1000);
}
const formattedDate = date.toLocaleDateString("en-US", {
year: "numeric",
month: "numeric",
day: "numeric",
hour: "2-digit",
minute: "2-digit",
// second: "2-digit",
hour12: true,
timeZone: "UTC",
});
return formattedDate;
}
+4
View File
@@ -0,0 +1,4 @@
export let errorMsg = {
error_unable_to_create_fund_for_task: "Insufficient Wallet Balance to Create Task",
}
+51 -12
View File
@@ -13,8 +13,13 @@ import { updateUserJobList } from "../store/userJobList";
import { updateWalletDetails } from "../store/walletDetails";
import { familyBannersList } from "../store/FamilyBannerList";
import { familyResources } from "../store/FamilyResources";
import {familyWalletRedeemOptList} from '../store/FamilyWalletRedeemOpt'
import { SocketValues } from "../components/Contexts/SocketIOContext";
const AuthRoute = ({ redirectPath = "/login", children }) => {
let {joinRoom} = SocketValues() // destructures 'SEND MESSAGE' and 'JOIN ROOM' FUNCTIONS FROM SOCKET
const apiCall = useMemo(() => new usersService(), []);
const dispatch = useDispatch();
const [lastActivityTime, setLastActivityTime] = useState(Date.now());
@@ -22,23 +27,20 @@ const AuthRoute = ({ redirectPath = "/login", children }) => {
const [loadProfileDetails, setLoadProfileDetails] = useState([]);
const navigate = useNavigate();
const { jobListTable, walletTable, familyBannersListTable } = useSelector(
const { jobListTable, marketTableList, walletTable, familyBannersListTable } = useSelector(
(state) => state.tableReload
);
const {
userDetails: { username, uid, session, account_type },
userDetails: { username, uid, session, account_type, parent_uid },
} = 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
useEffect(() => {
//Removing Data stored at localStorage after session expires
const expireSession = () => {
localStorage.removeItem("uid");
localStorage.removeItem("member_id");
localStorage.removeItem("session_token");
sessionStorage.removeItem("family_uid");
sessionStorage.clear();
localStorage.clear();
navigate("/login", { replace: true }); // redirects user to login page after session expires
};
@@ -104,6 +106,8 @@ const AuthRoute = ({ redirectPath = "/login", children }) => {
});
};
loadProfile();
}else{
setIsLogin({ loading: false, status: true });
}
}, []);
@@ -188,6 +192,9 @@ const AuthRoute = ({ redirectPath = "/login", children }) => {
}, [jobListTable, isLogin.status]);
useEffect(() => {
if((!loggedIn && !isLogin.status) || account_type == 'FAMILY'){ // DO NOT CALL THIS, IF USER ACCOUNT TYPE IS FAMILY
return
}
const getMyWalletList = async () => {
dispatch(updateWalletDetails({ loading: true, data: [] }));
try {
@@ -202,7 +209,7 @@ const AuthRoute = ({ redirectPath = "/login", children }) => {
}
};
getMyWalletList();
}, [walletTable]);
}, [walletTable, isLogin.status]);
useEffect(() => {
if((!loggedIn && !isLogin.status) || account_type == 'FAMILY'){ // DO NOT CALL THIS, IF USER ACCOUNT TYPE IS FAMILY
@@ -212,13 +219,14 @@ const AuthRoute = ({ redirectPath = "/login", children }) => {
const getMarketActiveJobList = async () => {
try {
const res = await apiCall.getActiveJobList();
dispatch(updateJobs(res.data));
dispatch(updateJobs({loading: false, ...res.data}));
} catch (error) {
dispatch(updateJobs({loading: false}));
console.log("Error getting mode");
}
};
getMarketActiveJobList();
}, [apiCall, dispatch, jobListTable, isLogin.status]);
}, [apiCall, dispatch, marketTableList, isLogin.status]);
//FUNCTION TO GET COMMON HEAD DATA
useEffect(() => {
@@ -228,7 +236,7 @@ const AuthRoute = ({ redirectPath = "/login", children }) => {
apiCall
.getHeroJBanners()
.then((res) => {
if (res.data.internal_return < 0) {
if (res.data?.internal_return < 0) {
return;
}
dispatch(commonHeadBanner(res.data));
@@ -240,6 +248,9 @@ const AuthRoute = ({ redirectPath = "/login", children }) => {
//FUNCTION TO GET COMMON HEAD DATA
useEffect(() => {
if((!loggedIn && !isLogin.status) || account_type == 'FAMILY'){ // DO NOT CALL THIS, IF USER ACCOUNT TYPE IS FAMILY
return
}
apiCall
.getRecentActivitiedData()
.then((res) => {
@@ -252,7 +263,7 @@ const AuthRoute = ({ redirectPath = "/login", children }) => {
.catch((error) => {
console.log("ERROR ", error);
});
}, []);
}, [isLogin.status]);
//FUNCTION TO GET FAMILY BANNERS
@@ -291,6 +302,34 @@ const AuthRoute = ({ redirectPath = "/login", children }) => {
}, [isLogin.status]);
//FUNCTION TO GET FAMILY WALLET REDEEM OPTIONS
useEffect(() => {
if((!loggedIn && !isLogin.status) || account_type == 'FULL'){ // DO NOT CALL THIS, IF USER ACCOUNT TYPE IS FULL
return
}
const familyWalletRedeemOptions = async () => { // FUNCTION TO GET FAMILY WALLET REDDEM OPTIONS
dispatch(familyWalletRedeemOptList({loading: true, image: '', data:{}}))
apiCall.getFamilyWalletRedeemOptions().then((res)=>{
dispatch(familyWalletRedeemOptList({loading: false, image: res?.data?.session_image_server, data:res?.data?.result_list}))
}).catch((err)=>{
console.log(err)
dispatch(familyWalletRedeemOptList({loading: false, image: '', data:{}}))
})
};
familyWalletRedeemOptions()
}, [isLogin.status]);
useEffect(()=>{ // sends an event to the socket to enable user join a room to be able to receive update when jobs enters the market
joinRoom('full-markets-jobs')
},[isLogin.status])
useEffect(()=>{ // sends an event to the socket to enable user join a room to be able to receive update for parent child job assign
if(loggedIn || isLogin.status){
joinRoom(`FAMILY-${account_type == 'FULL' ? uid : sessionStorage.getItem('parent_uid')}`)
console.log(`Room joined for parent child task assign as ${account_type} with ${account_type == 'FULL' ? uid : sessionStorage.getItem('parent_uid')}}`)
}
},[isLogin.status])
// RENDER PAGE
return isLogin.loading && !loggedIn ? (
<LoadingSpinner size="32" color="sky-blue" height="h-screen" />
+66 -3
View File
@@ -388,13 +388,26 @@ class usersService {
uid: localStorage.getItem("uid"),
member_id: localStorage.getItem("member_id"),
sessionid: localStorage.getItem("session_token"),
action: apiConst.WRENCHBOARD_JOB_CREATEJOB,
action: apiConst.WRENCHBOARD_JOB_CREATEJOB || apiConst.WRENCHBOARD_FAMILY_LIST,
limit: 30,
offset: 0,
};
return this.postAuxEnd("/familywaitingtasks", postData);
}
ManageFamilyNewWaitlist() {
var postData = {
uid: localStorage.getItem("uid"),
member_id: localStorage.getItem("member_id"),
sessionid: localStorage.getItem("session_token"),
action: apiConst.WRENCHBOARD_FAMILY_LIST,
limit: 20,
page: 1,
offset: 0,
};
return this.postAuxEnd("/familywaitingtasks", postData);
}
ManageFamilyPending() {
var postData = {
uuid: localStorage.getItem("uid"),
@@ -1161,7 +1174,7 @@ class usersService {
return this.postAuxEnd("/suggeststatus", postData);
}
// FUNCTION TO GET FAMILY WALLET
// FUNCTION TO GET FAMILY WALLET AS A KID
getFamilyWallet(reqData) {
var postData = {
uid: localStorage.getItem("uid"),
@@ -1173,6 +1186,18 @@ class usersService {
return this.postAuxEnd("/familywallet", postData);
}
// FUNCTION TO GET FAMILY WALLET AS A PARENT
getKidWallets(reqData) {
var postData = {
uid: localStorage.getItem("uid"),
member_id: localStorage.getItem("member_id"),
sessionid: localStorage.getItem("session_token"),
action: apiConst.WRENCHBOARD_FAMILY_WALLET,
...reqData,
};
return this.postAuxEnd("/kidwallets", postData);
}
// FUNCTION TO START FAMILY TRANSFER
familyTransferStart(reqData) {
var postData = {
@@ -1292,7 +1317,45 @@ class usersService {
};
return this.postAuxEnd("/blogdata", postData);
}
// API FUNCTION FOR FAMILY WALLET REDEEM OPTIONS
getFamilyWalletRedeemOptions() {
var postData = {
uuid: localStorage.getItem("uid"),
member_id: localStorage.getItem("member_id"),
sessionid: localStorage.getItem("session_token"),
action: apiConst.WRENCHBOARD_JOB_ACTIVE,
offset: 0,
limit: 30,
};
return this.postAuxEnd("/familywallet/redeem/options", postData);
}
// API FUNCTION FOR PARENT TO CALL COMMON MEDIA
getParentCommonMedia() {
var postData = {
uid: localStorage.getItem("uid"),
member_id: localStorage.getItem("member_id"),
sessionid: localStorage.getItem("session_token"),
action: apiConst.WRENCHBOARD_ACCOUNT_FAMILY_RESOURCES,
offset: 1,
limit: 20,
};
return this.postAuxEnd("/commonmedia", postData);
}
// API FUNCTION FOR PARENT TO ASSIGN MEDIA TASK
parentAssignMediaTask(reqData) {
var postData = {
uid: localStorage.getItem("uid"),
member_id: localStorage.getItem("member_id"),
sessionid: localStorage.getItem("session_token"),
action: apiConst.WRENCHBOARD_JOB_OFFER_SYSTEM,
...reqData
};
return this.postAuxEnd("/assignmediatask", postData);
}
/*
- 20:27:30.118 FLOG_MAX [757411]: REQ_STRING(username)
- 20:27:30.118 FLOG_MAX [757411]: REQ_STRING(password)
+20
View File
@@ -0,0 +1,20 @@
import { createSlice } from "@reduxjs/toolkit";
const initialState = {
familyWalletRedeemOptList: {loading: true, image: '', data: {}}
};
export const familyWalletRedeemOptListSlice = createSlice({
name: "familyWalletRedeemOptList",
initialState,
reducers: {
familyWalletRedeemOptList: (state,action) => {
state.familyWalletRedeemOptList = {...action.payload}
},
},
});
// Action creators are generated for each case reducer function
export const { familyWalletRedeemOptList } = familyWalletRedeemOptListSlice.actions;
export default familyWalletRedeemOptListSlice.reducer;
+20
View File
@@ -9,6 +9,11 @@ const initialState = {
walletTable: false,
uploadsTable: false,
familyBannersListTable: false,
chatMessageList: false,
marketTableList: false,
familyOfferList: false,
parentFamilyTaskList: false,
offerInterestListReload: false,
};
export const tableReloadSlice = createSlice({
@@ -41,6 +46,21 @@ export const tableReloadSlice = createSlice({
case "FAMILYBANNERSLIST":
state.familyBannersListTable = !state.familyBannersListTable;
return;
case "CHATMESSAGELIST":
state.chatMessageList = !state.chatMessageList;
return;
case "MARKETTABLELIST":
state.marketTableList = !state.marketTableList;
return;
case "FAMILYOFFERLIST":
state.familyOfferList = !state.familyOfferList;
return;
case "PARENTFAMILYTASKLIST": // reloads list of active family task on parent side
state.parentFamilyTaskList = !state.parentFamilyTaskList;
return;
case "OFFERINTERESTLISTRELOAD": // to reload offer interest list of owner when a worker sends interest in a job
state.offerInterestListReload = !state.offerInterestListReload;
return;
default:
return state;
}
+1 -1
View File
@@ -1,7 +1,7 @@
import { createSlice } from "@reduxjs/toolkit";
const initialState = {
jobLists: {}
jobLists: {loading: true}
};
export const jobSlice = createSlice({
+3 -1
View File
@@ -10,6 +10,7 @@ import userJobListReducer from "./userJobList";
import walletDetails from "./walletDetails";
import familyBannerListReducer from "./FamilyBannerList"
import familyResourcesReducer from './FamilyResources'
import familyWalletRedeemOptListReducer from './FamilyWalletRedeemOpt'
export default configureStore({
reducer: {
@@ -22,6 +23,7 @@ export default configureStore({
notifications: notificationsReducer,
walletDetails: walletDetails,
familyBannersList: familyBannerListReducer,
familyResources: familyResourcesReducer
familyResources: familyResourcesReducer,
familyWalletRedeemOptList: familyWalletRedeemOptListReducer
},
});
+5 -5
View File
@@ -6,13 +6,13 @@ function AddJobPage({ action, situation, categories }) {
return (
<ModalCom action={action} situation={situation}>
<div className="lg:w-[600px] w-11/12 lg:overflow-hidden lg:rounded-2xl bg-white dark:bg-dark-white dark:text-white">
<div className="heading flex justify-between items-center py-6 md:px-[30px] px-[23px] border-b border-light-purple dark:border-[#5356fb29] ">
<p className="text-26 font-bold text-dark-gray dark:text-white tracking-wide">
Create New Job
</p>
<div className="modal-header-con">
<h1 className="modal-title">
New Job
</h1>
<button
type="button"
className="text-[#374557] dark:text-red-500"
className="modal-close-btn"
onClick={action}
>
<svg
+2 -2
View File
@@ -1,5 +1,5 @@
import FamilyWallet from "../components/MyWallet/FamilyWallet";
import FamilyWalletCon from "../components/MyWallet/FamilyWalletCon";
export default function FamilyWalletPage() {
return <FamilyWallet />;
return <FamilyWalletCon />;
}
+6 -1
View File
@@ -2,12 +2,17 @@ import React, { useEffect, useState } from "react";
import { useLocation, useNavigate } from "react-router-dom";
import ActiveJobs from "../components/MyActiveJobs/ActiveJobs";
import usersService from "../services/UsersService";
import { useSelector } from "react-redux";
/**
* This code defines a React functional component called `ManageActiveJobs`.
* It fetches a list of active job messages and renders the `ActiveJobs` component with the necessary props.
*/
function ManageActiveJobs() {
const { chatMessageList } = useSelector(
(state) => state.tableReload
);
const ApiCall = new usersService();
const navigate = useNavigate();
@@ -54,7 +59,7 @@ function ManageActiveJobs() {
}
setDetails(state);
getActiveJobMesList();
}, [activeJobMesListReload]);
}, [activeJobMesListReload, chatMessageList]);
return (
<ActiveJobs
+3 -3
View File
@@ -12,7 +12,7 @@ export default function MyReviewDueJobsPage() {
let { othersInterestedTable } = useSelector((state) => state.tableReload); // FOR OTHERS INTERESTED TABLE RELOAD
const apiCall = new usersService();
const [othersInterestedList, setOthersInterestedList] = useState({loading: true, data: []})
const [othersInterestedList, setOthersInterestedList] = useState({loading: true, data: [], imageServer:''})
useEffect(() => {
if(!state){
@@ -26,9 +26,9 @@ export default function MyReviewDueJobsPage() {
}else{
newData = []
}
setOthersInterestedList({loading: false, data: newData})
setOthersInterestedList({loading: false, data: newData, imageServer:res.data.session_image_server})
}).catch(err => {
setOthersInterestedList({loading: false, data: []})
setOthersInterestedList({loading: false, data: [], imageServer:''})
console.log('Error: ', err)
})
}, [othersInterestedTable]);
+16 -3
View File
@@ -5,20 +5,33 @@ import OffersInterest from "../components/OffersInterest";
import usersService from "../services/UsersService";
import { SocketValues } from "../components/Contexts/SocketIOContext"; // for reading socket context values
export default function OffersInterestPage() {
const { offerInterestListReload } = useSelector((state) => state.tableReload); // table/list reload variable
const {userDetails} = useSelector((state) => state?.userDetails); // Gets USER Details
let {joinRoom} = SocketValues() // function to join room for socket
const apiCall = new usersService()
let {commonHeadBanner} = useSelector(state => state.commonHeadBanner)
let [offerInterestList, setOfferInterestList] = useState({loading: true, data: []})
let [offerInterestList, setOfferInterestList] = useState({loading: true, data: [], imgServer:''})
useEffect(()=>{
apiCall.offersInterestList().then(res => {
setOfferInterestList({loading: false, data: res.data.result_list})
setOfferInterestList({loading: false, data: res.data.result_list, imgServer:res.data.session_image_server})
}).catch(err => {
setOfferInterestList({loading: false, data: []})
setOfferInterestList({loading: false, data: [], imgServer:''})
console.log('Error: ', err)
})
},[offerInterestListReload])
useEffect(()=>{
joinRoom(`INTEREST-${userDetails?.uid}`)
},[])
return (