Compare commits

...

149 Commits

Author SHA1 Message Date
victorAnumudu 8a9ec35994 Assign family task API consumed 2023-07-01 00:06:22 +01:00
tokslaw fa2102eb61 Merge branch 'resources-page' of WrenchBoard/Users-Wrench into master 2023-06-30 18:21:38 +00:00
tokslaw bf73461c64 Merge branch 'price-fix' of WrenchBoard/Users-Wrench into master 2023-06-30 18:21:25 +00:00
victorAnumudu 5ffa6eb691 price fix 2023-06-30 16:01:11 +01:00
Ebube 5a0d8aebdb 1280 width fixed 2023-06-30 13:48:52 +01:00
Ebube e8ed10ddbf Added link to Family Market 2023-06-30 13:28:44 +01:00
ameye cca423a41c Merge branch 'family-assign-task' of WrenchBoard/Users-Wrench into master 2023-06-30 11:08:57 +00:00
victorAnumudu 6a6900a62b family assign task popout added 2023-06-30 11:57:47 +01:00
ameye c6b4fcc43d Merge branch 'delete-edit-icon' of WrenchBoard/Users-Wrench into master 2023-06-29 18:25:39 +00:00
ameye 29510c3b85 Merge branch 'resources-page' of WrenchBoard/Users-Wrench into master 2023-06-29 18:25:34 +00:00
victorAnumudu 1d8e54c57d delete and edit icon seperator width reduced 2023-06-29 13:56:14 +01:00
victorAnumudu 426599dd1f delete and edit icons added 2023-06-29 13:52:33 +01:00
Ebube c8d6d3d7d4 Removed Bg Img 2023-06-29 13:39:07 +01:00
Ebube 593ea74388 Merge branch 'master' of https://gitlab.chiefsoft.net/WrenchBoard/Users-Wrench into resources-page 2023-06-29 13:37:04 +01:00
Ebube d81b9970b3 Improved folder structure for resources and clean up 2023-06-29 13:18:31 +01:00
ameye 777ff54220 Merge branch 'resources-page' of WrenchBoard/Users-Wrench into master 2023-06-29 10:37:20 +00:00
Ebube 382a266b9a Resource Page Data Mapping 2023-06-29 09:09:30 +01:00
ameye 2d80dd9450 Merge branch 'no-interest-table-hidden' of WrenchBoard/Users-Wrench into master 2023-06-28 15:56:41 +00:00
victorAnumudu 0eaa72a5ec made others interest table hidden if its empty 2023-06-28 16:42:16 +01:00
CHIEFSOFT\ameye a2e2df867d resource props 2023-06-28 10:41:08 -04:00
ameye 10d4e169d3 Merge branch 'manage-active-job-delivery-date' of WrenchBoard/Users-Wrench into master 2023-06-28 13:38:48 +00:00
Ebube 79ed578483 Merge branch 'master' of https://gitlab.chiefsoft.net/WrenchBoard/Users-Wrench into manage-active-job-delivery-date 2023-06-28 14:10:42 +01:00
Ebube 25440a3c06 fixed minor bug 2023-06-28 14:10:14 +01:00
ameye 5f4c40a318 Merge branch 'manage-active-job-delivery-date' of WrenchBoard/Users-Wrench into master 2023-06-28 01:06:13 +00:00
Ebube ff7e8ea1ab Merge branch 'master' of https://gitlab.chiefsoft.net/WrenchBoard/Users-Wrench into manage-active-job-delivery-date 2023-06-28 02:02:27 +01:00
Ebube cba14f4265 Added error borders 2023-06-28 02:01:53 +01:00
ameye 9c342f87f7 Merge branch 'manage-active-job-delivery-date' of WrenchBoard/Users-Wrench into master 2023-06-27 21:01:38 +00:00
Ebube 498966dd23 Fixed bugs in market popup and made changes to Add/edit 2023-06-27 21:57:39 +01:00
ameye b282295924 Merge branch 'manage-active-job-delivery-date' of WrenchBoard/Users-Wrench into master 2023-06-27 18:21:46 +00:00
Ebube 7222a4d750 Merge branch 'master' of https://gitlab.chiefsoft.net/WrenchBoard/Users-Wrench into manage-active-job-delivery-date 2023-06-27 17:12:02 +01:00
Ebube 271f5635a0 Fixed Api Parameter, Banner Button Style and Slider header 2023-06-27 17:11:30 +01:00
ameye 86c4283507 Merge branch 'manage-active-job-delivery-date' of WrenchBoard/Users-Wrench into master 2023-06-27 14:19:36 +00:00
Ebube 5e5d953769 Add Job button Fixed 2023-06-27 15:15:41 +01:00
ameye aa7065c5b4 Merge branch 'manage-active-job-delivery-date' of WrenchBoard/Users-Wrench into master 2023-06-27 14:10:27 +00:00
Ebube 29fee11ec3 Merge branch 'master' of https://gitlab.chiefsoft.net/WrenchBoard/Users-Wrench into manage-active-job-delivery-date 2023-06-27 13:49:24 +01:00
Ebube 264d7b8501 . 2023-06-27 13:48:54 +01:00
ameye 8f90bcdf10 Merge branch 'manage-active-job-delivery-date' of WrenchBoard/Users-Wrench into master 2023-06-27 10:52:36 +00:00
Ebube 4b897cb3a9 . 2023-06-27 10:06:51 +01:00
ameye 0977650bf4 Merge branch 'faq-display' of WrenchBoard/Users-Wrench into master 2023-06-26 18:07:41 +00:00
Ebube 1f76dd0db8 Merge branch 'master' of https://gitlab.chiefsoft.net/WrenchBoard/Users-Wrench into faq-display 2023-06-26 19:01:49 +01:00
Ebube f772cf0a68 . 2023-06-26 18:57:41 +01:00
CHIEFSOFT\ameye c4c5c7967e Resource Starter 2023-06-26 13:36:43 -04:00
Ebube 45ecec24ac Merge branch 'master' of https://gitlab.chiefsoft.net/WrenchBoard/Users-Wrench into faq-display 2023-06-26 17:48:25 +01:00
Ebube 16fd35df83 . 2023-06-26 17:48:00 +01:00
CHIEFSOFT\ameye 3f6a391d30 witing Jobs 2023-06-26 09:23:28 -04:00
CHIEFSOFT\ameye 5eb64f49c0 text style 2023-06-26 07:50:49 -04:00
CHIEFSOFT\ameye 1761150bd3 My wait jobs 2023-06-26 07:44:03 -04:00
ameye a4b15dd06e Merge branch 'faq-display' of WrenchBoard/Users-Wrench into master 2023-06-25 21:38:00 +00:00
Ebube 1a15410e4c . 2023-06-25 20:07:35 +01:00
Ebube da1133ed43 Merge branch 'master' of https://gitlab.chiefsoft.net/WrenchBoard/Users-Wrench into faq-display 2023-06-25 20:00:49 +01:00
Ebube 23605bc358 . 2023-06-25 19:58:54 +01:00
ameye 2092682be6 Merge branch 'faq-display' of WrenchBoard/Users-Wrench into master 2023-06-25 11:19:58 +00:00
Ebube 0e270d8efa . 2023-06-25 07:09:17 +01:00
CHIEFSOFT\ameye 3bb4fe6a02 banner waiting 2023-06-25 00:01:44 -04:00
CHIEFSOFT\ameye fa728d3879 Waiting Jobs 2023-06-24 15:43:59 -04:00
ameye a9ce76123b Merge branch 'privacy-policy' of WrenchBoard/Users-Wrench into master 2023-06-24 11:17:42 +00:00
ameye 3ac1be9b89 Merge branch 'AddJob-Link-Fix' of WrenchBoard/Users-Wrench into master 2023-06-24 11:17:37 +00:00
ameye e4be117c90 Merge branch 'offer-interest-msg-list' of WrenchBoard/Users-Wrench into master 2023-06-24 11:17:11 +00:00
Ebube 58834cd7ca privacy policy 2023-06-24 08:52:33 +01:00
Ebube 59945c28e4 . 2023-06-24 07:23:39 +01:00
victorAnumudu 0feaf42f49 offer interest message list API added 2023-06-23 23:47:46 +01:00
ameye 5a623dd147 Merge branch 'AddJob-Link-Fix' of WrenchBoard/Users-Wrench into master 2023-06-23 21:52:50 +00:00
ameye 9265fde343 Merge branch 'offer-interest-msg-sending' of WrenchBoard/Users-Wrench into master 2023-06-23 21:52:39 +00:00
Ebube 452bb73bef AddJob_Link Fix 2023-06-23 21:42:26 +01:00
victorAnumudu cfec230ce3 Added API for sending offer interest message 2023-06-23 20:51:37 +01:00
ameye 87430f530d Merge branch 'manage-offer-reformat' of WrenchBoard/Users-Wrench into master 2023-06-22 23:48:41 +00:00
victorAnumudu 52cb0cb2da manage offer reformatted 2023-06-22 20:41:17 +01:00
ameye a19df45997 Merge branch 'terms-and-conditions' of WrenchBoard/Users-Wrench into master 2023-06-22 19:36:07 +00:00
Ebube 7868e7d689 Done 2023-06-22 18:34:33 +01:00
ameye 08f1ae1a9f Merge branch 'wallet-bal-format' of WrenchBoard/Users-Wrench into master 2023-06-22 14:17:09 +00:00
victorAnumudu c4cc27490b wallet balance formatted 2023-06-22 15:14:43 +01:00
ameye 90b609d457 Merge branch 'new-price-formatter' of WrenchBoard/Users-Wrench into master 2023-06-22 13:40:47 +00:00
victorAnumudu a6c6c36fbc price formatter style changed 2023-06-22 14:24:56 +01:00
ameye 03b79f0e0c Merge branch 'Cookies-addition-for-family' of WrenchBoard/Users-Wrench into master 2023-06-22 10:43:50 +00:00
Ebube 0af52df1d2 Merge branch 'master' of https://gitlab.chiefsoft.net/WrenchBoard/Users-Wrench into Cookies-addition-for-family 2023-06-22 11:42:20 +01:00
ameye 9e1a68f81a Merge branch 'JobList-Categories' of WrenchBoard/Users-Wrench into master 2023-06-22 10:37:26 +00:00
Ebube 12e4b7824a Added Cookies for family 2023-06-22 10:01:39 +01:00
Ebube 5769332e74 . 2023-06-22 08:42:42 +01:00
ameye bab0296f4f Merge branch 'offer-interest-format' of WrenchBoard/Users-Wrench into master 2023-06-21 15:06:12 +00:00
victorAnumudu d457550d58 offer interest page formatted 2023-06-21 15:58:40 +01:00
ameye f106e17ce6 Merge branch 'market-toggle-position' of WrenchBoard/Users-Wrench into master 2023-06-21 10:52:13 +00:00
victorAnumudu 8f1d2b6584 market category toggle position changed 2023-06-21 10:02:47 +01:00
ameye 3a3503447a Merge branch 'market-list-toggle' of WrenchBoard/Users-Wrench into master 2023-06-20 22:34:26 +00:00
ameye e46d2bea8d Merge branch 'JobList-Categories' of WrenchBoard/Users-Wrench into master 2023-06-20 22:34:15 +00:00
victorAnumudu b05c519571 adjusted market toggle position 2023-06-20 22:48:55 +01:00
victorAnumudu 48ab2d80ad market list toggle added, thumbnail images added in add pay card page 2023-06-20 22:37:18 +01:00
Ebube 47876875cf done 2023-06-20 21:33:18 +01:00
ameye d78ad0b648 Merge branch 'JobList-Categories' of WrenchBoard/Users-Wrench into master 2023-06-20 19:52:15 +00:00
Ebube d63690a43c Merge branch 'master' of https://gitlab.chiefsoft.net/WrenchBoard/Users-Wrench into JobList-Categories 2023-06-20 20:47:36 +01:00
Ebube 3ce97a4b76 . 2023-06-20 20:46:51 +01:00
ameye 6e9af99d46 Merge branch 'paycard-listing' of WrenchBoard/Users-Wrench into master 2023-06-20 19:44:35 +00:00
victorAnumudu 4ce8f813c4 added svg images 2023-06-20 20:22:08 +01:00
victorAnumudu 9163c42d77 API for paycard listing added 2023-06-20 20:15:05 +01:00
CHIEFSOFT\ameye 3b4ce9c99e Production to orion 2023-06-20 12:00:57 -04:00
ameye cfa6117a07 Merge branch 'history-new-layout' of WrenchBoard/Users-Wrench into master 2023-06-20 15:01:40 +00:00
victorAnumudu 6f26e2e88f history layout changed 2023-06-20 15:50:47 +01:00
ameye b7927a9d6a Merge branch 'JobList-Categories' of WrenchBoard/Users-Wrench into master 2023-06-19 22:43:57 +00:00
Ebube 2d366cd103 . 2023-06-19 22:32:18 +01:00
Ebube 6eed7bf1f3 Added Price Formatter to this files 2023-06-19 22:27:01 +01:00
Ebube 3c87a67581 Changed the status and moved the payload to the users-services 2023-06-19 21:47:40 +01:00
Ebube e31ee86299 Merge branch 'master' of https://gitlab.chiefsoft.net/WrenchBoard/Users-Wrench into JobList-Categories 2023-06-19 21:25:18 +01:00
Ebube 4d3fe6a799 Joblist cart 2023-06-19 21:24:57 +01:00
CHIEFSOFT\ameye 3e8c8e88ea Correct endpoint settings 2023-06-19 08:24:19 -04:00
ameye feca65eb80 Merge branch 'add-dollar-fund' of WrenchBoard/Users-Wrench into master 2023-06-19 11:45:43 +00:00
victorAnumudu 9ea846bc56 add dollar fund component added 2023-06-19 10:21:50 +01:00
CHIEFSOFT\ameye 8c31eb3145 banner description 2023-06-18 20:05:11 -04:00
ameye d049d1b2b0 Merge branch 'referral_page_payload_fix' of WrenchBoard/Users-Wrench into master 2023-06-18 22:17:02 +00:00
Ebube d34895c64c . 2023-06-18 23:09:18 +01:00
Ebube 9a3fa2a4a4 added fix 2023-06-18 23:06:16 +01:00
Ebube a9d982386c . 2023-06-18 22:46:42 +01:00
ameye 2341d2a17d Merge branch 'offer-interval-cleanup' of WrenchBoard/Users-Wrench into master 2023-06-18 16:45:26 +00:00
ameye 076df9438d Merge branch 'Market-Categories' of WrenchBoard/Users-Wrench into master 2023-06-18 16:45:21 +00:00
Ebube bbf03b2b10 Merge branch 'master' of https://gitlab.chiefsoft.net/WrenchBoard/Users-Wrench into Market-Categories 2023-06-18 15:52:19 +01:00
Ebube afead464b0 Done! 2023-06-18 15:51:59 +01:00
victorAnumudu dcdf03e9c5 redirect interval cleared from memory once the page redirects 2023-06-18 13:41:43 +01:00
ameye 44e2402cb3 Merge branch 'offer-interest-process' of WrenchBoard/Users-Wrench into master 2023-06-18 11:58:58 +00:00
victorAnumudu 5c223ba641 margin added to offer interest table 2023-06-18 08:20:29 +01:00
victorAnumudu cc0e2c2c6b offer interest process API added 2023-06-18 08:14:22 +01:00
ameye 3df97c0760 Merge branch 'return-to-wallet-fixed' of WrenchBoard/Users-Wrench into master 2023-06-17 22:29:15 +00:00
ameye 6d742e92b5 Merge branch 'wallet-add-icon' of WrenchBoard/Users-Wrench into master 2023-06-17 22:29:08 +00:00
victorAnumudu 44090c94a1 return to wallet added and add fund style fixed 2023-06-17 23:06:46 +01:00
victorAnumudu 63f4658449 Icon padding fixed 2023-06-17 22:19:57 +01:00
CHIEFSOFT\ameye 552224c489 refer banner 2023-06-17 12:45:17 -04:00
CHIEFSOFT\ameye 134be6a1f0 Add fund page 2023-06-17 08:55:12 -04:00
CHIEFSOFT\ameye fcca050ff6 Add crerdikt style 2023-06-17 08:01:14 -04:00
ameye 197dc2e0bc Merge branch 'interest-page-reformat' of WrenchBoard/Users-Wrench into master 2023-06-17 09:45:35 +00:00
ameye e0f8e8df12 Merge branch 'Move-AddJob-to-Popup' of WrenchBoard/Users-Wrench into master 2023-06-17 09:45:24 +00:00
victorAnumudu b5625ab799 margin adjusted 2023-06-17 08:01:52 +01:00
victorAnumudu 9dc8acc584 manage offer interest reformat 2023-06-17 07:55:26 +01:00
Ebube 6d51fdfc19 popup closes 2023-06-17 06:33:08 +01:00
Ebube 800c62d76f Merge branch 'master' of https://gitlab.chiefsoft.net/WrenchBoard/Users-Wrench into Move-AddJob-to-Popup 2023-06-17 06:21:49 +01:00
Ebube ae23195e0e Changed add job to modal 2023-06-17 06:20:09 +01:00
ameye 4ef9b2f20e Merge branch 'Family-page-Tasks-cleanup' of WrenchBoard/Users-Wrench into master 2023-06-16 14:06:58 +00:00
ameye 709b8ea8f2 Merge branch 'coupon-page-cleanup' of WrenchBoard/Users-Wrench into master 2023-06-16 14:06:51 +00:00
ameye 549af89a43 Merge branch 'faq-page' of WrenchBoard/Users-Wrench into master 2023-06-16 14:06:30 +00:00
Ebube 123ed2056a Family page Tasks 2023-06-16 14:23:54 +01:00
Ebube f04f4c713e . 2023-06-16 13:04:51 +01:00
victorAnumudu 6d16e7f63f faq content add from API 2023-06-16 12:15:08 +01:00
ameye 153bc7ab7d Merge branch 'refferal-style' of WrenchBoard/Users-Wrench into master 2023-06-15 20:37:21 +00:00
victorAnumudu 054688af8b referral layout and style fixed 2023-06-15 21:02:49 +01:00
CHIEFSOFT\ameye 9994ccc26a History page 2023-06-15 12:11:06 -04:00
ameye f5c24ffb0c Merge branch 'other-interest-amendment' of WrenchBoard/Users-Wrench into master 2023-06-15 15:43:20 +00:00
victorAnumudu d5ce5d758a Others interested offer table modified to show only list of persons interest in an offer 2023-06-15 16:31:28 +01:00
ameye 0b14c7675b Merge branch 'pagination-dark-mode' of WrenchBoard/Users-Wrench into master 2023-06-15 10:34:23 +00:00
victorAnumudu 6a3662d69e dark mode style changed for pagination and manage active job 2023-06-15 07:01:41 +01:00
CHIEFSOFT\ameye 8a6c8badbe home banner 2023-06-14 21:19:23 -04:00
ameye 99be0961a9 Merge branch 'interest-processing-page' of WrenchBoard/Users-Wrench into master 2023-06-14 20:00:06 +00:00
victorAnumudu 22bfcbf0c6 interest processing page added 2023-06-14 20:54:14 +01:00
ameye 7975bd3d11 Merge branch 'client-name' of WrenchBoard/Users-Wrench into master 2023-06-14 16:40:19 +00:00
123 changed files with 4685 additions and 2060 deletions
+5 -5
View File
@@ -9,15 +9,15 @@ REACT_APP_APPSITE="https://myfitapp.mermsemr.com"
# REACT_APP_AUX_ENDPOINT="http://10.20.30.32:9083/svs/user"
# REACT_APP_USERS_ENDPOINT="http://10.20.30.32:9083/svs/user"
REACT_APP_AUX_ENDPOINT="https://apigate.lotus.g1.wrenchboard.com/svs/user"
REACT_APP_USERS_ENDPOINT="https://apigate.lotus.g1.wrenchboard.com/svs/user"
#REACT_APP_AUX_ENDPOINT="https://apigate.lotus.g1.wrenchboard.com/svs/user"
#REACT_APP_USERS_ENDPOINT="https://apigate.lotus.g1.wrenchboard.com/svs/user"
#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"
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"
#"https://devapi.mermsemr.com/en/desktop/api/v2/myfituser"
REACT_APP_SESSION_EXPIRE_MINUTES=300000
REACT_APP_SESSION_EXPIRE_MINUTES=600000
REACT_APP_SESSION_EXPIRE_MINUTES_FAMILY=600000
REACT_APP_SESSION_EXPIRE_CHECKER=60000
+5 -5
View File
@@ -9,15 +9,15 @@ REACT_APP_APPSITE="https://myfitapp.mermsemr.com"
# REACT_APP_AUX_ENDPOINT="http://10.20.30.32:9083/svs/user"
# REACT_APP_USERS_ENDPOINT="http://10.20.30.32:9083/svs/user"
REACT_APP_AUX_ENDPOINT="https://apigate.lotus.g1.wrenchboard.com/svs/user"
REACT_APP_USERS_ENDPOINT="https://apigate.lotus.g1.wrenchboard.com/svs/user"
# REACT_APP_AUX_ENDPOINT="https://apigate.lotus.g1.wrenchboard.com/svs/user"
# REACT_APP_USERS_ENDPOINT="https://apigate.lotus.g1.wrenchboard.com/svs/user"
#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"
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"
#"https://devapi.mermsemr.com/en/desktop/api/v2/myfituser"
REACT_APP_SESSION_EXPIRE_MINUTES=300000
REACT_APP_SESSION_EXPIRE_MINUTES=600000
REACT_APP_SESSION_EXPIRE_MINUTES_FAMILY=600000
REACT_APP_SESSION_EXPIRE_CHECKER=60000
+5 -5
View File
@@ -9,15 +9,15 @@ REACT_APP_APPSITE="https://myfitapp.mermsemr.com"
# REACT_APP_AUX_ENDPOINT="http://10.20.30.32:9083/svs/user"
# REACT_APP_USERS_ENDPOINT="http://10.20.30.32:9083/svs/user"
REACT_APP_AUX_ENDPOINT="https://apigate.orion.g1.wrenchboard.com/svs/user"
REACT_APP_USERS_ENDPOINT="https://apigate.orion.g1.wrenchboard.com/svs/user"
#REACT_APP_AUX_ENDPOINT="https://apigate.orion.g1.wrenchboard.com/svs/user"
#REACT_APP_USERS_ENDPOINT="https://apigate.orion.g1.wrenchboard.com/svs/user"
#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"
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"
#"https://devapi.mermsemr.com/en/desktop/api/v2/myfituser"
REACT_APP_SESSION_EXPIRE_MINUTES=300000
REACT_APP_SESSION_EXPIRE_MINUTES=600000
REACT_APP_SESSION_EXPIRE_MINUTES_FAMILY=600000
REACT_APP_SESSION_EXPIRE_CHECKER=60000
+5 -1
View File
@@ -44,6 +44,8 @@ import MyPastDueJobsPage from "./views/MyPastDueJobsPage";
import BlogPage from "./views/BlogPage";
import MyReviewDueJobsPage from "./views/MyReviewDueJobsPage";
import OffersInterestPage from "./views/OffersInterestPage";
import ManageInterestOfferPage from './views/ManageInterestOfferPage'
import MyWaitingJobsPage from "./views/MyWaitingJobsPage";
export default function Routers() {
return (
@@ -88,10 +90,11 @@ export default function Routers() {
<Route exact path="/notification" element={<Notification />} />
<Route exact path="/mytask" element={<MyTaskPage />} />
<Route exact path="/myjobs" element={<MyJobsPage />} />
<Route exact path="/add-job" element={<AddJobPage />} />
{/* <Route exact path="/add-job" element={<AddJobPage />} /> */}
<Route exact path="/my-active-jobs" element={<MyActiveJobsPage />} />
<Route exact path="/my-pastdue-jobs" element={<MyPastDueJobsPage />} />
<Route exact path="/my-pending-jobs" element={<MyPendingJobsPage />} />
<Route exact path="/pend-interest" element={<MyWaitingJobsPage />} />
<Route exact path="/my-review-jobs" element={<MyReviewDueJobsPage />} />
<Route exact path="/acc-family" element={<FamilyAccPage />} />
<Route exact path="/manage-family" element={<FamilyManagePage />} />
@@ -99,6 +102,7 @@ export default function Routers() {
<Route exact path="/manage-active-job" element={<ManageActiveJobs />} />
<Route exact path="/blog-page" element={<BlogPage />} />
<Route exact path="/offer-interest" element={<OffersInterestPage />} />
<Route exact path="/manage-offer" element={<ManageInterestOfferPage />} />
<Route
Binary file not shown.

Before

Width:  |  Height:  |  Size: 77 KiB

After

Width:  |  Height:  |  Size: 69 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 72 KiB

After

Width:  |  Height:  |  Size: 82 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 64 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 68 KiB

+9
View File
@@ -0,0 +1,9 @@
<svg width="905" height="575" viewBox="0 0 905 575" fill="none" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
<rect width="905" height="575" fill="url(#pattern0)"/>
<defs>
<pattern id="pattern0" patternContentUnits="objectBoundingBox" width="1" height="1">
<use xlink:href="#image0_201_3" transform="matrix(0.00849979 0 0 0.0131639 -0.0662984 -0.354783)"/>
</pattern>
<image id="image0_201_3" width="130" height="130" xlink:href="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAIIAAACCCAMAAAC93eDPAAAApVBMVEX///82OzgAAAAuMi81OjdVXVfqpQCzs7NCSUSwsbAaIBslKyjk5OQuNDAhJiLZ2tpJT0opNTkvNzloVy31rAC1hRtPV1HNzc34+PgaLztBQUHLkg9UVFTw8PDGxsaioqKampp/f39sbGyIiIgaGho9Qj5MTExeXl5fUi+9vb03Nzd2dnYjIyORkZFjZWMNFQ9CQTWYcyLAjBSsfx12XytSSjEQEBBeBsYmAAAEiklEQVR4nO3a7XaqOBQG4BjoQXRraKc1GUoSPgQaETszZ473f2kTqm0VsKLLYs80b3+UhQgPOwlBESETExMTExMTExMTExMTk/9HQDk9xHUdcgiwmk7HPWQ0Gj0HrQiaWvagh9iWztSiTQH3rD4AW4I19nmDUI77EWwJ1ihpENJeWuGdYE0afXHSN2EMdUI/fXGHMK0Piv6rcJhg39VyadpRwt3g6Y+9PNl3/RLsvx7va3kcXLYOxwh/Ptz/qOX+79t+CY91wY8fD4ZgCIbwvQn/NAT3P/sl3P3bENz/uuwkcXSaun162M/PX5ctQofJ+u62lgtPlL/HLYshGIIhfDGC3chZ65ur7c6EYSN2+/r0wPYvn4/ttH19B4Lt3TRSGexZc316YP3LgZrrZ9sqTK5PsK7fEMcJn90d7eOEz84XIEyuT7CuTrCvT5hcn2BdnWBfnbAj+CaEtyvizrXROpfQOu0fAwyGnucN9X89XeiltPrebnIuwU5nO+n2dbmdbqfH1B6+Le02w4mEvYnX60J4PW61/fukb51PGO4SulWheRehMecTTu8LbTc+NzMvPZ9wctrupW505+yR0NYOelR41yVUgq5VaLnjOvmy0GyIStC5L6RH0okwbBN4g06ExpubFe00JPbKMNsIOl4XWofT6YTB4HU/3mDWLjjpc8RevE4APSF4VSFmQ31VTl8AqdWZcInuuEFUHWczLdgDuw7o637BnjSP/AHh8o9KPwR8fhXslsrXCfUHxpd8bH7k9LdpPDZHyajL+y6XUdQggD/uUzAeNNoBIWpN+xNMJy0/JNF1mD2Pespz0FKDDcJxe4k6BDAxMTExMTH5kunjZ8cfB+Gr53sTluE7YQ0gBSGrBEgEROIcCA0oVnEEkFfbCp44ZUYDAhLjiGAKCiugmASlXAGMa/teEBCLHCCSYZmEofBlqUIcACQ44r4PoJbVZnOk3ghzFPlMujyEbIViH8es4C6LSAKuAP16gaqlAlwomY9DVrCElSjhCVfSAYe6NYLPSq6oCFmohFIhl64kLqYkZEuFogzlqMSZEPo2nr4TijVLQk1YBihYa0IGigMNeZRF+vUcFTiGkiqxTpa45BIWUjIfQs4dh8KiXmGfBZQSqc9EACExsBcCuDHyKXd8hLmu9GY8wvKVADHnKJZIBIis8AoByTnoc4k2Z6hPeYlUKIRUfkXgcxxpQskBqTlhywYh3xB8DkRvByysCE6McghptiGwjYG+EgLMSigkwQHS5xTzAGdQIk2QVUP4q5BlnOVC5PqVqgq+cF+qkCCV64odICCMqEBLvadwW4UYCcj3quC/E1BElS5wjlaLqiFwwHUPAf2ho1jhGCUso2jh8IgFVV9ACSpRxBO2Ig4oShuEgju6ZJrgKraEgEmilrovaHKk/wKUbPsCmb+OCJphEReioDTRG+JAl9tXa7mKiZLSxWtJEpyo+ZwKx8eFwEoPG71XLLKo9Cmtd4aFomrp616gB1Xhrp0sSiQlmSBx4uLqKHK+GRHibUR8mCQ4tsW5mUrcjfD5+QKE/wC0yMO17JCUyAAAAABJRU5ErkJggg=="/>
</defs>
</svg>

After

Width:  |  Height:  |  Size: 2.3 KiB

+2
View File
@@ -0,0 +1,2 @@
<?xml version="1.0" ?><!-- Uploaded to: SVG Repo, www.svgrepo.com, Generator: SVG Repo Mixer Tools -->
<svg fill="#000000" width="800px" height="800px" viewBox="0 0 52 52" data-name="Layer 1" id="Layer_1" xmlns="http://www.w3.org/2000/svg"><path d="M14,16H2a2,2,0,0,1-2-2V2A2,2,0,0,1,2,0H14a2,2,0,0,1,2,2V14A2,2,0,0,1,14,16ZM4,12h8V4H4Z"/><path d="M14,34H2a2,2,0,0,1-2-2V20a2,2,0,0,1,2-2H14a2,2,0,0,1,2,2V32A2,2,0,0,1,14,34ZM4,30h8V22H4Z"/><path d="M14,52H2a2,2,0,0,1-2-2V38a2,2,0,0,1,2-2H14a2,2,0,0,1,2,2V50A2,2,0,0,1,14,52ZM4,48h8V40H4Z"/><path d="M32,16H20a2,2,0,0,1-2-2V2a2,2,0,0,1,2-2H32a2,2,0,0,1,2,2V14A2,2,0,0,1,32,16ZM22,12h8V4H22Z"/><path d="M32,34H20a2,2,0,0,1-2-2V20a2,2,0,0,1,2-2H32a2,2,0,0,1,2,2V32A2,2,0,0,1,32,34ZM22,30h8V22H22Z"/><path d="M32,52H20a2,2,0,0,1-2-2V38a2,2,0,0,1,2-2H32a2,2,0,0,1,2,2V50A2,2,0,0,1,32,52ZM22,48h8V40H22Z"/><path d="M50,16H38a2,2,0,0,1-2-2V2a2,2,0,0,1,2-2H50a2,2,0,0,1,2,2V14A2,2,0,0,1,50,16ZM40,12h8V4H40Z"/><path d="M50,34H38a2,2,0,0,1-2-2V20a2,2,0,0,1,2-2H50a2,2,0,0,1,2,2V32A2,2,0,0,1,50,34ZM40,30h8V22H40Z"/><path d="M50,52H38a2,2,0,0,1-2-2V38a2,2,0,0,1,2-2H50a2,2,0,0,1,2,2V50A2,2,0,0,1,50,52ZM40,48h8V40H40Z"/></svg>

After

Width:  |  Height:  |  Size: 1.1 KiB

+1
View File
@@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" id="delete"><g color="#000"><path fill="#ee4c45" style="line-height:normal;text-indent:0;text-align:start;text-decoration-line:none;text-decoration-style:solid;text-decoration-color:#000;text-transform:none;block-progression:tb;white-space:normal;isolation:auto;mix-blend-mode:normal;solid-color:#000;solid-opacity:1" d="M12 1028.362c-6.626 0-12 5.374-12 12s5.374 12 12 12 12-5.374 12-12-5.374-12-12-12z" font-family="sans-serif" font-weight="400" overflow="visible" transform="translate(0 -1028.362)"></path><path fill="#fff" style="isolation:auto;mix-blend-mode:normal;solid-color:#000;solid-opacity:1" d="M10.5 1034.362v1h-3v1h9v-1h-3v-1h-3zm-2 3v9h7v-9h-7zm1 1h1v7h-1v-7zm2 0h1v7h-1v-7zm2 0h1v7h-1v-7z" overflow="visible" transform="translate(0 -1028.362)"></path></g></svg>

After

Width:  |  Height:  |  Size: 841 B

+19
View File
@@ -0,0 +1,19 @@
<svg xmlns="http://www.w3.org/2000/svg" enable-background="new 0 0 60 60" viewBox="0 0 60 60" id="edit"><path fill="#3B4652" d="M52.9687347,19.3099976l-2.7999878-2.7999878c-0.9099731-0.9100342-2.3899536-0.9100342-3.2999878,0
l-1.4129639,1.4129639c0.0185547,0.017395,0.0423584,0.0234985,0.0601196,0.0422974l5.7765503,6.1199341
c0.0361328,0.038269,0.0506592,0.0881348,0.0813599,0.1296997l1.5949097-1.5949097
C53.8787689,21.7099609,53.8787689,20.2199707,52.9687347,19.3099976z"></path><path fill="#2B79C2" d="M10.8887787,25.5499878H34.998764l7.5900269-7.5900269l1.7599487-1.75V9c0-1.6500244-1.3499756-3-3-3h-32
c-1.6599731,0-3,1.3499756-3,3v42c0,1.6499634,1.3400269,3,3,3h32c1.6500244,0,3-1.3500366,3-3V34.0700073l-5.8899536,5.8800049
c-0.3099976,0.3199463-0.7000122,0.5599976-1.1199951,0.7099609l-7.2200317,2.5200195
c-0.3099976,0.1099854-0.6500244,0.1699829-0.9899902,0.1699829c-0.7800293,0-1.5200195-0.2999878-2.0900269-0.8399658
c-0.3399658-0.3400269-0.5799561-0.7400513-0.7299805-1.1600342H10.8887787c-0.5599976,0-1-0.4500122-1-1s0.4400024-1,1-1
h15.4199829v-0.0100098l1.7000122-4.8899536H10.8887787c-0.5599976,0-1-0.4500122-1-1c0-0.5500488,0.4400024-1,1-1H28.708786
l0.0999756-0.3000488c0.1699829-0.4699707,0.4400024-0.8800049,0.789978-1.1900024l3.4000244-3.4099731H10.8887787
c-0.5599976,0-1-0.4500122-1-1S10.3287811,25.5499878,10.8887787,25.5499878z M10.8887787,46.25h24.9099731
c0.5599976,0,1,0.4500122,1,1s-0.4400024,1-1,1H10.8887787c-0.5599976,0-1-0.4500122-1-1S10.3287811,46.25,10.8887787,46.25z
M10.8887787,11.75h24.9099731c0.5599976,0,1,0.4500122,1,1s-0.4400024,1-1,1H10.8887787c-0.5599976,0-1-0.4500122-1-1
S10.3287811,11.75,10.8887787,11.75z M10.8887787,18.6499634h24.9099731c0.5599976,0,1,0.4500122,1,1c0,0.5500488-0.4400024,1-1,1
H10.8887787c-0.5599976,0-1-0.4499512-1-1C9.8887787,19.0999756,10.3287811,18.6499634,10.8887787,18.6499634z"></path><path fill="#3B4652" d="M30.9387665,32.4400024c-0.1099854,0.0999756-0.1900024,0.2299805-0.2399902,0.3699951
l-2.5100098,7.2099609c-0.1300049,0.3600464-0.039978,0.7700195,0.2299805,1.0400391
c0.2000122,0.1900024,0.4500122,0.289978,0.710022,0.289978c0.1099854,0,0.2199707-0.0200195,0.3300171-0.0599976
l7.2099609-2.5100098c0.1400146-0.0499878,0.2700195-0.1300049,0.3800049-0.2399902l12.5599976-12.5599976l-5.9299927-6.2799683
L30.9387665,32.4400024z"></path></svg>

After

Width:  |  Height:  |  Size: 2.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.9 KiB

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 236 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 159 KiB

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 5.6 KiB

+157 -140
View File
@@ -1,24 +1,26 @@
import React, { useState, useEffect } from "react";
import { Link, useNavigate } from "react-router-dom";
import { Field, Form, Formik } from "formik";
import React, { useEffect, useState } from "react";
import { useDispatch } from "react-redux";
import * as Yup from "yup";
import usersService from "../../services/UsersService";
import { tableReload } from "../../store/TableReloads";
import InputCom from "../Helpers/Inputs/InputCom";
import LoadingSpinner from "../Spinners/LoadingSpinner";
import usersService from "../../services/UsersService";
import { useSelector, useDispatch } from "react-redux";
import { tableReload } from '../../store/TableReloads'
import { Field, Form, Formik } from "formik";
import * as Yup from "yup";
const validationSchema = Yup.object().shape({
country: Yup.string()
.min(1, "Minimum 3 characters")
.max(25, "Maximum 25 characters")
.required("Country is required"),
price: Yup.number()
.typeError("you must specify a number")
price: Yup.string()
.typeError("Invalid number")
.min(1, "Price must be greater than 0")
.test("no-e", "Invalid number", (value) => {
if (value && /\d+e/.test(value)) {
return false;
}
return true;
})
.required("Price is required"),
title: Yup.string()
.min(3, "Minimum 3 characters")
@@ -36,32 +38,29 @@ const validationSchema = Yup.object().shape({
.typeError("you must specify a number")
.min(1, "Price must be greater than 0")
.required("Timeline is required"),
category: Yup.array().min(1, "Select at least one checkbox"),
});
function AddJob() {
function AddJob({ popUpHandler, categories }) {
const ApiCall = new usersService();
const navigate = useNavigate();
let dispatch = useDispatch()
let dispatch = useDispatch();
let { userDetails } = useSelector((state) => state.userDetails);
let [pageLoading, setPageLoading] = useState(true); // State used for knowing when the page is mounting
let [country, setCountry] = useState({
let [currency, setCurrency] = useState({
loading: true,
status: false,
data: [],
}); // To Hold the array of country getUserCountry returns
data: null,
}); // To Hold the array of currency getUserCurrency returns
let initialValues = {
// initial values for formik
country: userDetails.country,
country: "",
price: "",
title: "",
description: "",
job_detail: "",
timeline_days: "",
category: [],
};
let [requestStatus, setRequestStatus] = useState({
@@ -70,30 +69,42 @@ function AddJob() {
message: "",
}); // Holds state when submit button is pressed
// FUNCTION TO GET COUNTRY
const getUserCountry = () => {
setCountry((prev) => ({ ...prev, loading: true }));
ApiCall.getSignupCountryData()
// FUNCTION TO GET Currency
const getUserCurrency = () => {
setCurrency((prev) => ({ ...prev, loading: true }));
ApiCall.getUserWallets()
.then((res) => {
if (res.data.internal_return < 1) {
setCountry({ loading: false, status: true, data: [] });
if (res.data.internal_return < 0) {
setCurrency({ loading: false, status: true, data: [] });
return;
}
setCountry({
console.log("Res for currency >> ", res);
setCurrency({
loading: false,
status: true,
data: res.data.signup_country,
data: res.data.result_list,
});
})
.catch((err) => {
setCountry({ loading: false, status: false, data: [] });
setCurrency({ loading: false, status: false, data: [] });
});
};
// FUNCTION TO HANDLE ADD JOB FORM
const handleAddJob = (values, helpers) => {
let reqData = {
country: values?.country,
price: Number(values.price) * 100,
title: values?.title,
description: values?.description,
job_detail: values?.job_detail,
timeline_days: values?.timeline_days,
category: values.category?.join("@"),
};
setRequestStatus({ loading: true, status: false, message: "" });
ApiCall.jobManagerCreateJob(values)
ApiCall.jobManagerCreateJob(reqData)
.then((res) => {
if (res.data.internal_return < 1) {
setRequestStatus({
@@ -101,6 +112,9 @@ function AddJob() {
status: false,
message: "Could not complete your request at the moment",
});
setTimeout(() => {
popUpHandler();
}, 1500);
return;
}
setRequestStatus({
@@ -109,15 +123,15 @@ function AddJob() {
message: "Job Added Successfully",
});
setTimeout(() => {
dispatch(tableReload({type:'JOBTABLE'}))
navigate("/myjobs", { replace: true });
dispatch(tableReload({ type: "JOBTABLE" }));
popUpHandler();
}, 1000);
})
.catch((err) => {
setRequestStatus({
loading: false,
status: false,
message: "Opps! soemthing went wrong. Try Again",
message: "Opps! something went wrong. Try Again",
});
})
.finally(() => {
@@ -128,17 +142,10 @@ function AddJob() {
};
useEffect(() => {
getUserCountry();
setPageLoading(false);
getUserCurrency();
}, []);
return pageLoading.loading ? (
<div className="personal-info-tab w-full flex flex-col justify-between">
<div className="p-3">
<LoadingSpinner size="32" color="sky-blue" />
</div>
</div>
) : (
return (
<div className="add-job p-5 w-full bg-white rounded-md flex flex-col justify-between">
<Formik
initialValues={initialValues}
@@ -148,64 +155,47 @@ function AddJob() {
{(props) => {
return (
<Form>
<h1 className="py-2 my-4 text-lg md:text-xl font-bold tracking-wide">
Create New Job
</h1>
<div className="flex flex-col-reverse sm:flex-row">
<div className="fields w-full">
{/* inputs starts here */}
{/* country */}
<div className="xl:flex xl:space-x-7 mb-6">
<div className="xl:flex xl:space-x-7 mb-[5px]">
<div className="field w-full mb-6 xl:mb-0">
{/* <InputCom
fieldClass="px-6 cursor-not-allowed"
label="Country"
labelClass='tracking-wide'
inputBg = 'bg-slate-100'
type="text"
name="country"
disable={true}
value={country.loading ? 'loading' : country.data ? country.data : 'no country found!'}
inputHandler={(e)=> setCountry((prev) => ({...prev, data:e.target.value}))}
/> */}
<label
htmlFor="country"
className="input-label text-[#181c32] dark:text-white text-[13.975px] leading-[20.9625px] font-semibold block"
>
Country
Currency
</label>
<select
id="country"
name="country"
disabled
value={props.values.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 p-2 mt-3 rounded-md placeholder:text-base text-dark-gray dark:text-white w-full h-10 bg-slate-100 dark:bg-[#11131F] focus:ring-0 focus:outline-none ${
props.errors.country && props.touched.country
? "border-[#ff0a0a63] shadow-red-500 border-[0.5px] animate-shake"
: "border border-[#f5f8fa] dark:border-[#5e6278]"
}`}
onChange={props.handleChange}
onBlur={props.handleBlur}
>
{country.loading ? (
{currency.loading ? (
<option className="text-slate-500 text-lg" value="">
Loading...
</option>
) : country.data.length ? (
) : currency.data.length ? (
<>
<option className="text-slate-500 text-lg" value="">
Select...
Select a currency
</option>
{country.data.map((item, index) => {
if (item[0] == userDetails.country) {
return (
<option
key={index}
className="text-slate-500 text-lg"
value={item[0]}
>
{item[1]}
</option>
);
}
})}
{currency.data?.map((item, index) => (
<option
key={index}
className="text-slate-500 text-lg"
value={item?.country}
>
{item?.description}
</option>
))}
</>
) : (
<option className="text-slate-500 text-lg" value="">
@@ -213,11 +203,6 @@ function AddJob() {
</option>
)}
</select>
{props.errors.country && props.touched.country && (
<p className="text-sm text-red-500">
{props.errors.country}
</p>
)}
</div>
{/* Price */}
@@ -233,18 +218,14 @@ function AddJob() {
value={props.values.price}
inputHandler={props.handleChange}
blurHandler={props.handleBlur}
errorBorder={props.errors.price && props.touched.price}
/>
{props.errors.price && props.touched.price && (
<p className="text-sm text-red-500">
{props.errors.price}
</p>
)}
</div>
</div>
{/* Title */}
<div className="field w-full mb-6">
<div className="field w-full mb-[5px]">
<InputCom
fieldClass="px-6"
label="Title"
@@ -252,20 +233,15 @@ function AddJob() {
inputBg="bg-slate-100"
type="text"
name="title"
// placeholder="Enter Job Title"
value={props.values.title}
inputHandler={props.handleChange}
blurHandler={props.handleBlur}
errorBorder={props.errors.title && props.touched.title}
/>
{props.errors.title && props.touched.title && (
<p className="text-sm text-red-500">
{props.errors.title}
</p>
)}
</div>
{/* Description */}
<div className="field w-full mb-6">
<div className="field w-full mb-[5px]">
<InputCom
fieldClass="px-6"
label="Description"
@@ -273,44 +249,76 @@ function AddJob() {
inputBg="bg-slate-100"
type="text"
name="description"
// placeholder="Enter a description"
value={props.values.description}
inputHandler={props.handleChange}
blurHandler={props.handleBlur}
errorBorder={
props.errors.description && props.touched.description
}
/>
{props.errors.description && props.touched.description && (
<p className="text-sm text-red-500">
{props.errors.description}
</p>
)}
</div>
{/* Details */}
<div className="field w-full mb-6">
<label
htmlFor="Job Delivery Details"
className='className="input-label text-[#181c32] dark:text-white text-[13.975px] leading-[20.9625px] font-semibold block"'
>
Job Delivery Details
</label>
<textarea
id="Job Delivery Details"
rows="7"
className={`input-field p-6 mt-3 rounded-md placeholder:text-base text-dark-gray dark:text-white w-full h-full bg-slate-100 dark:bg-[#11131F] focus:ring-0 focus:outline-none`}
style={{ resize: "none" }}
name="job_detail"
value={props.values.job_detail}
onChange={props.handleChange}
onBlur={props.handleBlur}
/>
{props.errors.job_detail && props.touched.job_detail && (
<p className="text-sm text-red-500">
{props.errors.job_detail}
</p>
)}
<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"'
>
Job Delivery Details
</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] ${
props.errors.job_detail && props.touched.job_detail
? "border-[#ff0a0a63] shadow-red-500 border-[0.5px] animate-shake"
: "border border-[#f5f8fa] dark:border-[#5e6278]"
} rounded-[10px]`}
style={{ resize: "none" }}
name="job_detail"
value={props.values.job_detail}
onChange={props.handleChange}
onBlur={props.handleBlur}
/>
</div>
<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"'
id="checked-group"
>
Categories
</div>
<div
className="sm:flex-col flex flex-wrap px-3 mt-3"
role="group"
aria-labelledby="checked-group"
>
{Object?.entries(categories).map(([key, value]) => (
<label
key={key}
className="flex gap-1 w-full items-center"
>
<Field
type="checkbox"
name="category"
value={key}
/>
<span className="text-[13.975px]">{value}</span>
</label>
))}
<span className="h-5 text-sm italic text-[#cf3917]">
{props.errors.category &&
props.touched.category &&
"please select a category"}
</span>
</div>
</div>
</div>
<div className="field w-full mb-6">
<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"
@@ -326,7 +334,12 @@ function AddJob() {
<Field
component="select"
name="timeline_days"
className="input-field p-2 mt-3 rounded-md placeholder:text-base text-dark-gray dark:text-white w-full h-10 bg-slate-100 dark:bg-[#11131F] focus:ring-0 focus:outline-none"
className={`input-field p-2 mt-3 rounded-md placeholder:text-base text-dark-gray dark:text-white w-full h-10 bg-slate-100 dark:bg-[#11131F] focus:ring-0 focus:outline-none ${
props.errors.timeline_days &&
props.touched.timeline_days
? "border-[#ff0a0a63] shadow-red-500 border-[0.5px] animate-shake"
: "border border-[#f5f8fa] dark:border-[#5e6278]"
}`}
value={props.values.timeline_days}
>
<option value="">Select Duration</option>
@@ -339,12 +352,6 @@ function AddJob() {
</option>
))}
</Field>
{props.errors.timeline_days &&
props.touched.timeline_days && (
<p className="text-sm text-red-500">
{props.errors.timeline_days}
</p>
)}
</div>
{/* inputs ends here */}
</div>
@@ -371,17 +378,20 @@ function AddJob() {
))}
{/* End of error or success display */}
<div className="w-full h-[120px] border-t border-light-purple dark:border-[#5356fb29] flex justify-end items-center">
<div className="w-full h-[70px] border-t border-light-purple dark:border-[#5356fb29] flex justify-end items-center">
<div className="flex items-center space-x-4 mr-9">
<Link
to="/myjobs"
<button
type="button"
className="text-18 text-light-red tracking-wide "
>
<span className="border-b dark:border-[#5356fb29] border-light-red">
<span
className="border-b dark:border-[#5356fb29] border-light-red"
onClick={popUpHandler}
>
{" "}
Cancel
</span>
</Link>
</button>
{requestStatus.loading ? (
<LoadingSpinner size="8" color="sky-blue" />
@@ -419,3 +429,10 @@ const publicArray = [
{ duration: 21, name: "3 weeks" },
{ duration: 28, name: "4 weeks" },
];
// .test("no-e", "Invalid number", (value) => {
// if (value && /\d+e/.test(value)) {
// return false;
// }
// return true;
// })
@@ -43,7 +43,7 @@ export default function CollectionTab({ className, products }) {
<DataIteration
datas={products}
startLength={process.env.REACT_APP_ZERO_STATE}
endLength={products.length}
endLength={products?.length}
>
{({ datas }) => (
<CollectionCard key={datas.uniqKey} collectionData={datas} />
@@ -42,7 +42,9 @@ export default function CreateSaleSlider({
{/* heading */}
<div className="flex justify-between items-center mb-6">
<div>
<h1 className="text-26 font-bold text-dark-gray dark:text-white">Create for Sell</h1>
<h1 className="text-26 font-bold text-dark-gray dark:text-white">
Create for Sell
</h1>
</div>
<div className="slider-btns flex space-x-4">
<button onClick={nextHandler} type="button">
@@ -89,7 +91,7 @@ export default function CreateSaleSlider({
<div className="trending-products relative w-full">
<SliderCom selector={trendingSlider} settings={settings}>
{products &&
products.length > 0 &&
products?.length > 0 &&
products.map((item) => (
<ProductCardStyleTwo
key={item.id}
@@ -49,7 +49,9 @@ export default function CreatedBidsSlider({
{/* heading */}
<div className="flex justify-between items-center mb-6">
<div>
<h1 className="text-26 font-bold text-dark-gray dark:text-white">Create for Bits</h1>
<h1 className="text-26 font-bold text-dark-gray dark:text-white">
Create for Bits
</h1>
</div>
<div className="slider-btns flex space-x-4">
<button onClick={nextHandler} type="button">
@@ -96,7 +98,7 @@ export default function CreatedBidsSlider({
<div className="trending-products relative w-full">
<SliderCom selector={trendingSlider} settings={settings}>
{products &&
products.length > 0 &&
products?.length > 0 &&
products.map((item) => (
<ProductCardStyleOne
key={item.id}
@@ -43,7 +43,7 @@ export default function OnSaleTab({ className, products }) {
<DataIteration
datas={products}
startLength={process.env.REACT_APP_ZERO_STATE}
endLength={products.length}
endLength={products?.length}
>
{({ datas }) => (
<ProductCardStyleTwo key={datas.id} datas={datas} />
@@ -43,7 +43,7 @@ export default function OwnTab({ className, products }) {
<DataIteration
datas={products}
startLength={process.env.REACT_APP_ZERO_STATE}
endLength={products.length}
endLength={products?.length}
>
{({ datas }) => (
<ProductCardStyleOne key={datas.id} datas={datas} />
+19 -3
View File
@@ -18,6 +18,9 @@ export default function Login() {
const dispatch = useDispatch();
let [loginType, setLoginType] = useState({ full: true, family: false });
const [selectedLoginType, setSelectedLoginType] = useState(
document.cookie.includes("loginType=family") ? "loginfamily" : "loginfull"
);
const [checked, setValue] = useState(false);
const [loginLoading, setLoginLoading] = useState(false);
@@ -77,6 +80,10 @@ export default function Login() {
login_mode: 1100,
action: 11025,
};
// Clear the loginType cookie if the user switches to loginfull
document.cookie =
"loginType=; expires=Thu, 01 Jan 1970 00:00:00 UTC; path=/;";
} else if (name == "loginfamily") {
// Post Data Info for family Login
postData = {
@@ -86,6 +93,8 @@ export default function Login() {
login_mode: 1105,
action: 11025,
};
// Set the loginType cookie to remember the user's selection
document.cookie = "loginType=family; expires=Session; path=/;";
} else {
setLoginLoading(false);
setMsgError("Invalid Login Type. Consider refreshing the page");
@@ -127,15 +136,22 @@ export default function Login() {
};
const googleLogin = useGoogleLogin({
flow: 'auth-code',
ux_mode:'redirect',
flow: "auth-code",
ux_mode: "redirect",
redirect_uri: process.env.REACT_APP_GOOGLE_REDIRECT_URL,
onSuccess: async (codeResponse) => {
console.log(codeResponse);
console.log("GOOGLE LOGIN GOOD --- ", codeResponse);
},
onError: (errorResponse) => console.log(errorResponse),
});
// In order to update the selected login type whenever the component renders
useEffect(() => {
setSelectedLoginType(
document.cookie.includes("loginType=family") ? "loginfamily" : "loginfull"
);
}, []);
useEffect(() => {
setMail("");
setPassword("");
+3 -3
View File
@@ -80,7 +80,7 @@ export default function SignUp() {
if (res.status === 200) {
const { data } = res;
if (data.status === -1 && data.acc === "DULPICATE") {
if (data && data.acc === "DULPICATE") {
setMsgError("This account has been already created");
setSignUpLoading(false);
}
@@ -108,12 +108,12 @@ export default function SignUp() {
useEffect(() => {
getCountryList();
});
}, []);
return (
<>
<div className="layout-wrapper login">
<div className="main-wrapper w-full xl:h-screen h-full xl:py-10 py-12 overflow-y-auto">
<div className="main-wrapper login-wrapper w-full xl:h-screen h-full xl:py-10 py-12 overflow-y-auto">
<div className=" h-full">
<div className="flex-1 flex justify-center items-center">
<div className="w-full">
@@ -43,7 +43,7 @@ export default function CollectionTab({ className, products }) {
<DataIteration
datas={products}
startLength={process.env.REACT_APP_ZERO_STATE}
endLength={products.length}
endLength={products?.length}
>
{({ datas }) => (
<CollectionCard key={datas.uniqKey} collectionData={datas} />
@@ -42,7 +42,9 @@ export default function CreateSaleSlider({
{/* heading */}
<div className="flex justify-between items-center mb-6">
<div>
<h1 className="text-26 font-bold text-dark-gray dark:text-white">Create for Sell</h1>
<h1 className="text-26 font-bold text-dark-gray dark:text-white">
Create for Sell
</h1>
</div>
<div className="slider-btns flex space-x-4">
<button onClick={nextHandler} type="button">
@@ -89,7 +91,7 @@ export default function CreateSaleSlider({
<div className="trending-products relative w-full">
<SliderCom selector={trendingSlider} settings={settings}>
{products &&
products.length > 0 &&
products?.length > 0 &&
products.map((item) => (
<ProductCardStyleTwo
key={item.id}
@@ -49,7 +49,9 @@ export default function CreatedBidsSlider({
{/* heading */}
<div className="flex justify-between items-center mb-6">
<div>
<h1 className="text-26 font-bold text-dark-gray dark:text-white">Create for Bits</h1>
<h1 className="text-26 font-bold text-dark-gray dark:text-white">
Create for Bits
</h1>
</div>
<div className="slider-btns flex space-x-4">
<button onClick={nextHandler} type="button">
@@ -96,7 +98,7 @@ export default function CreatedBidsSlider({
<div className="trending-products relative w-full">
<SliderCom selector={trendingSlider} settings={settings}>
{products &&
products.length > 0 &&
products?.length > 0 &&
products.map((item) => (
<ProductCardStyleOne
key={item.id}
@@ -43,7 +43,7 @@ export default function OnSaleTab({ className, products }) {
<DataIteration
datas={products}
startLength={process.env.REACT_APP_ZERO_STATE}
endLength={products.length}
endLength={products?.length}
>
{({ datas }) => (
<ProductCardStyleTwo key={datas.id} datas={datas} />
@@ -43,7 +43,7 @@ export default function OwnTab({ className, products }) {
<DataIteration
datas={products}
startLength={process.env.REACT_APP_ZERO_STATE}
endLength={products.length}
endLength={products?.length}
>
{({ datas }) => (
<ProductCardStyleOne key={datas.id} datas={datas} />
+96 -48
View File
@@ -1,57 +1,33 @@
import React, { useCallback, useEffect, useMemo, useState } from "react";
import { Link, useNavigate, } from "react-router-dom";
import { toast } from "react-toastify";
import localImgLoad from "../../lib/localImgLoad";
import Icons from "../Helpers/Icons";
import { useCallback, useEffect, useMemo, useState } from "react";
import { Link, useNavigate } from "react-router-dom";
import MarketPopUp from "../MarketPlace/PopUp/MarketPopUp";
import usersService from "../../services/UsersService";
import { PriceFormatter } from "../Helpers/PriceFormatter";
import dataImage2 from "../../assets/images/data-table-user-2.png";
export default function AvailableJobsCard({
className,
datas,
hidden = false,
contentDisplay
}) {
//debugger;
const [addFavorite, setValue] = useState(datas.whishlisted);
const [marketPopUp, setMarketPopUp] = useState({ show: false, data: {} });
const [manageInt, setManageInt] = useState(null)
const [imageUrl, setImageUrl] = useState("");
const navigate = useNavigate();
const apiCall = useMemo(() => new usersService(), []);
const favoriteHandler = () => {
if (!addFavorite) {
setValue(true);
toast.success("Added to Favorite List");
} else {
setValue(false);
toast.warn("Remove to Favorite List");
}
};
const marketInterestData = useCallback(async() => {
let { offer_code } = datas;
let reqData = { offer_code };
try {
const manageInt = await apiCall.MarketInterest(reqData);
const manageIntRes = await manageInt?.data;
setManageInt(manageIntRes)
} catch (error) {
throw new Error(error)
}
}, [])
let thePrice = PriceFormatter(
datas?.price * 0.01,
datas?.currency_code,
datas?.currency
);
useEffect(() => {
if (!datas) {
navigate("/market", { replace: true });
}
marketInterestData()
}, [marketInterestData, datas])
const imagePath = require(`../../assets/images/${datas.thumbnil}`); // Replace with your directory path for local images
setImageUrl(imagePath);
}, []);
return (
<>
{contentDisplay == 'grid' ?
<div
className={`card-style-two w-full h-[426px] p-[20px] bg-white dark:bg-dark-white rounded-2xl section-shadow ${
className || ""
@@ -87,11 +63,9 @@ export default function AvailableJobsCard({
</div>
<div className="thumbnail-area w-full">
<div
className="w-full h-[236px] p-6 rounded-xl overflow-hidden"
className="w-full h-[236px] p-6 rounded-xl overflow-hidden bg-center bg-cover bg-no-repeat"
style={{
background: `url(${localImgLoad(
`images/${datas.thumbnil}`
)}) 0% 0% / cover no-repeat`,
backgroundImage: `url('${imageUrl}')`,
}}
>
<div className="flex justify-center">{datas.description}</div>
@@ -124,11 +98,14 @@ export default function AvailableJobsCard({
<div className="flex items-center space-x-2">
<div>
<p className="font-bold text-xl tracking-wide line-clamp-1 text-dark-gray dark:text-white">
{datas.price * 0.01}
{datas.currency} | {datas.timeline_days} day(s)
{/* {thePrice} | {datas.timeline_days} day(s) */}
{thePrice}
</p>
<p className="text-sm text-lighter-gray">
( {datas.offer_code})
( {datas.offer_code}) |
<span className="italic ml-1">
{datas.timeline_days} day(s)
</span>
</p>
</div>
</div>
@@ -137,7 +114,7 @@ export default function AvailableJobsCard({
type="button"
className="px-4 py-2.5 text-white text-sm bg-pink rounded-full tracking-wide"
onClick={() => {
setMarketPopUp({show: true, data: datas})
setMarketPopUp({ show: true, data: datas });
}}
>
View
@@ -147,6 +124,78 @@ export default function AvailableJobsCard({
</div>
</div>
</div>
:
<div className="card-style-two w-full p-8 my-2 flex items-center gap-4 bg-white dark:bg-dark-white rounded-2xl section-shadow">
<div className="flex gap-5 items-center w-full">
<div className="w-full h-[60px] rounded-full overflow-hidden flex justify-center items-center flex-[0.1] min-w-[60px] max-w-[60px]">
<img src={dataImage2} alt="data" className="w-full h-full" />
</div>
<div className="flex flex-col flex-[0.9]">
<Link to="/shop-details" className="">
<h1 className="font-bold text-xl tracking-wide line-clamp-1 text-dark-gray dark:text-white capitalize">
{datas?.title}
</h1>
</Link>
<div className="my-2">
<p className="text-dark-gray dark:text-white">{datas?.description}</p>
</div>
{/* <div className="card-two-info flex gap-2 items-center">
<div className="owned-by flex space-x-2 items-center">
<div>
<p className="text-thin-light-gray text-sm leading-3">Added</p>
<p className="text-base text-dark-gray dark:text-white">
{datas.offer_added}
</p>
</div>
</div>
<div className="w-[1px] bg-light-purple dark:bg-dark-light-purple h-7"></div>
<div className="created-by flex space-x-2 items-center flex-row-reverse">
<div>
<p className="text-thin-light-gray text-sm leading-3 text-right">
Expires
</p>
<p className="text-base text-dark-gray dark:text-white text-right">
{datas.expire}
</p>
</div>
</div>
</div> */}
<div className="block sm:flex flex-wrap gap-4">
<p className="text-sm text-thin-light-gray flex flext-start gap-1">
Price: <span className="text-purple">{thePrice}</span>
</p>
<p className="text-sm text-thin-light-gray">
Duration:{" "}
<span className="text-purple italic">
{" "}
{datas?.timeline_days} day(s)
</span>
</p>
<p className="text-sm text-thin-light-gray">
Code:{" "}
<span className="text-purple">
{" "}
{datas?.offer_code}
</span>
</p>
</div>
</div>
</div>
<div className="">
<button
type="button"
className="px-4 py-2.5 text-white text-sm bg-pink rounded-full tracking-wide"
onClick={() => {
setMarketPopUp({ show: true, data: datas });
}}
>
View
</button>
</div>
</div>
}
{marketPopUp.show && (
<MarketPopUp
details={datas}
@@ -154,7 +203,6 @@ export default function AvailableJobsCard({
setMarketPopUp({ show: false, data: {} });
}}
situation={marketPopUp.show}
marketInt={manageInt}
/>
)}
</>
@@ -9,7 +9,6 @@ export default function HomeBannerOffersCard(props) {
let { banner, banner_location } = props?.itemData;
if (banner_location === "LOCAL") {
const imagePath = require(`../../assets/images/${banner}`); // Replace with your directory path for local images
console.log("This is local");
setImageUrl(imagePath);
} else if (banner_location === "URL") setImageUrl(banner);
else return null;
@@ -24,21 +23,20 @@ export default function HomeBannerOffersCard(props) {
}}
>
<div className="flex flex-col justify-between h-full">
<div className="content flex justify-between items-center mb-5">
<div className="content flex justify-between items-center">
<div className="siderCardHeader">
<h1 className="text-2xl font-bold text-dark-gray dark:text-white tracking-wide">
<>{props.itemData.title}</>
<span className="heroSilderTitle">{props.itemData.title}</span>
</h1>
</div>
{/*<SelectBox datas={filterDatas} action={dataSetHandler} />*/}
</div>
<div className="h-[233px]">
<div className="flex flex-col justify-around items-center flex-1">
<div className="siderCardDescription">
{props.itemData.description}
</div>
<div className="siderCardButton">
[ {props.itemData.button_text} ]
</div>
<button className="w-[150px] h-11 flex justify-center items-center btn-gradient text-base rounded-full text-white">
{props.itemData.button_text}
</button>
</div>
</div>
</Link>
+4 -1
View File
@@ -1,4 +1,4 @@
import React, { useState } from "react";
import { useState } from "react";
import { Link } from "react-router-dom";
import { toast } from "react-toastify";
import localImgLoad from "../../lib/localImgLoad";
@@ -6,6 +6,7 @@ import CountDown from "../Helpers/CountDown";
import Icons from "../Helpers/Icons";
export default function ProductCardStyleOne({ datas, hidden = false }) {
const [imageUrl, setImageUrl] = useState("");
const [addFavorite, setValue] = useState(false);
const [options, setOption] = useState(false);
const favoriteHandler = () => {
@@ -17,6 +18,8 @@ export default function ProductCardStyleOne({ datas, hidden = false }) {
toast.warn("Remove to Favorite List");
}
};
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">
@@ -0,0 +1,261 @@
import React, {useState, useEffect} from 'react'
import ModalCom from '../../Helpers/ModalCom'
import Detail from '../../jobPopout/popoutcomponent/Detail'
import usersService from '../../../services/UsersService'
import LoadingSpinner from '../../Spinners/LoadingSpinner'
import { PriceFormatter } from '../../Helpers/PriceFormatter'
function AssignTaskPopout({action, situation, familyDetails}) {
const apiCall = new usersService()
let [requestStatus, setRequestStatus] = useState({loading: false, status: false, message: ''}) // HOLDS RESPONSE FOR SENDING API REQUEST
let [familyTask, setFamilyTask] = useState({loading: true, data: []})
let [taskType, setTaskType] = useState('select') // SWITCHES BTW SELECT TASK AND NEW TASK
let [activeTask, setActiveTask] = useState({id: 0, data: {}}) // HOLDS SELECTED 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})
}
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){
setRequestStatus({loading: false, status: false, message: 'No Task is seleted'})
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: familyDetails.uid,
job_description: activeTask.data?.description,
assign_mode: 110011,
}
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'})
setTimeout(()=>{
setRequestStatus({loading: false, status: false, message: ''})
}, 5000)
}).catch(err => {
setRequestStatus({loading: false, status: false, message: 'An Error occured, try again'})
setTimeout(()=>{
setRequestStatus({loading: false, status: false, message: ''})
}, 5000)
})
}
if(taskType == 'new'){ // RUNS HERE IF TASK TYPE IS NEW TASK
console.log('TESTING')
}
}
useEffect(()=>{
const reqData = {
limit: 30,
offset: 0,
job_type: 'FAMILY',
action: 13005
}
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)
})
},[])
return (
<>
<ModalCom
action={action}
situation={situation}
>
<div className="w-full h-full lg:w-[700px] lg:h-auto bg-white dark:bg-dark-white lg:rounded-2xl">
<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 {familyDetails?.firstname}
</h1>
<button
type="button"
className="text-[#374557] dark:text-red-500"
onClick={action}
>
<svg
width="36"
height="36"
viewBox="0 0 36 36"
fill="none"
className="fill-current"
xmlns="http://www.w3.org/2000/svg"
>
<path
d="M36 16.16C36 17.4399 36 18.7199 36 20.0001C35.7911 20.0709 35.8636 20.2554 35.8385 20.4001C34.5321 27.9453 30.246 32.9248 22.9603 35.2822C21.9006 35.6251 20.7753 35.7657 19.6802 35.9997C18.4003 35.9997 17.1204 35.9997 15.8401 35.9997C15.5896 35.7086 15.2189 35.7732 14.9034 35.7093C7.77231 34.2621 3.08728 30.0725 0.769671 23.187C0.435002 22.1926 0.445997 21.1199 0 20.1599C0 18.7198 0 17.2798 0 15.8398C0.291376 15.6195 0.214408 15.2656 0.270759 14.9808C1.71321 7.69774 6.02611 2.99691 13.0428 0.700951C14.0118 0.383805 15.0509 0.386897 15.9999 0C17.2265 0 18.4532 0 19.6799 0C19.7156 0.124041 19.8125 0.136067 19.9225 0.146719C27.3 0.868973 33.5322 6.21922 35.3801 13.427C35.6121 14.3313 35.7945 15.2484 36 16.16ZM33.011 18.0787C33.0433 9.77105 26.3423 3.00309 18.077 2.9945C9.78479 2.98626 3.00344 9.658 2.98523 17.8426C2.96667 26.1633 9.58859 32.9601 17.7602 33.0079C26.197 33.0577 32.9787 26.4186 33.011 18.0787Z"
fill=""
fillOpacity="0.6"
/>
<path
d="M15.9309 18.023C13.9329 16.037 12.007 14.1207 10.0787 12.2072C9.60071 11.733 9.26398 11.2162 9.51996 10.506C9.945 9.32677 11.1954 9.0811 12.1437 10.0174C13.9067 11.7585 15.6766 13.494 17.385 15.2879C17.9108 15.8401 18.1633 15.7487 18.6375 15.258C20.3586 13.4761 22.1199 11.7327 23.8822 9.99096C24.8175 9.06632 26.1095 9.33639 26.4967 10.517C26.7286 11.2241 26.3919 11.7413 25.9133 12.2178C24.1757 13.9472 22.4477 15.6855 20.7104 17.4148C20.5228 17.6018 20.2964 17.7495 20.0466 17.9485C22.0831 19.974 24.0372 21.8992 25.9689 23.8468C26.9262 24.8119 26.6489 26.1101 25.4336 26.4987C24.712 26.7292 24.2131 26.3441 23.7455 25.8757C21.9945 24.1227 20.2232 22.3892 18.5045 20.6049C18.0698 20.1534 17.8716 20.2269 17.4802 20.6282C15.732 22.4215 13.9493 24.1807 12.1777 25.951C11.7022 26.4262 11.193 26.7471 10.4738 26.4537C9.31345 25.9798 9.06881 24.8398 9.98589 23.8952C11.285 22.5576 12.6138 21.2484 13.9387 19.9355C14.5792 19.3005 15.2399 18.6852 15.9309 18.023Z"
fill="#"
fillOpacity="0.6"
/>
</svg>
</button>
</div>
{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 md:grid-cols-2">
<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>
<div className='p-4 w-full h-[400px] overflow-y-auto bg-slate-100'>
{
taskType == 'select' ?
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="text-dark-gray tracking-wide">{item?.title}</p>
</div>
))
:
<p className="p-8 text-lg text-dark-gray dark:text-white tracking-wide text-center">No Task found!</p>
:
<p className="p-8 text-lg text-dark-gray dark:text-white tracking-wide text-center">SPACE FOR NEW TASK</p>
}
</div>
</div>
{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 outline-none border border-slate-300 rounded-md`}
rows="5"
style={{ resize: "none" }}
defaultValue={activeTask?.data?.job_detail}
// onChange={handleInputChange}
/>
{/* <p>{}</p> */}
</div>
</div>
</div>
:
<></>
}
</div>
{/* BTN */}
<div className='p-2 border-t-2 flex justify-end items-center gap-3'>
{/* error or success display */}
{requestStatus.message != "" &&
(!requestStatus.status ? (
<div
className={`relative p-2 text-[#912741] bg-[#fcd9e2] border-[#fbc6d3] mb-4 rounded-[0.475rem] text-md font-light leading-[19.5px] text-[13px]`}
>
{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>
)
))}
{/* End of error or success display */}
<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' />
:
<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>
}
</div>
</div>
</>
}
</div>
</ModalCom>
</>
)
}
export default AssignTaskPopout
@@ -117,7 +117,7 @@ export default function FamilyManageTabs({
return (
<div
className={`update-table w-full bg-white dark:bg-dark-white overflow-y-auto rounded-2xl section-shadow min-h-[520px] max-h-[600px] ${
className={`w-full bg-white dark:bg-dark-white overflow-y-auto rounded-2xl section-shadow min-h-[520px] max-h-[600px] ${
className || ""
}`}
>
@@ -171,6 +171,7 @@ export default function FamilyManageTabs({
className={className}
loader={details.familyTasks.loading}
familyData={details.familyTasks.data}
familyDetails={details.familyDetails.data}
/>
)}
{name === "Account" && (
+60 -32
View File
@@ -5,15 +5,20 @@ import { handlePagingFunc } from "../../Pagination/HandlePagination";
import PaginatedList from "../../Pagination/PaginatedList";
import LoadingSpinner from "../../Spinners/LoadingSpinner";
import Icons from "../../Helpers/Icons";
import { PriceFormatter } from "../../Helpers/PriceFormatter";
import ModalCom from "../../Helpers/ModalCom";
import Detail from "../../jobPopout/popoutcomponent/Detail";
import AssignTaskPopout from "../FamilyPopout/AssignTaskPopout";
export default function FamilyTasks({ familyData, className, loader }) {
const filterCategories = ["All Categories", "Explore", "Featured"];
const [selectedCategory, setCategory] = useState(filterCategories[0]);
export default function FamilyTasks({ familyData, familyDetails, className, loader }) {
let navigate = useNavigate();
let { pathname } = useLocation();
let data = ["1", "2", "3", "4", "5", "6"]; // to be replaced later by result from API CALL
let [familyTaskPopout, setFamilyTaskPopout] = useState(false)
const familyPopUpHandler = () => {
setFamilyTaskPopout(prev => !prev)
}
const [currentPage, setCurrentPage] = useState(0);
const indexOfFirstItem = Number(currentPage);
@@ -24,10 +29,7 @@ export default function FamilyTasks({ familyData, className, loader }) {
indexOfLastItem
);
const handlePagination = (e) => {
handlePagingFunc(e, setCurrentPage);
};
const handlePagination = (e) => handlePagingFunc(e, setCurrentPage);
return (
<div
@@ -41,7 +43,15 @@ export default function FamilyTasks({ familyData, className, loader }) {
</div>
) : (
<>
{" "}
<div className="w-full p-2 my-2 flex justify-end items-center">
<button
type="button"
onClick={familyPopUpHandler}
className="px-1 h-11 flex justify-center items-center btn-gradient text-base rounded-full text-white"
>
Add task for {familyDetails?.firstname}
</button>
</div>
{familyData && familyData?.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">
@@ -52,8 +62,14 @@ export default function FamilyTasks({ familyData, className, loader }) {
familyData?.result_list &&
familyData.result_list.length > 0 &&
currentTask.map((value, index) => {
// find due date
const dueDate = value?.delivery_date.split(" ")[0]
// find due date
const dueDate = value?.delivery_date.split(" ")[0];
// the price
let thePrice = PriceFormatter(
value?.price * 0.01,
value?.currency_code,
value?.currency
);
return (
<tr
key={index}
@@ -73,37 +89,42 @@ export default function FamilyTasks({ familyData, className, loader }) {
{value.title}
</h1>
<div className="flex gap-4 items-center">
<span className="text-sm text-thin-light-gray">
Price:{" "}
<span className="text-purple">
{value.price * 0.01}
<span className="text-sm text-thin-light-gray flex flex-start gap-1">
Price:{" "}
<span className="text-purple">
{thePrice}
</span>
</span>
</span>
<span className="text-sm text-thin-light-gray">
Duration:{" "}
<span className="text-purple">
{" "}
{value.timeline_days} day(s)
<span className="text-sm text-thin-light-gray">
Duration:{" "}
<span className="text-purple">
{" "}
{value.timeline_days} day(s)
</span>
</span>
</span>
<span className="text-sm text-thin-light-gray">
Due Date:{" "}
<span className="text-purple">
{" "}
{dueDate}
<span className="text-sm text-thin-light-gray">
Due Date:{" "}
<span className="text-purple">
{" "}
{dueDate}
</span>
</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 },
state: {
...value,
pathname,
dueDate,
thePrice,
},
});
}}
className="w-20 h-11 flex justify-center items-center btn-gradient text-base rounded-full text-white"
@@ -112,7 +133,7 @@ export default function FamilyTasks({ familyData, className, loader }) {
</button>
</td>
</tr>
)
);
})}
</>
}
@@ -137,6 +158,13 @@ export default function FamilyTasks({ familyData, className, loader }) {
)}
</>
)}
{familyTaskPopout &&
<AssignTaskPopout
action={familyPopUpHandler}
situation={familyTaskPopout}
familyDetails={familyDetails}
/>
}
</div>
);
}
+2 -2
View File
@@ -10,7 +10,7 @@ export default function Accordion({ datas }) {
<>
<div className="accordion-item overflow-hidden relative z-[1]">
<div
className="accordion-title-bar flex items-center space-x-3 py-5 border-b border-light-purple dark:border-[#5356fb29] "
className="accordion-title-bar flex items-center space-x-3 py-5 border-b border-light-purple dark:border-[#5356fb29] cursor-pointer"
onClick={accordionHandler}
>
<div className="accordion-title-icon relative">
@@ -36,7 +36,7 @@ export default function Accordion({ datas }) {
<div className="w-[3px] h-auto bg-purple rounded-[28px]"></div>
<div className="flex-1">
<p className="text-base text-thin-light-gray tracking-wide">
{datas.content}
{datas.msg}
</p>
</div>
</div>
+56 -51
View File
@@ -1,59 +1,64 @@
/* eslint-disable no-underscore-dangle */
import React, { useEffect, useState } from "react";
import { useEffect, useState } from "react";
function CountDown({ lastDate = "" }) {
// const [showDate, setDate] = useState(0);
const [showHour, setHour] = useState(0);
const [showMinute, setMinute] = useState(0);
const [showSecound, setDateSecound] = useState(0);
// count Down
const provideDate = new Date(lastDate);
// format date
const year = provideDate.getFullYear();
const month = provideDate.getMonth();
// console.log(month);
const date = provideDate.getDate();
// console.log(date);
const hours = provideDate.getHours();
// console.log(hours);
const minutes = provideDate.getMinutes();
// console.log(minutes);
const seconds = provideDate.getSeconds();
// console.log(seconds);
// date calculation logic
const _seconds = 1000;
const _minutes = _seconds * 60;
const _hours = _minutes * 60;
const _date = _hours * 24;
// interval function
const startInterval = () => {
const timer = setInterval(() => {
const now = new Date();
const distance =
new Date(year, month, date, hours, minutes, seconds).getTime() -
now.getTime();
if (distance < 0) {
clearInterval(timer);
return;
}
// setDate(Math.floor(distance / _date));
setMinute(Math.floor((distance % _hours) / _minutes));
setHour(Math.floor((distance % _date) / _hours));
setDateSecound(Math.floor((distance % _minutes) / _seconds));
}, 1000);
};
// effect
useEffect(() => {
if (lastDate !== "") {
startInterval();
}
// State to store the countdown values
const [countdownValues, setCountdownValues] = useState({
showHour: 0,
showMinute: 0,
showSecond: 0,
});
useEffect(() => {
if (lastDate) {
// Interval function to update countdown values
const intervalId = setInterval(() => {
const now = new Date().getTime();
const targetDate = new Date(lastDate).getTime();
const distance = targetDate - now;
if (distance < 0) {
// If the countdown has reached zero or gone past the target date, clear the interval
clearInterval(intervalId);
setCountdownValues({
showHour: 0,
showMinute: 0,
showSecond: 0,
});
return;
}
// Calculate the countdown values (days, hours, minutes, seconds)
const days = Math.floor(distance / (1000 * 60 * 60 * 24));
const hours = Math.floor(
(distance % (1000 * 60 * 60 * 24)) / (1000 * 60 * 60)
);
const minutes = Math.floor((distance % (1000 * 60 * 60)) / (1000 * 60));
const seconds = Math.floor((distance % (1000 * 60)) / 1000);
// since we don't have a slot for days...
const totalHours = days * 24 + hours;
// Update the countdown values in the state
setCountdownValues({
showHour: totalHours,
showMinute: minutes,
showSecond: seconds,
});
}, 1000);
// Clean up the interval on component unmount or when the lastDate prop changes
return () => clearInterval(intervalId);
}
}, [lastDate]);
// Destructure the countdown values from the state
const { showHour, showMinute, showSecond } = countdownValues;
return (
<span>
{showHour} : {showMinute} : {showSecound}
{showHour < 10 ? "0" + showHour : showHour} :{" "}
{showMinute < 10 ? "0" + showMinute : showMinute} :{" "}
{showSecond < 10 ? "0" + showSecond : showSecond}
</span>
);
}
+10
View File
@@ -1,5 +1,9 @@
import React from "react";
import ATMCard from '../../assets/images/card.svg'
import VisaCard from '../../assets/images/visa.svg'
import MasterCard from '../../assets/images/master.svg'
export default function Icons({ name }) {
return (
<>
@@ -483,6 +487,12 @@ export default function Icons({ name }) {
c0.3,0,0.5-0.1,0.7-0.3l5.7-5.7c0,0,0,0,0,0C15.9,12.3,15.9,11.7,15.5,11.3z"
></path>
</svg>
) : name === "atm-card" ? (
<img className="w-[20px]" src={ATMCard} alt="card" />
) : name === "visa-card" ? (
<img className="w-[20px]" src={VisaCard} alt="card" />
) : name === "master-card" ? (
<img className="w-[20px]" src={MasterCard} alt="card" />
) : (
""
)}
@@ -1,6 +1,6 @@
import React, { useRef } from "react";
import Icons from "../../Icons";
import { Link } from "react-router-dom";
import Icons from "../../Icons";
export default function InputCom({
label,
@@ -21,7 +21,8 @@ export default function InputCom({
blurHandler,
spanTag,
inputBg,
direction
direction,
errorBorder,
}) {
const inputRef = useRef(null);
// Entry Validation
@@ -40,8 +41,8 @@ export default function InputCom({
// for Patterns
const inputPatterns = () => {
const inputConfig = inputConfigs[inputRef?.current?.name]?.pattern;
return inputConfig || ""
}
return inputConfig || "";
};
return (
<div className={`input-com ${parentClass}`}>
<div className={`flex items-center justify-between mb-2.5 ${labelClass}`}>
@@ -51,7 +52,12 @@ export default function InputCom({
htmlFor={name}
>
{label}
{spanTag && (
{spanTag && spanTag == "*" ? (
<span className="text-red-700 text-sm tracking-wide">
{" "}
{spanTag}
</span>
) : (
<span className="text-green-700 text-sm tracking-wide">
{spanTag}
</span>
@@ -68,13 +74,17 @@ export default function InputCom({
)}
</div>
<div
className={`input-wrapper border border-[#f5f8fa] dark:border-[#5e6278] w-full rounded-[0.475rem] h-[42px] overflow-hidden relative font-medium leading-6 bg-clip-padding text-[#5e6278] dark:text-gray-100 bg-[#f5f8fa] dark:bg-[#5e6278] text-base ${inputClass}`}
className={`input-wrapper border ${
errorBorder
? "border-[#ff0a0a63] border-[0.5px] shadow-red-500 animate-shake"
: "border border-[#f5f8fa] dark:border-[#5e6278]"
} w-full rounded-full h-[42px] overflow-hidden relative font-medium leading-6 bg-clip-padding text-[#5e6278] dark:text-gray-100 bg-[#f5f8fa] dark:bg-[#5e6278] text-base ${inputClass}`}
>
<input
placeholder={placeholder}
value={value}
onChange={inputHandler}
className={`input-field placeholder:text-base text-dark-gray w-full h-full ${
className={`input-field placeholder:text-base text-dark-gray w-full h-full tracking-wide ${
inputBg ? inputBg : "bg-[#FAFAFA]"
} dark:bg-[#11131F] focus:ring-0 focus:outline-none ${fieldClass}`}
type={type}
@@ -89,8 +99,10 @@ export default function InputCom({
dir={direction}
/>
{iconName && (
<div className="absolute right-6 bottom-[10px] z-10">
<Icons name={iconName} />
<div className="absolute right-6 bottom-[10px] z-10 flex gap-2">
{iconName.split(" ").map((item, index) => (
<Icons key={index} name={item} />
))}
</div>
)}
{passIcon && (
@@ -116,6 +128,7 @@ const inputConfigs = {
province: { minLength: 3, maxLength: 25, pattern: "[a-zA-Z]+" },
city: { minLength: 3, maxLength: 25, pattern: "[a-zA-Z]+" },
amount: { minLength: 1, maxLength: 9, pattern: "[0-9]+" },
description: { minLength: 5, maxLength: 250 },
};
/* Numbers Only: <input type="text" pattern="[0-9]*" /> strictly numbers
+45 -13
View File
@@ -1,16 +1,48 @@
export const PriceFormatter = (price, currency, currencyName) => {
const supportedCurrencies = ["USD", "EUR", "GBP"];
const symbolFormatter = supportedCurrencies.includes(currency)
? currency
: undefined;
import React from 'react'
const formatter = new Intl.NumberFormat("en", {
style: symbolFormatter,
currencyDisplay: "symbol",
minimumFractionDigits: 2,
});
// export const PriceFormatter = (price, currency, currencyName) => {
// const supportedCurrencies = ["USD", "EUR", "GBP"];
// const symbolFormatter = supportedCurrencies.includes(currency)
// ? currency
// : undefined;
const displayCurrencyName = symbolFormatter ? "" : currencyName;
// const formatter = new Intl.NumberFormat("en", {
// style: symbolFormatter,
// currencyDisplay: "symbol",
// minimumFractionDigits: 2,
// });
return `${formatter.format(price)} ${displayCurrencyName}`;
};
// const displayCurrencyName = symbolFormatter ? "" : currencyName;
// return `${formatter.format(price)} ${displayCurrencyName}`;
// };
export const PriceFormatter = (price='00', currency='', currencyName='') => {
// Convert the number to a string
let numStr = String(price);
// Split the string into integer and decimal parts
let parts = numStr.split('.');
let integerPart = parts[0];
let decimalPart = parts[1] || '';
// Add thousands separators to the integer part
// let formattedInteger = integerPart.replace(/\B(?=(\d{3})+(?!\d))/g, ',');
let formattedInteger = integerPart;
// Truncate or pad the decimal part to two decimal points
let formattedDecimal = decimalPart.slice(0, 2).padEnd(2, '0');
// Combine the formatted integer and decimal parts
// let formattedNumber = formattedInteger + '.' + formattedDecimal;
// return formattedNumber;
return (
<span className='text-sm flex items-center'>
<sup>{currency || currencyName || ''}</sup>
<span className='px-1 font-bold text-lg'>{formattedInteger}</span>
<sup>{formattedDecimal}</sup>
</span>
)
}
+2 -2
View File
@@ -1,7 +1,7 @@
import React, { useState } from "react";
import useToggle from "../../../hooks/useToggle";
function SelectBox({ datas = [], className, action, contentBodyClasses }) {
function SelectBox({ datas = [], className, action, contentBodyClasses, position }) {
const [item, setItem] = useState(datas[0]);
// custom hook
const [toggle, setToggle] = useToggle(false);
@@ -49,7 +49,7 @@ function SelectBox({ datas = [], className, action, contentBodyClasses }) {
<div
style={{ boxShadow: "0px 4px 87px 0px #0000002B" }}
className={`drop-down-content w-[120px] bg-white dark:bg-dark-white rounded-[4px] p-3 absolute right-0 top-[100%] z-20 ${
className={`drop-down-content w-[120px] bg-white dark:bg-dark-white rounded-[4px] p-3 absolute ${position =='left' ? 'left-0' : 'right-0'} top-[100%] z-20 ${
toggle ? "active" : ""
} ${contentBodyClasses || ""}`}
>
+53 -27
View File
@@ -13,7 +13,9 @@ import LoadingSpinner from "../Spinners/LoadingSpinner";
export default function History() {
const apiCall = new usersService()
const apiCall = new usersService()
let [tab, setTab] = useState("purchases"); //STATE FOR SWITCHING BETWEEN TABS
let [paymentHistory, setPaymentHistory] = useState({ // FOR PAYMENT HISTORY
loading: true,
@@ -63,7 +65,7 @@ export default function History() {
<>
<Layout>
<div className="history-wrapper w-full mb-10">
<div className="main-wrapper w-full">
<div className="w-full">
<div className="sm:flex justify-between items-center mb-6">
<div className="mb-5 sm:mb-0">
<h1 className="text-26 font-bold text-dark-gray dark:text-white">
@@ -213,35 +215,59 @@ export default function History() {
{/*</div>*/}
{/*<MarketHistorySection />*/}
{/* <TopHxBox className="mb-11" /> */}
<div className='w-full lg:flex xl:space-x-8 lg:space-x-4 bottomMargin'>
{/* PURCHASE SECTION */}
<div className="lg:w-1/2 w-full mb-10 lg:mb-0">
<div className="wallet w-full md:p-8 p-4 h-full max-h-[700px] bg-white dark:bg-dark-white overflow-y-auto rounded-2xl shadow">
<h1 className="text-xl font-bold text-dark-gray dark:text-white tracking-wide">Purchases</h1>
{purchaseHistory.loading ?
<LoadingSpinner size='16' color='sky-blue' />
:
<PurchasesTable purchase={purchaseHistory} />
}
</div>
<div className='w-full p-4 md:p-8 bg-white dark:bg-dark-white rounded-2xl shadow bottomMargin'>
{/* switch button */}
<div className="my-1 flex items-center border-b border-slate-300">
<button
name="purchases"
onClick={(e) => setTab(e.target.name)}
className={`p-2 text-lg font-bold text-slate-600 dark:text-white border ${
tab == "purchases" ? "border-sky-blue" : "border-slate-300"
} tracking-wide transition duration-200`}
>
Purchases
</button>
<button
name="recent"
onClick={(e) => setTab(e.target.name)}
className={`p-2 text-lg font-bold text-slate-600 dark:text-white border ${
tab == "recent" ? "border-sky-blue" : "border-slate-300"
} tracking-wide transition duration-200`}
>
Recent Activity
</button>
</div>
{/* END OF PURCHASE SECTION */}
{/* RECENT ACTIVITY SECTION */}
<div className="lg:w-1/2 w-full mb-10 lg:mb-0">
<div className="wallet w-full md:p-8 p-4 h-full max-h-[700px] bg-white dark:bg-dark-white overflow-y-auto rounded-2xl shadow">
<h1 className="text-xl font-bold text-dark-gray dark:text-white tracking-wide">Recent Activity</h1>
{/* <p className='text-base text-slate-500 dark:text-white'>Activity Report</p> */}
{paymentHistory.loading ?
<LoadingSpinner size='16' color='sky-blue' />
:
<RecentActivityTable payment={paymentHistory} />
{/* END OF switch button */}
<div className="history-tables w-full">
{/* PURCHASE SECTION */}
{tab == 'purchases' &&
<div className="wallet w-full border-t">
<h1 className="p-2 text-xl font-bold text-dark-gray dark:text-white tracking-wide">Purchases</h1>
{purchaseHistory.loading ?
<LoadingSpinner size='16' color='sky-blue' />
:
<PurchasesTable purchase={purchaseHistory} />
}
</div>
}
</div>
{/* END OF PURCHASE SECTION */}
{/* RECENT ACTIVITY SECTION */}
{tab == 'recent' &&
<div className="wallet w-full border-t">
<h1 className="p-2 text-xl font-bold text-dark-gray dark:text-white tracking-wide">Recent Activity</h1>
{/* <p className='text-base text-slate-500 dark:text-white'>Activity Report</p> */}
{paymentHistory.loading ?
<LoadingSpinner size='16' color='sky-blue' />
:
<RecentActivityTable payment={paymentHistory} />
}
</div>
}
{/* END OF RECENT ACTIVITY SECTION */}
</div>
{/* END OF RECENT ACTIVITY SECTION */}
</div>
<HistoryTable />
{/*<HistoryTable />*/}
</div>
</div>
</Layout>
+1 -2
View File
@@ -3,11 +3,10 @@ import slider2 from "../../assets/images/slider-2.jpg";
import HomeBannerOffersCard from "../Cards/HomeBannerOffersCard";
export default function HomeSliders(props) {
// console.log("BANNER LIST IN HomeSliders->", props.bannerList);
// debugger;
return (
<>
<div className="hero-slider relative 2xl:w-[600px] xl:w-[500px] lg:w-[420px] w-full mb-2 lg:mb-0 ">
<div className="hero-slider relative 2xl:w-[600px] xl:w-[400px] lg:w-[420px] w-full mb-2 lg:mb-0 ">
<div className="w-full">
<SliderCom settings={props.settings}>
{props.bannerList?.length <= 0 && (
+81 -82
View File
@@ -1,106 +1,105 @@
import React, { useEffect, useState } from "react";
import React, { useEffect, useMemo, useState } from "react";
import DataIteration from "../Helpers/DataIteration";
import AvailableJobsCard from "../Cards/AvailableJobsCard";
import ListView from '../../assets/images/list-view.png'
import GridView from '../../assets/images/grid-view.svg'
import SelectBox from "../Helpers/SelectBox";
export default function MainSection({ className, marketPlaceProduct }) {
const [tab, setTab] = useState("all");
const [products, setProducts] = useState(marketPlaceProduct);
export default function MainSection({
className,
marketPlaceProduct,
categories,
}) {
// Creating All cart..
let marketCategories = useMemo(
() => ({ All: "All Categories", ...categories }),
[categories]
);
const [tab, setTab] = useState(Object.keys(marketCategories)[0]);
let [contentDisplay, setContentDisplay] = useState('grid') // STATE TO HOLD LIST VIEW STYLE
// Convert to array in order to map
const mappedArray = Object.entries(marketCategories).map(([key, value]) => {
return { key, value };
});
const [products, setProducts] = useState([]);
const tabHandler = (value) => {
setTab(value);
};
useEffect(() => {
if (tab === "artist") {
setProducts(marketPlaceProduct?.slice(0, 3));
} else if (tab === "market") {
setProducts(marketPlaceProduct?.slice(0, 6));
} else if (tab === "shop") {
setProducts(marketPlaceProduct?.slice(6, 9));
} else if (tab === "assets") {
setProducts(marketPlaceProduct?.slice(3, 6));
} else {
setProducts(marketPlaceProduct);
// Handles the category selection on mobile view
const handleSetCategory = (value) => {
for (let i in marketCategories) {
if (marketCategories[i] == value) {
setTab(i);
}
}
}, [tab, marketPlaceProduct]);
};
useEffect(() => {
if (tab === "All") {
setProducts(marketPlaceProduct);
} else {
const filteredProducts = marketPlaceProduct.filter((product) =>
product.category.includes(tab)
);
setProducts(filteredProducts);
}
}, [tab, marketPlaceProduct, categories, marketCategories]);
return (
<div className={`market-place-section w-full ${className || ""}`}>
<div className="market-place-wrapper w-full">
<div className="filter-navigate-area lg:flex justify-between mb-8 items-center">
<div className="tab-item lg:mb-0 mb-5">
<div className="md:flex md:space-x-8 space-x-2">
<span
onClick={() => tabHandler("all")}
className={`md:text-[18px] text-md text-dark-gray dark:text-white hover:text-pink border-b hover:border-pink font-medium cursor-pointer ${
tab === "all"
? "text-pink border-pink"
: " border-transparent"
}`}
>
All
</span>
<span
onClick={() => tabHandler("artist")}
className={`md:text-[18px] text-md text-dark-gray dark:text-white hover:text-pink border-b hover:border-pink font-medium cursor-pointer ${
tab === "artist"
? "text-pink border-pink"
: " border-transparent"
}`}
>
Featured Artist
</span>
<span
onClick={() => tabHandler("market")}
className={`md:text-[18px] text-md text-dark-gray dark:text-white hover:text-pink border-b hover:border-pink font-medium cursor-pointer ${
tab === "market"
? "text-pink border-pink"
: " border-transparent"
}`}
>
Open Market
</span>
<span
onClick={() => tabHandler("shop")}
className={`md:text-[18px] text-md text-dark-gray dark:text-white hover:text-pink border-b hover:border-pink font-medium cursor-pointer ${
tab === "shop"
? "text-pink border-pink"
: " border-transparent"
}`}
>
Partner Shops
</span>
<span
onClick={() => tabHandler("assets")}
className={`md:text-[18px] text-md text-dark-gray dark:text-white hover:text-pink border-b hover:border-pink font-medium cursor-pointer ${
tab === "assets"
? "text-pink border-pink"
: " border-transparent"
}`}
>
Game Assets
</span>
<div className="filter-navigate-area flex justify-between items-center mb-8">
<div className="tab-item w-full flex items-center">
<div className="hidden lg:flex md:space-x-8 space-x-2">
{mappedArray.map(({ key, value }) => (
<span
key={key}
onClick={() => tabHandler(key)}
className={`md:text-[18px] text-md text-dark-gray dark:text-white hover:text-pink border-b hover:border-pink font-medium cursor-pointer ${
tab === key
? "text-pink border-pink"
: " border-transparent"
}`}
>
{value}
</span>
))}
</div>
{/* market categories on screen smaller than large screen */}
<div className="w-[80%] lg:hidden">
<SelectBox
action={handleSetCategory}
datas={Object.values(marketCategories)}
className="Update-table-dropdown"
contentBodyClasses="w-auto min-w-max"
position='left'
/>
</div>
</div>
{/*<div className="search-item flex lg:flex-none justify-end">*/}
{/* <SearchCom*/}
{/* className="lg:bg-transparent"*/}
{/* inputClasses="lg:bg-transparent"*/}
{/* />*/}
{/*</div>*/}
{/* contentDisplay toggler */}
<div className="p-2 w-[35px] h-[35px] bg-white dark:bg-slate-200 rounded-lg">
<img
title={contentDisplay=='grid'? 'list view' : 'grid view'}
onClick={()=>setContentDisplay((prev)=>prev=='grid'? 'list' : 'grid')}
src={contentDisplay=='grid'? ListView : GridView}
className="w-full h-full cursor-pointer" alt="view"
/>
</div>
{/* end of contentDisplay toggler */}
</div>
<div className="filter-navigate-content w-full min-h-screen">
<div className="grid lg:grid-cols-3 sm:grid-cols-2 gap-[30px]">
<div className={contentDisplay == 'grid' ? 'grid lg:grid-cols-3 sm:grid-cols-2 gap-[30px]' : 'w-full'}>
<DataIteration
datas={products}
startLength={process.env.REACT_APP_ZERO_STATE}
endLength={products?.length}
>
{({ datas }) => (
<AvailableJobsCard key={datas.id} datas={datas} />
<AvailableJobsCard contentDisplay={contentDisplay} key={datas.id} datas={datas} />
)}
</DataIteration>
</div>
@@ -0,0 +1,12 @@
import React from 'react'
function Detail({label, value, bg,}) {
return (
<>
<label className='w-full md:w-1/4 text-slate-900 tracking-wide font-semibold'>{label}</label>
<p className={`p-1 w-full md:w-3/4 text-sm text-slate-900 ${bg ? bg : null}`}>{value}</p>
</>
)
}
export default Detail
+139 -101
View File
@@ -1,22 +1,98 @@
import React, { useCallback, useMemo, useState } from "react";
import { useCallback, useEffect, useMemo, useState } from "react";
import ModalCom from "../../Helpers/ModalCom";
import { toast } from "react-toastify";
import { Form, Formik } from "formik";
import usersService from "../../../services/UsersService";
import LoadingSpinner from "../../Spinners/LoadingSpinner";
import { PriceFormatter } from "../../Helpers/PriceFormatter";
const MarketPopUp = ({ details, onClose, situation, marketInt }) => {
const [marketMsg, setMarketMsg] = useState({
loading: false,
data: {},
state: undefined,
});
const [manageInt, setManageInt] = useState({
loading: false,
data: {},
state: undefined,
msg: "",
});
const MarketCalls = (details) => {
const [marketMsg, setMarketMsg] = useState({
loading: false,
data: {},
state: undefined,
});
const [manageInt, setManageInt] = useState({
loading: false,
data: {},
state: undefined,
msg: "",
});
const { offer_code } = details;
const reqData = { offer_code };
const MarketDetail = async () => {
try {
setMarketMsg({ loading: true });
if (!textValue) return;
reqData.yourmessage = textValue;
const marketMessage = await apiCall.MarketMessage(reqData);
const marketMessageRes = marketMessage?.data;
if (marketMessageRes?.internal_return < 0) {
toast.warn("Something wrong happened", {
autoClose: 2000,
hideProgressBar: true,
});
onClose();
return;
}
toast.success("Message sent", {
autoClose: 2500,
hideProgressBar: true,
});
setMarketMsg({ data: marketMessageRes, state: true });
setTimeout(() => onClose(), 2000);
} catch (error) {
throw new Error(error);
} finally {
setTimeout(() => {
setTextValue("");
setMarketMsg({ loading: false });
}, 2000);
}
};
const ManageInterest = async () => {
try {
setManageInt({ loading: true });
const manageInt = await apiCall.MarketInterest(reqData);
const manageIntRes = manageInt?.data;
if (manageIntRes?.internal_return < 0) {
setManageInt({
loading: false,
msg: manageIntRes?.status,
data: manageIntRes,
state: false,
});
} else {
setManageInt({
loading: false,
msg: manageIntRes?.status,
data: manageIntRes,
state: true,
});
}
setTimeout(() => setManageInt({ msg: "" }), 3000);
} catch (error) {
throw new Error(error);
}
};
// useEffect(() => {
// ManageInterest();
// }, []);
return { MarketDetail, ManageInterest, manageInt, marketMsg };
};
const [textValue, setTextValue] = useState("");
@@ -26,84 +102,33 @@ const MarketPopUp = ({ details, onClose, situation, marketInt }) => {
const apiCall = useMemo(() => new usersService(), []);
const marketCalls = useCallback(
async (e) => {
try {
const nameOfCall = e?.target?.name;
const { offer_code } = details;
const reqData = { offer_code };
let { manageInt, ManageInterest, MarketDetail, marketMsg } = MarketCalls(details);
if (nameOfCall === "market-message") {
setMarketMsg({ loading: true });
if (!textValue) return;
reqData.yourmessage = textValue;
const marketMessage = await apiCall.MarketMessage(reqData);
const marketMessageRes = marketMessage?.data;
if (marketMessageRes?.internal_return < 0) {
toast.warn("Something wrong happened", {
autoClose: 2000,
hideProgressBar: true,
});
onClose();
return;
}
toast.success("Message sent", {
autoClose: 2500,
hideProgressBar: true,
});
setMarketMsg({ data: marketMessageRes, state: true });
setTimeout(() => onClose(), 2000);
} else {
setManageInt({ loading: true });
const manageInt = await apiCall.MarketInterest(reqData);
const manageIntRes = manageInt?.data;
if (manageIntRes?.internal_return < 0) {
setManageInt({
loading: false,
msg: `Error - ${manageIntRes?.status}`,
data: manageIntRes,
state: false,
});
} else {
setManageInt({
loading: false,
msg: manageIntRes?.status,
data: manageIntRes,
state: true,
});
}
setTimeout(() => setManageInt({ msg: "" }), 3000);
return;
}
} catch (error) {
throw new Error(error);
} finally {
setTimeout(() => {
setTextValue("");
setMarketMsg({ loading: false });
}, 2000);
}
},
[apiCall, details, onClose, textValue]
let thePrice = PriceFormatter(
details?.price * 0.01,
details?.currency_code,
details?.currency
);
console.log('Checking my mangeInt',manageInt)
// let addedIntDate = marketInt?.added?.split(" ")[0];
let expireIntDate = marketInt?.expire?.split(" ")[0];
// let expireIntDate = marketInt?.expire?.split(" ")[0];
let cleanedText = details?.job_description
?.replace(/&lt;/g, "<")
.replace(/&gt;/g, ">")
.replace(/&quot;/g, '"')
.replace(/&amp;/g, "&");
console.log("first wait", {
manageInt,
ManageInterest,
MarketDetail,
marketMsg,
});
return (
<ModalCom action={onClose} situation={situation} className="edit-popup">
<div className="logout-modal-wrapper md:w-[750px] md:h-[700px] h-full bg-white dark:bg-dark-white lg:rounded-2xl overflow-y-auto">
<div className="logout-modal-wrapper md:w-[750px] md:h-[660px] h-full bg-white dark:bg-dark-white lg:rounded-2xl overflow-y-auto">
<div className="logout-modal-header w-full flex items-center justify-between lg:p-6 px-[30px] py-[23px]">
<h1 className="text-26 font-bold text-dark-gray dark:text-white tracking-wide">
{details.offer_code}
@@ -112,8 +137,8 @@ const MarketPopUp = ({ details, onClose, situation, marketInt }) => {
</div>
<div className="md:flex bg-white rounded-lg">
<div className="p-4 w-full md:w-3/4 md:border-r-1">
<div className="min-h-[290px]">
<div className="p-4 w-full md:w-[75%] md:border-r-1">
<div className="min-h-[263px]">
<h2 className="font-semibold text-slate-900 dark:text-black tracking-wide">
{details?.title}
</h2>
@@ -128,17 +153,17 @@ const MarketPopUp = ({ details, onClose, situation, marketInt }) => {
name: "",
content: {
text: `Timeline: ${details.timeline_days} day(s) -- `,
bold: `Budget: ${details.price} naira`,
bold: `Budget: ${thePrice}`,
},
},
{
name: "Delivery Detail",
content: details.job_description,
content: cleanedText,
danger: true,
},
].map(({ name, content, danger }, idx) => (
<div className={`my-3 md:flex items-center`} key={idx}>
<label className="w-full md:w-1/4 text-slate-900 tracking-wide font-semibold">
<label className="w-full md:w-[19%] text-slate-900 tracking-wide font-semibold whitespace-pre-wrap">
{name}
</label>
<div
@@ -152,19 +177,23 @@ const MarketPopUp = ({ details, onClose, situation, marketInt }) => {
<p
className={``}
dangerouslySetInnerHTML={{
__html: danger && content?.replace(/"/g, ""),
__html: danger && content,
}}
/>
) : (
<p className={`w-full md:w-3/4 text-slate-900`}>
<p className={`w-full text-slate-900`}>
{name !== "Delivery Detail" ? (
<>
{typeof content !== "object" ? content : null}
{typeof content === "object" && (
<span className="flex items-center gap-2">
{content?.text}
<strong>{content?.bold}</strong>
</span>
<>
<hr className="mb-1" />
<span className="flex items-center gap-2">
{content?.text}
<strong>{thePrice}</strong>
</span>
<hr className="mt-1" />
</>
)}
</>
) : (
@@ -194,7 +223,7 @@ const MarketPopUp = ({ details, onClose, situation, marketInt }) => {
<button
className="self-end w-[150px] h-[52px] rounded-md text-base bg-yellow-500 text-white"
name="market-message"
onClick={marketCalls}
onClick={MarketDetail}
>
{marketMsg.loading ? (
<LoadingSpinner size={5} color="white" />
@@ -207,17 +236,17 @@ const MarketPopUp = ({ details, onClose, situation, marketInt }) => {
</div>
</div>
<div className="w-full md:w-1/4 h-full ">
<div className="w-[90%] mx-auto bg-[#f1f8ff] p-4 rounded-md md:min-h-[550px] flex flex-col justify-between">
<div className="w-full md:w-[23%] h-full ">
<div className="mx-auto bg-[#f1f8ff] p-4 rounded-md md:min-h-[498px] flex flex-col justify-between">
<div className="w-full flex flex-col justify-center py-4 gap-2">
<p className="w-full text-slate-900 tracking-wide my-1">
Interested in the task?
</p>
<hr />
<button
className="bg-[#57cd89] text-center text-lg font-semibold text-white py-2 px-4 rounded-md inline-flex flex-col items-center justify-center"
className="bg-[#57cd89] text-center text-lg font-semibold text-white py-2 px-4 rounded-md inline-flex sm:flex-col flex-row sm:gap-0 gap-1 items-center justify-center"
name="market-interest"
onClick={marketCalls}
onClick={ManageInterest}
>
{" "}
<span>Send</span>
@@ -248,9 +277,18 @@ const MarketPopUp = ({ details, onClose, situation, marketInt }) => {
Interest: <b className="ml-1">{details.interest_count}</b>
</p>
<hr />
<p className="my-1">Expire: {expireIntDate}</p>
<p className="my-1">
Expire: {details.expire}
</p>
</div>
</div>
<button
className="self-end w-[150px] mt-2 h-[52px] rounded-md text-base bg-transparent border border-red-500 text-red-500 mx-auto"
name="cancel"
onClick={onClose}
>
Cancel
</button>
</div>
{/* END OF ACTION SECTION */}
</div>
+9 -8
View File
@@ -1,22 +1,23 @@
import React from "react";
// import products from "../../data/marketplace_data.json";
//import CreateNft from "../Home/CreateNft";
import Layout from "../Partials/Layout";
import MainSection from "./MainSection";
import CommonHead from "../UserHeader/CommonHead";
import { useSelector } from "react-redux";
export default function MarketPlace({commonHeadData}) {
export default function MarketPlace({ commonHeadData }) {
let { jobLists } = useSelector((state) => state.jobLists);
const marketData = jobLists?.result_list;
const categories = jobLists?.categories;
// const marketProduct = products.data;
return (
<>
<Layout>
<CommonHead commonHeadData={commonHeadData} />
<MainSection marketPlaceProduct={marketData} className="mb-10" />
<CommonHead commonHeadData={commonHeadData} />
<MainSection
marketPlaceProduct={marketData}
categories={categories}
className="mb-10"
/>
</Layout>
</>
);
}
}
+31 -22
View File
@@ -248,9 +248,11 @@ function ActiveJobs(props) {
}
}, [passDue]);
console.log("AC JOBS >>", props);
return (
<Layout>
<div className="py-[20px] bg-white px-4 rounded-2xl shadow-md md:flex justify-between items-start gap-16">
<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">
{/* job title */}
<div className="w-full md:w-8/12">
<div className="w-full flex justify-start space-x-3 items-start">
@@ -278,12 +280,14 @@ function ActiveJobs(props) {
<div className="w-full my-2">
<p className="w-full text-base text-right text-sky-blue">
{props.details.job_to && props.details.job_to}
{props?.details && props.details.job_to}
</p>
<div className="text-base text-slate-700 dark:text-black tracking-wide">
<p className="font-semibold text-black">Description: </p>
<div className="text-base text-slate-700 dark:text-white tracking-wide">
<p className="font-semibold text-black dark:text-white">
Description:{" "}
</p>
<p className="p-2 border border-sky-blue">
{props.details?.description && props.details.description}
{props?.details && props.details.description}
</p>
</div>
</div>
@@ -295,13 +299,12 @@ function ActiveJobs(props) {
<p className="text-base text-sky-blue">Delivery Detail</p>
{passDue ? (
<div className="my-1">
<p className="text-base text-slate-700 dark:text-black">
<p className="text-base text-slate-700">
<span className="font-semibold">Due: </span>
{props.details?.delivery_date &&
props.details.delivery_date.split(" ")[0]}
{props?.details && props.details.delivery_date.split(" ")[0]}
</p>
<p className="py-2 text-base text-slate-700 dark:text-black">
{props.details?.delivery_date &&
<p className="py-2 text-base text-slate-700">
{props?.delivery_date &&
props.details.delivery_date.split(" ")[1]}
</p>
</div>
@@ -309,10 +312,12 @@ function ActiveJobs(props) {
<div className="my-1 flex items-start gap-3">
<p className="font-semibold">Due: </p>
<div className="flex flex-col justify-between">
<p className="text-base text-slate-700 dark:text-black tracking-wide">
<CountDown lastDate={props.details.delivery_date} />
<p className="text-base text-slate-700 tracking-wide">
<CountDown
lastDate={props?.details && props.details.delivery_date}
/>
</p>
<div className="text-base text-slate-700 dark:text-black tracking-wide flex gap-[5px]">
<div className="text-base text-slate-700 tracking-wide flex gap-[5px]">
<span>Hrs</span>
<span>Min</span>
<span>Sec</span>
@@ -321,15 +326,19 @@ function ActiveJobs(props) {
</div>
)}
<div className="my-1 text-base text-slate-700 dark:text-black tracking-wide flex items-center gap-3">
<span className="font-semibold text-black">Duration: </span>
<div className="my-1 text-base text-slate-700 tracking-wide flex items-center gap-3">
<span className="font-semibold text-black dark:text-white">
Duration:{" "}
</span>
<span className="">
{props.details?.timeline_days && props.details.timeline_days}{" "}
day(s)
</span>
</div>
<div className="my-1 text-base text-slate-700 dark:text-black tracking-wide flex items-center gap-3">
<span className="font-semibold text-black">No: </span>
<div className="my-1 text-base text-slate-700 tracking-wide flex items-center gap-3">
<span className="font-semibold text-black dark:text-white">
No:{" "}
</span>
<span className="">
{props.details?.contract && props.details.contract}
</span>
@@ -338,7 +347,7 @@ function ActiveJobs(props) {
{/* end of job details */}
</div>
<div className="my-4 py-[20px] bg-white px-4 rounded-2xl shadow-md lg:flex justify-between items-start space-y-4 lg:space-x-4 lg:space-y-0">
<div className="my-4 py-[20px] bg-white dark:bg-black px-4 rounded-2xl shadow-md lg:flex justify-between items-start space-y-4 lg:space-x-4 lg:space-y-0">
<div className="w-full lg:w-1/2">
<div className="">
<h1 className="text-lg font-bold text-dark-gray dark:text-white tracking-wide">
@@ -358,7 +367,7 @@ function ActiveJobs(props) {
<button
name="message"
onClick={(e) => setTab(e.target.name)}
className={`p-2 text-lg font-bold text-slate-600 dark:text-black border ${
className={`p-2 text-lg font-bold text-slate-600 dark:text-white border ${
tab == "message" ? "border-sky-blue" : "border-slate-300"
} tracking-wide transition duration-200`}
>
@@ -367,7 +376,7 @@ function ActiveJobs(props) {
<button
name="files"
onClick={(e) => setTab(e.target.name)}
className={`p-2 text-lg font-bold text-slate-600 dark:text-black border ${
className={`p-2 text-lg font-bold text-slate-600 dark:text-white border ${
tab == "files" ? "border-sky-blue" : "border-slate-300"
} tracking-wide transition duration-200`}
>
@@ -376,7 +385,7 @@ function ActiveJobs(props) {
</div>
{tab == "message" ? (
<textarea
className="p-4 w-full h-[300px] text-base text-slate-600 border border-slate-300 outline-none"
className="p-4 w-full h-[300px] text-base text-slate-600 dark:text-white bg-white dark:bg-black border border-slate-300 outline-none"
// rows="10"
style={{ resize: "none" }}
name="message"
@@ -498,7 +507,7 @@ function ActiveJobs(props) {
{/* MESSAGE SECTION */}
<div className="w-full lg:w-1/2">
<div className="flex justify-between items-center gap-5">
<p className="text-lg font-bold text-dark-gray dark:text-black tracking-wide">
<p className="text-lg font-bold text-dark-gray dark:text-white tracking-wide">
Message
</p>
<button
@@ -2,9 +2,9 @@ import React from 'react'
function CurrentJobAction() {
return (
<div className='job-action'>
<div className='job-action bg-white dark:bg-black'>
<p className="my-3 py-1 text-base active-owner">
<table className="w-full text-sm text-left text-gray-500 dark:text-gray-400">
<table className="w-full text-sm text-left text-gray-500">
<tbody>
<tr>
<td>
@@ -60,9 +60,9 @@ function CurrentTaskAction({jobDetails}) {
}
return (
<div className='job-action'>
<div className='job-action bg-white dark:bg-black'>
<table className="w-full text-sm text-left text-gray-500 dark:text-gray-400 active-worker">
<table className="w-full text-sm text-left text-gray-500 active-worker">
<tbody>
<tr>
<td>
@@ -79,7 +79,7 @@ function CurrentTaskAction({jobDetails}) {
<tr>
<td>
<div className="flex justify-center items-center">
<button onClick={popUpHandler} type="button" className="w-120 h-11 flex justify-center items-center btn-gradient text-base rounded-full text-white">
<button onClick={popUpHandler} type="button" className="w-[150px] h-11 flex justify-center items-center btn-gradient text-base rounded-full text-white">
Send of Review
</button>
</div>
@@ -10,8 +10,8 @@ import ReviewJobAction from './ReviewJobAction'
import ReviewTaskAction from './ReviewTaskAction'
function IndexJobActions({details}) { // FUNCTION TO RENDER SPECIFIC JOB ACTION DEPENDING ON OWNER STATUS & STATUS DESCRIPTION
let owner = details.owner_status
let description = details.status_description
let owner = details?.owner_status
let description = details?.status_description
switch(owner) {
case 'OWNER':
return (()=>{
@@ -101,9 +101,9 @@ function PastDueJobAction({jobDetails}) {
}
return (
<div className='job-action'>
<div className='job-action bg-white dark:bg-black'>
<table className="w-full text-sm text-left text-gray-500 dark:text-gray-400 owner-pastdue">
<table className="w-full text-sm text-left text-gray-500 owner-pastdue">
<tbody>
<tr>
<td>
@@ -119,7 +119,7 @@ function PastDueJobAction({jobDetails}) {
<tr>
<td>
<div className="flex justify-center items-center">
<button type="button" onClick={popUpHandler} className="w-120 h-11 flex justify-center items-center btn-gradient text-base rounded-full text-white">
<button type="button" onClick={popUpHandler} className="w-[150px] h-11 flex justify-center items-center btn-gradient text-base rounded-full text-white">
Cancel or Extend Timeline
</button>
</div>
@@ -2,9 +2,9 @@ import React from 'react'
function PastDueTaskAction() {
return (
<div className='job-action'>
<div className='job-action bg-white dark:bg-black'>
<table className="w-full text-sm text-left text-gray-500 dark:text-gray-400 worker-pastdue">
<table className="w-full text-sm text-left text-gray-500 worker-pastdue">
<tbody>
<tr>
<td>
@@ -20,7 +20,7 @@ function PastDueTaskAction() {
<tr>
<td>
<div className="flex justify-center items-center">
<button type="button" className="w-120 h-11 flex justify-center items-center btn-gradient text-base rounded-full text-white">
<button type="button" className="w-[150px] h-11 flex justify-center items-center btn-gradient text-base rounded-full text-white">
Request Extension
</button>
</div>
@@ -90,9 +90,9 @@ function ReviewJobAction({jobDetails}) {
})
}
return (
<div className='job-action'>
<div className='job-action bg-white dark:bg-black'>
<div className="my-3 py-1 text-base">
<table className="w-full text-sm text-left text-gray-500 dark:text-gray-400 review-owner">
<table className="w-full text-sm text-left text-gray-500 review-owner">
<tbody>
<tr>
<td>
@@ -108,7 +108,7 @@ function ReviewJobAction({jobDetails}) {
<tr>
<td>
<div className="flex justify-center items-center">
<button type="button" onClick={popUpHandler} className="w-120 h-11 flex justify-center items-center btn-gradient text-base rounded-full text-white">
<button type="button" onClick={popUpHandler} className="w-[150px] h-11 flex justify-center items-center btn-gradient text-base rounded-full text-white">
Reject or Accept
</button>
</div>
@@ -2,8 +2,8 @@ import React from 'react'
function ReviewTaskAction() {
return (
<div className='job-action'>
<p className="my-3 py-1 text-base">
<div className='job-action bg-white dark:bg-black'>
<p className="my-3 py-1 text-base text-dark-gray dark:text-white">
Waiting for the completion message from the client before you can approve. Worker True & Review Job
</p>
</div>
@@ -68,7 +68,7 @@ export default function MyActiveJobTable({ MyJobList, className }) {
{value.title}
</h1>
<div>{value.description}</div>
<span className="text-sm text-thin-light-gray">
<span className="text-sm text-thin-light-gray flex flext-start gap-1">
Price:{" "}
<span className="text-purple">
{thePrice}
+1
View File
@@ -10,6 +10,7 @@ export default function MyActiveJobs(props) {
setValue(value);
};
console.log("AMEYE LOC1", props.MyJobList);
return (
<Layout>
<CommonHead commonHeadData={props.commonHeadData} />
+37 -19
View File
@@ -1,10 +1,17 @@
import { useMemo, useState } from "react";
import { toast } from "react-toastify";
import usersService from "../../services/UsersService";
import { useDispatch } from "react-redux";
import { tableReload } from "../../store/TableReloads";
const CouponPopup = ({ popUpHandler, data }) => {
const [loader, setLoader] = useState(false);
const apiCall = useMemo(() => new usersService(), []);
const [statusMsg, setStatusMsg] = useState({
success: "",
error: "",
});
const dispatch = useDispatch();
const redeemCouponHandler = async () => {
setLoader(true);
@@ -14,28 +21,34 @@ const CouponPopup = ({ popUpHandler, data }) => {
const reqData = { code_id: Number(coupon_id), code };
const res = await apiCall.getCouponRedeem(reqData);
if (res.statusText === "OK") {
toast.success("Great news! Your coupon has been redeemed.", {
autoClose: 3000,
hideProgressBar: true,
});
}
if (res.data?.internal_return < 0)
setStatusMsg({ error: "An error occurred" });
else setStatusMsg({ success: res.data?.status_text });
dispatch(tableReload({ type: "COUPONTABLE" }));
setTimeout(() => {
popUpHandler();
setLoader(false);
}, 3000);
throw new Response(res);
}, 10000);
} catch (error) {
// error &&
// toast.warn("An error occurred while processing your coupon.", {
// autoClose: 3000,
// hideProgressBar: true,
// });
setLoader(false);
console.log(error);
return;
// throw new Error(error);
if (error?.status !== 200)
setStatusMsg({
error: {
status: true,
msg:
error?.statusText !== ""
? error?.statusText
: "An error occurred",
},
});
throw new Error(error);
} finally {
setStatusMsg({
success: "",
error: "",
});
}
};
@@ -77,9 +90,8 @@ const CouponPopup = ({ popUpHandler, data }) => {
Amount:
</label>
</div>
<div className=" flex-[0.7] max-w-[150px]">{`${data?.amount} Naira`}</div>
<div className=" flex-[0.7] max-w-[150px]">{data?.thePrice}</div>
</div>
<div className="signin-area w-full">
<div className="flex justify-end">
<button
@@ -96,6 +108,12 @@ const CouponPopup = ({ popUpHandler, data }) => {
)}
</button>
</div>
{statusMsg.success && (
<p className="text-sm text-green-500 italic">{statusMsg.success}</p>
)}
{statusMsg.error && (
<p className="text-sm text-red-500 italic">{statusMsg.error}</p>
)}
</div>
</form>
<div className="signin-area w-full px-5 py-4">
+11 -4
View File
@@ -4,6 +4,7 @@ import { handlePagingFunc } from "../Pagination/HandlePagination";
import ModalCom from "../Helpers/ModalCom";
import CouponPopup from "./CouponPopup";
import { useNavigate } from "react-router-dom";
import { PriceFormatter } from "../Helpers/PriceFormatter";
function CouponTable({ coupon }) {
const [currentPage, setCurrentPage] = useState(0);
@@ -38,12 +39,18 @@ function CouponTable({ coupon }) {
</thead>
{coupon.data.length ? (
<tbody>
{currentCoupon.map((item, index) => (
{currentCoupon.map((item, index) => {
let thePrice = PriceFormatter(
item?.amount * 0.01,
item?.currency_code,
item?.currency
);
return(
<tr key={index} className="text-slate-500">
<td className="p-2 cursor-default">
{item.added} <br /> {item.code}
</td>
<td className="p-2 text-center cursor-default">{`${item.amount} Naira`}</td>
<td className="p-2 text-center cursor-default">{thePrice}</td>
<td className="p-2 h-20 flex items-center justify-end">
<button
type="button"
@@ -51,7 +58,7 @@ function CouponTable({ coupon }) {
onClick={() => {
setCouponPopup((prev) => ({
state: !prev.state,
data: item,
data: {...item, thePrice},
}));
}}
>
@@ -59,7 +66,7 @@ function CouponTable({ coupon }) {
</button>
</td>
</tr>
))}
)})}
</tbody>
) : coupon.error ? (
<tbody>
+8 -6
View File
@@ -1,11 +1,13 @@
import React, { useEffect, useState } from "react";
import React, { useCallback, useEffect, useMemo, useState } from "react";
import Layout from "../Partials/Layout";
import LoadingSpinner from "../Spinners/LoadingSpinner";
import CouponTable from "./CouponTable";
import usersService from "../../services/UsersService";
import { useSelector } from "react-redux";
export default function MyCoupons() {
const apiCall = new usersService();
const apiCall = useMemo(() => new usersService(), []);
const {couponTable} = useSelector(state => state.tableReload)
let [couponHistory, setCouponHistory] = useState({
// FOR COUPON HISTORY
loading: true,
@@ -14,7 +16,7 @@ export default function MyCoupons() {
});
//FUNCTION TO GET COUPON HISTORY
const getCouponHistory = () => {
const getCouponHistory = useCallback(() => {
apiCall
.getCouponPending()
.then((res) => {
@@ -32,11 +34,11 @@ export default function MyCoupons() {
.catch((error) => {
setCouponHistory((prev) => ({ ...prev, loading: false, error: true }));
});
};
}, [apiCall]);
useEffect(() => {
getCouponHistory();
}, []);
}, [couponTable]);
return (
<>
@@ -59,7 +61,7 @@ export default function MyCoupons() {
<LoadingSpinner size="16" color="sky-blue" />
</div>
) : (
<CouponTable coupon={couponHistory} />
<CouponTable coupon={couponHistory} />
)}
</div>
</div>
+151 -118
View File
@@ -1,18 +1,37 @@
import React, { useCallback, useEffect, useMemo, useState } from "react";
import { useCallback, useEffect, useMemo, useState } from "react";
import dataImage2 from "../../assets/images/data-table-user-2.png";
import SelectBox from "../Helpers/SelectBox";
import DeleteJobPopout from "../jobPopout/DeleteJobPopout";
import JobListPopout from "../jobPopout/JobListPopout";
import LoadingSpinner from "../Spinners/LoadingSpinner";
import { useSelector } from "react-redux";
import usersService from "../../services/UsersService";
import { handlePagingFunc } from "../Pagination/HandlePagination";
import PaginatedList from "../Pagination/PaginatedList";
import EditJobPopOut from "../jobPopout/EditJobPopout";
import { PriceFormatter } from "../Helpers/PriceFormatter";
import EditIcon from '../../assets/images/icon-edit.svg'
import DeleteIcon from '../../assets/images/icon-delete.svg'
export default function MyJobTable({ MyJobList, reloadJobList, className }) {
// Getting the categories
const currentJobCart = MyJobList?.data?.categories;
// DropDown Box
const filterCategories = { All: "All Categories", ...currentJobCart };
const [selectedCategory, setCategory] = useState(
Object.keys(filterCategories)[0]
);
let [jobPopout, setJobPopout] = useState({ show: false, data: {} }); // STATE TO HOLD THE VALUE OF THE ALERT DETAILS AND DETERMINE WHEN TO SHOW
let [deleteJobPopout, setDeleteJobPopout] = useState({
show: false,
data: {},
}); // STATE TO HOLD THE VALUE OF THE ITEM DETAILS TO DELETE AND DETERMINE WHEN TO SHOW
const [editJob, setEditJob] = useState({ show: false, data: {} });
const [myCountry, setCountries] = useState("");
const {
userDetails: { country },
@@ -44,22 +63,25 @@ export default function MyJobTable({ MyJobList, reloadJobList, className }) {
getCountryList();
}, [getCountryList]);
const filterCategories = ["All Categories", "Explore", "Featured"];
const [selectedCategory, setCategory] = useState(filterCategories[0]);
let [jobPopout, setJobPopout] = useState({ show: false, data: {} }); // STATE TO HOLD THE VALUE OF THE ALERT DETAILS AND DETERMINE WHEN TO SHOW
let [deleteJobPopout, setDeleteJobPopout] = useState({
show: false,
data: {},
}); // STATE TO HOLD THE VALUE OF THE ITEM DETAILS TO DELETE AND DETERMINE WHEN TO SHOW
const [editJob, setEditJob] = useState({ show: false, data: {} });
// Handle Pagination
const [currentPage, setCurrentPage] = useState(0);
const indexOfFirstItem = Number(currentPage);
const indexOfLastItem =
Number(indexOfFirstItem) + Number(process.env.REACT_APP_ITEM_PER_PAGE);
const currentJobList = MyJobList?.data?.result_list?.slice(
// Handle Filter Job List
const filterJobList = () => {
if (selectedCategory === "All") return MyJobList?.data?.result_list;
else
return MyJobList?.data?.result_list?.filter((item) =>
item.category.includes(selectedCategory)
);
};
const currentJobList = filterJobList();
// Handling Filter Pagination
const filteredCurrentJobList = currentJobList?.slice(
indexOfFirstItem,
indexOfLastItem
);
@@ -68,6 +90,97 @@ export default function MyJobTable({ MyJobList, reloadJobList, className }) {
handlePagingFunc(e, setCurrentPage);
};
// Handles the category selection
const handleSetCategory = (value) => {
setCurrentPage(0);
for (let i in filterCategories) {
if (filterCategories[i] == value) {
setCategory(i);
}
}
};
const JobListItem = ({ value, index }) => {
let thePrice = PriceFormatter(
value?.price * 0.01,
value?.currency_code,
value?.currency
);
return (
<tr
key={index}
className="bg-white dark:bg-dark-white border-b dark:border-[#5356fb29] hover:bg-gray-50"
>
<td className="py-9">
<div className="sm:flex sm:space-x-2 sm:justify-between sm:items-center job-items">
<div className="flex space-x-2 items-center job-items w-full">
<div className="w-full h-[60px] rounded-full overflow-hidden flex justify-center items-center flex-[0.1] min-w-[60px] max-w-[60px]">
<img src={dataImage2} alt="data" className="w-full h-full" />
</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>
<span className="text-sm text-thin-light-gray">
Duration:{" "}
<span className="text-purple">
{" "}
{value.timeline_days} day(s)
</span>
</span>
</div>
</div>
<div className="h-[33px] w-[150px] flex flex-nowrap items-center self-end">
<button
type="button"
className="p-1 border-2 border-red-400 rounded-md"
onClick={() => {
setDeleteJobPopout({
show: true,
data: { thePrice, ...value },
});
}}
>
<img className="w-[21px] h-[21px]" src={DeleteIcon} alt='delete-icon' />
</button>
<div className="mx-[4px] h-full w-[1px] bg-black dark:bg-dark-gray"></div>
<button
type="button"
className="p-1 border-2 border-sky-blue rounded-md flex items-center"
onClick={() => {
setEditJob({
show: true,
data: { thePrice, ...value },
});
}}
>
<img className="w-[21px] h-[21px]" src={EditIcon} alt='edit-icon' />
<span className="text-sm text-sky-blue">Edit</span>
</button>
</div>
</div>
</td>
<td className="text-right py-9 px-2">
<button
type="button"
onClick={() => {
setJobPopout({ show: true, data: { thePrice, ...value } });
}}
className="w-20 h-11 flex justify-center items-center btn-gradient text-base rounded-full text-white"
>
Manage
</button>
</td>
</tr>
);
};
return (
<div
className={`update-table w-full p-8 bg-white dark:bg-dark-white overflow-hidden rounded-2xl section-shadow min-h-[520px] ${
@@ -77,12 +190,12 @@ export default function MyJobTable({ MyJobList, reloadJobList, className }) {
<div className="header w-full flex justify-between items-center mb-5">
<div className="flex space-x-2 items-center">
<h1 className="text-xl font-bold text-dark-gray dark:text-white tracking-wide">
All Jobs
{filterCategories[selectedCategory]} Jobs
</h1>
</div>
</div>
<SelectBox
action={setCategory}
datas={filterCategories}
action={handleSetCategory}
datas={Object.values(filterCategories)}
className="Update-table-dropdown"
contentBodyClasses="w-auto min-w-max"
/>
@@ -93,108 +206,27 @@ export default function MyJobTable({ MyJobList, reloadJobList, className }) {
<div className="relative w-full overflow-x-auto sm:rounded-lg flex flex-col justify-between h-full">
<table className="table-auto min-w-full text-sm text-left text-gray-500 dark:text-gray-400">
<tbody>
<tr className="text-base text-thin-light-gray border-b default-border-b dark:border-[#5356fb29] ottom ">
<td className="py-1">.</td>
<td className="py-1 text-right">.</td>
</tr>
{selectedCategory === "All Categories" ? (
<>
{MyJobList &&
MyJobList?.data?.result_list &&
MyJobList.data?.result_list.length > 0 ?
currentJobList.map((value, index) => (
<tr
key={index}
className="bg-white dark:bg-dark-white border-b dark:border-[#5356fb29] hover:bg-gray-50"
>
<td className="py-9">
<div className="sm:flex sm:space-x-2 sm:justify-between sm:items-center job-items">
<div className="flex space-x-2 items-center job-items w-full">
<div className="w-full h-[60px] rounded-full overflow-hidden flex justify-center items-center flex-[0.1] min-w-[60px] max-w-[60px]">
<img
src={dataImage2}
alt="data"
className="w-full h-full"
/>
</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">
Price:{" "}
<span className="text-purple">
{value.price * 0.01}
</span>
</span>
<span className="text-sm text-thin-light-gray">
Duration:{" "}
<span className="text-purple">
{" "}
{value.timeline_days} day(s)
</span>
</span>
</div>
</div>
<div className="min-w-[110px] max-w-[110px] bg-yellow-300 mx-2 rounded-md flex flex-nowrap space-x-2 justify-center items-center self-end">
<button
type="button"
className="p-2 w-[60px] h-11"
onClick={() => {
setDeleteJobPopout({
show: true,
data: value,
});
}}
>
[Delete]
</button>
<span>|</span>
<button
type="button"
className="p-2 w-[40px] h-11"
onClick={() => {
setEditJob({
show: true,
data: value,
});
}}
>
Edit
</button>
</div>
</div>
</td>
<td className="text-right py-9 px-2">
<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"
>
Manage
</button>
</td>
</tr>
<>
{MyJobList &&
MyJobList?.data?.result_list &&
MyJobList.data?.result_list.length > 0 ? (
filteredCurrentJobList?.length ? (
filteredCurrentJobList.map((value, index) => (
<JobListItem index={index} key={index} value={value} />
))
:
) : (
<tr className="font-bold text-xl text-dark-gray dark:text-white whitespace-nowrap">
<td className="p-2">
No Jobs Avaliable!
No Jobs Available In This Category!
</td>
</tr>
}
</>
) : selectedCategory === "Explore" ? (
<></>
) : (
<></>
)}
)
) : (
<tr className="font-bold text-xl text-dark-gray dark:text-white whitespace-nowrap">
<td className="p-2">No Jobs Avaliable!</td>
</tr>
)}
</>
</tbody>
</table>
@@ -204,11 +236,11 @@ export default function MyJobTable({ MyJobList, reloadJobList, className }) {
prev={currentPage == 0 ? true : false}
next={
currentPage + Number(process.env.REACT_APP_ITEM_PER_PAGE) >=
MyJobList?.data?.result_list?.length
currentJobList?.length
? true
: false
}
data={MyJobList?.data?.result_list}
data={currentJobList}
start={indexOfFirstItem}
stop={indexOfLastItem}
/>
@@ -252,6 +284,7 @@ export default function MyJobTable({ MyJobList, reloadJobList, className }) {
}}
situation={editJob.show}
country={myCountry}
categories={currentJobCart}
/>
)}
</div>
+39 -27
View File
@@ -1,52 +1,64 @@
import React, { useState } from "react";
import { Link } from "react-router-dom";
import React, { useEffect, useState } from "react";
import { useLocation, useNavigate } from "react-router-dom";
import Layout from "../Partials/Layout";
import MyJobTable from "./MyJobTable";
import CommonHead from "../UserHeader/CommonHead";
import AddJobPage from "../../views/AddJobPage";
export default function MyJobs(props) {
const [selectTab, setValue] = useState("today");
const filterHandler = (value) => {
setValue(value);
let { state } = useLocation();
const navigate = useNavigate();
const [popUp, setPopUp] = useState(false);
const popUpHandler = () => {
setPopUp((prev) => !prev);
if (state?.popup) navigate("/", { replace: true })
else return
};
const categoryOptions = props.MyJobList?.data?.categories;
useEffect(() => {
if (!state?.popup) {
setPopUp(false);
} else {
setPopUp(true);
}
}, [state?.popup]);
return (
<Layout>
<CommonHead
commonHeadData={props.commonHeadData}
/>
<CommonHead commonHeadData={props.commonHeadData} />
<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 items-center mb-6">
<div className="mb-5 sm:mb-0">
<h1 className="text-26 font-bold flex items-center space-x-1 text-dark-gray dark:text-white gap-2">
<span
className={`${selectTab === "today" ? "block" : "hidden"}`}
>
My Jobs
</span>
<span>My Jobs</span>
<Link
to="/add-job"
<button
className="text-white btn-gradient text-lg tracking-wide px-5 py-2 rounded-full"
onClick={popUpHandler}
>
Add Job
</Link>
</button>
</h1>
</div>
<div className="slider-btns flex space-x-4">
<div
onClick={() => filterHandler("today")}
className="relative"
></div>
</div>
</div>
<MyJobTable
MyJobList={props.MyJobList}
/>
<MyJobTable MyJobList={props.MyJobList} />
</div>
</div>
{/* Add Job List Popout */}
{popUp && (
<AddJobPage
action={popUpHandler}
situation={popUp}
categories={categoryOptions}
/>
)}
{/* End of Add Job List Popout */}
</Layout>
);
}
@@ -67,7 +67,7 @@ export default function MyPendingJobTable({ MyJobList, className }) {
{value.title}
</h1>
<div>{value.description}</div>
<span className="text-sm text-thin-light-gray">
<span className="text-sm text-thin-light-gray flex items-start gap-1">
Price:{" "}
<span className="text-purple">
{thePrice}
+6 -6
View File
@@ -8,7 +8,7 @@ import { PriceFormatter } from "../Helpers/PriceFormatter";
const noTasksBg = require("../../assets/images/no-task-background.jpg");
export default function MyJobTable({ className, ActiveJobList }) {
export default function MyJobTable({ className, ActiveJobList, Account }) {
let navigate = useNavigate();
let { pathname } = useLocation();
@@ -30,7 +30,7 @@ export default function MyJobTable({ className, ActiveJobList }) {
const navigateMarket = () => {
setBtnLoader(true);
setTimeout(() => {
navigate("/market", { replace: true });
navigate(Account == "FULL" ? "/market" : "/", { replace: true });
setBtnLoader(false);
}, 2500);
};
@@ -60,6 +60,8 @@ export default function MyJobTable({ className, ActiveJobList }) {
<div className="h-auto w-full">
{ActiveJobList?.data?.length > 0 &&
currentTask?.map((task, idx) => {
// find due date
const dueDate = task?.delivery_date.split(" ")[0];
let thePrice = PriceFormatter(
task?.price * 0.01,
task?.currency_code,
@@ -86,7 +88,7 @@ export default function MyJobTable({ className, ActiveJobList }) {
<span className="text-base text-gray-600">
{task?.description}
</span>
<span className="text-sm text-thin-light-gray">
<span className="text-sm text-thin-light-gray flex flext-start gap-1">
Price:
<span className="text-purple ml-1">{thePrice}</span>
</span>
@@ -101,9 +103,7 @@ export default function MyJobTable({ className, ActiveJobList }) {
</span>
<span className="text-sm text-thin-light-gray">
Due Date:
<span className="text-purple ml-1">
{task?.delivery_date}
</span>
<span className="text-purple ml-1">{dueDate}</span>
</span>
<span className="text-sm text-thin-light-gray">
Confirmation:
+7 -3
View File
@@ -1,16 +1,20 @@
import React, { useState } from "react";
import { Link } from "react-router-dom";
import Layout from "../Partials/Layout";
import MyJobTable from "./MyJobTable";
import CommonHead from "../UserHeader/CommonHead";
import TopSellerTopBuyerSliderSection from "../Home/TopSellerTopBuyerSliderSection";
// import TopSellerTopBuyerSliderSection from "../Home/TopSellerTopBuyerSliderSection";
import MyOffersTable from "./MyOffersTable";
import { useSelector } from "react-redux";
export default function MyTasks({
MyActiveOffersList,
ActiveJobList,
commonHeadData,
}) {
const { userDetails: account_type } = useSelector(
(state) => state?.userDetails
);
const [selectTab, setValue] = useState("today");
const filterHandler = (value) => {
setValue(value);
@@ -45,7 +49,7 @@ export default function MyTasks({
className="mb-10"
/>
)}
<MyJobTable ActiveJobList={ActiveJobList} />
<MyJobTable ActiveJobList={ActiveJobList} Account={account_type} />
</div>
</div>
</Layout>
@@ -0,0 +1,152 @@
import { useState } from "react";
import dataImage2 from "../../assets/images/data-table-user-2.png";
import { handlePagingFunc } from "../Pagination/HandlePagination";
import PaginatedList from "../Pagination/PaginatedList";
import { PriceFormatter } from "../Helpers/PriceFormatter";
import MarketPopUp from "../MarketPlace/PopUp/MarketPopUp";
export default function MyWaitingJobTable({ MyJobList, className }) {
let [jobPopout, setJobPopout] = useState({ show: false, data: {} }); // STATE TO HOLD THE VALUE OF THE ALERT DETAILS AND DETERMINE WHEN TO SHOW
const [currentPage, setCurrentPage] = useState(0);
const indexOfFirstItem = Number(currentPage);
const indexOfLastItem =
Number(indexOfFirstItem) + Number(process.env.REACT_APP_ITEM_PER_PAGE);
const currentActiveJobList = MyJobList?.result_list?.slice(
indexOfFirstItem,
indexOfLastItem
);
const handlePagination = (e) => {
handlePagingFunc(e, setCurrentPage);
};
return (
<div
className={`update-table w-full p-8 bg-white dark:bg-dark-white overflow-hidden rounded-2xl section-shadow min-h-[520px] ${
className || ""
}`}
>
{MyJobList && MyJobList?.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>
{
<>
{MyJobList &&
MyJobList?.result_list &&
MyJobList.result_list.length > 0 ? (
currentActiveJobList.map((value, index) => {
// let deliveryDate = value?.expire?.split(" ")[0];
let thePrice = PriceFormatter(
value?.price * 0.01,
value?.currency_code,
value?.currency
);
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-full h-[60px] rounded-full overflow-hidden flex justify-center items-center flex-[0.1] max-w-[60px]">
<img
src={dataImage2}
alt="data"
className="w-full h-full"
/>
</div>
<div className="flex flex-col flex-[0.9]">
<h1 className="font-bold text-xl text-dark-gray dark:text-white">
{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">
{" "}
{value?.expire}
</span>
</span>
<span className="text-sm text-thin-light-gray">
Sent :{" "}
<span className="text-purple">
{" "}
{value?.sent}
</span>
</span>
</div>
</div>
</div>
</td>
<td className="text-right py-4 px-2">
<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 className="font-bold text-xl text-dark-gray dark:text-white whitespace-nowrap">
<td className="p-2">No Pending Task!</td>
</tr>
)}
</>
}
</tbody>
</table>
{/* PAGINATION BUTTON */}
<PaginatedList
onClick={handlePagination}
prev={currentPage == 0 ? true : false}
next={
currentPage + Number(process.env.REACT_APP_ITEM_PER_PAGE) >=
MyJobList?.result_list.length
? true
: false
}
data={MyJobList?.result_list}
start={indexOfFirstItem}
stop={indexOfLastItem}
/>
{/* END OF PAGINATION BUTTON */}
</div>
)}
{/* Active Job Popout */}
{jobPopout.show && (
<MarketPopUp
details={jobPopout.data}
onClose={() => {
setJobPopout({ show: false, data: {} });
}}
situation={jobPopout.show}
/>
)}
{/* End of Active Job Popout */}
</div>
);
}
+43
View File
@@ -0,0 +1,43 @@
import React, { useState } from "react";
import { Link } from "react-router-dom";
import Layout from "../Partials/Layout";
import CommonHead from "../UserHeader/CommonHead";
import MyWaitingJobTable from "./MyWaitingJobTable";
export default function MyWaitingJobs(props) {
const [selectTab, setValue] = useState("today");
const filterHandler = (value) => {
setValue(value);
};
console.log("AMEYE LOC1", props.MyJobList);
return (
<Layout>
<CommonHead
commonHeadData={props.commonHeadData}
/>
<div className="notification-page w-full mb-10">
<div className="notification-wrapper w-full">
{/* heading */}
<div className="sm:flex justify-between items-center mb-6">
<div className="mb-5 sm:mb-0">
<h1 className="text-26 font-bold text-dark-gray dark:text-white">
<span
className={`${selectTab === "today" ? "block" : "hidden"}`}
>
Waiting Job(s)
</span>
</h1>
</div>
<div className="slider-btns flex space-x-4">
<div
onClick={() => filterHandler("today")}
className="relative"
></div>
</div>
</div>
<MyWaitingJobTable MyJobList={props.MyJobList} />
</div>
</div>
</Layout>
);
}
+99 -47
View File
@@ -1,14 +1,18 @@
import React, {useState} from 'react'
import RecentActivityTable from './WalletComponent/RecentActivityTable'
import LoadingSpinner from '../Spinners/LoadingSpinner'
import { useNavigate } from 'react-router-dom'
import { useNavigate, useLocation } from 'react-router-dom'
import InputCom from '../Helpers/Inputs/InputCom'
import AddFundDollars from './AddFundDollars'
function AddFund({payment}) {
const navigate = useNavigate()
const {currency} = useLocation()?.state //GETS THE USER CURRENCY FOR ADD FUND
//STATE FOR CONTROLLED INPUT
let [input, setInput] = useState('0')
let [input, setInput] = useState('')
let [inputError, setInputError] = useState('')
@@ -22,65 +26,113 @@ function AddFund({payment}) {
setInputError('')
if(!input || input == '0'){
setInputError('Please Enter Amount')
return
return setTimeout(()=>{setInputError('')}, 5000)
}
if(isNaN(input)){
setInputError('Amount must be a Number')
return
return setTimeout(()=>{setInputError('')}, 5000)
}
const stateData = {amount: Number(input)}
const stateData = {amount: Number(input), currency: 'naira'}
navigate('confirm-add-fund', {state: stateData})
setInput('')
}
return (
<div className="content-wrapper w-full lg:flex xl:space-x-8 lg:space-x-4 bottomMargin">
<div className="lg:w-1/2 w-full mb-10 lg:mb-0">
<div className="add-fund w-full bg-white dark:bg-dark-white rounded-2xl shadow">
<h2 className='md:p-8 p-4 text-slate-900 dark:text-white text-xl lg:text-2xl font-medium'>Add Credit with Account Deposit</h2>
<hr />
<form className='md:p-8 p-4 add-fund-info'>
<div className='md:flex items-center'>
<label className='w-full md:w-2/4 text-slate-600 text-lg'>Amount(Naira) <span className='text-red-500'>*</span></label>
<input className='w-full md:w-2/4 p-3 text-lg text-right bg-slate-100 rounded-md outline-0 placeholder:text-slate-500 placeholder:text-lg'
value={input}
name='amount'
type="text"
placeholder='Amount'
required
onChange={handleChange}
/>
</div>
{inputError && <p className='text-base text-red-500'>{inputError}</p>}
</form>
<hr />
<div className='md:p-8 p-4 add-fund-btn flex justify-end items-center py-4'>
<button onClick={handleSubmit} className='text-lg text-white bg-sky-blue px-4 py-2 hover:opacity-90 rounded-md'>Continue</button>
</div>
</div>
<div className="add-fund py-4 my-2 w-full bg-white dark:bg-dark-white rounded-2xl shadow">
<h2 className='md:px-8 px-4 py-4 text-slate-900 dark:text-white text-xl lg:text-2xl font-medium'>Add Credit with Account Deposit</h2>
<hr />
<p className='md:px-8 px-4 py-2 text-slate-600 dark:text-white text-base lg:text-xl font-medium'>Transfer fund to WrenchBoard GTB Account 0250869867</p>
<p className='md:px-8 px-4 py-2 text-slate-500 dark:text-white text-sm lg:text-base font-medium'>
Make sure you add your account username in the notes part of the transfer for prompt processing. When the transfer is complete notify here <a className='text-sky-blue' href='#'>Contact us</a>
</p>
</div>
</div>
<div className="lg:w-1/2 w-full mb-10 lg:mb-0">
<div className="wallet w-full md:p-8 p-4 h-full max-h-[590px] bg-white dark:bg-dark-white overflow-y-auto rounded-2xl shadow">
<h2 className='text-gray-900 dark:text-white text-xl lg:text-2xl font-medium'>Recent Activity</h2>
{/* <p className='text-base text-gray-600 dark:text-white'>Activity Report</p> */}
{payment.loading ?
<LoadingSpinner size='16' color='sky-blue' />
:
<RecentActivityTable payment={payment}/>
<div>
{/* heading */}
<div className="sm:flex justify-between items-center mb-6">
<div className="w-full flex justify-start space-x-3 items-center">
<button
type="button"
className="min-w-[45px] h-auto text-[#374557] border border-sky-blue p-1 rounded-full"
onClick={() =>
navigate('/my-wallet', { replace: true })
}
>
<svg
xmlns="http://www.w3.org/2000/svg"
width="35"
height="35"
viewBox="0 0 24 24"
fill="skyblue"
>
<path d="M19 11H7.83l5.59-5.59L12 4l-8 8 8 8 1.41-1.41L7.83 13H19v-2z" />
</svg>
</button>
<h1 className="text-26 font-bold inline-flex gap-3 text-dark-gray dark:text-white items-center">
Add Credit
</h1>
</div>
<div className="slider-btns flex space-x-4">
<div
onClick={() => filterHandler("today")}
className="relative"
></div>
</div>
</div>
<div className="content-wrapper w-full lg:flex xl:space-x-8 lg:space-x-4 bottomMargin">
<div className="lg:w-2/2 w-full mb-10 lg:mb-0">
<div className="add-fund w-full bg-white dark:bg-dark-white rounded-2xl shadow">
{/*<h2 className='md:p-8 p-4 text-slate-900 dark:text-white text-xl lg:text-2xl font-medium'>Add Credit with Account Deposit</h2>*/}
{/*<hr />*/}
<form className='md:p-8 p-4 add-fund-info'>
<div className="field w-full mb-6">
<InputCom
fieldClass="px-6"
label={currency == 'US Dollars' ? "Amount (USD)" : "Amount (Naira)"}
type="text"
name="amount"
placeholder="0"
value={input}
inputHandler={handleChange}
/>
{inputError && <p className='text-base text-red-500'>{inputError}</p>}
</div>
</form>
<hr />
{/* SHOWS THIS IF USER CURRENCY IS DOLLARS */}
{currency == 'US Dollars' &&
<div className='w-full md:p-8 p-4 bg-white dark:bg-dark-white rounded-2xl shadow'>
<AddFundDollars setInputError={setInputError} input={input} setInput={setInput} />
</div>
}
{/* HIDES THIS BUTTON IF CURENCY IS NAIRA */}
{currency != 'US Dollars' &&
<div className='md:p-8 p-4 add-fund-btn flex justify-end items-center py-4'>
<button
onClick={handleSubmit}
type="button"
className="px-2 py-1 h-11 flex justify-center items-center btn-gradient text-base rounded-full text-white"
>
<span className="text-white">Continue</span>
</button>
</div>
}
</div>
</div>
</div>
{/* HIDES THIS SECTION IF CURENCY IS NAIRA */}
{currency != 'US Dollars' &&
<div className="content-wrapper w-full lg:flex xl:space-x-8 lg:space-x-4 bottomMargin">
<div className="lg:w-2/2 w-full mb-10 lg:mb-0">
<div className="wallet w-full md:p-8 p-4 h-full min-h-[590px] bg-white dark:bg-dark-white rounded-2xl shadow">
<h2 className='text-gray-900 dark:text-white text-xl lg:text-2xl font-medium'>Recent Activity</h2>
{/* <p className='text-base text-gray-600 dark:text-white'>Activity Report</p> */}
{payment.loading ?
<LoadingSpinner size='16' color='sky-blue' />
:
<RecentActivityTable payment={payment}/>
}
</div>
</div>
</div>
}
</div>
)
}
+480
View File
@@ -0,0 +1,480 @@
import React,{useEffect, useState} from 'react'
import { useNavigate } from 'react-router-dom'
import InputCom from '../Helpers/Inputs/InputCom';
import PaginatedList from '../Pagination/PaginatedList';
import { handlePagingFunc } from '../Pagination/HandlePagination';
import LoadingSpinner from '../Spinners/LoadingSpinner';
import usersService from '../../services/UsersService';
import { Form, Formik } from "formik";
import * as Yup from "yup";
const validationSchema = Yup.object().shape({
name: Yup.string()
.min(3, "Minimum 3 characters")
.max(50, "Maximum 50 characters")
.required("Name is required"),
cardNum: Yup.string()
.min(3, "Minimum 3 characters")
.max(25, "Maximum 25 characters")
.required("Card Number is required"),
code: Yup.string()
.min(3, "Minimum 3 characters")
.max(25, "Maximum 25 characters")
.required("Postal Code is required"),
state: Yup.string()
.min(3, "Minimum 3 characters")
.max(25, "Maximum 25 characters")
.required("State is required"),
address: Yup.string()
.min(3, "Minimum 3 characters")
.max(50, "Maximum 50 characters")
.required("Address is required"),
expirationYear: Yup.string()
.min(4, "Minimum 4 characters")
.max(4, "Maximum 4 characters")
.required("Expiration Year is required"),
expirationMonth: Yup.string()
.min(1, "Minimum 1 characters")
.max(2, "Maximum 2 characters")
.required("Expiration Month is required"),
cvv: Yup.string()
.min(3, "Minimum 3 characters")
.max(4, "Maximum 4 characters")
.required("CVV Year is required"),
});
const initialValues = {
name: '',
cardNum: '',
code: '',
state: '',
address: '',
expirationYear: '',
expirationMonth: '',
cvv: ''
};
function AddFundDollars(props) {
const navigate = useNavigate()
let apiCall = new usersService()
let [tab, setTab] = useState("previous"); //STATE FOR SWITCHING BETWEEN TABS
let [prevCardDetails, setPrevCardDetails] = useState(null) // STATE TO HOLD PREVIOUS CARD SELECTED
let [payListCard, setPayListCard] = useState({loading: true, data:[]}) //USER PREVIOUS CARDS
const [currentPage, setCurrentPage] = useState(0);
const indexOfFirstItem = Number(currentPage);
const indexOfLastItem = Number(indexOfFirstItem)+Number(process.env.REACT_APP_ITEM_PER_PAGE);
const currentPreviousCards = payListCard?.data?.slice(indexOfFirstItem, indexOfLastItem);
const handlePagination = (e) => {
handlePagingFunc(e,setCurrentPage)
}
// FUNCTION TO SUBMIT
const handleSubmit = (values, helpers) => {
props.setInputError('')
if(!props.input || props.input == '0'){
props.setInputError('Please Enter Amount')
return setTimeout(()=>{props.setInputError('')}, 5000)
}
if(isNaN(props.input)){
props.setInputError('Amount must be a Number')
return setTimeout(()=>{props.setInputError('')}, 5000)
}
if(tab == 'previous'){
const stateData = {amount: Number(props.input), currency: 'dollars'}
navigate('confirm-add-fund', {state: stateData}) // State will change later dummy for now
}
if(tab == 'new'){
const stateData = {amount: Number(props.input), currency: 'dollars', ...values}
navigate('confirm-add-fund', {state: stateData}) // State will change later dummy for now
}
props.setInput('')
}
useEffect(()=>{
apiCall.payListCard().then(res => {
setPayListCard({loading: false, data: res.data.result_list})
}).catch(err => {
console.log('PAYCARDLIST ERROR', err)
setPayListCard({loading: false, data: []})
})
}, [])
return (
<>
<h1 className='mb-2 text-xl font-bold text-dark-gray dark:text-white'>Payment Method</h1>
<div className="w-full">
{/* switch button */}
<div className="my-1 flex items-center border-b border-slate-300">
<button
name="previous"
onClick={(e) => setTab(e.target.name)}
className={`p-2 text-lg font-bold text-slate-600 dark:text-white border ${
tab == "previous" ? "border-sky-blue" : "border-slate-300"
} tracking-wide transition duration-200`}
>
Previous Cards
</button>
<button
name="new"
onClick={(e) => setTab(e.target.name)}
className={`p-2 text-lg font-bold text-slate-600 dark:text-white border ${
tab == "new" ? "border-sky-blue" : "border-slate-300"
} tracking-wide transition duration-200`}
>
Add New Card
</button>
</div>
{/* END OF switch button */}
{/* previous tab */}
{tab == 'previous' ?
<div className="p-4 previous-details w-full border min-h-[300px] flex flex-col justify-between items-center">
{ payListCard.loading ?
<LoadingSpinner size='10' color='sky-blue' />
:
payListCard?.data?.length ?
<table className="my-3 w-full">
<tbody>
{currentPreviousCards.map((item, index)=>(
<tr key={index} className={index != 0 && 'border-t-2'}>
<td>
<div className='my-2 flex items-center gap-5'>
<input type="radio" className='w-8 h-8' name='card' value='value' />
<div className='card-details'>
<h1 className='text-lg font-bold text-dark-gray dark:text-white tracking-wide'>{item.description} Card</h1>
<p className='text-base font-bold text-dark-gray dark:text-white tracking-wide'>Bank **************{item.digits}</p>
<div className='w-full sm:flex items-center gap-5'>
<p className='text-base font-bold text-dark-gray dark:text-white tracking-wide'>{item.added}</p>
<p className='text-sm font-bold text-green-700 dark:text-white tracking-wide'>Verified</p>
</div>
</div>
</div>
</td>
<td>
<button
// onClick={handleSubmit}
type="button"
className="px-2 py-1 h-11 flex justify-center items-center btn-gradient text-base rounded-full text-white"
>
<span className="text-white">Manage</span>
</button>
</td>
</tr>
))
}
</tbody>
</table>
:
<div className='w-full flex flex-col items-center'>
<p className='my-5 text-base font-bold text-dark-gray dark:text-white tracking-wide'>No Previous Card Found!</p>
<button
onClick={()=> setTab('new')}
type="button"
className="my-5 px-2 py-1 h-11 flex justify-center items-center btn-gradient text-base rounded-full text-white"
>
<span className="text-white">Add Card</span>
</button>
</div>
}
{/* PAGINATION BUTTON */}
<div className='w-full'>
<PaginatedList onClick={handlePagination} prev={currentPage == 0 ? true : false} next={currentPage+Number(process.env.REACT_APP_ITEM_PER_PAGE) >= payListCard?.data?.length ? true : false} data={payListCard?.data} start={indexOfFirstItem} stop={indexOfLastItem} />
</div>
{/* END OF PAGINATION BUTTON */}
</div>
:
<div className="new-details w-full min-h-[300px] border-t">
<div className="w-full flex flex-col justify-between">
<Formik
initialValues={initialValues}
validationSchema={validationSchema}
onSubmit={handleSubmit}
>
{(props) => {
return (
<Form>
<div className="flex flex-col-reverse sm:flex-row">
<div className="flex-1 sm:mr-10">
<div className="fields w-full">
{/* inputs starts here */}
{/* Name */}
<div className="field w-full my-6">
<InputCom
fieldClass="px-6"
spanTag='*'
label="Name on Card"
type="text"
name="name"
placeholder="DUMMY NAME"
value={props.values.name}
inputHandler={props.handleChange}
blurHandler={props.handleBlur}
/>
{props.errors.name && props.touched.name && (
<p className="text-sm text-red-500">
{props.errors.name}
</p>
)}
</div>
{/* CARD NUMBER */}
<div className="field w-full mb-6">
<InputCom
fieldClass="px-6"
spanTag='*'
iconName='master-card visa-card atm-card'
label="Card Number"
type="text"
name="cardNum"
placeholder="Enter Card Number"
value={props.values.cardNum}
inputHandler={props.handleChange}
blurHandler={props.handleBlur}
/>
{props.errors.cardNum && props.touched.cardNum && (
<p className="text-sm text-red-500">
{props.errors.cardNum}
</p>
)}
</div>
{/* EXPIRE YEAR, YEAR AND CVV */}
<div className="sm:grid gap-5 grid-cols-3 mb-6">
<div className="field w-full mb-6 xl:mb-0 col-span-1">
<div className="select-option">
<div className={`flex items-center justify-between mb-2.5`}>
<label
className="input-label text-[#181c32] dark:text-white text-[13.975px] leading-[20.9625px] font-semibold block"
htmlFor='expiration'
>Expiration Month <span className="text-red-700 text-sm tracking-wide">*</span></label>
</div>
<div
className={`input-wrapper border border-[#f5f8fa] dark:border-[#5e6278] w-full rounded-full h-[42px] overflow-hidden relative font-medium leading-6 bg-clip-padding text-[#5e6278] dark:text-gray-100 bg-[#f5f8fa] dark:bg-[#5e6278] text-base`}
>
<select
className={`input-field placeholder:text-base text-dark-gray w-full h-full tracking-wide dark:bg-[#11131F] bg-[#f5f8fa] focus:ring-0 focus:outline-none`}
value={props.values.expirationMonth}
onChange={props.handleChange}
onBlur={props.handleBlur}
name='expirationMonth'
>
<option value=''>Select...</option>
{expireMonth?.length &&
expireMonth.map((item, index) => (
<option key={index} value={item.value}>{item.name}</option>
))
}
</select>
</div>
</div>
{props.errors.expirationMonth && props.touched.expirationMonth && (
<p className="text-sm text-red-500">
{props.errors.expirationMonth}
</p>
)}
</div>
<div className="field w-full mb-6 xl:mb-0 col-span-1">
<div className="select-option">
<div className={`flex items-center justify-between mb-2.5`}>
<label
className="input-label text-[#181c32] dark:text-white text-[13.975px] leading-[20.9625px] font-semibold block"
htmlFor='expiration'
>Expiration Year <span className="text-red-700 text-sm tracking-wide">*</span>
</label>
</div>
<div
className={`input-wrapper border border-[#f5f8fa] dark:border-[#5e6278] w-full rounded-full h-[42px] overflow-hidden relative font-medium leading-6 bg-clip-padding text-[#5e6278] dark:text-gray-100 bg-[#f5f8fa] dark:bg-[#5e6278] text-base`}
>
<select
className={`input-field placeholder:text-base text-dark-gray w-full h-full tracking-wide dark:bg-[#11131F] bg-[#f5f8fa] focus:ring-0 focus:outline-none`}
value={props.values.expirationYear}
onChange={props.handleChange}
onBlur={props.handleBlur}
name='expirationYear'
>
<option value=''>Select...</option>
{expireYear?.length &&
expireYear.map((item, index) => (
<option key={index} value={item}>{item}</option>
))
}
</select>
</div>
</div>
{props.errors.expirationYear && props.touched.expirationYear && (
<p className="text-sm text-red-500">
{props.errors.expirationYear}
</p>
)}
</div>
<div className="field w-full col-span-1">
<InputCom
fieldClass="px-6"
spanTag='*'
iconName='atm-card'
label="CVV"
type="text"
name="cvv"
placeholder="CVV"
value={props.values.cvv}
inputHandler={props.handleChange}
blurHandler={props.handleBlur}
/>
{props.errors.cvv && props.touched.cvv && (
<p className="text-sm text-red-500">
{props.errors.cvv}
</p>
)}
</div>
</div>
{/* Address */}
<div className="field w-full my-6">
<InputCom
fieldClass="px-6"
spanTag='*'
label="Billing Address"
type="text"
name="address"
placeholder="Billing Address"
value={props.values.Address}
inputHandler={props.handleChange}
blurHandler={props.handleBlur}
/>
{props.errors.address && props.touched.address && (
<p className="text-sm text-red-500">
{props.errors.address}
</p>
)}
</div>
{/* postal code and state */}
<div className="sm:grid gap-5 grid-cols-3 mb-6">
<div className="field w-full mb-6 xl:mb-0 col-span-1">
<InputCom
fieldClass="px-6"
spanTag='*'
label="Postal Code"
type="text"
name="code"
placeholder="Postal Code"
value={props.values.code}
inputHandler={props.handleChange}
blurHandler={props.handleBlur}
/>
{props.errors.code && props.touched.code && (
<p className="text-sm text-red-500">
{props.errors.code}
</p>
)}
</div>
<div className="field w-full col-span-1">
<InputCom
fieldClass="px-6"
spanTag='*'
label="State"
type="text"
name="state"
placeholder="State"
value={props.values.state}
inputHandler={props.handleChange}
blurHandler={props.handleBlur}
/>
{props.errors.state && props.touched.state && (
<p className="text-sm text-red-500">
{props.errors.state}
</p>
)}
</div>
</div>
</div>
</div>
</div>
{/* <div className="w-full">
{requestStatus.message != "" && (
<p
className={`text-center text-base ${
requestStatus.status ? "text-green-800" : "text-red-600"
}`}
>
{requestStatus.message}
</p>
)}
<div className="w-full h-[120px] border-t border-light-purple dark:border-[#5356fb29] flex justify-end items-center">
<div className="flex items-center space-x-4 mr-9">
<Link
to="/"
className="text-18 text-light-red tracking-wide "
>
<span className="border-b dark:border-[#5356fb29] border-light-red">
{" "}
Cancel
</span>
</Link>
{requestStatus.loading ? (
<LoadingSpinner size="8" color="sky-blue" />
) : (
<button
type="submit"
className="w-[152px] h-[46px] flex justify-center items-center btn-gradient text-base rounded-full text-white"
>
Update Profile
</button>
)}
</div>
</div>
</div> */}
<div className='md:p-8 p-4 add-fund-btn flex justify-end items-center py-4'>
<button
type="submit"
className="px-2 py-1 h-11 flex justify-center items-center btn-gradient text-base rounded-full text-white"
>
<span className="text-white">Continue</span>
</button>
</div>
</Form>
);
}}
</Formik>
</div>
</div>
}
</div>
{ tab == 'previous' &&
<div className='md:p-8 p-4 add-fund-btn flex justify-end items-center py-4'>
<button
onClick={handleSubmit}
type="button"
className="px-2 py-1 h-11 flex justify-center items-center btn-gradient text-base rounded-full text-white"
>
<span className="text-white">Continue</span>
</button>
</div>
}
</>
)
}
export default AddFundDollars
// FORMS ARRAY OF EXPIRATION YEAR FOR CARD
const expireYear = []
let currentYear = new Date().getFullYear()
for(let i=0; i<=6; i++){
expireYear.push(currentYear + i)
}
// FORMS ARRAY OF EXPIRATION MONTH FOR CARD
let month = ['January', 'February', 'March', 'April', 'May', 'June', 'July', 'August', 'September', 'October', 'November', 'December']
const expireMonth = []
for(let i=0; i<month.length; i++){
expireMonth.push({name:month[i], value:i+1})
}
+32 -24
View File
@@ -4,6 +4,7 @@ import RecentActivityTable from './WalletComponent/RecentActivityTable'
import PurchasesTable from './WalletComponent/PurchasesTable'
import CouponTable from './WalletComponent/CouponTable'
import LoadingSpinner from '../Spinners/LoadingSpinner'
import { PriceFormatter } from '../Helpers/PriceFormatter'
function Balance({wallet, coupon}) {
return (
@@ -33,32 +34,39 @@ function Balance({wallet, coupon}) {
:
wallet.data.length ?
wallet.data.map((item, index)=> (
<div key={index} className="md:flex items-center flex-wrap my-3 border-t-2 border-slate-400">
<div className='text-slate-900 w-full md:w-1/2 flex space-x-3 items-start justify-start'>
<div className='balance-info'>
<p className='py-2'></p>
<span className='text-xl text-dark-gray dark:text-white'>{item.description}</span>
<p className='text-base text-slate-500'>{item.symbol}</p>
<div key={index} className="p-3 md:flex items-center flex-wrap my-3 border-t-2 border-slate-400 wallet-box">
<div className='text-slate-900 w-full md:w-1/2 flex space-x-3 items-start justify-start'>
<div className='balance-info bal-col1'>
<p className='py-2'></p>
<span className='text-xl text-dark-gray dark:text-white'><b>{item.description}</b></span>
<p className='text-base text-slate-500'>{item.symbol}</p>
</div>
<div className='balance-info'>
<p className='py-2'>Balance</p>
<span className='py-1 px-2 bg-green-100 text-green-500 rounded-lg flex flex-start gap-1'>{PriceFormatter(item.amount * 0.01, item.code)}</span>
</div>
<div className='balance-info'>
<p className='py-2'>Escrow</p>
<span className='py-1 px-2 bg-red-100 text-red-500 rounded-lg flex flex-start gap-1'>{PriceFormatter(item.escrow * 0.01, item.code)}</span>
</div>
</div>
<div className='balance-info'>
<p className='py-2'>Balance</p>
<span className='text-sm py-1 px-2 bg-green-100 text-green-500 rounded-lg'>{item.symbol}{(item.amount*0.01).toFixed(2)}</span>
</div>
<div className='balance-info'>
<p className='py-2'>Escrow</p>
<span className='text-sm py-1 px-2 bg-red-100 text-red-500 rounded-lg'>{item.symbol}{(item.escrow*0.01).toFixed(2)}</span>
</div>
</div>
<div className='w-full my-2 md:my-0 md:w-1/2 flex space-x-2 items-center justify-start md:justify-end'>
{
item.action_type != 'AC_AD_FD_ONLY' ?
<Link to='transfer-fund' className='lg:flex hidden user-balance cursor-pointer lg:w-[152px] w-[150px] h-[48px]
items-center rounded-full relative bg-purple pr-1.5 pl-4 lg:text-xl text-lg font-bold text-white'>Transfer</Link>:''
}
<Link to='add-fund' className='lg:flex hidden user-balance cursor-pointer lg:w-[152px] w-[150px] h-[48px]
items-center rounded-full relative bg-purple pr-1.5 pl-4 lg:text-xl text-lg font-bold text-white'>+Add Credit</Link>
</div>
<div className='w-full my-2 md:my-0 md:w-1/2 flex space-x-2 items-center justify-start md:justify-end'>
{
item.action_type != 'AC_AD_FD_ONLY' ?
<Link to='transfer-fund' className='px-2 py-1 flex items-center gap-2 user-balance cursor-pointer h-[48px] rounded-full relative bg-purple lg:text-xl text-lg font-bold text-white'>Transfer</Link>:''
}
<Link to='add-fund' state={{currency:item.description}} className='px-2 py-1 flex items-center gap-2 user-balance cursor-pointer h-[48px] rounded-full relative bg-green lg:text-xl text-lg font-bold text-white'>
<span className="">
<svg xmlns="http://www.w3.org/2000/svg" width="38"
height="38" viewBox="0 0 42 42" fill="none"><path
d="M21 0C16.8466 0 12.7865 1.23163 9.33303 3.53914C5.8796 5.84665 3.18798 9.1264 1.59854 12.9636C0.00909901 16.8009 -0.406771 21.0233 0.403518 25.0969C1.21381 29.1705 3.21386 32.9123 6.15077 35.8492C9.08767 38.7861 12.8295 40.7862 16.9031 41.5965C20.9767 42.4068 25.1991 41.9909 29.0364 40.4015C32.8736 38.812 36.1534 36.1204 38.4609 32.667C40.7684 29.2135 42 25.1534 42 21C41.994 15.4323 39.7796 10.0944 35.8426 6.15741C31.9056 2.22045 26.5677 0.00602189 21 0ZM28 22.75H22.75V28C22.75 28.4641 22.5656 28.9092 22.2374 29.2374C21.9093 29.5656 21.4641 29.75 21 29.75C20.5359 29.75 20.0908 29.5656 19.7626 29.2374C19.4344 28.9092 19.25 28.4641 19.25 28V22.75H14C13.5359 22.75 13.0908 22.5656 12.7626 22.2374C12.4344 21.9092 12.25 21.4641 12.25 21C12.25 20.5359 12.4344 20.0907 12.7626 19.7626C13.0908 19.4344 13.5359 19.25 14 19.25H19.25V14C19.25 13.5359 19.4344 13.0908 19.7626 12.7626C20.0908 12.4344 20.5359 12.25 21 12.25C21.4641 12.25 21.9093 12.4344 22.2374 12.7626C22.5656 13.0908 22.75 13.5359 22.75 14V19.25H28C28.4641 19.25 28.9093 19.4344 29.2374 19.7626C29.5656 20.0907 29.75 20.5359 29.75 21C29.75 21.4641 29.5656 21.9092 29.2374 22.2374C28.9093 22.5656 28.4641 22.75 28 22.75Z"
fill="white"></path>
</svg>
</span>
<span className='text-white'>Add Credit</span>
</Link>
</div>
</div>
))
:
+19 -10
View File
@@ -50,7 +50,6 @@ function ConfirmAddFund({ payment }) {
toast.success("Account Topup was sucessful");
setTimeout(() => {
navigate("/my-wallet", { replace: true });
window.location.reload(true);
}, 1000);
})
.catch((err) => {
@@ -102,11 +101,11 @@ function ConfirmAddFund({ payment }) {
}, []);
return (
<div className="content-wrapper w-full lg:flex xl:space-x-8 lg:space-x-4 bottomMargin">
<div className="content-wrapper w-full">
{pageLoading ? (
<LoadingSpinner size="8" color="sky-blue" />
) : (
<div className="lg:w-1/2 w-full mb-10 lg:mb-0">
<div className="w-full mb-10">
<div className="add-fund w-full bg-white dark:bg-dark-white rounded-2xl shadow">
<h2 className="md:p-8 p-4 text-slate-900 dark:text-white text-xl lg:text-2xl font-medium">
Confirm Add Fund To Account
@@ -116,7 +115,7 @@ function ConfirmAddFund({ payment }) {
<div className="field w-full mb-3">
<InputCom
fieldClass="px-6"
label="Amount (Naira):"
label={state.currency == 'naira' ? "Amount (Naira):" : "Amount (Dollars):"}
type="text"
name="amount"
value={state.amount || ""}
@@ -127,17 +126,27 @@ function ConfirmAddFund({ payment }) {
<hr />
<div className="md:p-8 p-4 add-fund-btn flex justify-end items-center py-4">
<FlutterWaveButton
{...fwConfig}
className="text-lg text-white bg-sky-blue px-4 py-2 hover:opacity-90 rounded-md"
/>
{
state.currency == 'naira' ?
<FlutterWaveButton
{...fwConfig}
className="px-2 py-1 h-11 flex justify-center items-center btn-gradient text-base rounded-full text-white"
/>
:
<button
className="px-2 py-1 h-11 flex justify-center items-center btn-gradient text-base rounded-full text-white"
onClick={()=>console.log('WORKING')}
>
Continue
</button>
}
</div>
</div>
</div>
)}
<div className="lg:w-1/2 w-full mb-10 lg:mb-0">
<div className="wallet w-full md:p-8 p-4 h-full max-h-[600px] bg-white dark:bg-dark-white overflow-y-auto rounded-2xl shadow">
<div className="w-full mb-10">
<div className="wallet w-full md:p-8 p-4 h-full min-h-[600px] bg-white dark:bg-dark-white rounded-2xl shadow">
<h2 className="text-gray-900 dark:text-white text-xl lg:text-2xl font-medium">
Recent Activity
</h2>
@@ -1,66 +1,85 @@
import React, {useState} from 'react'
import React, { useState } from "react";
import PaginatedList from '../../Pagination/PaginatedList'
import { handlePagingFunc } from '../../Pagination/HandlePagination';
import PaginatedList from "../../Pagination/PaginatedList";
import { handlePagingFunc } from "../../Pagination/HandlePagination";
import { PriceFormatter } from "../../Helpers/PriceFormatter";
function CouponTable({coupon}) {
function CouponTable({ coupon }) {
const [currentPage, setCurrentPage] = useState(0);
const indexOfFirstItem = Number(currentPage);
const indexOfLastItem =
Number(indexOfFirstItem) + Number(process.env.REACT_APP_ITEM_PER_PAGE);
const currentCoupon = coupon?.data?.slice(indexOfFirstItem, indexOfLastItem);
const [currentPage, setCurrentPage] = useState(0);
const indexOfFirstItem = Number(currentPage);
const indexOfLastItem = Number(indexOfFirstItem)+Number(process.env.REACT_APP_ITEM_PER_PAGE);
const currentCoupon = coupon?.data?.slice(indexOfFirstItem, indexOfLastItem);
const handlePagination = (e) => {
handlePagingFunc(e,setCurrentPage)
}
const handlePagination = (e) => {
handlePagingFunc(e, setCurrentPage);
};
return (
<div className='flex flex-col justify-between h-full'>
<table className="wallet-activity w-full table-auto border-collapse text-left">
<thead className='border-b-2'>
<tr className='text-slate-600'>
<div className="flex flex-col justify-between h-full">
<table className="wallet-activity w-full table-auto border-collapse text-left">
<thead className="border-b-2">
<tr className="text-slate-600">
<th className="p-2">Date</th>
<th className="p-2">Description</th>
<th className="p-2">Amount</th>
<th className="p-2">Active</th>
</tr>
</tr>
</thead>
{coupon.data.length ?
(
<tbody>
{currentCoupon.map((item, index) => (
<tr key={index} className='text-slate-500'>
<td className="p-2">{item.added}</td>
<td className="p-2">{item.code}</td>
<td className="p-2">{item.amount}</td>
<td className="p-2">{item.status}</td>
{coupon.data.length ? (
<tbody>
{currentCoupon.map((item, index) => {
let thePrice = PriceFormatter(
item?.price * 0.01,
item?.currency_code,
item?.currency
);
return (
<tr key={index} className="text-slate-500">
<td className="p-2">{item.added}</td>
<td className="p-2">{item.code}</td>
<td className="p-2">{item.amount}</td>
<td className="p-2">{item.status}</td>
</tr>
);
})}
</tbody>
) : coupon.error ? (
<tbody>
<tr className="text-slate-500">
<td className="p-2" colSpan={4}>
Opps! an error occurred. Please try again!
</td>
</tr>
))}
</tbody>
)
:
coupon.error ?
(
<tbody>
<tr className='text-slate-500'>
<td className="p-2" colSpan={4}>Opps! an error occurred. Please try again!</td>
</tbody>
) : (
<tbody>
<tr className="text-slate-500">
<td className="p-2" colSpan={4}>
No Purchase History Found!
</td>
</tr>
</tbody>
)
:
<tbody>
<tr className='text-slate-500'>
<td className="p-2" colSpan={4}>No Purchase History Found!</td>
</tr>
</tbody>
}
</table>
</tbody>
)}
</table>
{/* PAGINATION BUTTON */}
<PaginatedList onClick={handlePagination} prev={currentPage == 0 ? true : false} next={currentPage+Number(process.env.REACT_APP_ITEM_PER_PAGE) >= coupon?.data?.length ? true : false} data={coupon?.data} start={indexOfFirstItem} stop={indexOfLastItem} />
{/* END OF PAGINATION BUTTON */}
{/* PAGINATION BUTTON */}
<PaginatedList
onClick={handlePagination}
prev={currentPage == 0 ? true : false}
next={
currentPage + Number(process.env.REACT_APP_ITEM_PER_PAGE) >=
coupon?.data?.length
? true
: false
}
data={coupon?.data}
start={indexOfFirstItem}
stop={indexOfLastItem}
/>
{/* END OF PAGINATION BUTTON */}
</div>
)
);
}
export default CouponTable
export default CouponTable;
@@ -15,11 +15,11 @@ function PurchasesTable({purchase}) {
}
return (
<div className='flex flex-col justify-between h-full'>
<div className='flex flex-col justify-between min-h-[500px]'>
<table className="wallet-activity w-full table-auto border-collapse text-left">
<thead className='border-b-2'>
<tr className='text-slate-600'>
<th className="p-4">Trx.</th>
<th className="p-2">Trx.</th>
<th className="p-2">Amount</th>
<th className="p-2">Fee</th>
</tr>
@@ -29,7 +29,7 @@ function PurchasesTable({purchase}) {
<tbody>
{currentPurchase.map((item, index) => (
<tr key={index} className='text-slate-500'>
<td className="p-4">{item.added_date}<br />
<td className="p-2">{item.added_date}<br />
<b>{item.confirmation} </b>
</td>
<td className="p-2">{item.amount}</td>
@@ -15,7 +15,7 @@ function RecentActivityTable({payment}) {
}
return (
<div className='flex flex-col justify-between h-full'>
<div className='flex flex-col justify-between min-h-[500px]'>
<table className="wallet-activity w-full table-auto border-collapse text-left">
<thead className='border-b-2'>
<tr className='text-slate-600'>
@@ -1,60 +1,73 @@
import React, {useState} from 'react'
import { useState } from "react";
import PaginatedList from "../../Pagination/PaginatedList";
import { handlePagingFunc } from "../../Pagination/HandlePagination";
import PaginatedList from '../../Pagination/PaginatedList';
import {handlePagingFunc} from '../../Pagination/HandlePagination';
function ReferralTable({ history }) {
const [currentPage, setCurrentPage] = useState(0);
const indexOfFirstItem = Number(currentPage);
const indexOfLastItem =
Number(indexOfFirstItem) + Number(process.env.REACT_APP_ITEM_PER_PAGE);
const currentReferral = history?.data?.slice(
indexOfFirstItem,
indexOfLastItem
);
function ReferralTable({history}) {
const [currentPage, setCurrentPage] = useState(0);
const indexOfFirstItem = Number(currentPage);
const indexOfLastItem = Number(indexOfFirstItem)+Number(process.env.REACT_APP_ITEM_PER_PAGE);
const currentReferral = history?.data?.slice(indexOfFirstItem, indexOfLastItem);
const handlePagination = (e) => {
handlePagingFunc(e,setCurrentPage)
}
const handlePagination = (e) => {
handlePagingFunc(e, setCurrentPage);
};
return (
<div className='flex flex-col justify-between h-full'>
<table className="referral-list w-full table-auto border-collapse text-left">
<thead className='border-b-2'>
<tr className='text-slate-600'>
<th className="p-3">Added/Name</th>
<th className="p-3">Email</th>
<th className="p-3">Status</th>
</tr>
</thead>
<tbody>
{history.data.length ?
currentReferral.map((item, index) => (
<tr key={index} className='text-slate-500'>
<td className="p-3">{item.added_date} / {item.firstname} {item.lastname}</td>
<td className="p-3">{item.email}</td>
<td className="p-3">{item.status}</td>
</tr>
))
:
(
history.error ?
<tr className='text-slate-500'>
<td colSpan={3}>Opps! couldn't get referral history. Try reloading the page</td>
</tr>
:
(
<tr className='text-slate-500'>
<td colSpan={3}>No Item Found on referral List</td>
</tr>
)
)
}
</tbody>
</table>
<div className="flex flex-col justify-between min-h-[420px] overflow-x-auto">
<table className="referral-list w-full table-auto border-collapse text-left">
<thead className="border-b-2">
<tr className="text-slate-600">
<th className="p-3">Added/Name</th>
<th className="p-3">Email</th>
<th className="p-3">Status</th>
</tr>
</thead>
<tbody>
{history.data.length ? (
currentReferral.map((item, index) => (
<tr key={index} className="text-slate-500">
<td className="p-3">
{item.added_date} / {item.firstname} {item.lastname}
</td>
<td className="p-3">{item.email}</td>
<td className="p-3">{item.status}</td>
</tr>
))
) : history.error ? (
<tr className="text-slate-500">
<td colSpan={3}>
Opps! couldn't get referral history. Try reloading the page
</td>
</tr>
) : (
<tr className="text-slate-500">
<td colSpan={3}>No Item Found on referral List</td>
</tr>
)}
</tbody>
</table>
{/* PAGINATION BUTTON */}
<PaginatedList onClick={handlePagination} prev={currentPage == 0 ? true : false} next={currentPage+Number(process.env.REACT_APP_ITEM_PER_PAGE) >= history?.data?.length ? true : false} data={history?.data} start={indexOfFirstItem} stop={indexOfLastItem} />
{/* END OF PAGINATION BUTTON */}
{/* PAGINATION BUTTON */}
<PaginatedList
onClick={handlePagination}
prev={currentPage == 0 ? true : false}
next={
currentPage + Number(process.env.REACT_APP_ITEM_PER_PAGE) >=
history?.data?.length
? true
: false
}
data={history?.data}
start={indexOfFirstItem}
stop={indexOfLastItem}
/>
{/* END OF PAGINATION BUTTON */}
</div>
)
);
}
export default ReferralTable
export default ReferralTable;
+2 -4
View File
@@ -5,6 +5,7 @@ import bank2 from "../../assets/images/bank-2.png";
import bank3 from "../../assets/images/bank-3.png";
import bank4 from "../../assets/images/bank-4.png";
import Accordion from "../Helpers/Accordion";
import { PriceFormatter } from "../Helpers/PriceFormatter";
export default function WalletHeader(props) {
// debugger;
@@ -58,10 +59,7 @@ export default function WalletHeader(props) {
</div>
<div>
<p className="eth text-xl font-bold text-purple">
{(value.amount * 0.01).toFixed(2)} {value.code}
</p>
<p className="usd text-base text-thin-light-gray text-right">
{/*(773.69 USD)*/}
{PriceFormatter(value.amount * 0.01, value.code)}
</p>
</div>
</div>
@@ -0,0 +1,380 @@
import React, { useEffect, useState } from "react";
import { useNavigate } from "react-router-dom";
import Layout from "../Partials/Layout";
import CommonHead from "../UserHeader/CommonHead";
import usersService from "../../services/UsersService";
import LoadingSpinner from "../Spinners/LoadingSpinner";
import OthersInterestedTable from "./OthersInterestedTable";
export default function ManageInterestOffer(props) {
const navigate = useNavigate()
const apiCall = new usersService()
let [redirectTime, setRedirectTime] = useState(5)
let [messageToSend, setMessageToSend] = useState('')
let [tab, setTab] = useState("info"); //message STATE FOR SWITCHING BETWEEN TABS
let [requestStatus, setRequestStatus] = useState({loading: false, status: false, message: '', processType: ''})
let [messageListReload, setMessageListReload] = useState(false) // STATE TO DETERMINE WHEN MESSAGE LIST WILL RELOAD
const [messageList, setMessageList] = useState({loading: true, data: []}) // TO BE REMOVED AND REPLACE WITH REAL MESSAGE FROM API CALL
const [selectTab, setValue] = useState("today");
const filterHandler = (value) => {
setValue(value);
};
//FUNCTION TO HANDLE ON CHANGE MESSAGE IN TEXTAREA
const onMessageChange = ({target:{value}}) => {
if(messageToSend.length > 149){
setRequestStatus({loading: false, status: false, message: 'max of 150 characters', processType: 'sendmessage'})
}else{
setRequestStatus({loading: false, status: false, message: '', processType: 'sendmessage'})
}
setMessageToSend(value)
}
//FUNCTION TO SEND MESSAGE TO CLIENT
const sendMessage = () => {
let reqData = { // API PAYLOADS
msg_type: 'MRKTINT',
yourmessage: messageToSend,
offer_uid: props.offerDetails.offer_uid,
interest_uid: props.offerDetails.interest_uid
}
setRequestStatus(prev => ({...prev, loading: true, processType: 'sendmessage'}))
if(!messageToSend){
setRequestStatus({loading: false, status: false, message: 'Please enter message to send', processType: 'sendmessage'})
return
}
if(messageToSend.length > 149){
return
}
apiCall.offerInterestMsg(reqData).then(res=>{
if(res.status != 200 || res.data.internal_return < 0){
setRequestStatus({loading: false, status: false, message: 'message not sent, try again', processType: 'sendmessage'})
return
}
setRequestStatus({loading: false, status: true, message: 'message sent', processType: 'sendmessage'})
setMessageToSend('') // sets message to empty strings
setMessageListReload(prev => !prev) //A FUNCTION TO MAKE MESSAGE LIST RELOAD
}).catch(error => {
setRequestStatus({loading: false, status: false, message: 'Opps, an error occured', processType: 'sendmessage'})
}).finally(()=>{
setTimeout(() => {
setRequestStatus({loading: false, status: false, message: '', processType: ''})
}, 5000);
})
}
//FUNCTION TO ACCEPT/REJECT OFFER INTEREST
const interestOfferProcess = ({target:{name}}) => {
setRequestStatus(prev => ({...prev, loading: true, processType: name}))
let reqData = { // API PAYLOADS
proc: name.toUpperCase(),
client_uid : props.offerDetails?.client_uid,
offer_code : props.offerDetails?.offer_code,
offer_uid: props.offerDetails?.offer_uid,
}
apiCall.offersInterestProc(reqData).then(res => {
if(res.status != 200 || res.data.internal_return < 0){
setRequestStatus({loading: false, status: false, message: 'Unable to complete request', processType: ''})
return
}
let intervalTime = setInterval(() => { // SETS REDIRECT COUNT DOWN
setRedirectTime(prev => prev - 1)
}, 1000);
setRequestStatus({loading: false, status: true, message: `Offer ${name}ed`, processType: ''})
setTimeout(()=>{
navigate('/offer-interest', {replace: true})
clearInterval(intervalTime)
},5000)
}).catch(err => {
setRequestStatus({loading: false, status: false, message: 'Opps! something went wrong. Try again', processType: ''})
}).finally(()=>{
setTimeout(()=>{
setRequestStatus({loading: false, status: false, message: '', processType: ''})
},5000)
})
}
useEffect(()=>{ //API to get Offer Interest message list
let reqData = { // API PAYLOADS
msg_type: 'MRKTINT',
offer_uid: props.offerDetails?.offer_uid,
interest_uid: props.offerDetails?.interest_uid
}
setMessageList(prev => ({...prev, loading: true}))
apiCall.offerInterestListMsg(reqData).then(res=>{
setMessageList({loading: false, data:res?.data?.result_list})
}).catch(err => {
setMessageList(prev => ({...prev, loading: false}))
console.log('Failed', err)
})
},[messageListReload])
return (
<Layout>
<CommonHead
commonHeadData={props.commonHeadData}
/>
<div className="notification-page w-full mb-10">
<div className="notification-wrapper w-full mb-4">
{/* heading */}
<div className="sm:flex justify-between items-center mb-3">
<div className="mb-3 sm:mb-0">
<h1 className="text-26 font-bold text-dark-gray dark:text-white">
<span className={`${selectTab === "today" ? "block" : "hidden"}`}>Manage Offer Interest</span>
</h1>
</div>
</div>
{/* manage offer section */}
<div className="w-full p-4 bg-white dark:bg-dark-white rounded-2xl section-shadow">
<div className="my-2 w-full sm:grid gap-5 grid-cols-4">
{/* Detail section */}
<div className="w-full mb-5 lg:mb-0 col-span-3">
<div className="w-full flex justify-start space-x-3 items-center">
<button
type="button"
className="min-w-[45px] h-auto text-[#374557] border border-sky-blue p-1 rounded-full"
onClick={() =>
navigate('/offer-interest', { replace: true })
}
>
<svg
xmlns="http://www.w3.org/2000/svg"
width="35"
height="35"
viewBox="0 0 24 24"
fill="skyblue"
>
<path d="M19 11H7.83l5.59-5.59L12 4l-8 8 8 8 1.41-1.41L7.83 13H19v-2z" />
</svg>
</button>
<h1 className="text-sm lg:text-xl font-bold text-sky-blue dark:text-white tracking-wide">
{props.offerDetails?.offer_code && props.offerDetails.offer_code}
</h1>
</div>
<h1 className="my-3 text-xl lg:text-2xl font-bold text-dark-gray dark:text-white tracking-wide border">
{props.offerDetails?.title}
</h1>
<div className="w-full">
{/* switch button */}
<div className="my-1 flex items-center border-b border-slate-300">
<button
name="info"
onClick={(e) => setTab(e.target.name)}
className={`p-2 text-lg font-bold text-slate-600 dark:text-white border ${
tab == "info" ? "border-sky-blue" : "border-slate-300"
} tracking-wide transition duration-200`}
>
Info
</button>
<button
name="message"
onClick={(e) => setTab(e.target.name)}
className={`p-2 text-lg font-bold text-slate-600 dark:text-white border ${
tab == "message" ? "border-sky-blue" : "border-slate-300"
} tracking-wide transition duration-200`}
>
Messages ({messageList.data.length})
</button>
</div>
{/* END OF switch button */}
{/* info tab */}
{tab == 'info' ?
<div className="info-details w-full border-t">
<div className="my-0 md:my-3 block md:flex items-center gap-10">
<div className="my-3 md:my-0 flex items-center gap-1">
<span className="w-[200px] text-lg font-bold text-dark-gray dark:text-white tracking-wide">Name</span>
<span className="min-w-[100px] text-sm font-bold text-dark-gray dark:text-white tracking-wide">{props.offerDetails?.client_name}</span>
</div>
<div className="my-3 md:my-0 flex items-center gap-1">
<span className="w-[200px] text-lg font-bold text-dark-gray dark:text-white tracking-wide">Member Since</span>
<span className="min-w-[100px] text-sm font-bold text-dark-gray dark:text-white tracking-wide">{props.offerDetails?.client_added}</span>
</div>
</div>
<div className="my-3 flex items-center gap-1">
<span className="w-[200px] text-lg font-bold text-dark-gray dark:text-white tracking-wide">Jobs completed</span>
<span className="min-w-[100px] text-sm font-bold text-dark-gray dark:text-white tracking-wide">{props.offerDetails?.client_jobs_completed ? props.offerDetails?.client_jobs_completed :0}</span>
</div>
<div className="my-0 md:my-3 block md:flex items-center gap-10">
<div className="my-3 md:my-0 flex items-center gap-1">
<span className="w-[200px] text-lg font-bold text-dark-gray dark:text-white tracking-wide">Jobs active</span>
<span className="min-w-[100px] text-sm font-bold text-dark-gray dark:text-white tracking-wide">{props.offerDetails?.client_jobs_active ? props.offerDetails?.client_jobs_active:0}</span>
</div>
<div className="my-3 md:my-0 flex items-center gap-1">
<span className="w-[200px] text-lg font-bold text-dark-gray dark:text-white tracking-wide">Jobs uncompleted</span>
<span className="min-w-[100px] text-sm font-bold text-dark-gray dark:text-white tracking-wide">{props.offerDetails?.client_jobs_missed ? props.offerDetails?.client_jobs_missed:0}</span>
</div>
</div>
<div className="block md:flex items-center gap-10">
<div className="my-3 md:my-0 flex items-center gap-1">
<span className="w-[200px] text-lg font-bold text-dark-gray dark:text-white tracking-wide">% completion</span>
<span className="min-w-[100px] text-sm font-bold text-dark-gray dark:text-white tracking-wide">{props.offerDetails?.client_percent_completion ? props.offerDetails?.client_percent_completion:0}</span>
</div>
<div className="flex items-center gap-1">
<span className="w-[200px] text-lg font-bold text-dark-gray dark:text-white tracking-wide">Pending Offers</span>
<span className="min-w-[100px] text-sm font-bold text-dark-gray dark:text-white tracking-wide">{props.offerDetails?.client_offers_pending ? props.offerDetails?.client_offers_pending:0}</span>
</div>
</div>
</div>
:
<div className="message-details w-full border-t">
<p className="my-1 text-base text-dark-gray dark:text-white tracking-wide">To: <span className="font-bold">{props.offerDetails?.client_name}</span></p>
<div className="w-full flex items-center gap-5">
<div className="w-full">
<textarea
value={messageToSend}
onChange={onMessageChange}
rows={2} autoFocus={true}
className="p-2 text-base font-bold text-dark-gray dark:text-white dark:bg-dark-gray border tracking-wide w-full resize-none rounded-md outline-none"
/>
</div>
<div className="flex justify-end items-center">
{requestStatus.loading && requestStatus.processType == 'sendmessage' ?
<LoadingSpinner color='sky-blue' size='10' />
:
<button
type="button"
onClick={sendMessage}
disabled={requestStatus.loading}
// className="px-2 py-1 h-11 flex justify-center items-center btn-gradient text-base rounded-full text-white"
className="w-[100px] h-[50px] bg-sky-blue text-center text-lg font-semibold text-white rounded-md shadow-md flex justify-center items-center"
>
Send
</button>
}
</div>
</div>
{/* ERROR DISPLAY FOR MESSAGE SENDING */}
<div className="w-full">
{/* error or success display */}
{requestStatus.message != "" && requestStatus.processType == 'sendmessage' &&
(!requestStatus.status ? (
<div
className={`relative p-4 my-4 text-[#912741] bg-[#fcd9e2] border-[#fbc6d3] mb-4 rounded-[0.475rem] text-md font-light leading-[19.5px] text-[13px]`}
>
{requestStatus.message}
</div>
) : (
requestStatus.status && (
<div
className={`relative p-4 my-4 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 DISPLAY FOR MESSAGE SENDING */}
{/* message list */}
<div className="my-1 min-h-[100px] max-h-[200px] border-t overflow-y-scroll">
{ messageList.loading ?
<LoadingSpinner color='sky-blue' size='16' />
: messageList?.data?.map((item, index)=>(
<div key={index} className="my-2 w-full flex items-center gap-1">
<p className="text-base font-bold text-dark-gray dark:text-white tracking-wide">{item.added.split(' ')[0]}-from { }<span className="font-normal">{item.sender || 'Dummy name'}</span></p>
<p className="text-base font-bold text-dark-gray dark:text-white tracking-wide">{item.msg}</p>
</div>
))
}
</div>
</div>
}
</div>
</div>
{/* END OF Detail section */}
{/* BUTTON section */}
<div className="p-4 w-full min-h-full bg-sky-100 dark:bg-dark-gray col-span-1">
<div className="w-full h-full">
<div className="h-full flex sm:flex-col justify-center items-center gap-10">
{requestStatus.loading && requestStatus.processType == 'accept' ?
<LoadingSpinner color='sky-blue' size='10' />
:
<button
type="button"
name='accept'
disabled={requestStatus.loading}
onClick={interestOfferProcess}
// className="px-2 py-1 h-11 flex justify-center items-center btn-gradient text-base rounded-full text-white"
className='max-w-[150px] h-[100px] bg-[#57cd89] text-center text-lg font-semibold text-white py-2 px-4 rounded-md shadow-md'
>
Accept this Interest
</button>
}
{requestStatus.loading && requestStatus.processType == 'reject' ?
<LoadingSpinner color='sky-blue' size='10' />
:
<button
type="button"
name='reject'
disabled={requestStatus.loading}
onClick={interestOfferProcess}
// className="px-2 py-1 h-11 flex justify-center items-center border-gradient text-base rounded-full text-black"
className='max-w-[150px] h-[100px] bg-red-300 text-center text-lg font-semibold text-white py-2 px-4 rounded-md shadow-md'
>
Reject this Interest
</button>
}
</div>
{/* ERROR DISPLAY */}
<div className="w-full">
{/* error or success display */}
{requestStatus.message != "" && requestStatus.processType != 'sendmessage' &&
(!requestStatus.status ? (
<div
className={`relative p-4 my-4 text-[#912741] bg-[#fcd9e2] border-[#fbc6d3] mb-4 rounded-[0.475rem] text-md font-light leading-[19.5px] text-[13px]`}
>
{requestStatus.message}
</div>
) : (
requestStatus.status && (
<div
className={`relative p-4 my-4 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} redirecting.... ${redirectTime}sec`}
</div>
)
))}
</div>
{/* End of error or success display */}
</div>
</div>
{/* END of BUTTON section */}
</div>
</div>
{/* END OF manage offer section */}
</div>
{props.othersInterestedList?.data?.length ?
<div className="w-full overflow-x-auto">
{/* heading */}
<div className="sm:flex justify-between items-center mb-3">
<div className="mb-3 sm:mb-0">
<h1 className="text-26 font-bold text-dark-gray dark:text-white">
<span className={`${selectTab === "today" ? "block" : "hidden"}`}>Others interested in this Task</span>
</h1>
</div>
</div>
<OthersInterestedTable othersInterestedList={props.othersInterestedList} />
</div>
:
null
}
</div>
</Layout>
);
}
@@ -29,7 +29,7 @@ export default function OffersInterestTable({offerInterestList, className}) {
return (
<div
className={`update-table w-full p-8 bg-white dark:bg-dark-white rounded-2xl section-shadow min-h-[520px] ${
className={`update-table w-full my-8 p-8 bg-white dark:bg-dark-white rounded-2xl section-shadow min-h-[520px] ${
className || ""
}`}
>
@@ -52,9 +52,9 @@ export default function OffersInterestTable({offerInterestList, className}) {
</tr> */}
</thead>
<tbody className="h-full">
{currentOfferInterestList?.map((item, idx) => {
{currentOfferInterestList?.map((item, index) => {
return (
<tr key={item?.offer_uid} className="bg-white dark:bg-dark-white border-b dark:border-[#5356fb29] hover:bg-gray-50">
<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">
@@ -87,7 +87,7 @@ export default function OffersInterestTable({offerInterestList, className}) {
<td className="text-right py-4">
<button
onClick={() => {
navigate("/manage-active-job", {
navigate("/manage-offer", {
state: { ...item, pathname },
});
}}
@@ -107,19 +107,7 @@ export default function OffersInterestTable({offerInterestList, className}) {
:
(
<div className="font-bold text-center text-xl md:text-2xl lg:text-4xl text-dark-gray md:flex items-center justify-between">
<div className="p-2 w-full md:w-1/2">
<p className="mb-4 p-3 md:p-16">No Offer list avaliable.</p>
<button
onClick={()=>{navigate('/market', {replace: true})}}
type="button"
className="text-white btn-gradient text-lg tracking-wide px-5 py-2 rounded-full"
>
Goto Market
</button>
</div>
<div className="p-2 w-full md:w-1/2">
<img className='w-full' src={familyImage && familyImage} alt="Add Family" />
</div>
<p className="mb-4 p-3">No list avaliable.</p>
</div>
)
}
@@ -0,0 +1,148 @@
import React, { useState } from "react";
import dataImage1 from "../../assets/images/data-table-user-1.png";
import LoadingSpinner from "../Spinners/LoadingSpinner";
import { useNavigate, useLocation, Link } from "react-router-dom";
import { handlePagingFunc } from "../Pagination/HandlePagination";
import PaginatedList from "../Pagination/PaginatedList";
import { useDispatch } from "react-redux";
import { tableReload } from "../../store/TableReloads";
import { PriceFormatter } from "../Helpers/PriceFormatter";
import familyImage from '../../assets/images/no-family-side.png'
export default function OthersInterestTable({othersInterestedList, className}) {
const dispatch = useDispatch()
const navigate = useNavigate();
let { pathname } = useLocation();
const filterCategories = ["All Categories", "Explore", "Featured"];
const [selectedCategory, setCategory] = useState(filterCategories[0]);
const [currentPage, setCurrentPage] = useState(0);
const indexOfFirstItem = Number(currentPage);
const indexOfLastItem =
Number(indexOfFirstItem) + Number(process.env.REACT_APP_ITEM_PER_PAGE);
const currentOthersInterestedList = othersInterestedList?.data?.slice(indexOfFirstItem, indexOfLastItem);
const handlePagination = (e) => {
handlePagingFunc(e, setCurrentPage);
};
return (
<div
className={`update-table w-full p-4 bg-white dark:bg-dark-white rounded-2xl section-shadow min-h-[520px] ${
className || ""
}`}
>
{othersInterestedList?.loading ? (
<div className="min-h-[520px] w-full flex flex-col justify-center items-center">
<LoadingSpinner size="16" color="sky-blue" />
</div>
)
:
othersInterestedList?.data?.length > 0 ?
(
<table className="w-full text-sm text-left text-gray-500 dark:text-gray-400 relative">
<thead className="sticky top-0">
{/* <tr className="text-base text-thin-light-gray whitespace-nowrap border-b dark:border-[#5356fb29] default-border-bottom ">
<th className="py-4">Name</th>
<th className="py-4 text-center">Last Login</th>
<th className="py-4 text-center">No of Tasks</th>
<th className="py-4 text-right"></th>
</tr> */}
</thead>
<tbody className="h-full">
{currentOthersInterestedList?.map((item, index) => {
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}
alt="data"
className="w-full h-full"
/>
</div>
<div className="flex flex-col">
<h1 className="font-bold text-xl text-dark-gray dark:text-white">
{item?.title}
</h1>
<span className="text-sm text-thin-light-gray">{item?.expire}</span>
</div>
</div>
</td>
<td className="text-center py-4 px-2">
<div className="flex space-x-1 items-center justify-center">
<p className="font-bold text-x text-dark-gray dark:text-white">{item?.client_name}</p>
</div>
</td>
<td className="text-center py-4 px-2">
<div className="flex space-x-1 items-center justify-center">
{/* <span className="font-bold text-x text-dark-gray dark:text-white">{formatNumber(item?.price * 0.01)}</span> */}
<span className="font-bold text-x text-dark-gray dark:text-white">{PriceFormatter(item?.price * 0.01,item?.currency_code,item?.currency)}</span>
</div>
</td>
<td className="text-right py-4">
<button
onClick={() => {
dispatch(tableReload({type:'OTHERSINTERESTEDTABLE'}))
navigate("/manage-offer", {
state: { ...item, pathname },
});
}}
type="button"
className="w-20 h-11 flex justify-center items-center btn-gradient text-base rounded-full text-white"
>
View
</button>
</td>
</tr>
);
})
}
</tbody>
</table>
)
:
(
<div className="font-bold text-center text-xl md:text-2xl lg:text-4xl text-dark-gray md:flex items-center justify-between">
<div className="p-2 w-full md:w-1/2">
<p className="mb-4 p-3 md:p-16">No Offer list avaliable.</p>
<button
onClick={()=>{navigate('/market', {replace: true})}}
type="button"
className="text-white btn-gradient text-lg tracking-wide px-5 py-2 rounded-full"
>
Goto Market
</button>
</div>
<div className="p-2 w-full md:w-1/2">
<img className='w-full' src={familyImage && familyImage} alt="Add Family" />
</div>
</div>
)
}
{/* PAGINATION BUTTON */}
<PaginatedList
onClick={handlePagination}
prev={currentPage == 0 ? true : false}
next={
currentPage + Number(process.env.REACT_APP_ITEM_PER_PAGE) >=
othersInterestedList?.data?.length
? true
: false
}
data={othersInterestedList?.data}
start={indexOfFirstItem}
stop={indexOfLastItem}
/>
{/* END OF PAGINATION BUTTON */}
</div>
);
}
+3 -3
View File
@@ -6,7 +6,7 @@ const PaginatedList = ({ onClick, prev, next, data, start, stop }) => {
{!prev && (
<button
className={`p-2 border ${
prev ? "border-black" : "border-transparent"
prev ? "border-black dark:border-white dark:text-white" : "border-transparent dark:text-white"
} btn-shine rounded-full h-11 w-11`}
name="prev"
onClick={onClick}
@@ -26,7 +26,7 @@ const PaginatedList = ({ onClick, prev, next, data, start, stop }) => {
key={index}
value={index}
className={`p-2 border ${
index === start ? "border-black" : "border-transparent"
index === start ? "border-black dark:border-white dark:text-white" : "border-transparent dark:text-white"
} btn-shine rounded-full h-11 w-11`}
onClick={onClick}
name="page_num"
@@ -43,7 +43,7 @@ const PaginatedList = ({ onClick, prev, next, data, start, stop }) => {
{!next && (
<button
className={`p-2 border ${
next ? "border-black" : "border-transparent"
next ? "border-black dark:border-white dark:text-white" : "border-transparent dark:text-white"
} btn-shine rounded-full h-11 w-11`}
name="next"
onClick={onClick}
+1 -1
View File
@@ -827,7 +827,7 @@ export default function Header({ logoutModalHandler, sidebarHandler }) {
type="button"
className="w-[122px] h-11 flex justify-center items-center btn-gradient text-base rounded-full text-white"
>
Cannected
Connected
</button>
</div>
</div>
+1 -2
View File
@@ -1,7 +1,6 @@
import React, {useEffect, useState} from "react";
import React, { useState} from "react";
import { useDispatch, useSelector } from "react-redux";
import { useNavigate } from "react-router-dom";
import { toast } from "react-toastify";
import useToggle from "../../hooks/useToggle";
import { drawerToggle } from "../../store/drawer";
import ModalCom from "../Helpers/ModalCom";
+4 -2
View File
@@ -225,9 +225,10 @@ export default function MobileSidebar({ sidebar, action, logoutModalHandler, myJ
<ul className="flex flex-col space-y-6">
<ListItem
title="Add Job"
route="/add-job"
route="/myjobs"
iconName="people-two"
sidebar={sidebar}
state={true}
/>
</ul>
</div>
@@ -301,11 +302,12 @@ export default function MobileSidebar({ sidebar, action, logoutModalHandler, myJ
);
}
const ListItem = ({ sidebar, route, title, bubble }) => {
const ListItem = ({ sidebar, route, title, bubble, popup }) => {
return (
<li className="item group">
<NavLink
to={route}
state={popup ? { popup: true } : { popup: false }}
className={`nav-item flex items-center ${
((navData) => (navData.isActive ? "active" : ""),
sidebar ? "justify-start space-x-3.5" : "justify-center")
+28 -23
View File
@@ -8,7 +8,12 @@ import {
import DarkModeContext from "../Contexts/DarkModeContext";
import Icons from "../Helpers/Icons";
export default function Sidebar({ sidebar, action, logoutModalHandler, myJobList }) {
export default function Sidebar({
sidebar,
action,
logoutModalHandler,
myJobList,
}) {
const darkMode = useContext(DarkModeContext);
let { userDetails } = useSelector((state) => state.userDetails);
@@ -131,27 +136,25 @@ export default function Sidebar({ sidebar, action, logoutModalHandler, myJobList
{/* menu and settings item */}
{userDetails?.account_type !== "FAMILY" && (
<div
className={`menu-item transition-all duration-300 ease-in-out ${
sidebar ? "my-5" : ""
}`}
>
<div className="heading mb-5">
<h1 className="title text-xl font-bold text-purple">
Family
</h1>
</div>
<div className="items">
<ul className="flex flex-col space-y-6">
<ListItem
title="Family Corner"
route="/acc-family"
iconName="people-two"
sidebar={sidebar}
/>
</ul>
</div>
<div
className={`menu-item transition-all duration-300 ease-in-out ${
sidebar ? "my-5" : ""
}`}
>
<div className="heading mb-5">
<h1 className="title text-xl font-bold text-purple">Family</h1>
</div>
<div className="items">
<ul className="flex flex-col space-y-6">
<ListItem
title="Family Corner"
route="/acc-family"
iconName="people-two"
sidebar={sidebar}
/>
</ul>
</div>
</div>
)}
{userDetails?.account_type !== "FAMILY" && (
<>
@@ -249,9 +252,10 @@ export default function Sidebar({ sidebar, action, logoutModalHandler, myJobList
<ul className="flex flex-col space-y-6">
<ListItem
title="Add Job"
route="/add-job"
route="/myjobs"
iconName="people-two"
sidebar={sidebar}
popup={true}
/>
</ul>
</div>
@@ -325,11 +329,12 @@ export default function Sidebar({ sidebar, action, logoutModalHandler, myJobList
);
}
const ListItem = ({ sidebar, route, title, bubble, iconName }) => {
const ListItem = ({ sidebar, route, title, bubble, iconName, popup }) => {
return (
<li className={`item group`}>
<NavLink
to={route}
state={popup ? { popup: true } : { popup: false }}
className={`nav-item flex items-center ${
((navData) => (navData.isActive ? "active" : ""),
sidebar ? "justify-start space-x-3.5" : "justify-center")
+6 -6
View File
@@ -1,13 +1,13 @@
import React from 'react'
import Layout from '../Partials/Layout'
import ReferralDisplay from './ReferralDisplay'
import React from "react";
import Layout from "../Partials/Layout";
import ReferralDisplay from "./ReferralDisplay";
function Referral() {
return (
<Layout>
<ReferralDisplay />
<ReferralDisplay />
</Layout>
)
);
}
export default Referral
export default Referral;
+68 -67
View File
@@ -1,5 +1,4 @@
import React, { useEffect, useState } from "react";
import { useNavigate } from "react-router-dom";
import { toast } from "react-toastify";
import usersService from "../../services/UsersService";
import InputCom from "../Helpers/Inputs/InputCom";
@@ -10,30 +9,29 @@ import * as Yup from "yup";
import ReferralTable from "../MyWallet/WalletComponent/ReferralTable";
const validationSchema = Yup.object().shape({
email: Yup.string()
ref_email: Yup.string()
.email("Wrong email format")
.min(3, "Minimum 3 characters")
.max(50, "Maximum 50 characters")
.required("Email is required"),
firstname: Yup.string()
ref_firstname: Yup.string()
.min(3, "Minimum 3 characters")
.max(25, "Maximum 25 characters")
.required("Firstname is required"),
lastname: Yup.string()
ref_lastname: Yup.string()
.min(3, "Minimum 3 characters")
.max(25, "Maximum 25 characters")
.required("Lastname is required"),
});
const initialValues = {
firstname: "",
lastname: "",
email: "",
ref_firstname: "",
ref_lastname: "",
ref_email: "",
};
function ReferralDisplay() {
const apiCall = new usersService(); // GET API CALL
const navigate = useNavigate();
let [refHistoryReload, setRefHistoryReload] = useState(false); // Determines when referral history reloads
@@ -76,13 +74,13 @@ function ReferralDisplay() {
.then((res) => {
if (res.data.internal_return < 0) {
setError({
message: "Email already referred",
message: res.data.status,
loading: false,
status: false,
});
return;
} else {
toast.success("Message Sent");
toast.success(res.data.status | "Message Sent!");
setError({ message: "", loading: false, status: true });
setRefHistoryReload((prev) => !prev);
}
@@ -99,16 +97,7 @@ function ReferralDisplay() {
//FUNCTION TO HANDLE SUBMIT
const handleSubmit = (values, helpers) => {
setError({ message: "", loading: true, status: false });
var postData = {
uid: localStorage.getItem("uid"),
member_id: localStorage.getItem("member_id"),
sessionid: localStorage.getItem("session_token"),
action: 11032,
...values,
};
sendReferralMsg(postData); // FUNCTION TO SEND REFERRAL MESSAGE
sendReferralMsg({...values}); // FUNCTION TO SEND REFERRAL MESSAGE
};
useEffect(() => {
@@ -116,10 +105,18 @@ function ReferralDisplay() {
}, [refHistoryReload]);
return (
<div className="content-wrapper w-full lg:flex xl:space-x-8 lg:space-x-4 bottomMargin">
<div className="lg:w-1/2 w-full mb-10 lg:mb-0">
<div className="referral w-full md:p-8 p-4 h-full bg-white dark:bg-dark-white rounded-2xl shadow">
<h2 className="text-slate-900 dark:text-white text-xl lg:text-2xl font-medium">
<div className="content-wrapper w-full lg:flex xl:space-x-8 bottomMargin">
<div className="lg:w-2/2 w-full mb-10 lg:mb-0">
<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>Refer a Friend</span>
</h1>
</div>
</div>
<div className="referral w-full md:p-8 p-4 bg-white dark:bg-dark-white rounded-2xl shadow">
<h2 className="mb-4 text-slate-900 dark:text-white text-xl lg:text-2xl font-medium">
Send Referral
</h2>
<Formik
@@ -129,42 +126,46 @@ function ReferralDisplay() {
>
{(props) => (
<Form className="referral-info">
{/* Firstname */}
<div className="field w-full mb-6">
<InputCom
fieldClass="px-6"
label="Firstname"
type="text"
name="firstname"
placeholder="Firstname"
value={props.values.firstname}
inputHandler={props.handleChange}
blurHandler={props.handleBlur}
/>
{props.errors.firstname && props.touched.firstname && (
<p className="text-sm text-red-500">
{props.errors.firstname}
</p>
)}
</div>
<div className="block md:mb-6 md:flex gap-10">
{/* Firstname */}
<div className="field w-full mb-6 md:mb-0">
<InputCom
fieldClass="px-6"
label="Firstname"
type="text"
name="ref_firstname"
placeholder="Firstname"
value={props.values.ref_firstname}
inputHandler={props.handleChange}
blurHandler={props.handleBlur}
/>
{props.errors.ref_firstname &&
props.touched.ref_firstname && (
<p className="text-sm text-red-500">
{props.errors.ref_firstname}
</p>
)}
</div>
{/* Lastname */}
<div className="field w-full mb-6">
<InputCom
fieldClass="px-6"
label="Lastname"
type="text"
name="lastname"
placeholder="Lastname"
value={props.values.lastname}
inputHandler={props.handleChange}
blurHandler={props.handleBlur}
/>
{props.errors.lastname && props.touched.lastname && (
<p className="text-sm text-red-500">
{props.errors.lastname}
</p>
)}
{/* Lastname */}
<div className="field w-full mb-6 md:mb-0">
<InputCom
fieldClass="px-6"
label="Lastname"
type="text"
name="ref_lastname"
placeholder="Lastname"
value={props.values.ref_lastname}
inputHandler={props.handleChange}
blurHandler={props.handleBlur}
/>
{props.errors.ref_lastname &&
props.touched.ref_lastname && (
<p className="text-sm text-red-500">
{props.errors.ref_lastname}
</p>
)}
</div>
</div>
<div className="field w-full mb-6">
@@ -172,14 +173,16 @@ function ReferralDisplay() {
fieldClass="px-6"
label="Email"
type="text"
name="email"
name="ref_email"
placeholder="Email"
value={props.values.email}
value={props.values.ref_email}
inputHandler={props.handleChange}
blurHandler={props.handleBlur}
/>
{props.errors.email && props.touched.email && (
<p className="text-sm text-red-500">{props.errors.email}</p>
{props.errors.ref_email && props.touched.ref_email && (
<p className="text-sm text-red-500">
{props.errors.ref_email}
</p>
)}
</div>
@@ -193,7 +196,7 @@ function ReferralDisplay() {
) : (
<button
type="submit"
className="text-lg text-white bg-sky-blue p-2 hover:opacity-90 rounded-md"
className="px-2 h-11 flex justify-center items-center btn-gradient text-base rounded-full text-white"
>
Send Message
</button>
@@ -203,10 +206,8 @@ function ReferralDisplay() {
)}
</Formik>
</div>
</div>
<div className="lg:w-1/2 w-full mb-10 lg:mb-0">
<div className="referral w-full md:p-8 p-4 h-full max-h-[700px] bg-white dark:bg-dark-white overflow-y-auto rounded-2xl shadow">
<div className="w-full md:p-8 p-4 bg-white dark:bg-dark-white rounded-2xl shadow">
<h2 className="mb-2 text-slate-900 dark:text-white text-xl lg:text-2xl font-medium">
Referral List
</h2>
+101 -98
View File
@@ -1,101 +1,123 @@
import React, { useState } from "react";
import { Link } from "react-router-dom";
import authProfilePic from "../../assets/images/auth-profile-picture.png";
import profileBanner from "../../assets/images/profile-cover.png";
import collections from "../../data/collectionplan_data.json";
import marketPlace from "../../data/marketplace_data.json";
// import authProfilePic from "../../assets/images/auth-profile-picture.png";
// import profileBanner from "../../assets/images/profile-cover.png";
// import collections from "../../data/collectionplan_data.json"; Should this be cleaned off?
// import marketPlace from "../../data/marketplace_data.json";
import LoadingSpinner from "../../components/Spinners/LoadingSpinner"
import products from "../../data/product_data.json";
import Layout from "../Partials/Layout";
import ActivitiesTab from "./ActivitiesTab";
import CollectionTab from "./CollectionTab";
import CreatedTab from "./CreatedTab";
import HiddenProductsTab from "./HiddenProductsTab";
import OnSaleTab from "./OnSaleTab";
import OwnTab from "./OwnTab";
import {
ActivitiesTab,
CollectionTab,
CreatedTab,
HiddenProductsTab,
OnSaleTab,
OwnTab,
} from "./tabs";
export default function Resources() {
const onSaleProducts = marketPlace.data;
const CreatedSell = marketPlace.data;
const CreatedBits = products.datas;
// const mainProducts = products.datas;
export default function Resources(props) {
// console.log("RESOURCES=>", props);
const ownProducts = products.datas;
const collectionProducts = collections.data;
// Resource Props
const __resources = props.MyResourceData;
const tabs = [
{
id: 1,
name: "onsale",
content: "On Sale",
},
{
id: 2,
name: "owned",
content: "Owned",
},
{
id: 3,
name: "created",
content: "Created",
},
{
id: 4,
name: "hidden",
content: "Hidden",
},
{
id: 5,
name: "collection",
content: "Collection",
},
{
id: 6,
name: "activity",
content: "Activity",
},
];
// Collection Items
const collectionProducts = __resources?.collectiondata?.data;
const tab_categories = __resources?.tab_categories?.data;
const onSaleProducts = __resources?.marketdata?.data;
const CreatedSell = __resources?.marketdata?.data;
const CreatedBits = __resources?.productdata?.data;
const [tab, setTab] = useState(tab_categories ? tab_categories[0]?.name : "");
const [tab, setTab] = useState(tabs[0].name);
const tabHandler = (value) => {
setTab(value);
};
// Category Components
const tabComponents = {
onsale: <OnSaleTab products={onSaleProducts} />,
owned: <OwnTab products={ownProducts} />,
created: (
<CreatedTab marketProducts={CreatedSell} mainProducts={CreatedBits} />
),
hidden: (
<HiddenProductsTab
marketProducts={CreatedSell}
mainProducts={CreatedBits}
/>
),
collection: <CollectionTab products={collectionProducts} />,
activity: <ActivitiesTab />,
};
const defaultTabComponent = <OnSaleTab products={onSaleProducts} />;
const selectedTabComponent = tabComponents[tab] || defaultTabComponent;
// Tab Item Component
const TabItem = ({ tabValue, isActive }) => {
return (
<li
className={`relative group inline`}
onClick={() => tabHandler(tabValue.name)}
>
<span
className={`py-4 sm:border-b-none border-b group-hover:border-purple border-transparent lg:text-xl text-sm tracking-wide font-bold group-hover:text-purple text-dark-gray dark:text-white relative z-10 cursor-pointer ${
isActive
? "text-purple border-purple"
: "text-dark-gray dark:text-white border-transparent"
}`}
>
{tabValue.content}
</span>
<span
className={`w-5 h-5 group-hover:bg-pink group-hover:text-white text-[10px] rounded-full absolute -top-2 -right-5 flex justify-center items-center ${
isActive
? "text-white bg-pink"
: "text-thin-light-gray bg-[#F2B8FD]"
}`}
>
16
</span>
</li>
);
};
// Tab List Component
const TabList = ({ tabCategories }) => {
return (
<ul className="lg:flex lg:space-x-14 space-x-8">
{tabCategories?.length > 0 &&
tabCategories?.map((tabValue, idx) => (
<TabItem
key={tabValue.id}
tabValue={tabValue}
isActive={tab === tabValue.name || (idx === 0 && tab === "")}
/>
))}
</ul>
);
};
return (
<>
<Layout>
<div className="nft-authprofile-wrapper w-full">
{__resources.length == 0 ||
Object.keys(__resources).length == 0 ? (
<div className="w-full h-full flex items-center justify-center">
<LoadingSpinner size={16} color="sky-blue" />
</div>
) : (
<div className="main-wrapper w-full">
<div className="content-wrapper-profile-only w-full mb-6">
<div className="auth-tab-content relative mb-10">
<div className="lg:flex justify-between">
<div className="tab-items">
<ul className="lg:flex lg:space-x-14 space-x-8">
{tabs &&
tabs.length > 0 &&
tabs.map((tabValue) => (
<li
key={tabValue.id}
className="relative group inline"
onClick={() => tabHandler(tabValue.name)}
>
<span
className={`py-4 sm:border-b-none border-b group-hover:border-purple border-transparent lg:text-xl text-sm tracking-wide font-bold group-hover:text-purple text-dark-gray dark:text-white relative z-10 cursor-pointer ${
tab === tabValue.name
? "text-purple border-purple "
: "text-dark-gray dark:text-white border-transparent "
}`}
>
{tabValue.content}
</span>
<span
className={`w-5 h-5 group-hover:bg-pink group-hover:text-white text-[10px] rounded-full absolute -top-2 -right-5 flex justify-center items-center ${
tab === tabValue.name
? "text-white bg-pink"
: "text-thin-light-gray bg-[#F2B8FD]"
}`}
>
16
</span>
</li>
))}
<TabList tabCategories={tab_categories} />
</ul>
</div>
<div style={{ transform: "translateY(-22px)" }}>
@@ -112,29 +134,10 @@ export default function Resources() {
</div>
<div className="tab-cotainer w-full mb-10">
{tab === "onsale" ? (
<OnSaleTab products={onSaleProducts} />
) : tab === "owned" ? (
<OwnTab products={ownProducts} />
) : tab === "created" ? (
<CreatedTab
marketProducts={CreatedSell}
mainProducts={CreatedBits}
/>
) : tab === "hidden" ? (
<HiddenProductsTab
marketProducts={CreatedSell}
mainProducts={CreatedBits}
/>
) : tab === "collection" ? (
<CollectionTab products={collectionProducts} />
) : tab === "activity" ? (
<ActivitiesTab />
) : (
""
)}
{selectedTabComponent}
</div>
</div>
)}
</div>
</Layout>
</>
@@ -1,8 +1,7 @@
import React from "react";
import dataImage1 from "../../assets/images/data-table-user-1.png";
import dataImage2 from "../../assets/images/data-table-user-2.png";
import dataImage3 from "../../assets/images/data-table-user-3.png";
import dataImage4 from "../../assets/images/data-table-user-4.png";
import dataImage1 from "../../../assets/images/data-table-user-1.png";
import dataImage2 from "../../../assets/images/data-table-user-2.png";
import dataImage3 from "../../../assets/images/data-table-user-3.png";
import dataImage4 from "../../../assets/images/data-table-user-4.png";
export default function ActivitiesTab({ className }) {
return (
@@ -1,7 +1,6 @@
import React from "react";
import CollectionCard from "../Cards/CollectionCard";
import DataIteration from "../Helpers/DataIteration";
import SearchCom from "../Helpers/SearchCom";
import CollectionCard from "../../Cards/CollectionCard";
import DataIteration from "../../Helpers/DataIteration";
import SearchCom from "../../Helpers/SearchCom";
export default function CollectionTab({ className, products }) {
return (
@@ -43,7 +42,7 @@ export default function CollectionTab({ className, products }) {
<DataIteration
datas={products}
startLength={process.env.REACT_APP_ZERO_STATE}
endLength={products.length}
endLength={products?.length}
>
{({ datas }) => (
<CollectionCard key={datas.uniqKey} collectionData={datas} />
@@ -1,7 +1,7 @@
import React, { useRef } from "react";
import ProductCardStyleTwo from "../Cards/ProductCardStyleTwo";
import Icons from "../Helpers/Icons";
import SliderCom from"../Helpers/SliderCom";
import { useRef } from "react";
import ProductCardStyleTwo from "../../Cards/ProductCardStyleTwo";
import Icons from "../../Helpers/Icons";
import SliderCom from "../../Helpers/SliderCom";
export default function CreateSaleSlider({
className,
@@ -42,7 +42,9 @@ export default function CreateSaleSlider({
{/* heading */}
<div className="flex justify-between items-center mb-6">
<div>
<h1 className="text-26 font-bold text-dark-gray dark:text-white">Create for Sell</h1>
<h1 className="text-26 font-bold text-dark-gray dark:text-white">
Create for Sell
</h1>
</div>
<div className="slider-btns flex space-x-4">
<button onClick={nextHandler} type="button">
@@ -89,7 +91,7 @@ export default function CreateSaleSlider({
<div className="trending-products relative w-full">
<SliderCom selector={trendingSlider} settings={settings}>
{products &&
products.length > 0 &&
products?.length > 0 &&
products.map((item) => (
<ProductCardStyleTwo
key={item.id}
@@ -1,7 +1,7 @@
import React, { useRef } from "react";
import ProductCardStyleOne from "../Cards/ProductCardStyleOne";
import Icons from "../Helpers/Icons";
import SliderCom from "../Helpers/SliderCom";
import { useRef } from "react";
import ProductCardStyleOne from "../../Cards/ProductCardStyleOne";
import Icons from "../../Helpers/Icons";
import SliderCom from "../../Helpers/SliderCom";
export default function CreatedBidsSlider({
className,
@@ -49,7 +49,9 @@ export default function CreatedBidsSlider({
{/* heading */}
<div className="flex justify-between items-center mb-6">
<div>
<h1 className="text-26 font-bold text-dark-gray dark:text-white">Create for Bits</h1>
<h1 className="text-26 font-bold text-dark-gray dark:text-white">
Create for Bits
</h1>
</div>
<div className="slider-btns flex space-x-4">
<button onClick={nextHandler} type="button">
@@ -96,7 +98,7 @@ export default function CreatedBidsSlider({
<div className="trending-products relative w-full">
<SliderCom selector={trendingSlider} settings={settings}>
{products &&
products.length > 0 &&
products?.length > 0 &&
products.map((item) => (
<ProductCardStyleOne
key={item.id}
@@ -1,5 +1,4 @@
import React from "react";
import SearchCom from "../Helpers/SearchCom";
import SearchCom from "../../Helpers/SearchCom";
import CreatedBidsSlider from "./CreatedBidsSlider";
import CreateSaleSlider from "./CreateSaleSlider";
@@ -1,5 +1,4 @@
import React from "react";
import SearchCom from "../Helpers/SearchCom";
import SearchCom from "../../Helpers/SearchCom";
import CreatedBidsSlider from "./CreatedBidsSlider";
import CreateSaleSlider from "./CreateSaleSlider";
@@ -1,7 +1,6 @@
import React from "react";
import ProductCardStyleTwo from "../Cards/ProductCardStyleTwo";
import DataIteration from "../Helpers/DataIteration";
import SearchCom from "../Helpers/SearchCom";
import ProductCardStyleTwo from "../../Cards/ProductCardStyleTwo";
import DataIteration from "../../Helpers/DataIteration";
import SearchCom from "../../Helpers/SearchCom";
export default function OnSaleTab({ className, products }) {
return (
@@ -43,7 +42,7 @@ export default function OnSaleTab({ className, products }) {
<DataIteration
datas={products}
startLength={process.env.REACT_APP_ZERO_STATE}
endLength={products.length}
endLength={products?.length}
>
{({ datas }) => (
<ProductCardStyleTwo key={datas.id} datas={datas} />
@@ -1,7 +1,6 @@
import React from "react";
import ProductCardStyleOne from "../Cards/ProductCardStyleOne";
import DataIteration from "../Helpers/DataIteration";
import SearchCom from "../Helpers/SearchCom";
import ProductCardStyleOne from "../../Cards/ProductCardStyleOne";
import DataIteration from "../../Helpers/DataIteration";
import SearchCom from "../../Helpers/SearchCom";
export default function OwnTab({ className, products }) {
return (
@@ -43,7 +42,7 @@ export default function OwnTab({ className, products }) {
<DataIteration
datas={products}
startLength={process.env.REACT_APP_ZERO_STATE}
endLength={products.length}
endLength={products?.length}
>
{({ datas }) => (
<ProductCardStyleOne key={datas.id} datas={datas} />
+15
View File
@@ -0,0 +1,15 @@
import ActivitiesTab from "./ActivitiesTab";
import CollectionTab from "./CollectionTab";
import CreatedTab from "./CreatedTab";
import HiddenProductsTab from "./HiddenProductsTab";
import OnSaleTab from "./OnSaleTab";
import OwnTab from "./OwnTab";
export {
ActivitiesTab,
CollectionTab,
CreatedTab,
HiddenProductsTab,
OnSaleTab,
OwnTab,
};
+1 -1
View File
@@ -8,7 +8,7 @@ export default function MainSection({ products }) {
<DataIteration
datas={products}
startLength={process.env.REACT_APP_ZERO_STATE}
endLength={products.length}
endLength={products?.length}
>
{({ datas }) => (
<div key={datas.id + Math.random()} className="item">
+9 -5
View File
@@ -1,14 +1,18 @@
import React from "react";
import Accordion from "../../Helpers/Accordion";
import LoadingSpinner from "../../Spinners/LoadingSpinner";
export default function FaqTab({ datas = [] }) {
export default function FaqTab({ datas }) {
return (
<div className="faq-tab w-full">
<div className="accordion-items w-full">
{datas &&
datas.length > 0 &&
datas.map((value) => (
<Accordion key={value.id + Math.random()} datas={value} />
{datas.loading ?
<LoadingSpinner color='sky-blue' size={16} />
:
datas &&
datas?.data?.length > 0 &&
datas.data.map((value, index) => (
<Accordion key={index + Math.random()} datas={value} />
))}
</div>
</div>
@@ -0,0 +1,210 @@
import React from "react";
export default function PrivacyPolicyTab() {
return (
<div className="terms-conditon-tab w-full">
<div className="terms-condition-wrappr w-full">
<div className="mb-6">
<h1 className="text-3xl tracking-wide font-bold text-dark-gray dark:text-white mb-4">
Privacy Policy
</h1>
<p className="text-base text-thin-light-gray leading-[28px] ">
Wrenchboard issues this policy to inform users about the information
we collect, the use and disclosures in the course of interactions
with the WrenchBoard services by customers. We value the privacy of
our customers and such strive to employ best industry standards and
practices to protect their privacy. By signing up for this services,
using our products or technologies offered through the WrenchBoard
website, you expressly agree to the terms of this Privacy Policy.
</p>
</div>
<hr />
<div className="my-8">
<h1 className="text-2xl tracking-wide font-bold text-dark-gray dark:text-white mb-4">
Collection of Information
</h1>
<p className="text-base text-thin-light-gray leading-[28px] ">
When you sign up for WrenchBoard platform as a user, freelancer,
worker or client, you inadvertently provide certain personal
information such as name and email address. This information may be
used to identify or contact you as part of the service we provide.
Below is information we may collect from registered users of the
service.
</p>
<ul className="flex flex-col rounded-[0.25rem]">
{[
"We collect financial information which include bank account numbers, debit/credit account.",
"We collect financial information which include bank account numbers, debit/credit account.",
"We collect personal information (names email addresses) from you and your friends when you use our referral program. We may also receive information about you from third party sites during promotions and campaigns.",
].map((item, idx) => (
<li
className="border-opacity-10 block relative px-[1.25rem] py-[0.75rem] first:rounded-tl first:rounded-tr text-[#505056]"
key={idx}
>
{item}
</li>
))}
</ul>
</div>
<hr />
<div className="my-8">
<h1 className="text-2xl tracking-wide font-bold text-dark-gray dark:text-white mb-4">
Use of Personal Information
</h1>
<p className="text-base text-thin-light-gray leading-[28px] ">
The information we collect about you is mainly utilized to provide a
secure, efficient and smooth user experience as you interact with
the site. We may also utilize the information to contact you to
resolve issues with your account or resolve disputes between
freelancers and clients. We also use your personal information in
the following ways.
</p>
<ul className="flex flex-col rounded-[0.25rem]">
{[
"To comply with the regulatory requirement, whenever the need arises.",
"To improve business relationship between Wrenchboard and users.",
"To manage and protect our IT infrastructure.",
"To contact you as the need arises as part of the services we offer to users.",
"To provide some indication on the promotional offers and advertisement we direct to you.",
"To protect you and your information. We regularly send you notification based on activities as you use the service.",
].map((item, idx) => (
<li
className="border-opacity-10 block relative px-[1.25rem] py-[0.75rem] first:rounded-tl first:rounded-tr text-[#505056]"
key={idx}
>
{item}
</li>
))}
</ul>
</div>
<hr />
<div className="my-8">
<h1 className="text-2xl tracking-wide font-bold text-dark-gray dark:text-white mb-4">
How We Share Your Personal Information:
</h1>
<p className="text-base text-thin-light-gray leading-[28px] ">
When you sign up to the Service as a freelancer and create a
profile, we will share your limited personal information with
clients or project owners for whom you may perform a task or do work
for based on expression of interest. Such information will include
name, profile information with experience and your ranking based on
previous performance. This information will be available to the
public.
<br />
When you register as a client or project owner and post a job in the
market place or offer jobs to preselected freelancers or workers on
this service, information about you, your business will be shared.
This information will be based on the information provided in your
profile. <br />
In order to deliver a quality service on WrenchBoard, we require the
services of third parties and agents such as PayPal, Quickteller
Service or banks to facilitate and process transactions. <br />
We may also share your personal information when and if compelled or
mandated by government agencies, or court injunctions or regulatory
requirement. In order to settle disputes or claims, we may also be
compelled to share your information in order to deliver services to
you. <br />
Finally, we may share your information with companies that we plan
to to merge with or to be acquired by. We will take necessary
measure to ensure that all such new affiliations and corporate
entities comply with the Privacy Policies hereby stated failing
which you will be notified duly.
</p>
</div>
<hr />
<div className="my-8">
<h1 className="text-2xl tracking-wide font-bold text-dark-gray dark:text-white mb-4">
How We Use Cookies and Related Technologies:
</h1>
<p className="text-base text-thin-light-gray leading-[28px] ">
When you access the service or website, www.wrenchboard.com , we
store small files called Cookies in your browser. Cookies and
related technologies like beacons and tags are used to identify
customers uniquely. With Cookies, we can track and trend the pages
of interest within the website. Hence, these technologies help us
focus only relevant promotions or advertisement to each customer.
Through this technologies, we are able to improve the service based
on the feedback from the interaction with users. We also rely on
these technologies to mitigate fraudulent attempts on your
WrenchBoard account. You may elect to disable cookies to prevent
installation of cookies from the website to your browser. However,
your functional experience with the service or website may be
impacted. Note that you automatically disconnect from cookies when
you close your browser session or log out of the service.
</p>
</div>
<hr />
<div className="my-8">
<h1 className="text-2xl tracking-wide font-bold text-dark-gray dark:text-white mb-4">
Measures We Take to Secure Your Information
</h1>
<p className="text-base text-thin-light-gray leading-[28px] ">
Your data and privacy are important to us as such we take extreme
care on how we protect your information. We use a combination of
industry standard encryption technologies to protect your
information. Your password is not visible to our technical support
team or any one in WrenchBoard. Our IT infrastructures are located
in hosting facilities with strict access controls, and security
protocols with only pre checked and authorized persons have access.
</p>
</div>
<hr />
<div className="my-8">
<h1 className="text-2xl tracking-wide font-bold text-dark-gray dark:text-white mb-4">
Updating Your Information or Opting Out
</h1>
<p className="text-base text-thin-light-gray leading-[28px] ">
Users of this service may update the information posted in profiles
or their accounts at any time. The accuracy of information posted on
the profiles and accounts is entirely the responsibility of users.
You may also decide to opt out the service or close your account at
any time. We may also deactivate your account if you violate the
terms of use of the website. Whilst we will endeavor to delete your
core information in such instances, we will retain minimal records
for reasons including disputes, claims or administrative purposes.
Also, note that it may not be possible to delete any information you
had shared with other users on the website previously.
</p>
</div>
<hr />
<div className="my-8">
<h1 className="text-2xl tracking-wide font-bold text-dark-gray dark:text-white mb-4">
Deactivate/Delete your account
</h1>
<p className="text-base text-thin-light-gray leading-[28px] ">
If you no longer wish to use our Services, you can deactivate your
Services account. If you want to deactivate your account, that
setting is available to you in your account settings. Otherwise,
please get in touch with our support. While we close any associated
process, you will not be able to access your account. Note that this
process cannot be reversed since we will remove your data. If you
want to use our services in the future, it will be a new account
altogether.
</p>
</div>
<hr />
<div className="mt-8">
<h1 className="text-2xl tracking-wide font-bold text-dark-gray dark:text-white mb-4">
Changes To the Policy
</h1>
<p className="text-base text-thin-light-gray leading-[28px] ">
We reserve the rights to update and make changes to this Privacy
policy at anytime. Changes will become effective once posted.
However, we will notify you by email or when you log on to the
service or website about any changes that fundamentally affect how
we manage your personal information. Contacting Us: You may contact
us about this policy through our email address anytime :
support@wrenchboard.com
</p>
</div>
</div>
</div>
);
}
@@ -4,77 +4,231 @@ export default function TermsConditionTab() {
return (
<div className="terms-conditon-tab w-full">
<div className="terms-condition-wrappr w-full">
<div className="mb-8">
<h1 className="text-xl tracking-wide font-bold text-dark-gray dark:text-white mb-4">
1. Definitions
<div className="mb-6">
<h1 className="text-3xl tracking-wide font-bold text-dark-gray dark:text-white mb-4">
Terms of use
</h1>
<p className="text-base text-thin-light-gray leading-[28px] ">
What you do own when you buy an NFT are the keys to a non-fungible
perhaps unique token. That token is yours to trade or hold or
display in Decentraland. But the digital file associated with an NFT
is just as easy to copy and paste and download as any other the
Finally, players lose their NFTs sometimes according to the rules
and regulations of the NFT game.
These Website Terms & Conditions (T&Cs) apply to your access and
use of www.wrenchboard.com,dashboard.wrenchboard.com (the Site),
including all software, data, reports, text, images, sounds, video,
and all contents made available through any portion of the Site
(collectively, the Content). Content includes all such elements as
a whole, as well as individual elements and portions thereof.
</p>
</div>
<div className="mb-8">
<h1 className="text-xl tracking-wide font-bold text-dark-gray dark:text-white mb-4">
2. Acceptance
<hr />
<div className="my-8">
<h1 className="text-2xl tracking-wide font-bold text-dark-gray dark:text-white mb-4">
Acceptance of Terms
</h1>
<p className="text-base text-thin-light-gray leading-[28px] ">
Amet minim mollit non deserunt ullamco est sit aliqua dolor do amet
sint. Velit officia consequat duis enim velit mollit. Exercitation
veniam consequat sunt nostrud amet.Capacity. You confirm that you
have the legal capacity to receive and hold and make use of the NFT
under French jurisdiction and any other relevant
jurisdiction.Acceptance. By participating in the Sale, You accept
and agree to these Terms and Conditions without any condition or
restriction. If You do not agree to this Contract, You shall not
participate in the Sale made by the Company Exercitation veniam
consequat sunt nostrud amet.Capacity. You confirm that you have the
legal capacity to receive and hold find to end.Contract, You shall
not participate in the Sale made by the Company Exercitation venia
WRENCHBOARD permits you (User or you or your) to access and
use the Site and Content, subject to these T&Cs. By accessing or
using any portion of the Site, you acknowledge that you have read,
understood, and agree to be bound by these T&Cs. If you do not agree
with these T&Cs, you must not accept these T&Cs or access or use the
site or content.
</p>
</div>
<div className="mb-8 bg-purple p-[23px] text-white rounded-lg">
<p className="text-[18px]">
These Terms and Conditions are related to the sale of NFTs by the
Company (the Company) on its Website. It solely governs the
contractual relationship between You and the Company regarding the
Sale and any related contract.
</p>
</div>
<div className="mb-8">
<h1 className="text-xl tracking-wide font-bold text-dark-gray dark:text-white mb-4">
3. The Sale
<hr />
<div className="my-8">
<h1 className="text-2xl tracking-wide font-bold text-dark-gray dark:text-white mb-4">
General Conditions of Use
</h1>
<p className="text-base text-thin-light-gray leading-[28px] ">
The Company offers NFTs featuring the Betonyou universe. The holders
of one or more NFTs will be able to win cryptos while playing video
games. In the future, the Company plans to develop its own games and
Metaverse around the Betonyou universe (Project). <br />
<b> Authorization to Access and Use Site and Content.</b> Subject to
your compliance with these T&Cs and the provisions hereof, you may
access or use the Site and Content solely for the purpose of your
evaluation of WRENCHBOARD and WRENCHBOARDs products and services.
You may only link to the Site or Content, or any portion thereof, as
expressly permitted by WRENCHBOARD.
<br />
To release the NFTs and fund the project, the Company offers NFTs
from a dedicated website("Sale"). The web address of this website
will be given at the time of the mint. The NFT acquisition does not
confer any rights on the Company or in the future development.
<b> Ownership and Restrictions.</b> All rights, title, and interest
in and to the Site and Content will remain with and belong
exclusively to WRENCHBOARD. You will not (a) sublicense, resell,
rent, lease, transfer, assign, time share or otherwise commercially
exploit or make the Site and any Content available to any third
party, (b) use the Site and Content in any unlawful manner
(including without limitation in violation of any data, privacy or
export control laws) or in any manner that interferes with or
disrupts the integrity or performance of the Site and Content or
their related components, or (c) modify, adapt or hack the Site and
Content to, or try to, gain unauthorized access to the restricted
portions of the Site and Content or related systems or networks
(i.e., circumvent any encryption or other security measures, gain
access to any source code or any other underlying form of technology
or information, and gain access to any part of the Site and Content,
or any other products or services of WRENCHBOARD that are not
readily made available to the general public). You are not permitted
to copy, modify, frame, repost, publicly perform or display, sell,
reproduce, distribute, or create derivative works of the Site and
Content, except that you may download, display, and print one copy
of the publicly available materials (i.e., the Content that does not
require an Account name or password to access) on any single
computer solely for your personal, non-commercial use, provided that
you do not modify the material in any way and you keep intact all
copyright, trademark, and other proprietary notices. You agree not
to access the Site or Content by any means other than through the
interface that is provided by WRENCHBOARD to access the same. You
may not use any page-scrape, deep-link, spider, or robot or
other automatic program, device, algorithm or methodology, or any
similar manual process, to access, copy, acquire, or monitor any
portion of the Site or any Content, or in any way reproduce or
circumvent the presentation or navigational structure of the Site or
any Content, to obtain or attempt to obtain any Content or other
information through any means not made generally available through
the Site by WRENCHBOARD. WRENCHBOARD reserves the right to take any
lawful measures to prevent any such activity. You may not forge
headers or otherwise manipulate identifiers in order to disguise the
origin of any message or transmittal you send to WRENCHBOARD on or
through the Site or any service offered on or through the Site. You
may not pretend that you are, or that you represent, someone else,
or impersonate any other individual or entity.
<b> Responsibility for Your Data.</b> You are solely responsible for
all data, information, and other content, that you upload, post, or
otherwise provide or store (hereafter post(ing)) in connection
with or relating to the Site.
</p>
</div>
<div className="mb-8">
<h1 className="text-xl tracking-wide font-bold text-dark-gray dark:text-white mb-4">
4. Purchasers obligations
<hr />
<div className="my-8">
<h1 className="text-2xl tracking-wide font-bold text-dark-gray dark:text-white mb-4">
Use of Intellectual Property.
</h1>
<p className="text-base text-thin-light-gray leading-[28px] ">
To the fullest extent permitted by applicable law, You undertake to
indemnify, defend and hold harmless the Company from and against all
claims, demands, actions, damages, losses, costs and expenses
(including attorneys fees) that arise from or relate to (i) your
Subscription or use of the NFTs; (ii) your responsibilities or
obligations under this Contract; and (iii) your breach of this
Contract. <br /> <br /> Company undertakes to act with the care
normally expected from a professional in his field and to comply
with the best practice in force. The best endeavor obligation only
binds the Company.
<b> Rights in User Content.</b> By posting your information and
other content (User Content) on or through the Site and Content,
you grant WRENCHBOARD a worldwide, non-exclusive, perpetual,
irrevocable, royalty-free, fully paid, sublicensable and
transferable license to use, modify, reproduce, distribute, display,
publish and perform User Content in connection with the Site and
Content. WRENCHBOARD has the right, but not the obligation, to
monitor the Site and Content and User Content. WRENCHBOARD may
remove or disable any User Content at any time for any reason, or
for no reason at all. You, the user, acknowledge that you bear sole
responsibility for adequate security, protection, and backup of User
Content. WRENCHBOARD will have no liability to you for any
unauthorized access or use of any of User Content, or any
corruption, deletion, destruction, or loss of any of User Content.
</p>
</div>
<hr />
<div className="my-8">
<h1 className="text-2xl tracking-wide font-bold text-dark-gray dark:text-white mb-4">
Feedback
</h1>
<p className="text-base text-thin-light-gray leading-[28px] ">
You may submit ideas, suggestions, or comments (Feedback)
regarding the Site and Content or WRENCHBOARDs business, products,
or services. By submitting any Feedback, you acknowledge and agree
that (a) your Feedback is provided by you voluntarily and
WRENCHBOARD may, without any obligations or limitation, use and
exploit such Feedback in any manner and for any purpose, (b) you
will not seek and are not entitled to any money or other form of
compensation, consideration, or attribution with respect to your
Feedback regardless of whether WRENCHBOARD considered or used your
Feedback in any manner, and (c) your Feedback is not the
confidential or proprietary information of you or any third party.
</p>
</div>
<hr />
<div className="my-8">
<h1 className="text-2xl tracking-wide font-bold text-dark-gray dark:text-white mb-4">
Termination of Access Due to Violations
</h1>
<p className="text-base text-thin-light-gray leading-[28px] ">
WRENCHBOARD may, in its sole discretion and without prior notice,
terminate your access to the Site and/or block your future access to
the Site if we determine that you have violated these T&Cs or other
agreements or guidelines which may be associated with your use of
the Site. You also agree that any violation by you of these T&Cs
will cause irreparable harm to WRENCHBOARD, for which monetary
damages would be inadequate, and you consent to WRENCHBOARD
obtaining any injunctive or equitable relief that WRENCHBOARD deems
necessary or appropriate in such circumstances, without limiting
WRENCHBOARDs other available remedies. Further, WRENCHBOARD may, in
its sole discretion and without prior notice, terminate your access
to the Site, for cause, which includes (but is not limited to) (1)
requests by law enforcement or other government agencies, (2)
discontinuance or material modification of the Site or any service
offered on or through the Site, or (3) unexpected technical issues
or problems.
</p>
</div>
<hr />
<div className="my-8">
<h1 className="text-2xl tracking-wide font-bold text-dark-gray dark:text-white mb-4">
T&Cs Updates
</h1>
<p className="text-base text-thin-light-gray leading-[28px] ">
WRENCHBOARD reserves the right, at its sole discretion, to change or
modify portions of these T&Cs at any time. WRENCHBOARD will post the
changes to these T&Cs on the Site and will indicate at the top of
this page the date these terms were last revised. It is your
responsibility to check the T&Cs periodically for changes. Your
continued use of the Site and Content after the date any such
changes become effective constitutes your acceptance of the new or
revised T&Cs.
</p>
</div>
<hr />
<div className="my-8">
<h1 className="text-2xl tracking-wide font-bold text-dark-gray dark:text-white mb-4">
NO WARRANTIES AND DISCLAIMER BY WRENCHBOARD
</h1>
<p className="text-base text-thin-light-gray leading-[28px] ">
THE SITE AND CONTENT, AND ALL SERVER AND NETWORK COMPONENTS, ARE
PROVIDED ON AN AS IS AND AS AVAILABLE BASIS WITH ALL ERRORS AND
DEFECTS AND WITHOUT ANY WARRANTIES OF ANY KIND, AND WRENCHBOARD
EXPRESSLY DISCLAIMS ALL REPRESENTATIONS AND WARRANTIES, INCLUDING
ANY IMPLIED WARRANTIES OF ACCURACY, COMPLETENESS, MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT, AND ANY
REPRESENTATIONS OR WARRANTIES ARISING FROM COURSE OF DEALING, COURSE
OF PERFORMANCE OR USAGE OF TRADE. YOU ACKNOWLEDGE THAT WRENCHBOARD
DOES NOT WARRANT THAT YOUR ACCESS OR USE OR BOTH OF THE SITE AND
CONTENT WILL BE UNINTERRUPTED, TIMELY, SECURE, ERROR-FREE OR
VIRUS-FREE, AND WRENCHBOARD DOES NOT MAKE ANY WARRANTY AS TO THE
RESULTS THAT MAY BE OBTAINED FROM USE OF THE SITE AND CONTENT. NO
INFORMATION, ADVICE OR SERVICES OBTAINED BY YOU FROM WRENCHBOARD OR
THROUGH THE SITE WILL CREATE ANY WARRANTY NOT EXPRESSLY STATED IN
THESE TERMS and CONDITIONS AND YOU SHOULD NOT RELY ON THE SITE AND
THE GENERAL CONTENT ALONE AS THE BASIS FOR YOUR BUSINESS DECISIONS.
</p>
</div>
<hr />
<div className="my-8">
<p className="text-base text-thin-light-gray leading-[28px] ">
WRENCHBOARD reserves the right to do any of the following, at any
time, without notice: (1) to modify, suspend or terminate operation
of or access to the Site, or any portion of the Site, for any
reason; (2) to modify or change the Site, or any portion of the
Site, for any reason; and (3) to interrupt the operation of the
Site, or any portion of the Site, as necessary to perform routine or
non-routine maintenance, error correction, or other changes..
</p>
</div>
<hr />
<div className="mt-8">
<h1 className="text-2xl tracking-wide font-bold text-dark-gray dark:text-white mb-4">
Changes To the Policy
</h1>
<p className="text-base text-thin-light-gray leading-[28px] ">
We reserve the rights to update and make changes to this Privacy
policy at anytime. Changes will become effective once posted.
However, we will notify you by email or when you log on to the
service or website about any changes that fundamentally affect how
we manage your personal information. Contacting Us: You may contact
us about this policy through our email address anytime :
support@wrenchboard.com
</p>
</div>
</div>

Some files were not shown because too many files have changed in this diff Show More