Compare commits

...

232 Commits

Author SHA1 Message Date
victorAnumudu 9f89376aa9 Hid text when session expires 2023-11-01 22:11:34 +01:00
ameye 4e91e47978 Merge branch 'session-implement' of WrenchBoard/Users-Wrench into master 2023-11-01 20:09:08 +00:00
ameye fcaa485b17 Merge branch 'history-header-removal' of WrenchBoard/Users-Wrench into master 2023-11-01 20:08:55 +00:00
victorAnumudu e4526652d3 Merge master into session-implement 2023-11-01 20:27:35 +01:00
victorAnumudu 64056bb2a4 changed payment to purchase 2023-11-01 18:45:25 +01:00
victorAnumudu e49c4d66f8 removed table headers if no record is found 2023-11-01 18:39:24 +01:00
ameye 8fdb939b72 Merge branch 'Family-Acc-layout-update' of WrenchBoard/Users-Wrench into master 2023-11-01 17:06:51 +00:00
victorAnumudu 79325450f3 session logout implemented 2023-11-01 18:04:47 +01:00
Chief Bube 7849a027b4 changed fill 2023-11-01 10:04:00 -07:00
ameye 38432a6d50 Merge branch 'Family-Acc-layout-update' of WrenchBoard/Users-Wrench into master 2023-11-01 16:08:13 +00:00
Chief Bube 83a54ff3ef . 2023-11-01 07:37:00 -07:00
Chief Bube 5e0fdffa1e done 2023-11-01 07:34:23 -07:00
ameye 11d2cb3e3a Merge branch 'Added-uid-to-completesignup' of WrenchBoard/Users-Wrench into master 2023-11-01 14:00:31 +00:00
ameye 93ac55b44b Merge branch 'error-page-style' of WrenchBoard/Users-Wrench into master 2023-11-01 14:00:23 +00:00
Chief Bube 237ce13a6c . 2023-11-01 06:40:13 -07:00
Chief Bube 4253174494 . 2023-11-01 06:31:05 -07:00
victorAnumudu da0ed0364b changed the height of the page 2023-11-01 13:43:51 +01:00
ameye 466175c49d Merge branch 'fixing-height-in-intrest-popup' of WrenchBoard/Users-Wrench into master 2023-11-01 10:39:10 +00:00
Chief Bube ed148ce267 Fixed Family Profile Break 2023-10-31 21:42:15 -07:00
ameye 6de795c07b Merge branch 'error-page' of WrenchBoard/Users-Wrench into master 2023-10-31 21:29:49 +00:00
victorAnumudu 436498bef9 added background image to error page 2023-10-31 19:51:36 +01:00
victorAnumudu 086b1202a4 error 404 page adjusted 2023-10-31 16:59:02 +01:00
ameye a81ccdc4d7 Merge branch 'reward-tab-cleanup' of WrenchBoard/Users-Wrench into master 2023-10-31 12:57:55 +00:00
victorAnumudu 33abbbcd2b adjusted reward component contents 2023-10-31 13:53:02 +01:00
ameye 04844af733 Merge branch 'lastname-bug-fix' of WrenchBoard/Users-Wrench into master 2023-10-31 12:42:42 +00:00
victorAnumudu 46d919090d lastname bug fixed 2023-10-31 13:36:53 +01:00
ameye 8e67892f16 Merge branch 'rewards-tab' of WrenchBoard/Users-Wrench into master 2023-10-31 12:21:17 +00:00
victorAnumudu a67d2b7bb4 reward tab clean up 2023-10-31 13:16:33 +01:00
victorAnumudu 99c0c24489 Merge master into rewards-tab 2023-10-31 13:15:22 +01:00
victorAnumudu 1a1d59a8c7 rewards API implemented 2023-10-31 13:13:58 +01:00
ameye 31dcfcfd0b Merge branch 'fixing-height-in-intrest-popup' of WrenchBoard/Users-Wrench into master 2023-10-31 11:45:48 +00:00
Chief Bube e9aa58f2f5 Added tracking text color 2023-10-31 04:42:50 -07:00
victorAnumudu 55fe2bf6d2 Merge master into rewards-tab 2023-10-31 04:23:42 +01:00
victorAnumudu 738eb1589e rewards tab added 2023-10-31 04:22:50 +01:00
ameye 139e0c2827 Merge branch 'send-btn-disable' of WrenchBoard/Users-Wrench into master 2023-10-30 17:43:22 +00:00
victorAnumudu 30642bba14 disabled send btn on successful transfer 2023-10-30 16:29:45 +01:00
ameye f5921612b8 Merge branch 'balance-bug' of WrenchBoard/Users-Wrench into master 2023-10-30 14:43:49 +00:00
victorAnumudu 38afece043 Merge master into balance-bug 2023-10-30 15:40:34 +01:00
victorAnumudu d44447c6b3 returned balance to 2 decimal places 2023-10-30 15:39:38 +01:00
ameye d942c6641a Merge branch 'fixing-height-in-intrest-popup' of WrenchBoard/Users-Wrench into master 2023-10-30 14:12:06 +00:00
Chief Bube beed565ba0 Updated Transaction popup layout 2023-10-30 07:04:01 -07:00
ameye 329e27b83d Merge branch 'family-transfer-bug' of WrenchBoard/Users-Wrench into master 2023-10-28 22:42:25 +00:00
victorAnumudu 3145656d80 checked that required parameters are not empty before outputting success message 2023-10-28 23:39:11 +01:00
victorAnumudu 5098a45bd5 fixed family transfer bug 2023-10-28 23:06:53 +01:00
ameye 9e65a6e7d5 Merge branch 'family-transfer' of WrenchBoard/Users-Wrench into master 2023-10-28 21:33:04 +00:00
victorAnumudu 83ac1087f3 Merge master into family-transfer 2023-10-28 17:48:42 +01:00
victorAnumudu 55b2bccdf0 family transfer API added 2023-10-28 17:47:36 +01:00
ameye 0c51474dd2 Merge branch 'fixing-height-in-intrest-popup' of WrenchBoard/Users-Wrench into master 2023-10-28 12:38:17 +00:00
CHIEFSOFT\ameye 8c7ffb52a5 captcha text 2023-10-28 08:30:28 -04:00
Chief Bube 6c698bc399 fixed array list 2023-10-27 20:37:25 -07:00
ameye 540ad6f9ad Merge branch 'family-start-transfer' of WrenchBoard/Users-Wrench into master 2023-10-27 19:55:00 +00:00
victorAnumudu 11342c32c6 Family start transfer API added 2023-10-27 19:22:54 +01:00
ameye 356c11d029 Merge branch 'fixing-height-in-intrest-popup' of WrenchBoard/Users-Wrench into master 2023-10-26 20:17:15 +00:00
Chief Bube 934d95e822 Added Tracking to Family Profile 2023-10-26 10:25:00 -07:00
ameye 0aa49b49a9 Merge branch 'fixing-height-in-intrest-popup' of WrenchBoard/Users-Wrench into master 2023-10-26 08:22:30 +00:00
Chief Bube be52792271 fixed height issues 2023-10-25 13:51:39 -07:00
ameye c23e61e06b Merge branch 'family-add-fund-modal' of WrenchBoard/Users-Wrench into master 2023-10-25 20:20:19 +00:00
victorAnumudu 24b9a0e348 made amount to align right 2023-10-25 19:37:36 +01:00
victorAnumudu fcb154ba49 request message removed 2023-10-25 19:27:08 +01:00
victorAnumudu c0065ed569 Merge master into family-add-fund-modal 2023-10-25 19:23:06 +01:00
victorAnumudu 05a2ebec61 added family add fund modal 2023-10-25 19:19:55 +01:00
ameye 63f7deaa2b Merge branch 'job-increse-bug-fix' of WrenchBoard/Users-Wrench into master 2023-10-25 14:47:55 +00:00
Chief Bube d762ef42e8 Fixed positioning 2023-10-25 07:43:47 -07:00
ameye 3e34c81789 Merge branch 'job-increse-bug-fix' of WrenchBoard/Users-Wrench into master 2023-10-25 09:15:19 +00:00
Chief Bube a2340af314 tiny bug fix and checker added 2023-10-24 19:09:09 -07:00
Chief Bube 2a445fd2d4 tiny feature added 2023-10-24 18:55:37 -07:00
Chief Bube b5e52fb34e Fixed button position 2023-10-24 18:48:17 -07:00
ameye 87512bbc23 Merge branch 'family-balance' of WrenchBoard/Users-Wrench into master 2023-10-24 18:07:43 +00:00
victorAnumudu 1e59059394 Merge master into family-balance 2023-10-24 17:47:13 +01:00
victorAnumudu e59c83d216 made balance container take whole width 2023-10-24 17:45:10 +01:00
ameye 395569dab5 Merge branch 'job-increse-bug-fix' of WrenchBoard/Users-Wrench into master 2023-10-24 15:47:03 +00:00
Chief Bube 25a537c5bc changed duplicate 2023-10-24 08:25:52 -07:00
Chief Bube 5244a2875e Interest Popup Fix 2023-10-24 08:18:32 -07:00
victorAnumudu 54560b3fec Merge master into family-balance 2023-10-24 15:27:23 +01:00
victorAnumudu 078d26317f family balance added 2023-10-24 15:26:24 +01:00
ameye 8dbf638c7f Merge branch 'interest-popout-size' of WrenchBoard/Users-Wrench into master 2023-10-24 11:39:09 +00:00
CHIEFSOFT\ameye a68af7c3a4 cooment 2023-10-24 06:38:38 -04:00
CHIEFSOFT\ameye 905d48ceb7 package json lock 2023-10-24 06:34:30 -04:00
victorAnumudu 1e5dac0104 adjusted interest popout style 2023-10-24 11:22:51 +01:00
ameye 11e52a02f6 Merge branch 'family-wallet' of WrenchBoard/Users-Wrench into master 2023-10-24 08:51:02 +00:00
ameye 4e74198ae5 Merge branch 'job-increse-bug-fix' of WrenchBoard/Users-Wrench into master 2023-10-24 08:50:55 +00:00
ameye 5d673a630c Merge branch 'banner-fix' of WrenchBoard/Users-Wrench into master 2023-10-24 08:50:47 +00:00
victorAnumudu adcb33764b added family wallet icon 2023-10-24 07:26:27 +01:00
Chief Bube 305bff1167 Job count increase - bug fixed 2023-10-23 22:51:47 -07:00
victorAnumudu 1ebe9fe12b Fixed banner image 2023-10-23 20:37:10 +01:00
ameye 3fbaf6b78a Merge branch 'update-family-validation' of WrenchBoard/Users-Wrench into master 2023-10-23 16:37:19 +00:00
victorAnumudu 421a24c185 added validation to update family component 2023-10-23 17:23:43 +01:00
ameye 6386fbcda6 Merge branch 'family-update' of WrenchBoard/Users-Wrench into master 2023-10-23 11:34:35 +00:00
victorAnumudu 2e09d77597 bug fix 2023-10-23 12:27:54 +01:00
victorAnumudu 10dae9193c added family update component 2023-10-23 12:17:32 +01:00
ameye 3f487337f4 Merge branch 'interest-style-fix' of WrenchBoard/Users-Wrench into master 2023-10-22 18:08:02 +00:00
victorAnumudu 7eabee8855 updated interest popout btn and font style 2023-10-22 19:04:14 +01:00
ameye 360f0500a9 Merge branch 'btn-customization' of WrenchBoard/Users-Wrench into master 2023-10-22 12:16:34 +00:00
victorAnumudu 9b6c5b72ad changed cancel btn style 2023-10-22 05:52:47 +01:00
ameye afc5e25cfc Merge branch 'login-captcha' of WrenchBoard/Users-Wrench into master 2023-10-17 17:40:06 +00:00
victorAnumudu 58fe5eb82c captcha added in login page 2023-10-17 18:26:46 +01:00
CHIEFSOFT\ameye 2f3fdc0695 google captach settings 2023-10-17 07:01:05 -04:00
ameye 0a465fceb0 Merge branch 'btn-height-adjustment' of WrenchBoard/Users-Wrench into master 2023-10-16 20:26:23 +00:00
victorAnumudu 5e968db2f5 adjusted btn height 2023-10-16 21:08:06 +01:00
ameye 1916bc1a65 Merge branch 'btn-rename' of WrenchBoard/Users-Wrench into master 2023-10-16 18:41:55 +00:00
ameye 1103127cae Merge branch 'btn-customized' of WrenchBoard/Users-Wrench into master 2023-10-16 18:41:47 +00:00
ameye 751369071c Merge branch 'family-task-reload' of WrenchBoard/Users-Wrench into master 2023-10-16 18:41:39 +00:00
ameye 4cc8f27c14 Merge branch 'profile-upload-revert' of WrenchBoard/Users-Wrench into master 2023-10-16 18:41:07 +00:00
ameye aab455c2c3 Merge branch 'Changed-Naira-Wallet-Country-Display' of WrenchBoard/Users-Wrench into master 2023-10-16 13:52:03 +00:00
Ebube 21843c4bc7 Changed Naira Wallet Country Display 2023-10-16 13:43:48 +01:00
Ebube 8702728ffa Merge branch 'master' of https://gitlab.chiefsoft.net/WrenchBoard/Users-Wrench into Recent-Activities 2023-10-16 13:12:24 +01:00
victorAnumudu 79dcd0f2b5 btn style customized 2023-10-16 11:49:03 +01:00
ameye 47b25f3dfe Merge branch 'google-recaptcha' of WrenchBoard/Users-Wrench into master 2023-10-16 00:30:06 +00:00
victorAnumudu 89925a6af9 added google recaptcha 2023-10-16 01:18:48 +01:00
CHIEFSOFT\ameye 3c0f951b6e Merge branch 'master' of https://gitlab.chiefsoft.net/WrenchBoard/Users-Wrench 2023-10-15 19:32:29 -04:00
CHIEFSOFT\ameye ffbfb1da35 added "react-google-recaptcha": "^3.1.0" 2023-10-15 19:32:22 -04:00
victorAnumudu a8416a8433 made family page to reload after adding a task 2023-10-10 10:17:22 +01:00
victorAnumudu 70c9d1778c renamed edit job btn to save 2023-10-10 07:02:52 +01:00
Ebube 930559c96b Family Picture 2023-10-09 13:32:07 +01:00
victorAnumudu c4af2dfcc8 changed timeout duration 2023-10-09 11:29:11 +01:00
victorAnumudu b186549b8d added profile upload API 2023-10-09 11:22:38 +01:00
ameye ab191b6a72 Merge branch 'upload-profile' of WrenchBoard/Users-Wrench into master 2023-10-08 12:02:52 +00:00
victorAnumudu d130687cf9 made upload profile to show from env 2023-10-08 08:58:37 +01:00
ameye 5e9a442eac Merge branch 'online-name' of WrenchBoard/Users-Wrench into master 2023-10-07 18:38:23 +00:00
victorAnumudu 1093c25207 added online name 2023-10-07 18:28:30 +01:00
ameye 0623227807 Merge branch 'Recent-Activities' of WrenchBoard/Users-Wrench into master 2023-10-02 13:07:50 +00:00
Ebube 3a05790125 fixed table responsive issue 2023-10-02 12:26:18 +01:00
Ebube 85b0456011 Added price to review due task 2023-09-30 07:18:23 +01:00
ameye 6ddd16ee24 Merge branch 'Recent-Activities' of WrenchBoard/Users-Wrench into master 2023-09-27 23:54:04 +00:00
Ebube d034f9be45 Merge branch 'master' of https://gitlab.chiefsoft.net/WrenchBoard/Users-Wrench into Recent-Activities 2023-09-27 17:33:22 +01:00
Ebube 2f3940f99c fixed responsiveness issues for the layout 2023-09-27 17:31:31 +01:00
ameye 5e998e5e42 Merge branch 'btn-name' of WrenchBoard/Users-Wrench into master 2023-09-27 13:48:05 +00:00
victorAnumudu e113f40c5c btn name changed based on job owner status 2023-09-27 14:13:04 +01:00
Ebube 42a3df2d65 Fixed assign popup data display delay 2023-09-27 13:33:12 +01:00
ameye 3fb996887b Merge branch 'send-to-group' of WrenchBoard/Users-Wrench into master 2023-09-27 07:52:15 +00:00
victorAnumudu 6d2794dd2f displays send to group based on env variable 2023-09-27 06:57:51 +01:00
CHIEFSOFT\ameye 8e683f12a2 env update 2023-09-26 17:07:16 -04:00
ameye 1910ea2bb6 Merge branch 'home-active-task-display' of WrenchBoard/Users-Wrench into master 2023-09-26 20:36:17 +00:00
victorAnumudu 5f6471a16e made active task to display if no offer 2023-09-26 19:35:05 +01:00
ameye 98a4baddab Merge branch 'offer-notice' of WrenchBoard/Users-Wrench into master 2023-09-26 11:49:23 +00:00
ameye 67f8de325f Merge branch 'bug-logout-modal' of WrenchBoard/Users-Wrench into master 2023-09-26 11:49:17 +00:00
victorAnumudu 594d2a4224 displays offerlist if it exist on home page 2023-09-26 12:42:16 +01:00
victorAnumudu edd48e72ec made the close btn on logout modal to appear on light mode 2023-09-26 09:16:50 +01:00
ameye 47c9e1bb6e Merge branch 'delete-family-suggest' of WrenchBoard/Users-Wrench into master 2023-09-25 21:18:45 +00:00
victorAnumudu 2fe80ecbe3 adjusted width of delete btn 2023-09-25 16:58:11 +01:00
victorAnumudu 1640f25d9d delete and send reminder API added 2023-09-25 16:54:43 +01:00
ameye 28f3bbcad1 Merge branch 'Recent-Activities' of WrenchBoard/Users-Wrench into master 2023-09-25 09:56:25 +00:00
ameye c4eca264b1 Merge branch 'send-reminder-modified' of WrenchBoard/Users-Wrench into master 2023-09-25 09:56:15 +00:00
Ebube 89a9e77380 reduced the year length to 17 2023-09-25 07:39:08 +01:00
Ebube 349b524151 Added two select elements for year and month respectively. 2023-09-25 07:30:50 +01:00
victorAnumudu 084370b641 added the correct last time reminded 2023-09-25 05:00:24 +01:00
ameye c2a6cff3eb Merge branch 'send-reminder' of WrenchBoard/Users-Wrench into master 2023-09-24 10:21:07 +00:00
victorAnumudu d45e533d91 Merged master into send-reminder 2023-09-24 07:46:33 +01:00
victorAnumudu c89de2559a added send reminder popout 2023-09-24 07:45:05 +01:00
ameye a5a267927a Merge branch 'Recent-Activities' of WrenchBoard/Users-Wrench into master 2023-09-23 21:02:25 +00:00
ameye 557e0acba4 Merge branch 'add_job_detail' of WrenchBoard/Users-Wrench into master 2023-09-23 21:02:21 +00:00
Ebube 3e4dfadb41 .env 2023-09-22 15:09:18 +01:00
Ebube b480d49096 Merge branch 'master' of https://gitlab.chiefsoft.net/WrenchBoard/Users-Wrench into Recent-Activities 2023-09-22 15:05:07 +01:00
Ebube ece7bf41d3 Added Max 8 for family, added responsive breakpoints for wallet and layout 2023-09-22 15:03:57 +01:00
victorAnumudu 250dd0b171 Add job delivery details textarea tag size increased 2023-09-22 13:26:19 +01:00
victorAnumudu f2475ddd88 Add job delivery details expanded to accept 499 characters 2023-09-22 13:17:45 +01:00
ameye f302ca80d7 Merge branch 'recent-activities-height' of WrenchBoard/Users-Wrench into master 2023-09-19 07:55:39 +00:00
victorAnumudu 11b96e56da reduced the height of each activity and image removed 2023-09-19 06:03:12 +01:00
ameye daad9d18ec Merge branch 'card-adding-limit' of WrenchBoard/Users-Wrench into master 2023-09-18 22:06:47 +00:00
ameye 3c842f6606 Merge branch 'Recent-Activities' of WrenchBoard/Users-Wrench into master 2023-09-18 22:06:40 +00:00
Ebube 23ef007bb1 wallet card sizes and others 2023-09-18 22:45:51 +01:00
Ebube a9b9a17381 Merge branch 'master' of https://gitlab.chiefsoft.net/WrenchBoard/Users-Wrench into Recent-Activities 2023-09-18 22:42:29 +01:00
victorAnumudu d5e66618aa added limit to card and account adding 2023-09-18 20:46:17 +01:00
CHIEFSOFT\ameye 2e89f07ee2 env add 2023-09-18 12:17:54 -04:00
Ebube 1a51bcf0b5 Merge branch 'master' of https://gitlab.chiefsoft.net/WrenchBoard/Users-Wrench into Recent-Activities 2023-09-13 18:16:31 +01:00
Ebube f1ee5150a9 Fixed confirmation page and added comments 2023-09-13 18:16:04 +01:00
ameye 9db7f5c985 Merge branch 'signup-page-pathname-fix' of WrenchBoard/Users-Wrench into master 2023-09-13 06:21:36 +00:00
victorAnumudu d56363276b made the pathname to retain the country query param 2023-09-13 05:12:27 +01:00
ameye 5a9b49559b Merge branch 'signup-page-modification' of WrenchBoard/Users-Wrench into master 2023-09-12 19:14:16 +00:00
victorAnumudu fbc8228977 modified signup page to show select country list or defaults to country passed on search param 2023-09-12 20:05:56 +01:00
victorAnumudu a1140f7006 modified signup page to show select country list or defaults to country passed on search param 2023-09-12 20:05:22 +01:00
ameye f3561ff0fb Merge branch 'signup-country-param' of WrenchBoard/Users-Wrench into master 2023-09-12 15:58:35 +00:00
victorAnumudu 42b792ab9d made country auto filled if pathname contains country value 2023-09-12 15:24:30 +01:00
ameye 1e3a166172 Merge branch 'Recent-Activities' of WrenchBoard/Users-Wrench into master 2023-09-12 10:22:34 +00:00
ameye e1c6cb357e Merge branch 'wallet-fix' of WrenchBoard/Users-Wrench into master 2023-09-12 10:22:26 +00:00
Ebube 1aa64fba1f Merge branch 'master' of https://gitlab.chiefsoft.net/WrenchBoard/Users-Wrench into Recent-Activities 2023-09-12 09:51:01 +01:00
Ebube 4f0a6f67c3 Fixed Layout for confirm add and added comments 2023-09-12 09:50:36 +01:00
victorAnumudu 4f863f7b1d increase the wallet font and made it navigable 2023-09-12 06:28:01 +01:00
ameye b66f9d7ced Merge branch 'job-created-value' of WrenchBoard/Users-Wrench into master 2023-09-11 15:30:21 +00:00
ameye 493f209162 Merge branch 'Recent-Activities' of WrenchBoard/Users-Wrench into master 2023-09-11 15:30:17 +00:00
victorAnumudu 0aa109e280 added job created value to add job popout 2023-09-11 14:45:45 +01:00
Ebube b0a287c6a8 Merge branch 'master' of https://gitlab.chiefsoft.net/WrenchBoard/Users-Wrench into Recent-Activities 2023-09-11 14:29:34 +01:00
Ebube 8c00b157ad Modified the content layout and added formatted date 2023-09-11 14:29:11 +01:00
ameye 85bbd25bbb Merge branch 'add-job-dark-theme' of WrenchBoard/Users-Wrench into master 2023-09-11 11:51:23 +00:00
victorAnumudu 4d7e42fba8 dark theme for add jop popout fixed 2023-09-11 04:28:07 +01:00
ameye f91e8e9910 Merge branch 'Recent-Activities' of WrenchBoard/Users-Wrench into master 2023-09-10 06:00:27 +00:00
ameye 9e9b0b5557 Merge branch 'QR-value-update' of WrenchBoard/Users-Wrench into master 2023-09-10 06:00:17 +00:00
Ebube bfcfcdbf16 added variable for hiding social links 2023-09-10 06:25:36 +01:00
victorAnumudu 7e99e7ccab QR value updated 2023-09-10 05:19:19 +01:00
ameye fd9adb1c08 Merge branch 'QR-value' of WrenchBoard/Users-Wrench into master 2023-09-09 21:15:23 +00:00
victorAnumudu 7f2448efff family QR scan value changed 2023-09-09 21:29:24 +01:00
ameye 82ec224d66 Merge branch 'Recent-Activities' of WrenchBoard/Users-Wrench into master 2023-09-09 06:07:32 +00:00
Ebube 35ea200de5 change btn type to submit 2023-09-09 07:00:18 +01:00
ameye 7cdd387045 Merge branch 'resource_upload' of WrenchBoard/Users-Wrench into master 2023-09-08 19:54:33 +00:00
ameye 6b568ff56c Merge branch 'Recent-Activities' of WrenchBoard/Users-Wrench into master 2023-09-08 19:54:23 +00:00
ameye 8808a2f81c Merge branch 'twitter-logos-added' of WrenchBoard/Users-Wrench into master 2023-09-08 19:54:17 +00:00
Ebube 1991bab675 added active animations for job list popup 2023-09-07 16:53:16 +01:00
victorAnumudu f650097db2 implemented resource video and pdf upload 2023-09-07 12:21:05 +01:00
Ebube c427521ac3 dark mode styles added 2023-09-05 09:51:54 +01:00
victorAnumudu 2412059fd0 added new twitter logo in image folder 2023-09-05 06:36:43 +01:00
ameye 34dc467e8b Merge branch 'Recent-Activities' of WrenchBoard/Users-Wrench into master 2023-09-04 14:37:01 +00:00
Ebube 9fa16278ab Merge branch 'master' of https://gitlab.chiefsoft.net/WrenchBoard/Users-Wrench into Recent-Activities 2023-09-04 14:31:54 +01:00
Ebube cae02d1dc6 . 2023-09-04 14:31:28 +01:00
ameye abbd7b3993 Merge branch 'add-job-bug' of WrenchBoard/Users-Wrench into master 2023-09-04 12:44:22 +00:00
victorAnumudu 5523511524 Add Job Bug fixed 2023-09-04 10:49:04 +01:00
CHIEFSOFT\ameye 35db8ca7ec activities 2023-09-03 17:57:14 -04:00
CHIEFSOFT\ameye 51e0482a16 Hoe activities 2023-09-03 16:34:19 -04:00
ameye a615e25fcb Merge branch 'resources-tab-visibility' of WrenchBoard/Users-Wrench into master 2023-09-03 13:01:03 +00:00
victorAnumudu addede388e made resources tab visible if enabled 2023-09-03 06:26:19 +01:00
CHIEFSOFT\ameye 76474bb3b5 invite 2023-09-02 16:37:29 -04:00
tokslaw 6337c82374 Merge branch 'coupon-wallet-reload' of WrenchBoard/Users-Wrench into master 2023-08-29 16:57:03 +00:00
victorAnumudu a970411719 made wallet to reload on coupon redeem 2023-08-29 09:47:35 +01:00
ameye 10967acf9e Merge branch 'my-uploads-page' of WrenchBoard/Users-Wrench into master 2023-08-22 10:58:13 +00:00
Ebube fb6831d44f An Optimized version 2023-08-21 23:28:32 +01:00
Ebube 7dcae39320 BUG - Assign Job with less than amount 2023-08-21 22:41:21 +01:00
ameye a0ba60a2bc Merge branch 'flutter_key_removal' of WrenchBoard/Users-Wrench into master 2023-08-21 13:12:38 +00:00
victorAnumudu f377326b48 removed flutter api key from env variables 2023-08-21 11:38:51 +01:00
CHIEFSOFT\ameye feb8bd718d all status 2023-08-20 17:07:25 -04:00
ameye a2a2dbfc24 Merge branch 'my-uploads-page' of WrenchBoard/Users-Wrench into master 2023-08-18 10:46:24 +00:00
Ebube 1c0cb6c97f added the session parameter 2023-08-15 21:02:51 +01:00
ameye 41c9c60fd1 Merge branch 'terms-of-use-update' of WrenchBoard/Users-Wrench into master 2023-08-15 11:45:12 +00:00
victorAnumudu ba5646fe2c Merge master into terms-of-use-update 2023-08-13 07:59:40 +01:00
victorAnumudu 1f9ab45206 updated terms of use 2023-08-13 07:58:37 +01:00
tolik 0ffa1123d2 Hide Apple button 2023-08-13 10:56:42 +08:00
ameye 9420a201e4 Merge branch 'image-anchor-added' of WrenchBoard/Users-Wrench into master 2023-08-11 18:59:57 +00:00
ameye 1b7ea7321e Merge branch 'my-uploads-page' of WrenchBoard/Users-Wrench into master 2023-08-11 18:59:50 +00:00
victorAnumudu b3e273d712 file messages wrapped with anthor 2023-08-11 19:37:01 +01:00
Ebube 7fa74d6fd3 added img 2023-08-11 18:13:34 +01:00
CHIEFSOFT\ameye 02c195500a myfiles 2023-08-11 12:11:31 -04:00
ameye ca8e56c6dd Merge branch 'cancel-popout-removed' of WrenchBoard/Users-Wrench into master 2023-08-10 22:42:31 +00:00
victorAnumudu c265e9db33 popout removed 2023-08-10 22:06:30 +01:00
ameye 2861473ea3 Merge branch 'cancel-upload' of WrenchBoard/Users-Wrench into master 2023-08-10 19:41:46 +00:00
104 changed files with 5030 additions and 2905 deletions
+25 -2
View File
@@ -24,7 +24,6 @@ REACT_APP_SESSION_EXPIRE_CHECKER=60000
REACT_APP_LOGIN_ERROR_TIMEOUT=7000
REACT_APP_SIGNUP_ERROR_TIMEOUT=7000
REACT_APP_FLUTTERWAVE_APIKEY=FLWPUBK_TEST-54c90141b028789d671067bd72f781a9-X
# Had to change the error time to 3sec cause it took too long
REACT_APP_RESET_START_ERROR_TIMEOUT=3000
@@ -64,12 +63,36 @@ REACT_APP_APPLE_REDIRECT_URL='http://localhost:9082/login/auth/apple'
# /* 'client_id' => */ 'com.wrenchboard.users.client',
# /* 'client_secret' => */ 'eyJ0eXAiOiJKV1QiLCJhbGciOiJFUzI1NiIsImtpZCI6Ilc1V1RXQzlEVEoifQ.eyJpc3MiOiJKUjM2M0ZFWThSIiwiaWF0IjoxNjU0MDgzODQxLCJleHAiOjE2NTkyNjc4NDEsImF1ZCI6Imh0dHBzOi8vYXBwbGVpZC5hcHBsZS5jb20iLCJzdWIiOiJjb20ud3JlbmNoYm9hcmQudXNlcnMuY2xpZW50In0.TIPMwjS2MgSysqEuw3yu1nrOcrH-6omzerDhx0CadjWn2yCO8wZhQiAlhIFs7F-WPektIJ6h-2BT62yGrILiTA',
# /* 'redirect_uri' => */ site_url('login/auth/apple')
#SOCIALS Links Display
REACT_APP_APPLE_SOCIAL_LOGIN=0
REACT_APP_LINKEDIN_SOCIAL_LOGIN=0
#File Handling
REACT_APP_MAX_FILE_SIZE=1000000
REACT_APP_TOTAL_NUM_FILE=4
#Auth Text(s)
REACT_APP_LOGOUT_TEXT="Sign Out"
#apigate.lotus.g1.wrenchboard.com:76.209.103.227
#apigate.orion.g1.wrenchboard.com:76.209.103.227
#Cards Handling
REACT_APP_MAX_CREDIT_CARDS=4
REACT_APP_MAX_CREDIT_BANK_ACCOUNT=4
#Family
REACT_APP_MAX_FAMILY_MEMBERS=8
REACT_APP_SHOW_OFFER_GROUP_JOB=0
#UPLOAD PROFILE PICTURE
REACT_APP_SHOW_UPLOAD_PROFILE_PICTURE=0
#GOOGLE RECAPTCHA SITEKEY
REACT_APP_GOOGLE_RECAPTCHA_SITEKEY=6Ld_qKooAAAAADNL1TPzRLmcBA8vlpvx__39Rj39
#6LeIxAcTAAAAAJcZVRqyHh71UMIEGNQ_MXjiZKhI
#FAMILY MEMBER MINIMUM AGE
REACT_APP_FAMILY_MINIMUM_AGE=4
REACT_APP_FAMILY_MAXIMUM_AGE=18
+22 -2
View File
@@ -24,7 +24,6 @@ REACT_APP_SESSION_EXPIRE_CHECKER=60000
REACT_APP_LOGIN_ERROR_TIMEOUT=7000
REACT_APP_SIGNUP_ERROR_TIMEOUT=7000
REACT_APP_FLUTTERWAVE_APIKEY=FLWPUBK_TEST-54c90141b028789d671067bd72f781a9-X
# Had to change the error time to 3sec cause it took too long
REACT_APP_RESET_START_ERROR_TIMEOUT=3000
@@ -43,4 +42,25 @@ REACT_APP_GOOGLE_REDIRECT_URL=http://localhost:9082/login/auth/
REACT_APP_MAX_FILE_SIZE=1000000
REACT_APP_TOTAL_NUM_FILE=4
REACT_APP_LOGOUT_TEXT="Sign Out"
REACT_APP_LOGOUT_TEXT="Sign Out"
REACT_APP_APPLE_SOCIAL_LOGIN=0
REACT_APP_LINKEDIN_SOCIAL_LOGIN=0
REACT_APP_MAX_CREDIT_CARDS=4
REACT_APP_MAX_CREDIT_BANK_ACCOUNT=4
REACT_APP_MAX_FAMILY_MEMBERS=8
REACT_APP_SHOW_OFFER_GROUP_JOB=0
#UPLOAD PROFILE PICTURE
REACT_APP_SHOW_UPLOAD_PROFILE_PICTURE=0
#GOOGLE RECAPTCHA SITEKEY
REACT_APP_GOOGLE_RECAPTCHA_SITEKEY=6Ld_qKooAAAAADNL1TPzRLmcBA8vlpvx__39Rj39
#6LeIxAcTAAAAAJcZVRqyHh71UMIEGNQ_MXjiZKhI
#FAMILY MEMBER MINIMUM AGE
REACT_APP_FAMILY_MINIMUM_AGE=4
REACT_APP_FAMILY_MAXIMUM_AGE=18
+21 -2
View File
@@ -24,7 +24,6 @@ REACT_APP_SESSION_EXPIRE_CHECKER=60000
REACT_APP_LOGIN_ERROR_TIMEOUT=7000
REACT_APP_SIGNUP_ERROR_TIMEOUT=7000
REACT_APP_FLUTTERWAVE_APIKEY=FLWPUBK_TEST-54c90141b028789d671067bd72f781a9-X
# Had to change the error time to 3sec cause it took too long
REACT_APP_RESET_START_ERROR_TIMEOUT=3000
@@ -50,4 +49,24 @@ DISABLE_ESLINT_PLUGIN=true
REACT_APP_MAX_FILE_SIZE=1000000
REACT_APP_TOTAL_NUM_FILE=4
REACT_APP_LOGOUT_TEXT="Sign Out"
REACT_APP_LOGOUT_TEXT="Sign Out"
REACT_APP_APPLE_SOCIAL_LOGIN=0
REACT_APP_LINKEDIN_SOCIAL_LOGIN=0
REACT_APP_MAX_CREDIT_CARDS=4
REACT_APP_MAX_CREDIT_BANK_ACCOUNT=4
REACT_APP_MAX_FAMILY_MEMBERS=8
REACT_APP_SHOW_OFFER_GROUP_JOB=0
#UPLOAD PROFILE PICTURE
REACT_APP_SHOW_UPLOAD_PROFILE_PICTURE=0
#GOOGLE RECAPTCHA SITEKEY
REACT_APP_GOOGLE_RECAPTCHA_SITEKEY=6Ld_qKooAAAAADNL1TPzRLmcBA8vlpvx__39Rj39
#6LeIxAcTAAAAAJcZVRqyHh71UMIEGNQ_MXjiZKhI
#FAMILY MEMBER MINIMUM AGE
REACT_APP_FAMILY_MINIMUM_AGE=4
REACT_APP_FAMILY_MAXIMUM_AGE=18
+2
View File
@@ -19,6 +19,8 @@
.env.test.local
.env.production.local
#package-lock.json
npm-debug.log*
yarn-debug.log*
yarn-error.log*
+74
View File
@@ -22,13 +22,17 @@
"flutterwave-react-v3": "^1.3.0",
"formik": "^2.2.9",
"react": "^18.2.0",
"react-apple-login": "^1.1.6",
"react-chartjs-2": "^4.1.0",
"react-countup": "^6.2.0",
"react-dom": "^18.2.0",
"react-google-recaptcha": "^3.1.0",
"react-qr-code": "^2.0.11",
"react-redux": "^8.0.5",
"react-router-dom": "^6.0.2",
"react-scripts": "5.0.1",
"react-slick": "^0.29.0",
"react-to-print": "^2.14.12",
"react-toastify": "^9.0.1",
"redux": "^4.2.0",
"slick-carousel": "^1.8.1",
@@ -14801,6 +14805,11 @@
"teleport": ">=0.2.0"
}
},
"node_modules/qr.js": {
"version": "0.0.0",
"resolved": "https://registry.npmjs.org/qr.js/-/qr.js-0.0.0.tgz",
"integrity": "sha512-c4iYnWb+k2E+vYpRimHqSu575b1/wKl4XFeJGpFmrJQz5I88v9aY2czh7s0w36srfCM1sXgC/xpoJz5dJfq+OQ=="
},
"node_modules/qs": {
"version": "6.11.0",
"resolved": "https://registry.npmjs.org/qs/-/qs-6.11.0.tgz",
@@ -14944,6 +14953,32 @@
"url": "https://opencollective.com/core-js"
}
},
"node_modules/react-apple-login": {
"version": "1.1.6",
"resolved": "https://registry.npmjs.org/react-apple-login/-/react-apple-login-1.1.6.tgz",
"integrity": "sha512-ySV6ax0aB+ksA7lKzhr4MvsgjwSH068VtdHJXS+7rL380IJnNQNl14SszR31k3UqB8q8C1H1oyjJFGq4MyO6tw==",
"engines": {
"node": ">=8",
"npm": ">=5"
},
"peerDependencies": {
"prop-types": "^15.5.4",
"react": "^15.0.0 || ^16.0.0 || ^17.0.0 || ^18.0.0",
"react-dom": "^15.0.0 || ^16.0.0 || ^17.0.0 || ^18.0.0"
}
},
"node_modules/react-async-script": {
"version": "1.2.0",
"resolved": "https://registry.npmjs.org/react-async-script/-/react-async-script-1.2.0.tgz",
"integrity": "sha512-bCpkbm9JiAuMGhkqoAiC0lLkb40DJ0HOEJIku+9JDjxX3Rcs+ztEOG13wbrOskt3n2DTrjshhaQ/iay+SnGg5Q==",
"dependencies": {
"hoist-non-react-statics": "^3.3.0",
"prop-types": "^15.5.0"
},
"peerDependencies": {
"react": ">=16.4.1"
}
},
"node_modules/react-chartjs-2": {
"version": "4.3.1",
"resolved": "https://registry.npmjs.org/react-chartjs-2/-/react-chartjs-2-4.3.1.tgz",
@@ -15044,11 +15079,41 @@
"resolved": "https://registry.npmjs.org/react-fast-compare/-/react-fast-compare-2.0.4.tgz",
"integrity": "sha512-suNP+J1VU1MWFKcyt7RtjiSWUjvidmQSlqu+eHslq+342xCbGTYmC0mEhPCOHxlW0CywylOC1u2DFAT+bv4dBw=="
},
"node_modules/react-google-recaptcha": {
"version": "3.1.0",
"resolved": "https://registry.npmjs.org/react-google-recaptcha/-/react-google-recaptcha-3.1.0.tgz",
"integrity": "sha512-cYW2/DWas8nEKZGD7SCu9BSuVz8iOcOLHChHyi7upUuVhkpkhYG/6N3KDiTQ3XAiZ2UAZkfvYKMfAHOzBOcGEg==",
"dependencies": {
"prop-types": "^15.5.0",
"react-async-script": "^1.2.0"
},
"peerDependencies": {
"react": ">=16.4.1"
}
},
"node_modules/react-is": {
"version": "16.13.1",
"resolved": "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz",
"integrity": "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ=="
},
"node_modules/react-qr-code": {
"version": "2.0.12",
"resolved": "https://registry.npmjs.org/react-qr-code/-/react-qr-code-2.0.12.tgz",
"integrity": "sha512-k+pzP5CKLEGBRwZsDPp98/CAJeXlsYRHM2iZn1Sd5Th/HnKhIZCSg27PXO58zk8z02RaEryg+60xa4vyywMJwg==",
"dependencies": {
"prop-types": "^15.8.1",
"qr.js": "0.0.0"
},
"peerDependencies": {
"react": "^16.x || ^17.x || ^18.x",
"react-native-svg": "*"
},
"peerDependenciesMeta": {
"react-native-svg": {
"optional": true
}
}
},
"node_modules/react-redux": {
"version": "8.0.5",
"resolved": "https://registry.npmjs.org/react-redux/-/react-redux-8.0.5.tgz",
@@ -15218,6 +15283,15 @@
"react-dom": "^0.14.0 || ^15.0.1 || ^16.0.0 || ^17.0.0 || ^18.0.0"
}
},
"node_modules/react-to-print": {
"version": "2.14.15",
"resolved": "https://registry.npmjs.org/react-to-print/-/react-to-print-2.14.15.tgz",
"integrity": "sha512-SKnwOzU2cJ8eaAkoJO7+gNhvfEDmm+Y34IdcHsjtHioUevUPhprqbVtvNJlZ2JkGJ8ExK2QNWM9pXECTDR5D8w==",
"peerDependencies": {
"react": "^15.0.0 || ^16.0.0 || ^17.0.0 || ^18.0.0",
"react-dom": "^15.0.0 || ^16.0.0 || ^17.0.0 || ^18.0.0"
}
},
"node_modules/react-toastify": {
"version": "9.1.1",
"resolved": "https://registry.npmjs.org/react-toastify/-/react-toastify-9.1.1.tgz",
+1
View File
@@ -21,6 +21,7 @@
"react-chartjs-2": "^4.1.0",
"react-countup": "^6.2.0",
"react-dom": "^18.2.0",
"react-google-recaptcha": "^3.1.0",
"react-qr-code": "^2.0.11",
"react-redux": "^8.0.5",
"react-router-dom": "^6.0.2",
+1
View File
@@ -0,0 +1 @@
{"name":"","short_name":"","icons":[{"src":"/android-chrome-192x192.png","sizes":"192x192","type":"image/png"},{"src":"/android-chrome-512x512.png","sizes":"512x512","type":"image/png"}],"theme_color":"#ffffff","background_color":"#ffffff","display":"standalone"}
+1
View File
@@ -57,6 +57,7 @@ export default function Routers() {
{/* guest routes */}
<Route exact path="/login" element={<LoginPage />} />
<Route exact path="/eoffer" element={<LoginPage />} />
<Route exact path="/invite" element={<LoginPage />} />
<Route exact path="/signup" element={<SignupPage />} />
<Route exact path="/login/auth" element={<AuthRedirect />} />
Binary file not shown.

After

Width:  |  Height:  |  Size: 71 KiB

+10
View File
@@ -0,0 +1,10 @@
<svg xmlns="http://www.w3.org/2000/svg" enable-background="new 0 0 64 64" viewBox="0 0 64 64" id="wallet"><path d="M60.94,33.76H59.6V19.85c0-1.69-1.37-3.06-3.06-3.06h-1.61l-0.47-6.54c-0.12-1.67-1.57-2.94-3.27-2.84l-7.3,0.52
l-0.18-2.48c-0.12-1.67-1.57-2.95-3.27-2.84L7.83,4.93c-0.82,0.06-1.56,0.43-2.1,1.05C5.19,6.6,4.93,7.39,4.99,8.2l0.61,8.59H3.06
C1.37,16.79,0,18.17,0,19.85v38.48c0,1.69,1.37,3.06,3.06,3.06h53.48c1.69,0,3.06-1.37,3.06-3.06V44.43h1.33
c1.69,0,3.06-1.37,3.06-3.06v-4.55C64,35.13,62.63,33.76,60.94,33.76z M51.42,9.94c0.27,0,0.51,0.22,0.53,0.49l0.45,6.36H18.55
l-0.28-3.97c-0.01-0.19,0.07-0.32,0.13-0.38c0.05-0.06,0.17-0.17,0.36-0.18L51.42,9.94z M7.65,7.64C7.7,7.58,7.82,7.47,8.01,7.46
l32.66-2.32c0.27,0,0.51,0.22,0.53,0.49l0.18,2.48L18.58,9.73c-0.82,0.06-1.56,0.43-2.1,1.05c-0.54,0.62-0.8,1.41-0.74,2.22
l0.27,3.79H8.14L7.52,8.02C7.5,7.83,7.59,7.7,7.65,7.64z M57.07,58.34c0,0.28-0.24,0.53-0.53,0.53H3.06
c-0.28,0-0.53-0.24-0.53-0.53V19.85c0-0.29,0.24-0.53,0.53-0.53h53.48c0.28,0,0.53,0.24,0.53,0.53v13.91h-4.43
c-1.69,0-3.06,1.37-3.06,3.06v4.55c0,1.69,1.37,3.06,3.06,3.06h4.43V58.34z M61.47,41.37c0,0.28-0.24,0.53-0.53,0.53h-8.3
c-0.28,0-0.53-0.24-0.53-0.53v-4.55c0-0.28,0.24-0.53,0.53-0.53h8.3c0.29,0,0.53,0.24,0.53,0.53V41.37z"></path></svg>

After

Width:  |  Height:  |  Size: 1.3 KiB

+1
View File
@@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 128 128" id="files"><circle cx="64" cy="64" r="64" fill="#EF4C45"></circle><path fill="#CD2E30" d="M128 64v-2L92.4 26.5l-63.1 74 27.2 27c2.4.3 4.9.4 7.4.4C99.3 128 128 99.3 128 64z"></path><path fill="#E5E8EC" d="M93.5 78.4V29c0-1.9-1.5-3.4-3.4-3.4H41.8c-1.9 0-3.4 1.5-3.4 3.4v49.4h55.1z"></path><path fill="#FFF" d="M89.5 78.4V44.6H78.1c-1.9 0-3.4-1.5-3.4-3.4V29.9H37.8c-1.9 0-3.4 1.5-3.4 3.4v45.1h55.1z"></path><path fill="#FFCC04" d="m79.3 65.9-1.5 4c-.5 1.2-1.6 2-2.9 2H53.1c-1.3 0-2.4-.8-2.9-2l-1.5-4c-.5-1.3-1.7-2.2-3.1-2.2H30.9c-1.9 0-3.4 1.5-3.4 3.4v28.8c0 3.5 2.9 6.4 6.4 6.4H94c3.5 0 6.4-2.9 6.4-6.4V67.1c0-1.9-1.5-3.4-3.4-3.4H82.5c-1.4 0-2.7.9-3.2 2.2z"></path><path fill="#CED4DF" d="m74.9 45.8-.1-.1.1.1z"></path><path fill="#22D2FC" d="M74.7 29.9v11.4c0 1.9 1.5 3.4 3.4 3.4h11.4L74.7 29.9z"></path><path fill="#FFF" d="M74.1 81.6H53.9c-2 0-3.7 1.6-3.7 3.7 0 2 1.6 3.7 3.7 3.7h20.2c2 0 3.7-1.6 3.7-3.7 0-2-1.7-3.7-3.7-3.7z"></path></svg>

After

Width:  |  Height:  |  Size: 1003 B

+1
View File
@@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 51 51" id="jpg"><circle cx="25.5" cy="25.5" r="24" fill="#FED000"></circle><path fill="#F39F03" d="M37.462 16.607v4.741a.924.924 0 0 1-.877.924v16.361c0 1.027-.84 1.867-1.867 1.867H18.282a1.866 1.866 0 0 1-1.867-1.867V22.272a.924.924 0 0 1-.877-.924v-4.741c0-.495.392-.906.877-.924v-1.316a1.86 1.86 0 0 1 1.867-1.867h16.436c1.027 0 1.867.83 1.867 1.867v1.316c.485.018.877.43.877.924z"></path><path fill="#FFF" d="M35.585 13.367v24.266c0 1.031-.836 1.867-1.867 1.867H17.282a1.867 1.867 0 0 1-1.867-1.867V13.367c0-1.031.836-1.867 1.867-1.867h16.436c1.03 0 1.867.836 1.867 1.867z" opacity=".96"></path><path fill="#33ACFE" d="M18.143 25.5h14.715v9.894H18.143z"></path><path fill="#273E56" d="M32.859 35.394h-7.622l3.959-4.357z"></path><path fill="#334861" d="M29.048 35.394H18.143l5.664-7.357z"></path><circle cx="27.697" cy="28.037" r="1.094" fill="#FED000"></circle><path fill="#EB4B33" d="M35.462 21.28H15.538a1 1 0 0 1-1-1v-4.604a1 1 0 0 1 1-1h19.924a1 1 0 0 1 1 1v4.605a1 1 0 0 1-1 1z"></path><path fill="#FFF" d="M22.511 18.797c0 .57-.155 1.499-1.475 1.499-.737 0-1.456-.403-1.456-1.37v-.384h.88v.199c0 .427.105.737.564.737.514 0 .514-.409.514-.725v-2.987h.973v3.03zm.857-3.031h1.996c1.109 0 1.53.7 1.53 1.42 0 .718-.421 1.418-1.53 1.418H24.34v1.587h-.973v-4.425zm.973 2.082h.756c.446 0 .855-.099.855-.663 0-.564-.409-.663-.855-.663h-.756v1.326zm6.36 1.84a1.49 1.49 0 0 1-1.214.608c-1.364 0-2.176-1.023-2.176-2.299 0-1.314.812-2.336 2.176-2.336.905 0 1.766.552 1.865 1.561h-.93c-.117-.496-.47-.743-.935-.743-.874 0-1.202.743-1.202 1.518 0 .738.328 1.48 1.202 1.48.638 0 .997-.334 1.053-.953h-.98V17.8h1.86v2.392h-.62l-.099-.502z"></path></svg>

After

Width:  |  Height:  |  Size: 1.7 KiB

+1
View File
@@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" version="1.2" viewBox="0 0 512 512" id="mp4-file"><circle cx="256" cy="256" r="256" fill="#8ABE4F"></circle><path fill="#7BA840" d="M511.9 262.5 346.6 96.8c-1.1-1.1-3.1-1.1-4.3 0l-37.4 43.4c-.6.6-.8 1.2-.7 1.9l-3.4-6.1H159.2c-13.1 0-23.7 10.6-23.7 23.7v145.6l7.9 5.9h-16.2v66.6l8.3 7.9v6.6c0 7.6 3.5 14.3 9 18.6.7.9 58.1 58.3 100.6 100.9 3.6.1 7.2.2 10.8.2 139.3 0 252.5-111.1 256-249.5z"></path><g fill="#FFF"><path d="M169.1 193.7h145.4V206H169.1zM169.1 213.5h145.4v12.3H169.1zM169.1 233.4h145.4v12.3H169.1zM169.1 253.3h145.4v12.3H169.1zM169.1 273.1H282v12.3H169.1zM384.1 140.2l-37.4-43.4c-1.1-1.1-3.1-1.1-4.3 0L305 140.2c-1.7 1.6-.4 4.3 2.1 4.3h17.6v34h39.8v-34h17.6c2.3 0 3.6-2.6 2-4.3zM324.6 184.7h39.8v15.5h-39.8zM324.6 212c0 1.4 1.3 2.6 2.9 2.6h34c1.6 0 2.9-1.1 2.9-2.6v-5.7h-39.8v5.7z"></path><path d="M339.7 389.3c0 10.1-8.2 18.3-18.3 18.3H162.2c-10.1 0-18.3-8.2-18.3-18.3v-5.6h-8.4v8.6c0 13.1 10.6 23.7 23.7 23.7h165.2c13.1 0 23.7-10.6 23.7-23.7V220.4h-8.4v168.9zM143.9 162.7c0-10.1 8.2-18.3 18.3-18.3h136.4c-.9-2.8-.2-6 1.9-8.2l.2-.3H159.2c-13.1 0-23.7 10.6-23.7 23.7v145.6h8.4V162.7z"></path><path d="M313.2 344.5c0-18.4-14.9-33.3-33.3-33.3H127.2v66.6h152.7c18.4 0 33.3-14.9 33.3-33.3zm-109 19.2h-6.4v-27.6h-.2L187 363.7h-4.4l-10.8-28.1-.2.1v28.1h-6.4v-38.4h8.4l11 29.5h.2l11.1-29.5h8.2v38.3zm34.4-17.8c-2.4 2.2-5.7 3.3-10 3.3h-8.3v14.6h-6.4v-38.4h14.7c4.2 0 7.6 1.1 10 3.3 2.4 2.2 3.6 5.1 3.6 8.6s-1.2 6.4-3.6 8.6zm36.5 9.3h-4.9v8.5h-6.4v-8.5h-16.1l-.2-3.9 16-26h6.6V350h4.9v5.2z"></path><path d="m263.1 335.8-9.2 14.2h9.9v-15.7h-.1zM228.6 330.4h-8.3V344h8.3c2.4 0 4.2-.6 5.4-1.9 1.2-1.3 1.8-2.9 1.8-4.8s-.6-3.6-1.8-4.9c-1.2-1.3-3-2-5.4-2z"></path></g></svg>

After

Width:  |  Height:  |  Size: 1.7 KiB

+1
View File
@@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" enable-background="new 0 0 90 90" viewBox="0 0 90 90" id="pdf"><circle cx="45" cy="45" r="44.5" fill="#84D2ED"></circle><polygon fill="#EFC41C" stroke="#010101" stroke-linejoin="round" stroke-miterlimit="10" stroke-width="1.978" points="35.6 18.8 35.6 31.9 22.5 31.9 22.5 71.2 62.8 71.2 62.8 18.8"></polygon><polygon fill="#8CC749" stroke="#010101" stroke-miterlimit="10" stroke-width="1.978" points="22.5 31.9 22.5 31.9 22.5 31.9"></polygon><polygon fill="#EB665F" stroke="#010101" stroke-linejoin="round" stroke-miterlimit="10" stroke-width="1.978" points="35.6 18.8 22.5 31.9 22.5 31.9 35.6 31.9"></polygon><rect width="35.9" height="15.7" x="31.6" y="40.2" fill="#F2DFD5" stroke="#010101" stroke-linejoin="round" stroke-miterlimit="10" stroke-width="1.978"></rect><path fill="#010101" d="M43 44c.7.6 1 1.5 1 2.7 0 1.2-.4 2.1-1.1 2.7-.7.6-1.8.9-3.3.9h-1.3v2.8h-2.2v-9.9h3.5C41.2 43.1 42.3 43.4 43 44zM41.4 47.9c.3-.3.4-.7.4-1.3 0-.6-.2-1-.5-1.2C41 45.2 40.4 45 39.7 45h-1.3v3.3h1.5C40.6 48.4 41.2 48.2 41.4 47.9zM53.4 44.4c.9.9 1.4 2.1 1.4 3.6 0 1.5-.5 2.8-1.4 3.7-.9.9-2.3 1.3-4.2 1.3h-3.4v-9.9h3.5C51.1 43.1 52.5 43.6 53.4 44.4zM51.8 50.3c.5-.5.8-1.3.8-2.2s-.3-1.7-.8-2.3c-.5-.5-1.4-.8-2.5-.8H48v6h1.4C50.5 51.1 51.2 50.9 51.8 50.3zM63.6 43.1v1.9H59v2.1h4.4v1.9H59v3.9h-2.2v-9.9H63.6z"></path></svg>

After

Width:  |  Height:  |  Size: 1.3 KiB

+1
View File
@@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" width="655.359" height="655.359" fill-rule="evenodd" clip-rule="evenodd" image-rendering="optimizeQuality" shape-rendering="geometricPrecision" text-rendering="geometricPrecision" viewBox="0 0 6.827 6.827" id="png-file"><circle cx="3.413" cy="3.413" r="3.413" fill="#42a5f5"></circle><path fill="#fffffe" d="M1.744 4.074h.27V1.75a.164.164 0 0 1 .164-.164H4.3l.023.022.544.545.023.023v1.898h.191a.198.198 0 0 1 .198.199v.77a.198.198 0 0 1-.198.198H1.744a.198.198 0 0 1-.198-.199v-.769a.198.198 0 0 1 .198-.199zm.425 0h2.567V2.24l-.004-.004h-.498v-.495H2.178a.009.009 0 0 0-.007.003.009.009 0 0 0-.002.006v2.324zm.389-.7h1.789v.156h-1.79v-.156zm0-.544h1.789v.156h-1.79V2.83zm.07 2.094V4.37h.179c.068 0 .112.003.132.009a.145.145 0 0 1 .08.054.172.172 0 0 1 .032.107.18.18 0 0 1-.018.086.152.152 0 0 1-.047.055.162.162 0 0 1-.058.026.637.637 0 0 1-.116.008h-.073v.209h-.111zm.111-.46v.157H2.8a.29.29 0 0 0 .089-.009.074.074 0 0 0 .034-.027.075.075 0 0 0 .013-.043.072.072 0 0 0-.018-.05.077.077 0 0 0-.044-.024.532.532 0 0 0-.08-.004h-.055zm.405.46V4.37h.109l.226.37v-.37h.104v.554h-.112l-.223-.361v.36h-.104zm.814-.204v-.093H4.2v.22a.372.372 0 0 1-.237.086.304.304 0 0 1-.15-.036.232.232 0 0 1-.098-.105.337.337 0 0 1-.032-.147.32.32 0 0 1 .036-.154.244.244 0 0 1 .106-.102.285.285 0 0 1 .132-.028.26.26 0 0 1 .161.043.195.195 0 0 1 .075.12l-.111.02a.118.118 0 0 0-.044-.064.133.133 0 0 0-.08-.024.152.152 0 0 0-.117.047c-.029.03-.043.077-.043.138 0 .065.014.114.044.147.029.033.067.05.114.05a.194.194 0 0 0 .07-.014.244.244 0 0 0 .061-.034v-.07h-.128z"></path><path fill="none" d="M1.547 1.547H5.28V5.28H1.547z"></path></svg>

After

Width:  |  Height:  |  Size: 1.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 100 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 101 KiB

+3
View File
@@ -0,0 +1,3 @@
<svg width="1200" height="1227" viewBox="0 0 1200 1227" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M714.163 519.284L1160.89 0H1055.03L667.137 450.887L357.328 0H0L468.492 681.821L0 1226.37H105.866L515.491 750.218L842.672 1226.37H1200L714.137 519.284H714.163ZM569.165 687.828L521.697 619.934L144.011 79.6944H306.615L611.412 515.685L658.88 583.579L1055.08 1150.3H892.476L569.165 687.854V687.828Z" fill="white"/>
</svg>

After

Width:  |  Height:  |  Size: 430 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 67 KiB

+105 -88
View File
@@ -1,6 +1,6 @@
import { Field, Form, Formik } from "formik";
import React, { useEffect, useState } from "react";
import { useDispatch } from "react-redux";
import { useDispatch, useSelector } from "react-redux";
import * as Yup from "yup";
import usersService from "../../services/UsersService";
import { tableReload } from "../../store/TableReloads";
@@ -11,7 +11,7 @@ const validationSchema = Yup.object().shape({
country: Yup.string()
.min(1, "Minimum 3 characters")
.max(25, "Maximum 25 characters")
.required("Country is required"),
.required("Currency is required"),
price: Yup.string()
.typeError("Invalid number")
.min(1, "Price must be greater than 0")
@@ -32,7 +32,7 @@ const validationSchema = Yup.object().shape({
.required("Description is required"),
job_detail: Yup.string()
.min(3, "Minimum 3 characters")
.max(1440, "Maximum 1440 characters")
.max(499, "Maximum 499 characters")
.required("Details is required"),
timeline_days: Yup.number()
.typeError("you must specify a number")
@@ -43,15 +43,9 @@ const validationSchema = Yup.object().shape({
function AddJob({ popUpHandler, categories }) {
const ApiCall = new usersService();
const { walletDetails } = useSelector((state) => state.walletDetails);
let dispatch = useDispatch();
let [currency, setCurrency] = useState({
loading: true,
status: false,
data: null,
}); // To Hold the array of currency getUserCurrency returns
let initialValues = {
// initial values for formik
country: "",
@@ -69,30 +63,15 @@ function AddJob({ popUpHandler, categories }) {
message: "",
}); // Holds state when submit button is pressed
// FUNCTION TO GET Currency
const getUserCurrency = () => {
setCurrency((prev) => ({ ...prev, loading: true }));
ApiCall.getUserWallets()
.then((res) => {
if (res.data.internal_return < 0) {
setCurrency({ loading: false, status: true, data: [] });
return;
}
setCurrency({
loading: false,
status: true,
data: res.data.result_list,
});
})
.catch((err) => {
setCurrency({ loading: false, status: false, data: [] });
});
const getWalletDetail = (country) => { // A FUNCTION TO GET USER BALANCE BASED ON COUNTRY SELECTED
const walletChecker = walletDetails?.data.find(
(item) => item.country === country
);
return walletChecker ? walletChecker.amount : 0;
};
// FUNCTION TO HANDLE ADD JOB FORM
const handleAddJob = (values, helpers) => {
let reqData = {
const handleAddJob = async (values, helpers) => {
const reqData = {
country: values?.country,
price: Number(values.price) * 100,
title: values?.title,
@@ -102,20 +81,35 @@ function AddJob({ popUpHandler, categories }) {
category: values.category?.join("@"),
};
const walletAmount = getWalletDetail(reqData.country); // GETTING USER BALANCE BASED ON COUNTRY SELECTED
if (reqData.price > walletAmount) {
setRequestStatus({
loading: false,
status: false,
message: "Insufficient Balance",
});
setTimeout(() => {
setRequestStatus({ loading: false, status: false, message: "" });
}, 1500);
return;
}
setRequestStatus({ loading: true, status: false, message: "" });
ApiCall.jobManagerCreateJob(reqData)
.then((res) => {
if (res.data.internal_return < 1) {
setRequestStatus({
loading: false,
status: false,
message: "Could not complete your request at the moment",
});
setTimeout(() => {
popUpHandler();
}, 1500);
return;
}
try {
const res = await ApiCall.jobManagerCreateJob(reqData);
if (res.data.internal_return < 1) {
setRequestStatus({
loading: false,
status: false,
message: "Could not complete your request at the moment",
});
setTimeout(() => {
popUpHandler();
}, 1500);
} else {
setRequestStatus({
loading: false,
status: true,
@@ -125,27 +119,22 @@ function AddJob({ popUpHandler, categories }) {
dispatch(tableReload({ type: "JOBTABLE" }));
popUpHandler();
}, 1000);
})
.catch((err) => {
setRequestStatus({
loading: false,
status: false,
message: "Opps! something went wrong. Try Again",
});
})
.finally(() => {
setTimeout(() => {
setRequestStatus({ loading: false, status: false, message: "" });
}, 5000);
}
} catch (err) {
setRequestStatus({
loading: false,
status: false,
message: "Oops! Something went wrong. Try Again",
});
} finally {
setTimeout(() => {
setRequestStatus({ loading: false, status: false, message: "" });
}, 5000);
}
};
useEffect(() => {
getUserCurrency();
}, []);
return (
<div className="add-job p-5 w-full bg-white rounded-md flex flex-col justify-between">
<div className="add-job p-5 w-full bg-white dark:bg-dark-white dark:text-white rounded-md flex flex-col justify-between">
<Formik
initialValues={initialValues}
validationSchema={validationSchema}
@@ -164,26 +153,30 @@ function AddJob({ popUpHandler, categories }) {
className="input-label text-[#181c32] dark:text-white text-[13.975px] leading-[20.9625px] font-semibold flex item-center gap-1"
>
Currency
{props.errors.country && props.touched.country && <span className="text-[12px] text-red-500">{props.errors.country}</span>}
{props.errors.country && props.touched.country && (
<span className="text-[12px] text-red-500">
{props.errors.country}
</span>
)}
</label>
<select
id="country"
name="country"
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 border`}
onChange={props.handleChange}
onBlur={props.handleBlur}
>
{currency.loading ? (
{walletDetails.loading ? (
<option className="text-slate-500 text-lg" value="">
Loading...
</option>
) : currency.data.length ? (
) : walletDetails.data.length ? (
<>
<option className="text-slate-500 text-lg" value="">
Select a currency
</option>
{currency.data?.map((item, index) => (
{walletDetails.data?.map((item, index) => (
<option
key={index}
className="text-slate-500 text-lg"
@@ -207,14 +200,17 @@ function AddJob({ popUpHandler, categories }) {
fieldClass="px-6 text-right"
label="Price"
labelClass="tracking-wide"
inputBg="bg-slate-100"
type="number"
name="price"
placeholder="0"
value={props.values.price}
inputHandler={props.handleChange}
blurHandler={props.handleBlur}
error={props.errors.price && props.touched.price && props.errors.price}
error={
props.errors.price &&
props.touched.price &&
props.errors.price
}
/>
</div>
</div>
@@ -225,13 +221,16 @@ function AddJob({ popUpHandler, categories }) {
fieldClass="px-6"
label="Title"
labelClass="tracking-wide"
inputBg="bg-slate-100"
type="text"
name="title"
value={props.values.title}
inputHandler={props.handleChange}
blurHandler={props.handleBlur}
error={props.errors.title && props.touched.title && props.errors.title}
error={
props.errors.title &&
props.touched.title &&
props.errors.title
}
/>
</div>
@@ -241,13 +240,16 @@ function AddJob({ popUpHandler, categories }) {
fieldClass="px-6"
label="Description"
labelClass="tracking-wide"
inputBg="bg-slate-100"
type="text"
name="description"
value={props.values.description}
inputHandler={props.handleChange}
blurHandler={props.handleBlur}
error={props.errors.description && props.touched.description && props.errors.description}
error={
props.errors.description &&
props.touched.description &&
props.errors.description
}
/>
</div>
@@ -259,12 +261,17 @@ function AddJob({ popUpHandler, categories }) {
className='className="input-label text-[#181c32] dark:text-white text-[13.975px] leading-[20.9625px] font-semibold flex items-center gap-1'
>
Job Delivery Details
{props.errors.job_detail && props.touched.job_detail && <span className="text-[12px] text-red-500">{props.errors.job_detail}</span>}
{props.errors.job_detail &&
props.touched.job_detail && (
<span className="text-[12px] text-red-500">
{props.errors.job_detail}
</span>
)}
</label>
<textarea
id="Job Delivery Details"
rows="5"
className={`input-field px-3 py-2 placeholder:text-base text-dark-gray dark:text-white w-full h-[100px] bg-slate-100 dark:bg-[#11131F] focus:ring-0 focus:outline-[#dce4e9] rounded-[10px]`}
className={`input-field px-3 py-2 placeholder:text-base text-dark-gray dark:text-white w-full h-[100px] bg-slate-100 dark:bg-[#11131F] focus:ring-0 focus:outline-[#dce4e9] rounded-[10px] border`}
style={{ resize: "none" }}
name="job_detail"
value={props.values.job_detail}
@@ -286,19 +293,28 @@ function AddJob({ popUpHandler, categories }) {
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>
{categories ? (
<>
{Object?.entries(categories).map(([key, value]) => (
<label
key={key}
className="flex gap-1 w-full items-center"
>
<Field
type="checkbox"
name="category"
value={key}
/>
<span className="text-[13.975px]">{value}</span>
</label>
))}
</>
) : (
<label className="flex gap-1 w-full items-center">
<Field type="checkbox" name="category" />
<span className="text-[13.975px]">null</span>
</label>
))}
)}
<span className="h-5 text-sm italic text-[#cf3917]">
{props.errors.category &&
props.touched.category &&
@@ -324,7 +340,7 @@ function AddJob({ popUpHandler, categories }) {
<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 border ${
props.errors.timeline_days &&
props.touched.timeline_days
? "border-[#ff0a0a63] shadow-red-500 border-[0.5px] animate-shake"
@@ -335,6 +351,7 @@ function AddJob({ popUpHandler, categories }) {
<option value="">Select Duration</option>
{publicArray.map(({ name, duration }, idx) => (
<option
key={idx}
className="text-slate-500 text-lg"
value={duration}
>
@@ -1,26 +1,26 @@
import React, { useEffect } from 'react';
import { useLocation, useNavigate } from 'react-router-dom';
import usersService from '../../../services/UsersService';
import {updateUserDetails} from "../../../store/UserDetails";
import React, { useEffect } from "react";
import { useDispatch } from "react-redux";
import { useLocation, useNavigate } from "react-router-dom";
import usersService from "../../../services/UsersService";
import { updateUserDetails } from "../../../store/UserDetails";
import AuthLayout from "../AuthLayout";
function Redirect() {
const location = useLocation();
const navigate = useNavigate();
const userApi = new usersService();
const dispatch = useDispatch()
const location = useLocation();
const navigate = useNavigate();
const userApi = new usersService();
const dispatch = useDispatch();
const queryParams = new URLSearchParams(location?.search);
const codeResponse = queryParams.get("code");
const queryParams = new URLSearchParams(location?.search);
const codeResponse = queryParams.get("code");
useEffect(()=>{
if(!codeResponse){
navigate('/login', {state: {error: true}})
return
}
console.log(codeResponse);
/*
useEffect(() => {
if (!codeResponse) {
navigate("/login", { state: { error: true } });
return;
}
console.log(codeResponse);
/*
POST /token HTTP/1.1
Host: oauth2.googleapis.com
Content-Type: application/x-www-form-urlencoded
@@ -31,34 +31,40 @@ function Redirect() {
redirect_uri=https%3A//oauth2.example.com/code&
grant_type=authorization_code
*/
var reqData = {
auth_type: "GOOGLE",
code: codeResponse,
redirect_uri: process.env.REACT_APP_GOOGLE_REDIRECT_URL,
};
userApi
.authStart(reqData)
.then((res) => {
if (res.status == 200 && res.data.internal_return >= 0 && res.data.member_id && res.data.uid && res.data.session) {
localStorage.setItem("member_id", `${res.data.member_id}`);
localStorage.setItem("uid", `${res.data.uid}`);
localStorage.setItem("session_token", `${res.data.session}`);
dispatch(updateUserDetails({...res.data}));
navigate('/', {replace: true})
return
}
navigate('/login', {state: {error: true}})
})
.catch((error) => {
navigate('/login', {state: {error: true}})
console.log(error);
});
},[])
var reqData = {
auth_type: "GOOGLE",
code: codeResponse,
redirect_uri: process.env.REACT_APP_GOOGLE_REDIRECT_URL,
};
userApi
.authStart(reqData)
.then((res) => {
if (
res.status == 200 &&
res.data.internal_return >= 0 &&
res.data.member_id &&
res.data.uid &&
res.data.session
) {
localStorage.setItem("member_id", `${res.data.member_id}`);
localStorage.setItem("uid", `${res.data.uid}`);
localStorage.setItem("session_token", `${res.data.session}`);
dispatch(updateUserDetails({ ...res.data }));
navigate("/", { replace: true });
return;
}
navigate("/login", { state: { error: true } });
})
.catch((error) => {
navigate("/login", { state: { error: true } });
console.log(error);
});
}, []);
return (
<AuthLayout>
<div className='min-h-[70vh]'>Redirecting ... </div>
</AuthLayout>
)
<div className="min-h-[70vh]">Redirecting ... </div>
</AuthLayout>
);
}
export default Redirect
export default Redirect;
@@ -24,7 +24,7 @@ const ForgetPwdResponse = ({title, message, type}) => {
<button
type="button"
onClick={() => navigate("/login")}
className={`rounded-[0.475rem] mb-6 text-[15px] font-semibold text-white flex justify-center bg-[#4687ba] hover:bg-[#009ef7] transition-all duration-300 items-center py-[0.8875rem] px-[1.81rem]`}
className={`h-[48px] rounded-full mb-6 text-[15px] font-semibold text-white flex justify-center bg-[#4687ba] hover:bg-[#009ef7] transition-all duration-300 items-center py-[0.8875rem] px-[1.81rem]`}
>
<span>Home</span>
</button>
@@ -7,6 +7,8 @@ import AuthLayout from "../AuthLayout";
import EmailValidator from "../../../lib/EmailValidator";
import ForgetPwdResponse from "../ForgetPwdResponse";
import ReCAPTCHA from "react-google-recaptcha";
export default function ForgotPassword() {
const [checked, setValue] = useState(false);
const [resetLoading, setResetLoading] = useState(false);
@@ -22,9 +24,18 @@ export default function ForgotPassword() {
setMail(e?.target.value);
};
const humanChecker = () => {
setValue(!checked);
};
// const humanChecker = () => {
// setValue(!checked);
// };
function humanChecker(value) {
// console.log("Captcha value:", value);
if(value){
setValue(true)
}else{
setValue(false)
}
}
const resetHandler = async () => {
if (email == "") {
@@ -113,10 +124,16 @@ export default function ForgotPassword() {
/>
</div>
{/* hCaptha clone for the time being */}
<div className="mb-10">
<div className="mb-10 flex justify-center w-full">
<ReCAPTCHA
sitekey={process.env.REACT_APP_GOOGLE_RECAPTCHA_SITEKEY}
onChange={humanChecker}
/>
</div>
{/* <div className="mb-10">
<div className="w-[303px] h-[78px] mx-auto overflow-hidden">
<div className="w-[300px] h-[74px] bg-white bottom-[1px] rounded border-gray-100 overflow-hidden cursor-pointer">
{/* Checkbox */}
<div className="h-full relative inline-block">
<div className="relative table top-0 h-full">
<div className="table-cell align-middle">
@@ -148,18 +165,25 @@ export default function ForgotPassword() {
<div className="h-full relative inline-block w-16"></div>
</div>
</div>
</div>
</div> */}
{msgError && (
<div className="relative p-4 text-[#912741] bg-[#fcd9e2] border-[#fbc6d3] mb-4 rounded-[0.475rem] text-md font-light leading-[19.5px] text-[13px]">
{msgError}
</div>
)}
<div className="signin-area mb-3.5">
<div className="flex justify-center items-center gap-2">
<div className="flex justify-center items-center gap-4">
<button
type="button"
onClick={() => navigate("/login")}
className={`h-[48px] rounded-full mb-6 text-[15px] font-semibold text-white hover:text-white flex justify-center bg-red-500 hover:bg-red-600 transition-all duration-300 items-center py-[0.8875rem] px-[1.8125rem] `}
>
Cancel
</button>
<button
type="button"
onClick={resetHandler}
className={`rounded-[0.475rem] mb-6 text-[15px] font-semibold text-white flex justify-center bg-[#4687ba] hover:bg-[#009ef7] transition-all duration-300 items-center py-[0.8875rem] px-[1.81rem]`}
className={`h-[48px] rounded-full mb-6 text-[15px] font-semibold text-white flex justify-center bg-[#4687ba] hover:bg-[#009ef7] transition-all duration-300 items-center py-[0.8875rem] px-[1.81rem]`}
>
{resetLoading ? (
<div className="signup btn-loader"></div>
@@ -167,13 +191,6 @@ export default function ForgotPassword() {
<span>Send Code</span>
)}
</button>
<button
type="button"
onClick={() => navigate("/login")}
className={`rounded-[0.475rem] mb-6 text-[15px] font-semibold text-[#009ef7] hover:text-white flex justify-center bg-[#f1faff] hover:bg-[#009ef7] transition-all duration-300 items-center py-[0.8875rem] px-[1.8125rem] `}
>
Cancel
</button>
</div>
</div>
</div>
+109 -38
View File
@@ -14,10 +14,18 @@ import { useGoogleLogin } from "@react-oauth/google";
import { useDispatch } from "react-redux";
import { updateUserDetails } from "../../../store/UserDetails";
import ReCAPTCHA from "react-google-recaptcha";
export default function Login() {
const queryParams = new URLSearchParams(location?.search);
const sessionExpired = queryParams.get("sessionExpired")
const dispatch = useDispatch();
const { state } = useLocation();
const [validCaptcha, setValidCaptcha] = useState({show: false, valid:''}); // FOR CAPTCHA
let [loginType, setLoginType] = useState("");
const [loginLoading, setLoginLoading] = useState(false);
@@ -107,6 +115,14 @@ export default function Login() {
}, Number(process.env.REACT_APP_LOGIN_ERROR_TIMEOUT));
return;
}
if(name == "full" && !validCaptcha.valid && validCaptcha.show){ // RUNS AND DISPLAYS CAPTCHA, IF ERROR OCCURED DURING LOGIN FOR FULL LOGIN ALONE
setMsgError("Please Verify Captcha");
setLoginLoading(false);
setTimeout(() => {
setMsgError("");
}, Number(process.env.REACT_APP_LOGIN_ERROR_TIMEOUT));
return;
}
userApi
.logInUser(postData)
.then((res) => {
@@ -120,6 +136,7 @@ export default function Login() {
// setMsgError("Wrong, email/password");
setLoginError(true);
setLoginLoading(false);
setValidCaptcha(prev => ({...prev, show:true})) // DISPLAYS CAPTCHA IF ERROR
return;
}
localStorage.setItem("member_id", `${res.data.member_id}`);
@@ -135,6 +152,7 @@ export default function Login() {
.catch((error) => {
setMsgError("Unable to login, try again");
setLoginLoading(false);
setValidCaptcha(prev => ({...prev, show:true})) // DISPLAYS CAPTCHA IF ERROR
})
.finally(() => {
setTimeout(() => {
@@ -145,6 +163,14 @@ export default function Login() {
});
};
function captchaChecker(value) { // FUNCTION TO VALIDATE CAPTCHA
if(value){
setValidCaptcha({show: true, valid:value})
}else{
setValidCaptcha({show: true, valid:''})
}
}
const googleLogin = useGoogleLogin({
flow: "auth-code",
ux_mode: "redirect",
@@ -219,6 +245,9 @@ export default function Login() {
</div>
<div className="content-wrapper login shadow-md w-full lg:max-w-[530px] mx-auto flex justify-center items-center xl:bg-white dark:bg-dark-white 2xl:w-[828px] rounded-[0.475rem] sm:p-7 p-5">
<div className="w-full">
{/* HIDES THIS IF USER SESSION HAS EXPIRED */}
{sessionExpired != 'true' &&
<div className="title-area flex flex-col justify-center items-center relative text-center mb-7">
{/* <h1 className="text-[#181c32] font-semibold dark:text-white mb-3 leading-[27.3px] text-[22.75px]">
Sign In to WrenchBoard
@@ -233,6 +262,14 @@ export default function Login() {
</Link>
</span>
</div>
}
{/* SHOWS THIS IF USER SESSION HAS EXPIRED */}
{sessionExpired == 'true' &&
<div className="w-full p-1 mb-7">
<p className="text-red-500 text-base text-center">Your session expired and will need to login again</p>
</div>
}
{/* switch login component */}
<div className="ml-7 flex justify-start items-center gap-3">
@@ -296,6 +333,17 @@ export default function Login() {
forgotPassword
/>
</div>
{/* hCaptha clone for the time being */}
{validCaptcha.show &&
<div className="mb-5 flex justify-center w-full">
<ReCAPTCHA
sitekey={process.env.REACT_APP_GOOGLE_RECAPTCHA_SITEKEY}
onChange={captchaChecker}
/>
</div>
}
{loginError && (
<div className="relative p-4 text-[#912741] bg-[#fcd9e2] border-[#fbc6d3] mb-4 rounded-[0.475rem] text-md font-thin leading-[19.5px] text-[13px]">
Invalid username or password- Please{" "}
@@ -320,7 +368,7 @@ export default function Login() {
onClick={doLogin}
type="button"
disabled={loginLoading}
className={`btn-login rounded-[0.475rem] mb-6 text-xl text-white flex justify-center bg-[#4687ba] hover:bg-[#009ef7] transition-all duration-300 items-center text-[15px]`}
className={`btn-login rounded-full mb-6 text-xl text-white flex justify-center bg-[#4687ba] hover:bg-[#009ef7] transition-all duration-300 items-center text-[15px]`}
>
{loginLoading ? (
<div className="signup btn-loader"></div>
@@ -329,34 +377,51 @@ export default function Login() {
)}
</button>
</div>
<div className="sm:flex sm:justify-between sm:items-center sm:space-x-2">
<BrandBtn
link="#"
imgSrc={googleLogo}
brand="Google"
onClick={googleLogin}
/>
<BrandBtn
// link={`https://appleid.apple.com/auth/authorize?response_type=code&response_mode=form_post&client_id=${process.env.REACT_APP_APPLE_CLIENT_ID}&redirect_uri=https%3A%2F%2Fwork.wrenchboard.com%2Flogin%2Fauth%2Fapple&state=4b2c4456b7&scope=name+email`}
link={`https://appleid.apple.com/auth/authorize?response_type=code&response_mode=form_post&client_id=${process.env.REACT_APP_APPLE_CLIENT_ID}&redirect_uri=${process.env.REACT_APP_APPLE_REDIRECT_URL}&state=4b2c4456b7&scope=name+email`}
imgSrc={appleLogo}
brand="Apple"
isAnchor={true}
/>
</div>
<div className="sm:flex sm:justify-between sm:items-center sm:space-x-2">
<BrandBtn
link={`https://www.facebook.com/v14.0/dialog/oauth?client_id=${process.env.REACT_APP_FACEBOOK_CLIENT_ID}&redirect_uri=${process.env.REACT_APP_FACEBOOK_REDIRECT_URL}&scope=${process.env.REACT_APP_FACEBOOK_CLIENT_SCOPE}`}
imgSrc={facebookLogo}
brand="Facebook"
isAnchor={true}
/>
<BrandBtn
// link="https://www.linkedin.com/oauth/v2/authorization?response_type=code&client_id=YOUR_CLIENT_ID&redirect_uri=YOUR_REDIRECT_URI&scope=comma-separated-list-of-scopes&state=YOUR_STATE_VALUE"
imgSrc={linkedInLogo}
brand="LinkedIn"
isAnchor={true}
/>
<div className="sm:grid grid-cols-2 gap-1">
<div className="w-full">
<BrandBtn
link="#"
imgSrc={googleLogo}
brand="Google"
onClick={googleLogin}
/>
</div>
<div
className={`w-full ${
process.env.REACT_APP_APPLE_SOCIAL_LOGIN !== 0 &&
"hidden"
}`}
>
<BrandBtn
// link={`https://appleid.apple.com/auth/authorize?response_type=code&response_mode=form_post&client_id=${process.env.REACT_APP_APPLE_CLIENT_ID}&redirect_uri=https%3A%2F%2Fwork.wrenchboard.com%2Flogin%2Fauth%2Fapple&state=4b2c4456b7&scope=name+email`}
link={`https://appleid.apple.com/auth/authorize?response_type=code&response_mode=form_post&client_id=${process.env.REACT_APP_APPLE_CLIENT_ID}&redirect_uri=${process.env.REACT_APP_APPLE_REDIRECT_URL}&state=4b2c4456b7&scope=name+email`}
imgSrc={appleLogo}
brand="Apple"
isAnchor={true}
// style={{visibility: 'hidden'}}
/>
</div>
<div className="w-full">
<BrandBtn
link={`https://www.facebook.com/v14.0/dialog/oauth?client_id=${process.env.REACT_APP_FACEBOOK_CLIENT_ID}&redirect_uri=${process.env.REACT_APP_FACEBOOK_REDIRECT_URL}&scope=${process.env.REACT_APP_FACEBOOK_CLIENT_SCOPE}`}
imgSrc={facebookLogo}
brand="Facebook"
isAnchor={true}
/>
</div>
<div
className={`w-full ${
process.env.REACT_APP_LINKEDIN_SOCIAL_LOGIN !== 0 &&
"hidden"
}`}
>
<BrandBtn
// link="https://www.linkedin.com/oauth/v2/authorization?response_type=code&client_id=YOUR_CLIENT_ID&redirect_uri=YOUR_REDIRECT_URI&scope=comma-separated-list-of-scopes&state=YOUR_STATE_VALUE"
imgSrc={linkedInLogo}
brand="LinkedIn"
isAnchor={true}
/>
</div>
</div>
</div>
</div>
@@ -416,7 +481,7 @@ export default function Login() {
onClick={doLogin}
disabled={loginLoading}
type="button"
className={`btn-login rounded-[0.475rem] text-xl text-white flex justify-center bg-[#4687ba] hover:bg-[#009ef7] transition-all duration-300 items-center text-[15px]`}
className={`btn-login rounded-full text-xl text-white flex justify-center bg-[#4687ba] hover:bg-[#009ef7] transition-all duration-300 items-center text-[15px]`}
>
{loginLoading ? (
<div className="signup btn-loader"></div>
@@ -432,12 +497,11 @@ export default function Login() {
}
{/* END of login component */}
{loginType == "full" &&
<div className="pt-5 text-[#181c32] text-center font-semibold text-[13.975px] leading-[20.9625px]">
This site is protected by hCaptcha and the our Privacy Policy
and Terms of Service apply.
</div>
}
{loginType == "full" && (
<div className="pt-5 text-[#181c32] text-center font-semibold text-[13.975px] leading-[20.9625px]">
This site is protected by a Captcha. Our Privacy Policy and Terms of Service apply.
</div>
)}
</div>
</div>
</div>
@@ -446,7 +510,14 @@ export default function Login() {
);
}
const BrandBtn = ({ link, imgSrc, brand, onClick, isAnchor = false }) => {
const BrandBtn = ({
link,
imgSrc,
brand,
onClick,
isAnchor = false,
style = { visibility: "visible" },
}) => {
// const doGoogle = async () => {
// alert("start google");
// };
@@ -466,7 +537,7 @@ const BrandBtn = ({ link, imgSrc, brand, onClick, isAnchor = false }) => {
// alert("start facebook");
// };
return (
<div className="w-full sm:w-1/2 flex justify-center bottomMargin">
<div className="w-full flex justify-center bottomMargin" style={style}>
{isAnchor ? (
<a
href={link}
+49 -10
View File
@@ -1,20 +1,27 @@
import React, { useCallback, useEffect, useState } from "react";
import { Link, useNavigate } from "react-router-dom";
import { Link, useLocation, useNavigate } from "react-router-dom";
import WrenchBoard from "../../../assets/images/wrenchboard-logo-text.png";
import usersService from "../../../services/UsersService";
import InputCom from "../../Helpers/Inputs/InputCom";
import AuthLayout from "../AuthLayout";
export default function SignUp() {
// eslint-disable-next-line no-restricted-globals
const queryParams = new URLSearchParams(location?.search);
const country = queryParams.get("cnt")?.toUpperCase();
const {pathname} = useLocation()
const currentPath = country ? `${pathname}?cnt=${country.toLowerCase()}`:pathname // Determines the new pathname is country query params exist
const [signUpLoading, setSignUpLoading] = useState(false);
const [checked, setValue] = useState(false);
// for the catch error
const [msgError, setMsgError] = useState("");
const [showPassword, setShowPassword] = useState(false);
const [countries, setCountries] = useState([]);
const [countries, setCountries] = useState({loading:true, data:[]});
const [formData, setFormData] = useState({
country: "",
country: country? country : "",
first_name: "",
last_name: "",
email: "",
@@ -45,9 +52,18 @@ export default function SignUp() {
try {
if (res.status === 200) {
const { signup_country } = await res.data;
setCountries(signup_country);
// setCountries(signup_country);
if(country){ // IF LINK/PATHNAME HAS CNT QUERY VALUE
let cnt = signup_country.filter(item => item[0]==country) // test to see country passed in query param exist from list of countries supplied by API
if(!cnt.length){ // IF CNT EMPTY, SET FORMDATA COUNTRY BACK TO EMPTY STRING: RE: THIS IS BCOS WE INITAIL SET COUNTRY VALUE IN FORMDATA, IF COUNTRY PARAM IS PRESENT IN LINK
setFormData(prev => ({...prev, country: ''}))
return setCountries({loading: false, data: signup_country});
}
return setCountries({loading: false, data: cnt});
}
setCountries({loading: false, data:signup_country});
} else if (res.data.result !== 100) {
setCountries("Nothing see here!");
setCountries({loading: false, data:[]});
}
} catch (error) {
throw new Error(error);
@@ -134,7 +150,7 @@ export default function SignUp() {
<AuthLayout slogan="Welcome to WrenchBoard">
<div className="w-full">
<div className="mb-5">
<Link to="#">
<Link to={currentPath}>
<img
src={WrenchBoard}
alt="wrenchboard"
@@ -172,6 +188,7 @@ export default function SignUp() {
name="country"
value={formData.country}
inputHandler={handleInputChange}
disable={country && countries?.data?.length <= 1 ? true : false}
/>
<div className="input-fl-name mb-5 sm:flex w-full sm:space-x-6 ">
<div className="input-item sm:w-1/2 w-full mb-5 sm:mb-0">
@@ -306,9 +323,10 @@ export default function SignUp() {
<div className="signin-area mb-1">
<div className="flex justify-center">
<button
disabled={countries.loading}
type="button"
onClick={handleSignUp}
className={`rounded-[0.475rem] mb-6 text-white flex justify-center bg-[#4687ba] hover:bg-[#009ef7] transition-all duration-300 items-center h-[42px] py-[0.8875rem] px-[1.81rem] text-[14.95px] btn-login`}
className={`rounded-full mb-6 text-white flex justify-center bg-[#4687ba] hover:bg-[#009ef7] transition-all duration-300 items-center h-[42px] py-[0.8875rem] px-[1.81rem] text-[14.95px] btn-login`}
>
{signUpLoading ? (
<div className="signup btn-loader"></div>
@@ -333,6 +351,7 @@ const SelectOption = ({
inputHandler,
value,
data, // passing the data from parent
disable
}) => {
return (
<div className="input-com mb-7">
@@ -346,19 +365,39 @@ const SelectOption = ({
</div>
<div>
<select
disabled={disable}
name={name}
id={name}
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 focus-visible:border-transparent focus-visible:outline-0 focus-visible:ring-transparent "
onChange={inputHandler}
value={value}
>
<option value={""}>Select your Country</option>
{data?.length > 0 &&
data?.map((item, idx) => (
{data?.data?.length > 1 ?
<>
<option value={""}>Select your Country</option>
{data?.data?.map((item, idx) => (
<option value={item[0]} key={idx}>
{item[1]}
</option>
))}
</>
:
data?.data?.length == 1 ?
data?.data?.map((item, idx) => (
<option value={item[0]} key={idx}>
{item[1]}
</option>
))
:
data?.data?.length < 1 && data.loading ?
<option value=''>
Loading...
</option>
:
<option value=''>
No Country Found!
</option>
}
</select>
</div>
</div>
@@ -54,6 +54,8 @@ export default function VerifyLink() {
localStorage.setItem("member_id", `${data?.member_id}`);
localStorage.setItem("session_token", `${data?.session}`);
localStorage.setItem("session", `${data?.session}`);
localStorage.setItem("uid", data?.uid)
navigate("/", { replace: true });
setLinkLoader(false);
+3 -26
View File
@@ -1,9 +1,7 @@
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 { useEffect, useState } from "react";
import dataImage2 from "../../assets/images/taskbanners/default.jpg";
import { PriceFormatter } from "../Helpers/PriceFormatter";
import dataImage2 from "../../assets/images/data-table-user-2.png";
import MarketPopUp from "../MarketPlace/PopUp/MarketPopUp";
export default function AvailableJobsCard({
className,
@@ -156,27 +154,6 @@ export default function AvailableJobsCard({
</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>
+188 -87
View File
@@ -1,19 +1,18 @@
import React, {
Suspense,
lazy,
useCallback,
useEffect,
useMemo,
useRef,
useState,
useTransition,
} from "react";
import { useReactToPrint } from "react-to-print";
import profile from "../../assets/images/profile-info-profile.png";
import profile from "../../assets/images/icons/family.svg";
import localImgLoad from "../../lib/localImgLoad";
import usersService from "../../services/UsersService";
import LoadingSpinner from "../Spinners/LoadingSpinner";
import AssignTaskPopout from "./FamilyPopout/AssignTaskPopout";
import FamilyWallet from "./Tabs/FamilyWallet";
// Lazy Imports for components
const FamilyWaitlist = lazy(() => import("./Tabs/FamilyWaitlist"));
@@ -29,113 +28,113 @@ export default function FamilyManageTabs({
listReload,
loader,
}) {
// Initial state for family details
const initialDetailState = {
loading: false,
data: null,
};
// State for family details, tasks, waitlist, and pending
const [details, setDetails] = useState({
familyDetails: { loading: false, data: null },
familyTasks: { loading: false, data: null },
familyWaitList: { loading: false, data: null },
familyPending: { loading: false, data: null },
familyDetails: { ...initialDetailState },
familyTasks: { ...initialDetailState },
familyWaitList: { ...initialDetailState },
familyPending: { ...initialDetailState },
});
// Function to reset family details, tasks, waitlist, and pending
const resetDetails = () => {
setDetails({
familyDetails: { ...initialDetailState },
familyTasks: { ...initialDetailState },
familyWaitList: { ...initialDetailState },
familyPending: { ...initialDetailState },
});
};
const [updatePage, setUpdatePage] = useState(false) // State to determine when to update the page
// State for family task data
const [familyTask, setFamilyTask] = useState({ loading: false, data: [] });
// State for active task
const [activeTask, setActiveTask] = useState({ id: 0, data: {} });
// State for error messages
const [errMsg, setErrMsg] = useState("");
// State for family task popout
const [familyTaskPopout, setFamilyTaskPopout] = useState(false);
// State for profile image
const [profileImg, setProfileImg] = useState(profile);
// Ref for profile image input
const profileImgInput = useRef(null);
const [isPending, startTransition] = useTransition();
// Create an instance of the usersService class
const apiCall = useMemo(() => new usersService(), []);
// Function to handle toggling the family task popout
const familyPopUpHandler = () => {
setFamilyTaskPopout((prev) => !prev);
};
// Function to trigger a click on the hidden profile image input
const browseProfileImg = () => {
profileImgInput.current.click();
};
/**
* Handles the change event of the profile image input field.
* Checks if the selected file exceeds the maximum file size limit and displays an alert if it does.
* If the file is within the size limit, it reads the file using the FileReader API and sets the profile image state with the result.
*/
const profileImgChangeHandler = (e) => {
if (e.target.value !== "") {
const MAX_FILE_SIZE = 5 * 1024 * 1024; // 5MB
const file = e.target.files[0];
if (file && file.size > MAX_FILE_SIZE) {
alert("File size exceeds the limit.");
return;
}
if (file) {
const imgReader = new FileReader();
imgReader.onload = (event) => {
setProfileImg(event.target.result);
imgReader.onload = () => {
const imageDataUrl = imgReader.result;
// Set the profile image
setProfileImg(imageDataUrl);
};
imgReader.readAsDataURL(e.target.files[0]);
imgReader.readAsDataURL(file);
}
};
const manageFamily = useCallback(async () => {
try {
setDetails((prevDetails) => ({
...prevDetails,
familyDetails: { loading: true },
familyTasks: { loading: true },
familyWaitList: { loading: true },
familyPending: { loading: true },
}));
const { family_uid } = accountDetails;
const reqData = { family_uid };
const [familyRes, tasksRes, familyWaitRes, familyPending] =
await Promise.all([
apiCall.ManageFamily(reqData),
apiCall.ManageTasks(reqData),
apiCall.ManageFamilyWaitlist(),
apiCall.ManageFamilyPending(),
]);
const familyData = familyRes.data;
const tasksData = tasksRes.data;
const familyWaitData = familyWaitRes.data;
const familyPendingData = familyPending.data;
if (
familyData?.internal_return < 0 ||
tasksData?.internal_return < 0 ||
familyWaitData?.internal_return < 0 ||
familyPendingData?.internal_return < 0
) {
return;
}
startTransition(() => {
setDetails({
familyDetails: { loading: false, data: familyData },
familyTasks: { loading: false, data: tasksData },
familyWaitList: { loading: false, data: familyWaitData },
familyPending: { loading: false, data: familyPendingData },
});
});
} catch (error) {
setDetails((prevDetails) => ({
...prevDetails,
familyDetails: { loading: false },
familyTasks: { loading: false },
familyWaitList: { loading: false },
familyPending: { loading: false },
}));
setErrMsg("An error occurred");
throw new Error(error);
}
}, [apiCall, accountDetails]);
// Ref for the account section
const accountRef = useRef();
// React-to-Print hook for handling printing
const useHandlePrint = useReactToPrint({
content: () => accountRef.current,
});
// Array of tab names
const tabs = [
{ id: 1, name: "Tasks" },
{ id: 2, name: "Waiting" },
{ id: 3, name: "Pending" },
];
// State for the currently selected tab
const [tab, setTab] = useState(tabs[0].name);
// Function to handle tab changes
const tabHandler = (value) => {
startTransition(() => {
setTab(value);
});
setTab(value);
};
// Object that maps tab names to their corresponding components
const tabComponents = {
Tasks: (
<FamilyTasks
@@ -156,7 +155,7 @@ export default function FamilyManageTabs({
<FamilyPending
familyData={details.familyPending.data}
accountDetails={accountDetails}
loader={details.familyWaitList.loading}
loader={details.familyPending.loading}
/>
),
Account: (
@@ -167,9 +166,11 @@ export default function FamilyManageTabs({
handlePrint={useHandlePrint}
/>
),
Profile: <FamilyProfile />,
Profile: <FamilyProfile familyData={details.familyDetails.data} />,
wallet: <FamilyWallet familyData={details.familyDetails.data} />,
};
// Default tab component
const defaultTabComponent = (
<FamilyTasks
className={className}
@@ -179,17 +180,101 @@ export default function FamilyManageTabs({
/>
);
// Selected tab component based on the current 'tab'
const selectedTabComponent = tabComponents[tab] || defaultTabComponent;
// Effect to manage family details and related data
useEffect(() => {
let __manageFamily = true;
if (__manageFamily) {
manageFamily();
}
return () => {
__manageFamily = false;
const manageFamily = async () => {
try {
resetDetails();
setDetails({
familyDetails: { loading: true },
familyTasks: { loading: true },
familyWaitList: { loading: true },
familyPending: { loading: true },
});
const { family_uid } = accountDetails;
const reqData = { family_uid };
const [familyRes, tasksRes, familyWaitRes, familyPending] =
await Promise.all([
apiCall.ManageFamily(reqData),
apiCall.ManageTasks(reqData),
apiCall.ManageFamilyWaitlist(),
apiCall.ManageFamilyPending(),
]);
const familyData = familyRes.data;
const tasksData = tasksRes.data;
const familyWaitData = familyWaitRes.data;
const familyPendingData = familyPending.data;
// Function to check for errors in data
const checkDataError = (data) => data?.internal_return < 0;
if (
checkDataError(familyData) ||
checkDataError(tasksData) ||
checkDataError(familyWaitData) ||
checkDataError(familyPendingData)
) {
return;
}
setDetails({
familyDetails: { loading: false, data: familyData },
familyTasks: { loading: false, data: tasksData },
familyWaitList: { loading: false, data: familyWaitData },
familyPending: { loading: false, data: familyPendingData },
});
} catch (error) {
resetDetails();
setErrMsg("An error occurred");
throw new Error(error);
}
};
}, [tab, manageFamily]);
// Invoke the manageFamily function when the component mounts
manageFamily();
}, [updatePage]);
// Effect to manage family tasks
useEffect(() => {
let checkFamilyTask = true;
const reqData = {
limit: 30,
offset: 0,
job_type: "FAMILY",
action: 13005,
};
if (checkFamilyTask) {
setFamilyTask({ loading: true });
apiCall
.getMyJobList(reqData)
.then((res) => {
setFamilyTask({ loading: false, data: res?.data?.result_list });
if (res?.data?.result_list?.length) {
setActiveTask((prev) => ({
...prev,
data: res?.data?.result_list[0],
}));
}
})
.catch((err) => {
setFamilyTask({ loading: false, data: [] });
console.log("Error", err);
});
}
// Cleanup function to prevent memory leaks
return () => {
checkFamilyTask = false;
};
}, []);
return (
<div
@@ -214,17 +299,17 @@ export default function FamilyManageTabs({
browseProfileImg={browseProfileImg}
accountDetails={accountDetails}
/>
<div className="mt-4 flex justify-start items-center gap-2">
<div className="mt-4 flex flex-col justify-center items-center gap-2 lg:flex-row lg:justify-center lg:items-center xl:flex-col xl:justify-center xl:items-center 2xl:flex-row 2xl:justify-center 2xl:items-center 2xl:gap-[2px]">
<button
onClick={() => tabHandler("Account")}
className="family-icon p-2 border-2 border-sky-blue rounded-2xl flex flex-col justify-between items-center max-w-[65px] w-full"
>
<img
src={localImgLoad("images/icons/account.svg")}
className="max-w-[30px] w-full"
className="max-w-[30px]"
alt="Settings-Icon"
/>
<p className="text-lg text-sky-blue">Acc.</p>
<p className="text-[16px] text-sky-blue">Acc.</p>
</button>
<button
onClick={() => tabHandler("Profile")}
@@ -232,10 +317,21 @@ export default function FamilyManageTabs({
>
<img
src={localImgLoad("images/icons/profile.svg")}
className="max-w-[30px] w-full"
className="max-w-[30px]"
alt="Settings-Icon"
/>
<p className="text-lg text-sky-blue">Profile</p>
<p className="text-[16px] text-sky-blue">Profile</p>
</button>
<button
onClick={() => tabHandler("wallet")}
className="family-icon p-2 border-2 border-sky-blue rounded-2xl flex flex-col justify-between items-center max-w-[65px] w-full"
>
<img
src={localImgLoad("images/icons/wallet.svg")}
className="max-w-[30px]"
alt="Settings-Icon"
/>
<p className="text-[16px] text-sky-blue">Wallet</p>
</button>
</div>
</div>
@@ -265,7 +361,7 @@ export default function FamilyManageTabs({
Add task
</button>
</div>
<div className="flex-[0.9] h-full">
<div className="h-full relative overflow-y-auto">
<Suspense
@@ -285,7 +381,12 @@ export default function FamilyManageTabs({
<AssignTaskPopout
action={familyPopUpHandler}
situation={familyTaskPopout}
familyDetails={details.familyDetails.data}
familyTask={familyTask}
setFamilyTask={setFamilyTask}
setActiveTask={setActiveTask}
activeTask={activeTask}
familyDetailsData={details.familyDetails.data}
setUpdatePage={setUpdatePage}
/>
)}
</div>
@@ -1,441 +1,420 @@
import React, { useState, useEffect, useTransition } from "react";
import ModalCom from "../../Helpers/ModalCom";
import Detail from "../../jobPopout/popoutcomponent/Detail";
import React, { useState } from "react";
import usersService from "../../../services/UsersService";
import LoadingSpinner from "../../Spinners/LoadingSpinner";
import ModalCom from "../../Helpers/ModalCom";
import { PriceFormatter } from "../../Helpers/PriceFormatter";
import LoadingSpinner from "../../Spinners/LoadingSpinner";
import Detail from "../../jobPopout/popoutcomponent/Detail";
import { NewTasks } from "./forms";
const AssignTaskPopout = React.memo(({ action, details, situation, familyDetails }) => {
const apiCall = new usersService();
const AssignTaskPopout = React.memo(
({
action,
details,
situation,
familyDetailsData,
familyTask,
activeTask,
setActiveTask,
setUpdatePage
}) => {
const apiCall = new usersService();
let [requestStatus, setRequestStatus] = useState({
loading: false,
status: false,
message: "",
}); // HOLDS RESPONSE FOR SENDING API REQUEST
let [requestStatus, setRequestStatus] = useState({
loading: false,
status: false,
message: "",
}); // HOLDS RESPONSE FOR SENDING API REQUEST
let [familyTask, setFamilyTask] = useState({ loading: false, data: [] });
let [taskType, setTaskType] = useState(details ? "new" : "select"); // SWITCHES BTW SELECT TASK AND NEW TASK
let [taskType, setTaskType] = useState(details ? "new" : "select"); // SWITCHES BTW SELECT TASK AND NEW TASK
const switchTaskType = ({ target: { value } }) => {
// FUNCTION TO CHANGE SELECTED ACTIVE TASK
setTaskType(value);
};
let [activeTask, setActiveTask] = useState({ id: 0, data: {} }); // HOLDS SELECTED TASK
const handleActiveTask = (id = 0, data = {}) => {
// FUNCTION TO CHANGE SELECTED ACTIVE TASK
setActiveTask({ id, data });
};
const switchTaskType = ({ target: { value } }) => {
// FUNCTION TO CHANGE SELECTED ACTIVE TASK
setTaskType(value);
};
// New Task
const [formState, setFormState] = useState({
// Initialize form state with desired fields
banner: details?.banner || "default.jpg",
country: details?.country || "",
price: details?.price || "",
title: details?.title || "",
description: details?.description || "",
job_detail: details?.job_detail || "",
timeline_days: details?.timeline_days || "",
category: details?.category || "",
});
const handleActiveTask = (id = 0, data = {}) => {
// FUNCTION TO CHANGE SELECTED ACTIVE TASK
setActiveTask({ id, data });
};
// New Task
const [formState, setFormState] = useState({
// Initialize form state with desired fields
banner: details?.banner || "default.jpg",
country: details?.country || "",
price: details?.price || "",
title: details?.title || "",
description: details?.description || "",
job_detail: details?.job_detail || "",
timeline_days: details?.timeline_days || "",
category: details?.category || "",
});
const assignFamilyTask = () => {
setRequestStatus({ loading: true, status: false, message: "" });
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 || details?.family_uid,
job_description: activeTask.data?.description,
assign_mode: 110011,
};
}
if (taskType === "new") {
const {
banner,
category,
country,
description,
job_detail,
price,
timeline_days,
title,
} = formState;
const requiredFields = {
banner,
// category,
country,
description,
job_detail,
price,
timeline_days,
title,
};
for (let field in requiredFields) {
if (requiredFields[field] == "") {
// let currencyErrMsg = field == "country" && "currency"
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: `${field} is empty`,
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: familyDetailsData?.uid || details?.family_uid,
job_description: activeTask.data?.description,
assign_mode: 110011,
};
}
reqData = {
banner,
category,
country,
description,
job_detail,
price: price * 100,
timeline_days,
title,
assign_mode: 110055,
family_uid: details?.family_uid || familyDetails?.uid,
};
}
if (taskType === "new") {
const {
banner,
category,
country,
description,
job_detail,
price,
timeline_days,
title,
} = formState;
apiCall
.assignFamilyTask(reqData)
.then((res) => {
if (res.status != 200 || res.data.internal_return < 0) {
const requiredFields = {
banner,
// category,
country,
description,
job_detail,
price,
timeline_days,
title,
};
for (let field in requiredFields) {
if (requiredFields[field] == "") {
// let currencyErrMsg = field == "country" && "currency"
setRequestStatus({
loading: false,
status: false,
message: `${field} is empty`,
});
return setTimeout(() => {
setRequestStatus({ loading: false, status: false, message: "" });
}, 3000);
}
}
reqData = {
banner,
category,
country,
description,
job_detail,
price: price * 100,
timeline_days,
title,
assign_mode: 110055,
family_uid: details?.family_uid || familyDetailsData?.uid,
};
}
apiCall
.assignFamilyTask(reqData)
.then((res) => {
if (res.status != 200 || res.data.internal_return < 0) {
setRequestStatus({
loading: false,
status: false,
message: "failed to assign task",
});
return setTimeout(() => {
setRequestStatus({ loading: false, status: false, message: "" });
}, 5000);
}
setRequestStatus({
loading: false,
status: true,
message: "action successful",
});
setUpdatePage(prev => !prev) // Updates family task page by calling the useeffect hook
setTimeout(() => {
setRequestStatus({ loading: false, status: false, message: "" });
action(); // FUNCTION THAT CLOSES THE MODAL BOX
}, 5000);
})
.catch((err) => {
setRequestStatus({
loading: false,
status: false,
message: "failed to assign task",
message: "An Error occured, try again",
});
return setTimeout(() => {
setTimeout(() => {
setRequestStatus({ loading: false, status: false, message: "" });
}, 5000);
}
setRequestStatus({
loading: false,
status: true,
message: "action successful",
});
setTimeout(() => {
setRequestStatus({ loading: false, status: false, message: "" });
action(); // FUNCTION THAT CLOSES THE MODAL BOX
}, 5000);
})
.catch((err) => {
setRequestStatus({
loading: false,
status: false,
message: "An Error occured, try again",
});
setTimeout(() => {
setRequestStatus({ loading: false, status: false, message: "" });
}, 5000);
});
};
useEffect(() => {
let checkFamilyTask = true;
const reqData = {
limit: 30,
offset: 0,
job_type: "FAMILY",
action: 13005,
};
apiCall
.getMyJobList(reqData)
.then((res) => {
if (checkFamilyTask) {
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 () => {
checkFamilyTask = false;
};
}, []);
return (
<>
<ModalCom
action={action}
situation={situation}
className="assign-task-popup"
>
<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 || details.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"
return (
<>
<ModalCom
action={action}
situation={situation}
className="assign-task-popup"
>
<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{" "}
{familyDetailsData?.firstname || details?.firstName}
</h1>
<button
type="button"
className="text-[#374557] dark:text-red-500"
onClick={action}
>
<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" />
<svg
width="36"
height="36"
viewBox="0 0 36 36"
fill="none"
className="fill-current"
xmlns="http://www.w3.org/2000/svg"
>
<path
d="M36 16.16C36 17.4399 36 18.7199 36 20.0001C35.7911 20.0709 35.8636 20.2554 35.8385 20.4001C34.5321 27.9453 30.246 32.9248 22.9603 35.2822C21.9006 35.6251 20.7753 35.7657 19.6802 35.9997C18.4003 35.9997 17.1204 35.9997 15.8401 35.9997C15.5896 35.7086 15.2189 35.7732 14.9034 35.7093C7.77231 34.2621 3.08728 30.0725 0.769671 23.187C0.435002 22.1926 0.445997 21.1199 0 20.1599C0 18.7198 0 17.2798 0 15.8398C0.291376 15.6195 0.214408 15.2656 0.270759 14.9808C1.71321 7.69774 6.02611 2.99691 13.0428 0.700951C14.0118 0.383805 15.0509 0.386897 15.9999 0C17.2265 0 18.4532 0 19.6799 0C19.7156 0.124041 19.8125 0.136067 19.9225 0.146719C27.3 0.868973 33.5322 6.21922 35.3801 13.427C35.6121 14.3313 35.7945 15.2484 36 16.16ZM33.011 18.0787C33.0433 9.77105 26.3423 3.00309 18.077 2.9945C9.78479 2.98626 3.00344 9.658 2.98523 17.8426C2.96667 26.1633 9.58859 32.9601 17.7602 33.0079C26.197 33.0577 32.9787 26.4186 33.011 18.0787Z"
fill=""
fillOpacity="0.6"
/>
<path
d="M15.9309 18.023C13.9329 16.037 12.007 14.1207 10.0787 12.2072C9.60071 11.733 9.26398 11.2162 9.51996 10.506C9.945 9.32677 11.1954 9.0811 12.1437 10.0174C13.9067 11.7585 15.6766 13.494 17.385 15.2879C17.9108 15.8401 18.1633 15.7487 18.6375 15.258C20.3586 13.4761 22.1199 11.7327 23.8822 9.99096C24.8175 9.06632 26.1095 9.33639 26.4967 10.517C26.7286 11.2241 26.3919 11.7413 25.9133 12.2178C24.1757 13.9472 22.4477 15.6855 20.7104 17.4148C20.5228 17.6018 20.2964 17.7495 20.0466 17.9485C22.0831 19.974 24.0372 21.8992 25.9689 23.8468C26.9262 24.8119 26.6489 26.1101 25.4336 26.4987C24.712 26.7292 24.2131 26.3441 23.7455 25.8757C21.9945 24.1227 20.2232 22.3892 18.5045 20.6049C18.0698 20.1534 17.8716 20.2269 17.4802 20.6282C15.732 22.4215 13.9493 24.1807 12.1777 25.951C11.7022 26.4262 11.193 26.7471 10.4738 26.4537C9.31345 25.9798 9.06881 24.8398 9.98589 23.8952C11.285 22.5576 12.6138 21.2484 13.9387 19.9355C14.5792 19.3005 15.2399 18.6852 15.9309 18.023Z"
fill="#"
fillOpacity="0.6"
/>
</svg>
</button>
</div>
) : (
<>
<div
className={`job-action-modal-body w-full md:grid ${
taskType !== "new" ? "md:grid-cols-2" : "md:grid-cols-1"
}`}
>
<div className="p-4">
<div className="mb-2 w-full flex items-center gap-4">
<div className="flex items-center gap-2 text-sky-blue text-base">
<input
type="radio"
name="task-type"
value="select"
className="w-[20px] h-[20px] cursor-pointer"
checked={taskType == "select"}
onChange={switchTaskType}
/>
<span>Select Task</span>
{familyTask.loading ? (
<div className="h-[100px] w-full flex justify-center items-center">
<LoadingSpinner color="sky-blue" size="16" />
</div>
) : (
<>
<div
className={`job-action-modal-body w-full md:grid ${
taskType !== "new" ? "md:grid-cols-2" : "md:grid-cols-1"
}`}
>
<div className="p-4">
<div className="mb-2 w-full flex items-center gap-4">
<div className="flex items-center gap-2 text-sky-blue text-base">
<input
type="radio"
name="task-type"
value="select"
className="w-[20px] h-[20px] cursor-pointer"
checked={taskType == "select"}
onChange={switchTaskType}
/>
<span>Select Task</span>
</div>
<div className="flex items-center gap-2 text-sky-blue text-base">
<input
type="radio"
name="task-type"
value="new"
className="w-[20px] h-[20px] cursor-pointer"
checked={taskType == "new"}
onChange={switchTaskType}
/>
<span>New Task</span>
</div>
</div>
<div className="flex items-center gap-2 text-sky-blue text-base">
<input
type="radio"
name="task-type"
value="new"
className="w-[20px] h-[20px] cursor-pointer"
checked={taskType == "new"}
onChange={switchTaskType}
/>
<span>New Task</span>
</div>
</div>
{/* Task Type === select */}
{taskType == "select" && (
<div className="p-4 w-full h-[400px] overflow-y-auto bg-slate-100">
{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={() =>
{/* Task Type === select */}
{taskType == "select" && (
<div className="p-4 w-full h-[400px] overflow-y-auto bg-slate-100 rounded-md dark:bg-[#11131f] dark:text-white">
{familyTask?.data?.length ? (
familyTask?.data?.map((item, index) => (
<div
key={item.job_uid}
className="mb-2 flex justify-start items-center gap-2 text-sky-blue text-base cursor-pointer"
onClick={() =>
handleActiveTask(item.job_uid, item)
}
className="w-[15px] h-[15px] cursor-pointer"
/>
<p className="w-full text-dark-gray tracking-wide">
{item?.title}
>
<input
type="radio"
name="task-list"
checked={
activeTask.id == item.job_uid ||
(activeTask.id == index && true)
}
onChange={() =>
handleActiveTask(item.job_uid, item)
}
className="w-[15px] h-[15px] cursor-pointer"
/>
<p className="w-full text-dark-gray dark:text-white tracking-wide">
{item?.title}
</p>
</div>
))
) : (
<p className="p-8 text-lg text-dark-gray dark:text-white tracking-wide text-center cursor-default">
No Task found!
</p>
)}
</div>
)}
{taskType == "new" && (
<div className="p-4 w-full h-[400px]">
<NewTasks
formState={formState}
setFormState={setFormState}
/>
</div>
)}
</div>
{/*Right Hand Side for details && Task Type === select */}
{taskType == "select" && (
<>
{familyTask?.data?.length > 0 ? (
<div className="p-4">
<div className="w-full">
<p className="text-lg font-bold text-dark-gray dark:text-white tracking-wide border-b-2">
{activeTask?.data?.title}
</p>
<div className="my-3">
<Detail
label="Description"
value={activeTask?.data?.description}
/>
</div>
<div className="flex items-center">
<div className="my-3 w-full flex items-center gap-1">
<label className="text-slate-900 dark:text-white tracking-wide font-semibold">
Price
</label>
<p className="p-1 text-sm text-slate-900 dark:text-white">
{PriceFormatter(
activeTask?.data?.price * 0.01,
activeTask?.data?.currency,
activeTask?.data?.curreny_code
)}
</p>
</div>
<div className="my-3 w-full flex items-center gap-1">
<label className="text-slate-900 dark:text-white tracking-wide font-semibold">
Timeline
</label>
<p className="p-1 text-sm text-slate-900 dark:text-white">{`${activeTask?.data?.timeline_days} day(s)`}</p>
</div>
</div>
<div className="my-3 sm:flex items-center">
<Detail
label="Created"
value={`Dummy, no value found for created!`}
/>
</div>
<div className="my-3">
<label className="w-full text-slate-900 dark:text-white tracking-wide font-semibold">
Delivery Detail
</label>
<textarea
className={`p-1 w-full text-sm text-slate-900 dark:text-white bg-transparent outline-none border border-slate-300 rounded-md`}
rows="5"
style={{ resize: "none" }}
value={activeTask?.data?.job_detail}
readOnly
// onChange={handleInputChange}
/>
</div>
</div>
))
</div>
) : (
<p className="p-8 text-lg text-dark-gray dark:text-white tracking-wide text-center cursor-default">
No Task found!
</p>
<></>
)}
</div>
)}
{taskType == "new" && (
<div className="p-4 w-full h-[400px]">
<NewTasks
formState={formState}
setFormState={setFormState}
/>
</div>
</>
)}
</div>
{/*Right Hand Side for details && Task Type === select */}
{taskType == "select" && (
<>
{familyTask?.data?.length > 0 ? (
<div className="p-4">
<div className="w-full">
<p className="text-lg font-bold text-dark-gray dark:text-white tracking-wide border-b-2">
{activeTask?.data?.title}
</p>
<div className="my-3">
<Detail
label="Description"
value={activeTask?.data?.description}
/>
</div>
<div className="flex items-center">
<div className="my-3 w-full flex items-center gap-1">
<label className="text-slate-900 dark:text-white tracking-wide font-semibold">
Price
</label>
<p className="p-1 text-sm text-slate-900 dark:text-white">
{PriceFormatter(
activeTask?.data?.price * 0.01,
activeTask?.data?.currency,
activeTask?.data?.curreny_code
)}
</p>
</div>
<div className="my-3 w-full flex items-center gap-1">
<label className="text-slate-900 dark:text-white tracking-wide font-semibold">
Timeline
</label>
<p className="p-1 text-sm text-slate-900 dark:text-white">{`${activeTask?.data?.timeline_days} day(s)`}</p>
</div>
</div>
<div className="my-3 sm:flex items-center">
<Detail
label="Created"
value={`Dummy, no value found for created!`}
/>
</div>
<div className="my-3">
<label className="w-full text-slate-900 dark:text-white tracking-wide font-semibold">
Delivery Detail
</label>
<textarea
className={`p-1 w-full text-sm text-slate-900 outline-none border border-slate-300 rounded-md`}
rows="5"
style={{ resize: "none" }}
value={activeTask?.data?.job_detail}
readOnly
// onChange={handleInputChange}
/>
</div>
</div>
</div>
) : (
<></>
)}
</>
)}
</div>
{/* BTN */}
<div className="py-2 px-4 border-t-2 flex justify-between items-center">
{/* error or success display */}
<div className="w-auto h-auto flex items-center">
{requestStatus.message != "" &&
(!requestStatus.status ? (
<div
className={`relative p-2 text-[#912741] bg-[#fcd9e2] border-[#fbc6d3] rounded-[0.475rem] text-md font-light leading-[19.5px] text-[13px] self-start`}
>
{requestStatus.message}
</div>
) : (
requestStatus.status && (
{/* BTN */}
<div className="py-2 px-4 border-t-2 flex justify-between items-center">
{/* error or success display */}
<div className="w-auto h-auto flex items-center">
{requestStatus.message != "" &&
(!requestStatus.status ? (
<div
className={`relative p-2 text-green-700 bg-slate-200 border-slate-800 mb-4 rounded-[0.475rem] text-md font-light leading-[19.5px] text-[13px]`}
className={`relative p-2 text-[#912741] bg-[#fcd9e2] border-[#fbc6d3] rounded-[0.475rem] text-md font-light leading-[19.5px] text-[13px] self-start`}
>
{requestStatus.message}
</div>
)
))}
</div>
) : (
requestStatus.status && (
<div
className={`relative p-2 text-green-700 bg-slate-200 border-slate-800 mb-4 rounded-[0.475rem] text-md font-light leading-[19.5px] text-[13px]`}
>
{requestStatus.message}
</div>
)
))}
</div>
{/* End of error or success display */}
<div className="w-auto h-auto flex items-center gap-3">
<button
disabled={requestStatus.loading}
onClick={action}
type="button"
className="w-20 h-11 flex justify-center items-center border-gradient text-base rounded-full text-white cursor-pointer"
>
<span className="text-gradient">Close</span>
</button>
<div className="">
{requestStatus.loading ? (
<LoadingSpinner color="sky-blue" size="8" />
) : taskType == "select" ? (
<button
type="button"
disabled={requestStatus.loading}
onClick={assignFamilyTask}
className="px-1 w-20 h-11 flex justify-center items-center btn-gradient text-base rounded-full text-white cursor-pointer"
>
Assign
</button>
) : (
<button
type="button"
disabled={requestStatus.loading}
onClick={assignFamilyTask}
className="px-1 w-40 h-11 flex justify-center items-center btn-gradient text-base rounded-full text-white cursor-pointer"
>
{`Assign to ${
familyDetails?.firstname || details?.firstName
}`}
</button>
)}
{/* End of error or success display */}
<div className="w-auto h-auto flex items-center gap-3">
<button
disabled={requestStatus.loading}
onClick={action}
type="button"
className="w-20 h-11 flex justify-center items-center border-gradient text-base rounded-full text-white cursor-pointer"
>
<span className="text-gradient">Close</span>
</button>
<div className="">
{requestStatus.loading ? (
<LoadingSpinner color="sky-blue" size="8" />
) : taskType == "select" ? (
<button
type="button"
disabled={requestStatus.loading}
onClick={assignFamilyTask}
className="px-1 w-20 h-11 flex justify-center items-center btn-gradient text-base rounded-full text-white cursor-pointer"
>
Assign
</button>
) : (
<button
type="button"
disabled={requestStatus.loading}
onClick={assignFamilyTask}
className="px-1 w-40 h-11 flex justify-center items-center btn-gradient text-base rounded-full text-white cursor-pointer"
>
{`Assign to ${
familyDetailsData?.firstname || details?.firstName
}`}
</button>
)}
</div>
</div>
</div>
</div>
</>
)}
</div>
</ModalCom>
</>
);
})
</>
)}
</div>
</ModalCom>
</>
);
}
);
export default AssignTaskPopout;
export default AssignTaskPopout;
+187 -124
View File
@@ -1,29 +1,165 @@
import React, { useState } from "react";
import dataImage1 from "../../assets/images/data-table-user-1.png";
import LoadingSpinner from "../Spinners/LoadingSpinner";
import { useNavigate } from "react-router-dom";
import { handlePagingFunc } from "../Pagination/HandlePagination";
import PaginatedList from "../Pagination/PaginatedList";
import LoadingSpinner from "../Spinners/LoadingSpinner";
import familyImage from '../../assets/images/no-family-side.png'
import familyImage from "../../assets/images/no-family-side.png";
import { formatDateString } from "../../lib";
import localImgLoad from "../../lib/localImgLoad";
export default function FamilyTable({ className, familyList, loader, popUpHandler }) {
const navigate = useNavigate();
/**
* Renders a list of family members that can be managed.
* It has its current maximum members at 8 and it comes with pagination and loading spinner functionality.
* @returns {JSX.Element} - The rendered component.
*/
export default function FamilyTable({
className,
familyList,
loader,
popUpHandler,
}) {
const navigate = useNavigate();
const [currentPage, setCurrentPage] = useState(0);
const itemsPerPage = Number(process.env.REACT_APP_ITEM_PER_PAGE);
const indexOfFirstItem = Number(currentPage);
const indexOfLastItem =
Number(indexOfFirstItem) + Number(process.env.REACT_APP_ITEM_PER_PAGE);
const currentFamilyList = familyList?.slice(indexOfFirstItem, indexOfLastItem);
const currentFamilyList = familyList?.slice(
indexOfFirstItem,
indexOfLastItem
);
const handleManageClick = (familyMember) => {
navigate("/manage-family", { state: familyMember });
};
const handlePagination = (e) => {
handlePagingFunc(e, setCurrentPage);
};
/**
* Renders a table row for a family member.
* @returns {JSX.Element} - The table row component.
*/
const FamilyRow = ({
firstname,
lastname,
age,
added,
last_login,
task_count,
family_uid,
banner,
enable_traking,
profile_picture,
}) => {
// Check for valid dates
const addedDate = added ? added.split(" ")[0] : "N/A";
const loginDate = last_login ? formatDateString(last_login) : "N/A";
const key = `family-${family_uid}`; // Assign a unique key
const trackingStatus =
enable_traking === "0"
? "Stopped"
: enable_traking === "100"
? "Active"
: "";
return (
<tr
className="bg-white dark:bg-dark-white border-b dark:border-[#5356fb29] hover:bg-gray-50"
key={key}
>
<td className="py-4">
<div className="flex space-x-2 items-center w-full">
<div className="w-[60px] h-[60px] rounded-full overflow-hidden flex justify-center items-center flex-[0.1]">
<img
src={profile_picture}
// src={profile_picture || localImgLoad(`images/icons/${banner}`)}
alt={`Avatar of ${firstname} ${lastname}`}
className="w-full h-full"
/>
</div>
<div className="flex flex-col flex-[0.9]">
<h1 className="font-bold text-xl text-dark-gray dark:text-white whitespace-nowrap">
{`${firstname} ${lastname} (${age})`}
</h1>
<span className="text-sm text-thin-light-gray">
Added: <span className="text-purple ml-1">{addedDate}</span>
</span>
<span className="text-sm text-thin-light-gray">
Last Login:{" "}
<span className="text-purple ml-1">{loginDate}</span>
</span>
</div>
</div>
</td>
<td className="text-center py-4 px-2">
<div className="flex space-x-1 items-center justify-center">
<span
className={`text-base font-medium whitespace-nowrap ${
enable_traking === "0"
? "text-[#FF0000] dark:text-[#FF6666]"
: enable_traking === "100"
? "text-[#00A000] dark:text-[#00FF00]"
: "text-dark-gray dark:text-white"
}`}
>
{trackingStatus}
</span>
</div>
</td>
<td className="text-center py-4 px-2">
<div className="flex space-x-1 items-center justify-center">
<span className="text-base text-dark-gray dark:text-white font-medium whitespace-nowrap">
{task_count}
</span>
</div>
</td>
<td className="text-right py-4 px-2 flex items-center justify-center">
<button
onClick={() =>
handleManageClick({
firstname,
lastname,
age,
added,
last_login,
task_count,
family_uid,
banner,
})
}
type="button"
className="w-12 h-11 flex justify-center items-center btn-gradient text-base rounded-full text-white"
>
<svg
xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 11 20"
id="Arrow"
className="w-[0.7rem]"
>
<path
fill-rule="evenodd"
d="M.366 19.708c.405.39 1.06.39 1.464 0l8.563-8.264a1.95 1.95 0 0 0 0-2.827L1.768.292A1.063 1.063 0 0 0 .314.282a.976.976 0 0 0-.011 1.425l7.894 7.617a.975.975 0 0 1 0 1.414L.366 18.295a.974.974 0 0 0 0 1.413"
// fill=""
className="color000000 svgShape fill-[#fff]"
></path>
</svg>
</button>
</td>
</tr>
);
};
return (
<div
className={`update-table w-full h-full p-4 bg-white dark:bg-dark-white overflow-y-auto rounded-2xl section-shadow min-h-[520px] ${
className={`update-table w-full h-full p-4 bg-white dark:bg-dark-white overflow-y-auto rounded-2xl section-shadow min-h-[520px] flex flex-col justify-between ${
className || ""
}`}
>
@@ -32,125 +168,52 @@ export default function FamilyTable({ className, familyList, loader, popUpHandle
<div className="h-full min-h-[500px] w-full overflow-hidden flex justify-center items-center">
<LoadingSpinner size="16" color="sky-blue" />
</div>
)
:
familyList?.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">
{currentFamilyList?.map((props, idx) => {
let {
firstname,
lastname,
age,
added,
last_login,
task_count,
family_uid,
banner
} = props;
let addedDate = added?.split(" ")[0];
let LoginDate = last_login?.split(" ")[0];
return (
<tr
className="bg-white dark:bg-dark-white border-b dark:border-[#5356fb29] hover:bg-gray-50"
key={family_uid}
>
<td className=" py-4">
<div className="flex space-x-2 items-center w-full">
<div className="w-[60px] h-[60px] rounded-full overflow-hidden flex justify-center items-center flex-[0.1]">
<img
// src={dataImage1}
src={localImgLoad(`images/icons/${banner}`)}
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 whitespace-nowrap">
{`${firstname} ${lastname} (${age})`}
</h1>
<span className="text-sm text-thin-light-gray">
Added:{" "}
<span className="text-purple ml-1">
{addedDate}
</span>
</span>
</div>
</div>
</td>
<td className="text-center py-4 px-2">
<div className="flex space-x-1 items-center justify-center">
<span className="text-base text-dark-gray dark:text-white font-medium whitespace-nowrap">
{LoginDate}
</span>
</div>
</td>
<td className="text-center py-4 px-2">
<div className="flex space-x-1 items-center justify-center">
<span className="text-base text-dark-gray dark:text-white font-medium whitespace-nowrap">
{task_count}
</span>
</div>
</td>
<td className="text-right py-4 px-2 flex items-center justify-center">
<button
onClick={() => {
navigate("/manage-family", {
state: { ...props },
});
}}
type="button"
className="w-20 h-11 flex justify-center items-center btn-gradient text-base rounded-full text-white"
>
Manage
</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">Add your family, assign tasks, and get the whole team engaged.</p>
<button
onClick={popUpHandler}
type="button"
className="text-white btn-gradient text-lg tracking-wide px-5 py-2 rounded-full"
>
Add Family
</button>
</div>
<div className="p-2 w-full md:w-1/2">
<img className='w-full' src={familyImage} alt="Add Family" />
</div>
</div>
)
}
) : (
<>
{familyList?.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">Tracking</th>
<th className="py-4 text-center">No of Tasks</th>
<th className="py-4 text-right"></th>
</tr>
</thead>
<tbody className="h-full">
{currentFamilyList?.map((familyMember, index) => {
return <FamilyRow key={index} {...familyMember} />;
})}
</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">
Add your family, assign tasks, and get the whole team
engaged.
</p>
<button
onClick={popUpHandler}
type="button"
className="text-white btn-gradient text-lg tracking-wide px-5 py-2 rounded-full"
>
Add Family
</button>
</div>
<div className="p-2 w-full md:w-1/2">
<img className="w-full" src={familyImage} alt="Add Family" />
</div>
</div>
)}
</>
)}
</div>
{/* PAGINATION BUTTON */}
<PaginatedList
{/* PAGINATION BUTTON */}
<PaginatedList
onClick={handlePagination}
prev={currentPage == 0 ? true : false}
next={
currentPage + Number(process.env.REACT_APP_ITEM_PER_PAGE) >=
familyList?.length
? true
: false
}
prev={currentPage == 0}
next={currentPage + itemsPerPage >= familyList.length}
data={familyList}
start={indexOfFirstItem}
stop={indexOfLastItem}
@@ -1,7 +1,10 @@
import { forwardRef } from "react";
import QRCode from "react-qr-code";
import { useSelector } from "react-redux";
import LoadingSpinner from "../../Spinners/LoadingSpinner";
const FamilyAccount = forwardRef(({ familyData, myRef, handlePrint }, ref) => {
const FamilyAccount = forwardRef(({ familyData, myRef, handlePrint, loader }, ref) => {
const { userDetails } = useSelector((state) => state.userDetails);
return (
<div
className="w-full lg:min-h-[538px] p-3 h-full flex flex-col items-center justify-center"
@@ -32,12 +35,19 @@ const FamilyAccount = forwardRef(({ familyData, myRef, handlePrint }, ref) => {
<p className="text-xl tracking-wide mb-[15px] text-center font-bold text-dark-gray dark:text-white">
Scan the code from mobile app
</p>
{loader ?
<div className="w-full">
<LoadingSpinner size='8' color='sky-blue' />
</div>
:
<QRCode
size={256}
style={{ height: "auto", maxWidth: "100%", width: "100%" }}
value={`https://www.google.com`}
// value={`https://www.google.com`}
value={`${userDetails?.uid}@${familyData?.username}@${familyData?.uid}`}
viewBox={`0 0 256 256`}
/>
}
</div>
</div>
<div className="h-[50px] w-full flex justify-center items-center">
+300 -3
View File
@@ -1,11 +1,308 @@
export default function FamilyProfile({ className }) {
import { useState } from "react";
import { useNavigate } from "react-router-dom";
import usersService from "../../../services/UsersService";
import InputCom from "../../Helpers/Inputs/InputCom";
import LoadingSpinner from "../../Spinners/LoadingSpinner";
import { Form, Formik } from "formik";
import * as Yup from "yup";
export default function FamilyProfile({ familyData, className }) {
const validationSchema = Yup.object().shape({
firstname: Yup.string().required("Firstname is required"),
lastname: Yup.string()
// .min(3, "Minimum 3 characters")
// .max(25, "Maximum 25 characters")
.required("Lastname is required"),
year: Yup.number()
.required("Year is required")
.min(
new Date().getFullYear() - process.env.REACT_APP_FAMILY_MAXIMUM_AGE,
`Age must be within ${process.env.REACT_APP_FAMILY_MINIMUM_AGE} to ${process.env.REACT_APP_FAMILY_MAXIMUM_AGE} years`
)
.max(
new Date().getFullYear() - process.env.REACT_APP_FAMILY_MINIMUM_AGE,
`Age must be within ${process.env.REACT_APP_FAMILY_MINIMUM_AGE} to ${process.env.REACT_APP_FAMILY_MAXIMUM_AGE} years`
),
month: Yup.number()
.required("Month is required")
.min(1, "Month is required"),
});
const navigate = useNavigate();
const api = new usersService();
let [requestStatus, setRequestStatus] = useState({
loading: false,
status: false,
message: "",
});
// let [updateDetails, setUpdateDetails] = useState({
// family_uid: familyData.uid,
// firstname: familyData.firstname,
// lastname: familyData.lastname,
// year: familyData.year,
// month: familyData.month,
// action: 22020
// })
// initial values for formik
let initialValues = {
family_uid: familyData?.uid,
firstname: familyData?.firstname,
lastname: familyData?.lastname,
year: familyData?.year,
month: familyData?.month,
enable_traking: familyData?.enable_traking,
action: 22020,
};
// const handleChange = ({ target: { name, value } }) => {
// setUpdateDetails((prev) => ({ ...prev, [name]: value }));
// };
const updateFamily = (values, helpers) => {
setRequestStatus({ loading: true, status: false, message: "" });
// let {firstname, lastname, year, month} = updateDetails
// if(!firstname || !lastname || year == 0 || month == 0) {
// setRequestStatus({loading:false, status:false, message: 'Please fill all fields'})
// return setTimeout(()=>{
// setRequestStatus({loading:false, status:false, message: ''})
// }, 5000)
// }
api
.getFamilyUpdate(values)
.then((res) => {
if (res.data.internal_return < 0) {
return setRequestStatus({
loading: false,
status: false,
message: "Failed, try again!",
});
}
setRequestStatus({
loading: false,
status: true,
message: "Family account updated",
});
setTimeout(() => {
navigate("/acc-family", { replace: true });
}, 5000);
})
.catch((error) => {
setRequestStatus({
loading: false,
status: false,
message: "Unable to update, try again!",
});
})
.finally(() => {
setTimeout(() => {
setRequestStatus({ loading: false, status: false, message: "" });
}, 5000);
});
};
return (
<div
className={`update-table w-full bg-white dark:bg-dark-white overflow-hidden rounded-2xl section-shadow lg:min-h-[538px] p-3 ${
className={`w-full bg-white dark:bg-dark-white overflow-hidden rounded-2xl section-shadow lg:min-h-[538px] p-3 ${
className || ""
}`}
>
<h1>Profile</h1>
<Formik
initialValues={initialValues}
validationSchema={validationSchema}
onSubmit={updateFamily}
>
{(props) => {
return (
<Form className="logout-modal-body w-full lg:w-[500px] lg:mx-auto flex flex-col items-center px-10 py-8 gap-4">
<InputCom
placeholder="Firstname"
label="First Name:"
name="firstname"
type="text"
parentClass="flex items-center gap-1 w-full"
labelClass="flex-[0.2] mb-0"
inputClass={`flex-[0.8] input-curve lg border border-[#dce4e9] ${
props.errors.firstname && props.touched.firstname
? "border border-red-500"
: ""
}`}
fieldClass="px-2"
value={props.values.firstname}
inputHandler={props.handleChange}
/>
<InputCom
placeholder="Lastname"
label="Last Name:"
name="lastname"
type="text"
parentClass="flex items-center gap-1 w-full"
labelClass="flex-[0.2] mb-0"
inputClass={`flex-[0.8] input-curve lg border border-[#dce4e9] ${
props.errors.lastname && props.touched.lastname
? "border border-red-500"
: ""
}`}
fieldClass="px-2"
value={props.values?.lastname}
inputHandler={props.handleChange}
/>
<div className="input-com flex flex-col gap-1 w-full">
{/* Age dropdown */}
<div className="">
<label
className="input-label text-[#181c32] dark:text-white text-[15px] font-semibold"
htmlFor="age-selection"
>
Birthday: (Year/Month)
</label>
{((props.errors.year && props.touched.year) ||
(props.errors.month && props.touched.month)) && (
<span className="text-[12px] text-red-500">
{" "}
{props.errors.year || props.errors.month}
</span>
)}
</div>
<div className="flex max-w-[330px] w-full self-end gap-4">
<select
name="year"
id="yearDropdown"
value={props.values.year}
onChange={props.handleChange}
className={`input-wrapper border border-[#f5f8fa] ${
props.errors.year && props.touched.year
? "border border-red-500"
: ""
} dark:border-[#5e6278] w-56 rounded-[35px] h-10 overflow-hidden relative font-medium leading-6 bg-clip-padding text-[#5e6278] dark:text-gray-100 bg-[#f5f8fa] dark:bg-[#5e6278] text-base focus-visible:border-transparent focus-visible:outline-0 focus-visible:ring-transparent px-4`}
>
<option value="">Select a Year</option>
{years.map((year) => (
<option key={year} value={year}>
{year}
</option>
))}
</select>
<select
name="month"
id="monthDropdown"
// value={(months.filter(month => month.id == selectedMonth)[0]?.id) || ''}
value={
months.filter(
(month) => month.id == props.values.month
)[0]?.id || 0
}
onChange={props.handleChange}
className={`input-wrapper border border-[#f5f8fa] ${
props.errors.month && props.touched.month
? "border border-red-500"
: ""
} dark:border-[#5e6278] w-56 rounded-[35px] h-10 overflow-hidden relative font-medium leading-6 bg-clip-padding text-[#5e6278] dark:text-gray-100 bg-[#f5f8fa] dark:bg-[#5e6278] text-base focus-visible:border-transparent focus-visible:outline-0 focus-visible:ring-transparent px-4`}
>
<option value="">Select a Month</option>
{months.map(({ id, name }) => (
<option key={id} value={id}>
{name}
</option>
))}
</select>
</div>
</div>
<div className="input-com mb-7 flex flex-col gap-1 w-full">
{/* Location dropdown */}
<div className="">
<label
className="input-label text-[#181c32] dark:text-white text-[15px] font-semibold"
htmlFor="age-selection"
>
Location Tracking
</label>
{/* {((props.errors.year && props.touched.year) ||
(props.errors.month && props.touched.month)) && (
<span className="text-[12px] text-red-500">
{" "}
{props.errors.year || props.errors.month}
</span>
)} */}
</div>
<div className="flex max-w-[330px] w-full self-end gap-4">
<select
name="enable_traking"
id="enable_traking"
value={props.values.enable_traking}
onChange={props.handleChange}
className={`input-wrapper border border-[#f5f8fa] dark:border-[#5e6278] w-56 rounded-[35px] h-10 overflow-hidden relative font-medium leading-6 bg-clip-padding text-[#5e6278] dark:text-gray-100 bg-[#f5f8fa] dark:bg-[#5e6278] text-base focus-visible:border-transparent focus-visible:outline-0 focus-visible:ring-transparent px-4`}
>
<option value={+0}>Disabled</option>
<option value={+100}>Enabled</option>
</select>
</div>
</div>
{requestStatus.message && (
<div
className={`relative p-2 ${
!requestStatus.status
? "text-[#912741] bg-[#fcd9e2] border-[#fbc6d3]"
: "text-green-700"
} mb-2 rounded-[0.475rem] text-xs font-light leading-[19.5px]`}
>
{requestStatus.message}
</div>
)}
<div className="flex justify-end w-full">
{requestStatus.loading ? (
<>
<div className="signup btn-loader"></div>
<LoadingSpinner size="4" />
</>
) : (
<button
type="submit"
// onClick={updateFamily}
// className={`rounded-[0.475rem] text-white flex justify-center bg-[#4687ba] hover:bg-[#009ef7] transition-all duration-300 items-center h-[42px] py-[0.8875rem] px-[1.81rem] text-[14.95px] btn-login`}
className="text-white btn-gradient text-lg tracking-wide px-6 py-2 rounded-full"
>
Update
</button>
)}
</div>
</Form>
);
}}
</Formik>
</div>
);
}
// Get the current year
// const currentYear = new Date().getFullYear() - process.env.REACT_APP_FAMILY_MINIMUM_AGE;
// Calculate the current year and the year 3 years before the current year
const currentYear = new Date().getFullYear();
const startYear = currentYear - 3;
// Generate an array of years from 4 years before the current year to 18 years before the current year
const years = Array.from({ length: 15 }, (_, index) => startYear - index).reverse();
// const latestYears = years.slice(0, process.env.REACT_APP_FAMILY_YEAR_RANGE);
const months = [
{ id: "1", name: "January" },
{ id: "2", name: "February" },
{ id: "3", name: "March" },
{ id: "4", name: "April" },
{ id: "5", name: "May" },
{ id: "6", name: "June" },
{ id: "7", name: "July" },
{ id: "8", name: "August" },
{ id: "9", name: "September" },
{ id: "10", name: "October" },
{ id: "11", name: "November" },
{ id: "12", name: "December" },
];
@@ -90,7 +90,7 @@ export default function FamilyTasks({
<h1 className="font-bold text-xl text-dark-gray dark:text-white">
{value.title}
</h1>
<div className="flex gap-4 items-center">
<div className="flex flex-col sm:flex-row items-start gap-1 md:gap-4 md:items-center">
<span className="text-sm text-thin-light-gray flex flex-start gap-1">
Price:{" "}
<span className="text-purple">
@@ -0,0 +1,45 @@
import React, { useEffect, useState } from 'react'
import usersService from '../../../services/UsersService'
import LoadingSpinner from '../../Spinners/LoadingSpinner'
import background from '../../../assets/images/bg-sky-blue.jpg'
import Wallet from './wallet/Wallet'
function FamilyWallet({familyData}) {
const apiUrl = new usersService()
let [familyWallet, setFamilyWallet] = useState({loading:true, data: []})
let [familyWalletReload, setFamilyWalletReload] = useState(false) // STATE TO DETERMINE WHEN TO RELOAD FAMILY WALLET TAB/PAGE
useEffect(()=>{
setFamilyWallet({loading:true, data: []})
apiUrl.getFamilyWallet({family_uid:familyData?.uid}).then(res => {
setFamilyWallet({loading:false, data: res?.data?.result_list || []})
}).catch(error => {
setFamilyWallet({loading:false, data: []})
})
},[familyWalletReload])
return (
<div className='p-3 w-full h-full bg-white dark:bg-dark-white flex flex-col justify-start items-start'>
{familyWallet.loading ?
<div className='w-full h-20 flex justify-center items-center'>
<LoadingSpinner size='8' color='sky-blue' />
</div>
:
familyWallet?.data?.length > 0 ?
<div className='w-full p-4 flex flex-col gap-2'>
{familyWallet?.data?.map((wallet, index)=>(
<Wallet key={index} wallet={wallet} familyData={familyData} setFamilyWalletReload={setFamilyWalletReload} />
))}
</div>
:
<p className='text-lg text-gray-500 dark:text-gray-400'>No Wallet Found!</p>
}
</div>
)
}
export default FamilyWallet
+10 -5
View File
@@ -1,26 +1,31 @@
import React from "react";
import localImgLoad from "../../../lib/localImgLoad";
export default function ProfileInfo({
profileImg,
profileImgInput,
profileImgChangHandler,
profileImgChangeHandler,
browseProfileImg,
accountDetails,
}) {
// console.log(accountDetails.banner)
return (
<div className="flex flex-col items-center gap-4">
<div className="flex justify-center">
<div className="w-full relative">
<img
src={localImgLoad(`images/icons/${accountDetails.banner}`)}
alt=""
src={
profileImg
? profileImg
: localImgLoad(`images/icons/${accountDetails.banner}`)
}
alt="profile"
className="sm:w-[180px] sm:h-[180px] w-[120px] h-[120px] rounded-full overflow-hidden object-cover"
/>
<input
ref={profileImgInput}
onChange={(e) => profileImgChangHandler(e)}
accept="image/*"
onChange={profileImgChangeHandler}
type="file"
className="hidden"
/>
@@ -0,0 +1,301 @@
import React, { useEffect, useState } from 'react'
import ModalCom from '../../../Helpers/ModalCom'
import InputCom from '../../../Helpers/Inputs/InputCom'
import { Form, Formik } from "formik";
import * as Yup from "yup";
import {AmountTo2DP} from '../../../Helpers/PriceFormatter'
import usersService from '../../../../services/UsersService';
import LoadingSpinner from '../../../Spinners/LoadingSpinner';
import { PriceFormatter } from '../../../Helpers/PriceFormatter';
import { tableReload } from '../../../../store/TableReloads';
import { useDispatch } from 'react-redux';
const validationSchema = Yup.object().shape({
// amount: Yup.string()
// .typeError("Invalid number")
// .min(1, "Price must be greater than 0")
// .test("no-e", "Invalid number", (value) => {
// if (value && /\d+e/.test(value)) {
// return false;
// }
// return true;
// })
// .required("Amount is required"),
amount: Yup.number('Please enter a number')
.min(1, "Price must be greater than 0")
.required("Amount is required"),
comment: Yup.string()
.required("Comment is required"),
});
function FamilyAddFundPopout({action, situation, wallet, familyData}) {
const dispatch = useDispatch()
const apiUrl = new usersService()
const [startTransfer, setStartTransfer] = useState({loading:true, data: {}})
const [requestStatus, setRequestStatus] = useState({loading:false, status:false, message:''})
// initial values for formik
let initialValues = {
amount: '',
from : AmountTo2DP(startTransfer?.data?.origing_current_balance*0.01),
to: `${familyData.firstname} ${familyData.lastname}`,
comment: ''
};
// FUNCTION TO PERFORM FAMILY TRANSFER
const handleAddFund = (values) => {
setRequestStatus({loading:true, status:false, message:''})
let senderBal = startTransfer?.data?.origing_current_balance || '' // SENDER'S ACCOUNT BALANCE
let senderLimit = startTransfer?.data?.origing_transfer_limit || '' // SENDER'S TRANSFER LIMIT
let reqData = { // API REQUEST DATA
family_uid : familyData.uid,
wallet_uid : wallet.wallet_uid,
origing_wallet_uid : startTransfer?.data?.origing_wallet_uid,
currency : startTransfer?.data?.currency,
amount : values.amount*100,
description : values.comment,
family_transfer_mode : 100,
action : 22014
}
if(!senderBal || !senderLimit){ // RETURNS UNAUTHORIZED, IF SENDER BAL OR LIMIT IS NOT AVAILABLE
setRequestStatus({loading:false, status:false, message:'Unauthorized, try again later'})
return setTimeout(()=>{
setRequestStatus({loading:false, status:false, message:''})
}, 5000)
}
if(values.amount > senderBal*0.01){ // CHECKS TO SEE IF SENDER IS SENDING MORE THAN HIS BALANCE
setRequestStatus({loading:false, status:false, message:'You cannot send more than your balance'})
return setTimeout(()=>{
setRequestStatus({loading:false, status:false, message:''})
}, 5000)
}
if(values.amount > senderLimit*0.01){ // CHECKS TO SEE IF SENDER IS SENDING MORE THAN HIS LIMIT
setRequestStatus({loading:false, status:false, message:`You cannot exceed ${senderLimit*0.01} ${startTransfer?.data?.origing_currency.charAt(0).toUpperCase() + startTransfer?.data?.origing_currency.slice(1).toLowerCase()}`})
return setTimeout(()=>{
setRequestStatus({loading:false, status:false, message:''})
}, 5000)
}
apiUrl.familyTransfer(reqData).then(({data}) => {
if(data.internal_return < 0 || data.credit_confirm == '' || data.pay_confirm == ''){
setRequestStatus({loading:false, status:false, message:'Transfer Failed'})
return setTimeout(()=>{
setRequestStatus({loading:false, status:false, message:''})
}, 5000)
}
setRequestStatus({loading:false, status:true, message:'Transfer Successful'})
setTimeout(()=>{
setRequestStatus({loading:false, status:false, message:''})
dispatch(tableReload({ type: "WALLETTABLE" })); // UPDATES PARENT WALLET ACCOUNT
action() // TO CLOSE THE MODAL
}, 5000)
}).catch(error => {
setRequestStatus({loading:false, status:false, message:'Network Error, try again'})
setTimeout(()=>{
setRequestStatus({loading:false, status:false, message:''})
}, 5000)
})
}
// LOAD FAMILY START TRANSFER
useEffect(()=>{
let reqData = {
family_uid: familyData.uid,
wallet_uid: wallet.wallet_uid,
action: 22013
}
apiUrl.familyTransferStart(reqData).then(response => {
setStartTransfer({loading:false, data:response?.data })
}).catch(err => {
setStartTransfer({loading:false, data: {}})
})
},[])
return (
<ModalCom action={action} situation={situation}>
<div className="relative logout-modal-wrapper lg:w-[500px] h-full lg:h-auto bg-white dark:bg-dark-white lg:rounded-2xl">
<div className="logout-modal-header w-full flex items-center justify-between lg:px-10 lg:py-8 px-[30px] py-[23px] border-b border-light-purple dark:border-[#5356fb29] ">
<h1 className="text-26 font-bold text-dark-gray dark:text-white tracking-wide">
Add Fund
</h1>
<button
type="button"
className="text-[#374557] dark:text-red-500"
onClick={action}
>
<svg
width="36"
height="36"
viewBox="0 0 36 36"
fill="none"
className="fill-current"
xmlns="http://www.w3.org/2000/svg"
>
<path
d="M36 16.16C36 17.4399 36 18.7199 36 20.0001C35.7911 20.0709 35.8636 20.2554 35.8385 20.4001C34.5321 27.9453 30.246 32.9248 22.9603 35.2822C21.9006 35.6251 20.7753 35.7657 19.6802 35.9997C18.4003 35.9997 17.1204 35.9997 15.8401 35.9997C15.5896 35.7086 15.2189 35.7732 14.9034 35.7093C7.77231 34.2621 3.08728 30.0725 0.769671 23.187C0.435002 22.1926 0.445997 21.1199 0 20.1599C0 18.7198 0 17.2798 0 15.8398C0.291376 15.6195 0.214408 15.2656 0.270759 14.9808C1.71321 7.69774 6.02611 2.99691 13.0428 0.700951C14.0118 0.383805 15.0509 0.386897 15.9999 0C17.2265 0 18.4532 0 19.6799 0C19.7156 0.124041 19.8125 0.136067 19.9225 0.146719C27.3 0.868973 33.5322 6.21922 35.3801 13.427C35.6121 14.3313 35.7945 15.2484 36 16.16ZM33.011 18.0787C33.0433 9.77105 26.3423 3.00309 18.077 2.9945C9.78479 2.98626 3.00344 9.658 2.98523 17.8426C2.96667 26.1633 9.58859 32.9601 17.7602 33.0079C26.197 33.0577 32.9787 26.4186 33.011 18.0787Z"
fill=""
fillOpacity="0.6"
/>
<path
d="M15.9309 18.023C13.9329 16.037 12.007 14.1207 10.0787 12.2072C9.60071 11.733 9.26398 11.2162 9.51996 10.506C9.945 9.32677 11.1954 9.0811 12.1437 10.0174C13.9067 11.7585 15.6766 13.494 17.385 15.2879C17.9108 15.8401 18.1633 15.7487 18.6375 15.258C20.3586 13.4761 22.1199 11.7327 23.8822 9.99096C24.8175 9.06632 26.1095 9.33639 26.4967 10.517C26.7286 11.2241 26.3919 11.7413 25.9133 12.2178C24.1757 13.9472 22.4477 15.6855 20.7104 17.4148C20.5228 17.6018 20.2964 17.7495 20.0466 17.9485C22.0831 19.974 24.0372 21.8992 25.9689 23.8468C26.9262 24.8119 26.6489 26.1101 25.4336 26.4987C24.712 26.7292 24.2131 26.3441 23.7455 25.8757C21.9945 24.1227 20.2232 22.3892 18.5045 20.6049C18.0698 20.1534 17.8716 20.2269 17.4802 20.6282C15.732 22.4215 13.9493 24.1807 12.1777 25.951C11.7022 26.4262 11.193 26.7471 10.4738 26.4537C9.31345 25.9798 9.06881 24.8398 9.98589 23.8952C11.285 22.5576 12.6138 21.2484 13.9387 19.9355C14.5792 19.3005 15.2399 18.6852 15.9309 18.023Z"
fill="#"
fillOpacity="0.6"
/>
</svg>
</button>
</div>
<div className="logout-modal-body w-full flex flex-col items-center px-10 py-8">
{startTransfer.loading && <LoadingSpinner size='16' color='sky-blue' height={'h-64'} />}
{ !startTransfer.loading &&
<Formik
initialValues={initialValues}
validationSchema={validationSchema}
onSubmit={handleAddFund}
>
{(props) => (
<Form className="w-full">
<div className="flex flex-col-reverse sm:flex-row">
<div className="fields w-full">
{/* AMOUNT */}
<div className="field w-full mb-[0.5rem]">
<InputCom
placeholder="0"
label={`Amount (${startTransfer?.data?.currency})`}
name="amount"
type="text"
parentClass="flex items-center gap-1 w-full"
labelClass="flex-[0.3] mb-0"
inputClass={`flex-[0.7] input-curve lg border border-[#dce4e9] ${props.errors.amount && props.touched.amount ? 'border border-red-500' : ''}`}
fieldClass="px-2 text-right"
value={props.values.amount}
inputHandler={props.handleChange}
/>
</div>
{/* FROM */}
<div className="field w-full mb-[0.5rem]">
<InputCom
placeholder="From"
label={`From (${startTransfer?.data?.origing_currency})`}
name="from"
type="text"
parentClass="flex items-center gap-1 w-full"
labelClass="flex-[0.3] mb-0"
inputClass={`flex-[0.7] input-curve lg border border-[#dce4e9]`}
fieldClass="px-2 text-right"
value={props.values.from}
disable={true}
/>
</div>
{/* TO */}
<div className="field w-full mb-[0.5rem]">
<InputCom
placeholder="To"
label="To:"
name="to"
type="text"
parentClass="flex items-center gap-1 w-full"
labelClass="flex-[0.3] mb-0"
inputClass={`flex-[0.7] input-curve lg border border-[#dce4e9]`}
fieldClass="px-2 text-right"
value={props.values.to}
disable={true}
/>
</div>
{/* COMMENT */}
<div className="field w-full mb-[0.5rem]">
<div className="w-full">
<label
htmlFor="Job Delivery Details"
className="input-label text-[#181c32] dark:text-white text-[13.975px] leading-[20.9625px] font-semibold flex items-center gap-1"
>
Comment
{/* {props.errors.comment && props.touched.comment && <span className='text-sm text-red-500'>{' '}{props.errors.comment}</span>} */}
</label>
<textarea
// id="Job Delivery Details"
rows="2"
className={`input-field px-3 py-2 placeholder:text-base text-dark-gray dark:text-white w-full bg-slate-100 dark:bg-[#11131F] focus:ring-0 focus:outline-[#dce4e9] rounded-[10px] border ${props.errors.comment && props.touched.comment ? 'border border-red-500' : ''}`}
style={{ resize: "none" }}
name="comment"
value={props.values.comment}
onChange={props.handleChange}
/>
</div>
</div>
{/* inputs ends here */}
</div>
</div>
{/* ERROR DISPLAY AND SUBMIT BUTTON */}
<div className="content-footer w-full">
{/* error or success display */}
{requestStatus.message != "" &&
(!requestStatus.status ? (
<div
className={`relative p-4 text-[#912741] bg-[#fcd9e2] border-[#fbc6d3] rounded-[0.475rem] text-md font-light leading-[19.5px] text-[13px]`}
// className={`pb-1 absolute bottom-0 left-1/2 -translate-x-1/2 text-[#912741] rounded-[0.475rem] text-md font-light leading-[19.5px] text-[13px]`}
>
{requestStatus.message}
</div>
) : (
requestStatus.status && (
<div
className={`relative p-4 text-green-700 bg-slate-200 border-slate-800 rounded-[0.475rem] text-md font-light leading-[19.5px] text-[13px]`}
// className={`pb-1 absolute bottom-0 left-1/2 -translate-x-1/2 text-green-700 rounded-[0.475rem] text-md font-light leading-[19.5px] text-[13px]`}
>
{requestStatus.message}
</div>
)
))}
{/* End of error or success display */}
<div className="pt-2 w-full border-t border-light-purple dark:border-[#5356fb29] flex justify-end items-center">
<div className="w-full flex justify-between gap-2 items-center">
<button
type="button"
onClick={action}
className="w-[150px] h-[48px] rounded-full text-base text-white bg-red-600 hover:bg-red-500"
>
Cancel
</button>
<>
{requestStatus.loading ?
<LoadingSpinner size='6' color='sky-blue' />
:
<button
type="submit"
className={`w-[150px] h-[48px] rounded-full text-base text-white bg-sky-500 hover:bg-sky-400 ${requestStatus.status ? 'opacity-50' : ''}`}
disabled={requestStatus.status}
>
Send
</button>
}
</>
</div>
</div>
</div>
</Form>
)}
</Formik>
}
</div>
</div>
</ModalCom>
)
}
export default FamilyAddFundPopout
@@ -0,0 +1,77 @@
import React, { useState } from 'react'
import { localImgLoad } from '../../../../lib'
import { PriceFormatter } from '../../../Helpers/PriceFormatter'
import FamilyAddFundPopout from './FamilyAddFundPopout'
function Wallet({wallet, familyData, setFamilyWalletReload}) {
const [addFundPopout, setAddFundPopout] = useState({ show: false, data: {} })
return (
<div className='w-full p-4 bg-[aliceblue] rounded-lg'
// style={{
// background: `url(${background}) 0% 0% / cover no-repeat`,
// }}
>
<div className="w-full flex justify-start items-center gap-3">
<div className="min-w-[50px] min-h-[50px] max-w-min md:max-w-[80px] max-h-min md:max-h-[80px] rounded-full bg-[#e3e3e3] flex justify-center items-center">
<img
src={localImgLoad(`images/currency/${(wallet.code).toLowerCase()}.svg`)}
className="w-full h-full"
alt="currency-icon"
/>
</div>
<div className="w-full flex flex-col">
<div className="w-full flex gap-2">
<p className="text-base md:text-lg text-thin-light-gray tracking-wide">
Balance:
</p>
<p
className="text-base md:text-lg font-bold text-purple tracking-wide leading-10"
// className="text-[44px] lg:text-[62px] font-bold text-white tracking-wide leading-10 xxs:scale-100 lg:scale-100 xl:scale-125"
>
{PriceFormatter(
Number(wallet.amount) * 0.01,
wallet.code,
wallet.country,
""
)}
</p>
</div>
{/* BTN */}
<div className='mt-1 flex justify-end items-center gap-2'>
<button
className="w-[150px] h-[48px] rounded-full text-base text-white bg-sky-500 hover:bg-sky-400"
name="add"
onClick={()=>{
setAddFundPopout({ show: true, data: {} });
}}
>
Add Money
</button>
<button
className="w-[150px] h-[48px] rounded-full text-base text-white bg-[#4687ba] hover:bg-[#009ef7]"
name="plan"
// onClick={onClose}
>
Plan Wallet
</button>
</div>
</div>
</div>
{/* MODAL TO ADD FUND */}
{addFundPopout.show &&
<FamilyAddFundPopout
action={() => {
setAddFundPopout({ show: false, data: {} });
setFamilyWalletReload(prev => !prev) // TO RELOAD FAMILY WALLET // DETERMINES WHEN TO RELOAD FAMILY WALLET TAB/PAGE
}}
situation={addFundPopout.show}
wallet={wallet}
familyData={familyData}
/>
}
</div>
)
}
export default Wallet
+115 -49
View File
@@ -5,16 +5,18 @@ import React, {
useMemo,
useState,
} from "react";
import usersService from "../../services/UsersService";
import InputCom from "../Helpers/Inputs/InputCom";
import Layout from "../Partials/Layout";
import FamilyTable from "./FamilyTable";
import SiteService from "../../services/SiteService";
import ModalCom from "../Helpers/ModalCom";
import Layout from "../Partials/Layout";
import LoadingSpinner from "../Spinners/LoadingSpinner";
import FamilyTable from "./FamilyTable";
export default function FamilyAcc() {
const [selectTab, setValue] = useState("today");
const [selectedAge, setSelectedAge] = useState(undefined);
// State to store the selected year and month
const [selectedYear, setSelectedYear] = useState("");
const [selectedMonth, setSelectedMonth] = useState("");
const [familyList, setFamilyList] = useState([]);
const [loader, setLoader] = useState(false);
const [popUp, setPopUp] = useState(false);
@@ -25,7 +27,7 @@ export default function FamilyAcc() {
last_name: "",
});
const apiCall = useMemo(() => new SiteService(), []);
const apiCall = useMemo(() => new usersService(), []);
const popUpHandler = () => {
setPopUp((prev) => !prev);
@@ -35,17 +37,14 @@ export default function FamilyAcc() {
setValue(value);
};
const ageRange = useMemo(() => {
const startAge = 5;
const endAge = 16;
return Array.from(
{ length: endAge - startAge + 1 },
(_, index) => startAge + index
);
}, []);
// Handle year selection
const handleYearChange = (e) => {
setSelectedYear(e.target.value);
};
const handleAgeSelect = (event) => {
setSelectedAge(parseInt(event.target.value));
// Handle month selection
const handleMonthChange = (e) => {
setSelectedMonth(e.target.value);
};
const handleInputChange = (event) => {
@@ -53,6 +52,13 @@ export default function FamilyAcc() {
setFormData((prevFormData) => ({ ...prevFormData, [name]: value }));
};
// use the useEffect hook to clear the selected month if the year changes (to ensure consistency)
useEffect(() => {
if (selectedYear === "" && selectedMonth !== "") {
setSelectedMonth("");
}
}, [selectedYear]);
const addMember = async () => {
const { first_name, last_name } = formData;
setLoader(true);
@@ -61,7 +67,8 @@ export default function FamilyAcc() {
const reqData = {
firstname: first_name,
lastname: last_name,
age: selectedAge,
year: +selectedYear,
month: +selectedMonth,
};
const res = await apiCall.addFamily(reqData);
@@ -91,7 +98,8 @@ export default function FamilyAcc() {
first_name: "",
last_name: "",
});
setSelectedAge("");
setSelectedMonth("");
setSelectedYear("");
}
};
@@ -143,13 +151,16 @@ export default function FamilyAcc() {
>
Family Accounts
</span>
<button
onClick={popUpHandler}
type="button"
className="text-white btn-gradient text-lg tracking-wide px-5 py-2 rounded-full"
>
Add
</button>
{familyList.length < process.env.REACT_APP_MAX_FAMILY_MEMBERS &&
!loader && (
<button
onClick={popUpHandler}
type="button"
className="text-white btn-gradient text-lg tracking-wide px-5 py-2 rounded-full"
>
Add
</button>
)}
</h1>
</div>
<div className="slider-btns flex space-x-4">
@@ -173,9 +184,10 @@ export default function FamilyAcc() {
<FamilyForm
popUpHandler={popUpHandler}
value={formData}
ageHandler={handleAgeSelect}
ageRange={ageRange}
ageValue={selectedAge}
selectedYear={selectedYear}
selectedMonth={selectedMonth}
monthHandler={handleMonthChange}
yearHandler={handleYearChange}
inputHandler={handleInputChange}
msgErr={msgErr}
onClick={addMember}
@@ -191,8 +203,10 @@ const FamilyForm = ({
value: { first_name, last_name },
ageValue,
inputHandler,
ageHandler,
ageRange,
selectedMonth,
selectedYear,
monthHandler,
yearHandler,
msgErr,
loader,
onClick,
@@ -237,33 +251,22 @@ const FamilyForm = ({
value={last_name}
inputHandler={inputHandler}
/>
<div className="input-com mb-7 flex gap-1 items-center w-full justify-between">
<div className="input-com mb-7 flex flex-col gap-1 w-full">
{/* Age dropdown */}
<div className="flex items-center justify-between flex-[0.3]">
<div className="">
<label
className="input-label text-[#181c32] dark:text-white text-[15px] font-semibold"
htmlFor="age-selection"
>
Select your age:
Birthday: (Year/Month)
</label>
</div>
<div className=" flex-[0.7] max-w-[150px]">
<select
name="age-selection"
id="age-selection"
className="input-wrapper border border-[#f5f8fa] dark:border-[#5e6278] w-full rounded-[35px] 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 focus-visible:border-transparent focus-visible:outline-0 focus-visible:ring-transparent px-4"
onChange={ageHandler}
value={ageValue}
>
<option value={""}>Select age</option>
{ageRange?.length > 0 &&
ageRange?.map((age) => (
<option value={age} key={age}>
{age}
</option>
))}
</select>
</div>
<YearMonthDropdowns
handleMonthChange={monthHandler}
handleYearChange={yearHandler}
selectedMonth={selectedMonth}
selectedYear={selectedYear}
/>
</div>
{msgErr && (
<div className="relative p-4 text-[#912741] bg-[#fcd9e2] border-[#fbc6d3] mb-4 rounded-[0.475rem] text-xs font-light leading-[19.5px]">
@@ -312,3 +315,66 @@ const CloseIcon = () => (
/>
</svg>
);
function YearMonthDropdowns({
selectedMonth,
selectedYear,
handleMonthChange,
handleYearChange,
}) {
// Get the current year
const currentYear = new Date().getFullYear();
// Generate an array of years from the current year to (currentYear - 19)
const years = Array.from({ length: 17 }, (_, index) => currentYear - index);
// Array of month names
const months = [
"January",
"February",
"March",
"April",
"May",
"June",
"July",
"August",
"September",
"October",
"November",
"December",
];
return (
<div className="flex max-w-[330px] w-full self-end gap-4">
<select
id="yearDropdown"
value={selectedYear}
onChange={handleYearChange}
className="input-wrapper border border-[#f5f8fa] dark:border-[#5e6278] w-56 rounded-[35px] h-10 overflow-hidden relative font-medium leading-6 bg-clip-padding text-[#5e6278] dark:text-gray-100 bg-[#f5f8fa] dark:bg-[#5e6278] text-base focus-visible:border-transparent focus-visible:outline-0 focus-visible:ring-transparent px-4"
// size="5"
>
<option value="">Select a Year</option>
{years.map((year) => (
<option key={year} value={year}>
{year}
</option>
))}
</select>
<select
id="monthDropdown"
value={selectedMonth}
onChange={handleMonthChange}
className="input-wrapper border border-[#f5f8fa] dark:border-[#5e6278] w-56 rounded-[35px] h-10 overflow-hidden relative font-medium leading-6 bg-clip-padding text-[#5e6278] dark:text-gray-100 bg-[#f5f8fa] dark:bg-[#5e6278] text-base focus-visible:border-transparent focus-visible:outline-0 focus-visible:ring-transparent px-4"
// size="5"
>
<option value="">Select a Month</option>
{months.map((month, index) => (
<option key={month} value={index + 1}>
{month}
</option>
))}
</select>
</div>
);
}
+12 -5
View File
@@ -2,6 +2,8 @@ import React from "react";
// import Lottie from "react-lottie";
import { useNavigate } from "react-router-dom";
import * as animationData from "../../assets/images/Lotties/77618-website-404-error-animation.json";
import { localImgLoad } from "../../lib";
// import BgError from '../../assets/images/wrench-page-notfound.jpg'
export default function FourZeroFour() {
const navigate = useNavigate();
@@ -14,16 +16,21 @@ export default function FourZeroFour() {
},
};
return (
<div className="flex justify-center items-center w-full h-screen bg-[#232247]">
<div>
<div className={`my-custom-bg-class flex justify-center items-center w-full min-h-screen before:content-[''] before:absolute before:inset-0 before:bg-[#cdcdcd]/[.8]`}>
<div className="relative pt-32 max-w-3xl">
{/* <Lottie options={defaultOptions} width={600} height={600} /> */}
<div className="flex justify-center">
<div className="px-16 md:px-24 flex flex-col items-center justify-center gap-4">
<img src={localImgLoad('images/404.png')} className="w-72" alt='404Image' />
<p className="mt-8 text-red-600 font-black text-4xl md:text-5xl tracking-wider text-center">Sorry!</p>
<h1 className="text-black text-4xl md:text-5xl font-black tracking-wide text-center leading-tight">The page cant be found.</h1>
<p className="px-2 md:px-8 text-slate-700 text-base md:text-lg text-center">The page you're looking for isn't available. Use the go back button below</p>
<button
onClick={() => navigate(-1)}
type="button"
className="btn-gradient text-white text-lg w-[150px] h-[50px] rounded-full"
className="px-4 md:px-8 border-2 border-[#2b70fa] rounded-lg text-[#007bff] text-base md:text-lg h-[48px] flex justify-center items-center gap-1"
>
Go Back
<span>Go Back</span>
<span className="pb-1">&rarr;</span>
</button>
</div>
</div>
@@ -25,6 +25,7 @@ export default function InputCom({
maxLength = 45,
minLength = 0,
direction,
tabIndex,
error,
}) {
const inputRef = useRef(null);
@@ -92,14 +93,15 @@ export default function InputCom({
value={value}
onChange={inputHandler}
className={`input-field placeholder:text-base text-dark-gray w-full h-full ${
inputBg ? inputBg : "bg-[#FAFAFA] tracking-wide"
} dark:bg-[#11131F] focus:ring-0 focus:outline-none ${fieldClass}`}
inputBg ? inputBg : "bg-[#FAFAFA] dark:bg-[#11131F] dark:text-white tracking-wide"
} focus:ring-0 focus:outline-none ${fieldClass}`}
type={type}
id={name}
name={name}
onInput={onInput}
minLength={minLengthValidation()}
maxLength={maxLengthValidation()}
tabIndex={tabIndex}
// pattern={inputPatterns()}
ref={inputRef}
readOnly={disable}
+26
View File
@@ -50,3 +50,29 @@ export const PriceFormatter = (
</span>
);
};
// FUNCTION TO RETURN AMOUNT TO TWO DECIMAL PLACES
export const AmountTo2DP = (
amount = "00",
) => {
// Convert the number to a string
let numStr = String(amount);
// Split the string into integer and decimal parts
let parts = numStr.split(".");
let integerPart = parts[0] || "";
let decimalPart = parts[1] || "";
// Add thousands separators to the integer part
let formattedInteger = integerPart.replace(/\B(?=(\d{3})+(?!\d))/g, ',');
// Truncate or pad the decimal part to two decimal points
let formattedDecimal = decimalPart.slice(0, 2).padEnd(2, "0");
// Combine the formatted integer and decimal parts
let formattedNumber = formattedInteger + '.' + formattedDecimal;
// return formattedNumber;
return formattedNumber;
};
+103
View File
@@ -0,0 +1,103 @@
import React, {useEffect, useState} from 'react'
import Image from '../../assets/images/taskbanners/default.jpg'
import usersService from '../../services/UsersService';
import { handlePagingFunc } from '../../components/Pagination/HandlePagination';
import PaginatedList from '../../components/Pagination/PaginatedList';
import LoadingSpinner from '../Spinners/LoadingSpinner';
import { AmountTo2DP } from '../Helpers/PriceFormatter';
function RewardsTable() {
const apiCall = new usersService()
let [familyRewardHistory, setFamilyRewardHistory] = useState({ // FOR PURCHASE HISTORY
loading: true,
data: [],
error: false
})
const [currentPage, setCurrentPage] = useState(0);
const indexOfFirstItem = Number(currentPage);
const indexOfLastItem = Number(indexOfFirstItem)+Number(process.env.REACT_APP_ITEM_PER_PAGE);
const currentReward = familyRewardHistory?.data?.slice(indexOfFirstItem, indexOfLastItem);
const handlePagination = (e) => {
handlePagingFunc(e,setCurrentPage)
}
//FUNCTION TO GET FAMILY REWARD HISTORY
const getFamilyRewardHistory = ()=>{
apiCall.getFamilyRewardHx().then((res)=>{
if(res.data.internal_return < 0){ // success but no data
setFamilyRewardHistory(prev => ({...prev, loading: false}))
return
}
setFamilyRewardHistory(prev => ({...prev, loading: false, data: res.data.result_list}))
}).catch((error)=>{
setFamilyRewardHistory(prev => ({...prev, loading: false, error: true}))
})
}
useEffect(()=>{
getFamilyRewardHistory()
}, [])
return (
<div className='flex flex-col justify-between min-h-[500px]'>
{familyRewardHistory.loading ?
<LoadingSpinner size='16' color='sky-blue' height='h-[500px]' />
: familyRewardHistory.data.length ?
<table className="wallet-activity w-full table-auto border-collapse text-left">
<thead className='border-b-2'>
<tr className='text-slate-600'>
<th className="p-2"></th>
<th className="p-2">Amount</th>
<th className="p-2">Date</th>
<th className="p-2">Confirmation</th>
</tr>
</thead>
<tbody>
{currentReward.map((item, index) => {
let date = new Date(item.added).toLocaleDateString()
return (
<tr key={index} className='text-slate-500'>
<td className="p-2">
<div className='flex items-center gap-2'>
<img src={item.icon} className='min-w-[60px] max-w-[60px] min-h-[60px] max-h-[60px] rounded-full bg-slate-500' alt='Reward Logo' />
<div className='flex flex-col'>
<h1 className='text-lg font-bold'>Reward to {item.rec_firstname} {item.rec_lastname}</h1>
<p className='text-sm'>{item.description}</p>
</div>
</div>
</td>
<td className="p-2">{AmountTo2DP(item.amount*0.01)} {item.currency}</td>
<td className="p-2">{date}</td>
<td className="p-2">{item.confirmation}</td>
</tr>
)
}
)}
</tbody>
</table>
:familyRewardHistory.error ?
<div className="p-2 text-slate-500 flex flex-col grow justify-center items-center">
<span>Opps! an error occurred. Please try again!</span>
</div>
:
<div className="p-2 text-slate-500 flex flex-col grow justify-center items-center">
<span>No Rewards History Found!</span>
</div>
}
{/* PAGINATION BUTTON */}
<PaginatedList onClick={handlePagination} prev={currentPage == 0 ? true : false} next={currentPage+Number(process.env.REACT_APP_ITEM_PER_PAGE) >= familyRewardHistory?.data?.length ? true : false} data={familyRewardHistory?.data} start={indexOfFirstItem} stop={indexOfLastItem} />
{/* END OF PAGINATION BUTTON */}
</div>
)
}
export default RewardsTable
+25 -4
View File
@@ -10,6 +10,7 @@ import usersService from "../../services/UsersService";
import PurchasesTable from "../MyWallet/WalletComponent/PurchasesTable";
import RecentActivityTable from "../MyWallet/WalletComponent/RecentActivityTable";
import LoadingSpinner from "../Spinners/LoadingSpinner";
import RewardsTable from "./RewardsTable";
export default function History() {
@@ -58,6 +59,9 @@ export default function History() {
useEffect(()=>{
getPaymentHistory()
}, [])
useEffect(()=>{
getPurchaseHistory()
}, [])
@@ -236,15 +240,24 @@ export default function History() {
>
Recent Activity
</button>
<button
name="reward"
onClick={(e) => setTab(e.target.name)}
className={`px-4 py-1 rounded-t-2xl ${
tab == "reward" ? "bg-[#4687ba] border-[2px] border-[#4687ba] text-white" : "bg-white text-[#000] border-t-[2px]"
}`}
>
Rewards
</button>
</div>
{/* END OF switch button */}
<div className="history-tables w-full">
{/* PURCHASE SECTION */}
{tab == 'purchases' &&
<div className="wallet w-full border-t">
<h1 className="p-2 text-xl font-bold text-dark-gray dark:text-white tracking-wide">Purchases</h1>
{/* <h1 className="p-2 text-xl font-bold text-dark-gray dark:text-white tracking-wide">Purchases</h1> */}
{purchaseHistory.loading ?
<LoadingSpinner size='16' color='sky-blue' />
<LoadingSpinner size='16' color='sky-blue' height='h-[500px]' />
:
<PurchasesTable purchase={purchaseHistory} />
}
@@ -255,16 +268,24 @@ export default function History() {
{/* RECENT ACTIVITY SECTION */}
{tab == 'recent' &&
<div className="wallet w-full border-t">
<h1 className="p-2 text-xl font-bold text-dark-gray dark:text-white tracking-wide">Recent Activity</h1>
{/* <h1 className="p-2 text-xl font-bold text-dark-gray dark:text-white tracking-wide">Recent Activity</h1> */}
{/* <p className='text-base text-slate-500 dark:text-white'>Activity Report</p> */}
{paymentHistory.loading ?
<LoadingSpinner size='16' color='sky-blue' />
<LoadingSpinner size='16' color='sky-blue' height='h-[500px]' />
:
<RecentActivityTable payment={paymentHistory} />
}
</div>
}
{/* END OF RECENT ACTIVITY SECTION */}
{/* REWARD SECTION */}
{tab == 'reward' &&
<div className="wallet w-full border-t">
<RewardsTable />
</div>
}
{/* END OF REWARD SECTION */}
</div>
</div>
{/*<HistoryTable />*/}
+9 -12
View File
@@ -1,12 +1,9 @@
import React from "react";
import datas from "../../data/product_data.json";
import TopSellerTopBuyerSliderSection from "./TopSellerTopBuyerSliderSection";
import CommonHead from "../UserHeader/CommonHead";
import FamilyActiveLSlde from "./FamilyActiveLSlde";
import ParentWaiting from "../MyPendingJobs/ParentWaiting";
import MyOffersFamilyTable from "../MyTasks/MyOffersFamilyTable";
import FamilyActiveLSlde from "./FamilyActiveLSlde";
export default function FamilyDash({familyOffers, MyActiveJobList}) {
export default function FamilyDash({ familyOffers, MyActiveJobList }) {
console.log("PROPS IN FAMILY DASH->", familyOffers);
const trending = MyActiveJobList;
@@ -14,13 +11,13 @@ export default function FamilyDash({familyOffers, MyActiveJobList}) {
<div>
<div className="home-page-wrapper">
{/* <CommonHead commonHeadData={props.commonHeadData} /> */}
{familyOffers && familyOffers.length > 0 &&
<MyOffersFamilyTable familyOffers={familyOffers} className="mb-10" />
}
{trending && trending.length > 0 &&
<FamilyActiveLSlde trending={trending} className="mb-10" />
}
{familyOffers && familyOffers.length > 0 && (
<MyOffersFamilyTable familyOffers={familyOffers} className="mb-10" />
)}
{trending && trending.length > 0 && (
<FamilyActiveLSlde trending={trending} className="mb-10" />
)}
{/*<TopSellerTopBuyerSliderSection className="mb-10" />*/}
<ParentWaiting className="mb-10" />
</div>
+33 -30
View File
@@ -1,20 +1,17 @@
import React from "react";
import datas from "../../data/product_data.json";
import Hero from "./Hero";
// import HomeTaskDisplay from "./HomeTaskDisplay";
import usersService from "../../services/UsersService";
import { useSelector } from "react-redux";
// import SellHistoryMarketVisitorAnalytic from './SellHistoryMarketVisitorAnalytic';
// import TopSellerTopBuyerSliderSection from "./TopSellerTopBuyerSliderSection";
//import UpdateTable from "./UpdateTable";
import MyJobTable from "../MyTasks/MyJobTable";
import MyOffersTable from "../MyTasks/MyOffersTable";
import LoadingSpinner from "../Spinners/LoadingSpinner";
import Hero from "./Hero";
import HomeActivities from "./HomeActivities";
export default function FullAccountDash(props) {
console.log("PROPS IN HOME->", props);
// console.log("PROPS IN HOME->", props);
const { userDetails } = useSelector((state) => state?.userDetails);
return (
<div>
<>
<div className="home-page-wrapper">
<Hero
className="mb-10"
@@ -22,7 +19,31 @@ export default function FullAccountDash(props) {
bannerList={props.bannerList}
nextDueTask={props.nextDueTask}
/>
<HomeActivities className="mb-10" />
{props.offersList?.data?.result_list?.length ? (
<MyOffersTable
MyActiveOffersList={props.offersList?.data}
className="mb-10"
/>
) : props.MyActiveJobList?.data?.length ? (
<>
<div className="w-full mb-5 flex justify-between items-center gap-1">
<h1 className="text-26 font-bold text-dark-gray dark:text-white">
<span>My Tasks</span>
</h1>
</div>
<MyJobTable
ActiveJobList={props.MyActiveJobList}
Account={userDetails}
/>
</>
) : !props.offersList?.loading && !props.MyActiveJobList?.loading ? (
<HomeActivities className="mb-10" />
) : (
<div className="w-full h-[220px] flex items-center justify-center">
<LoadingSpinner size="16" color="sky-blue" />
</div>
)}
{/*<UpdateTable className="mb-10"/>*/}
{/*<SellHistoryMarketVisitorAnalytic className="mb-10"/>*/}
{/*<TopSellerTopBuyerSliderSection className="mb-10" />*/}
@@ -33,24 +54,6 @@ export default function FullAccountDash(props) {
{/* bannerList={props.bannerList}*/}
{/*/>*/}
</div>
</div>
</>
);
}
// /*
// <Layout>
// <div className="home-page-wrapper">
// <Hero className="mb-10" data={userDetails} />
// {/* <CreateNft />
// <TrendingSection trending={trending} className="mb-10" />*/}
// <HomeTaskDisplay
// jobData={jobData}
// className="mb-10"
// bannerList={props.bannerList}
// />
{
/* <SellHistoryMarketVisitorAnalytic className="mb-10"/>
<TopSellerTopBuyerSliderSection className="mb-10" />
<UpdateTable className="mb-10"/>*/
}
// </div>
// </Layout>
+124 -258
View File
@@ -1,267 +1,133 @@
import React, { useState } from "react";
import React, { useEffect, useState } 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 SelectBox from "../Helpers/SelectBox";
import usersService from "../../services/UsersService";
import LoadingSpinner from "../Spinners/LoadingSpinner";
export default function HomeActivities({ className }) {
const filterCategories = ["All Categories", "Explore", "Featured"];
const [selectedCategory, setCategory] = useState(filterCategories[0]);
return (
<div
className={`update-table w-full p-8 bg-white dark:bg-dark-white overflow-hidden rounded-2xl section-shadow relative min-h-[520px] ${
className || ""
}`}
>
<div className="header w-full sm:flex justify-between items-center mb-5">
<div className="flex space-x-2 items-center mb-2 sm:mb-0">
<h1 className="text-xl font-bold text-dark-gray dark:text-white tracking-wide">
Recent Activities
</h1>
const [recentActivitiesData, setRecentActivitiesData] = useState({
loading: false,
status: false,
msg: "",
data: null,
});
</div>
</div>
<div className="relative w-full overflow-x-auto sm:rounded-lg">
<table className="w-full text-sm text-left text-gray-500 dark:text-gray-400">
<tbody>
{/* table heading */}
{/*<tr className="text-base text-thin-light-gray whitespace-nowrap px-2 border-b dark:border-[#5356fb29] default-border-b dark:border-[#5356fb29] ottom ">*/}
{/* <td className="py-4 w-[300px] block whitespace-nowrap">*/}
{/* All Product*/}
{/* </td>*/}
{/* <td className="py-4 whitespace-nowrap text-center">.</td>*/}
{/* <td className="py-4 whitespace-nowrap text-center"></td>*/}
const _apiCall = new usersService();
{/* <td className="py-4 whitespace-nowrap text-right"></td>*/}
{/*</tr>*/}
<tr className="bg-white dark:bg-dark-white border-b dark:border-[#5356fb29] hover:bg-gray-50">
<td className=" py-4">
<div className="flex space-x-2 items-center">
<div className="w-[60px] h-[60px] 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">
Mullican Computer Joy
</h1>
<span className="text-sm text-thin-light-gray">
Owned by <span className="text-purple">Xoeyam</span>
</span>
</div>
</div>
</td>
<td className="text-center py-4 px-2">
<div className="flex space-x-1 items-center justify-center">
<span>
<svg
width="18"
height="18"
viewBox="0 0 18 18"
fill="none"
xmlns="http://www.w3.org/2000/svg"
>
<path
d="M9 18C13.9706 18 18 13.9706 18 9C18 4.02944 13.9706 0 9 0C4.02944 0 0 4.02944 0 9C0 13.9706 4.02944 18 9 18Z"
fill="#627EEA"
/>
<path
d="M9.28125 2.25V7.23937L13.4983 9.12375L9.28125 2.25Z"
fill="white"
fillOpacity="0.602"
/>
<path
d="M9.28012 2.25L5.0625 9.12375L9.28012 7.23937V2.25Z"
fill="white"
/>
<path
d="M9.28125 12.3582V15.7483L13.5011 9.91016L9.28125 12.3582Z"
fill="white"
fillOpacity="0.602"
/>
<path
d="M9.28012 15.7483V12.3576L5.0625 9.91016L9.28012 15.7483Z"
fill="white"
/>
<path
d="M9.28125 11.572L13.4983 9.12348L9.28125 7.24023V11.572Z"
fill="white"
fillOpacity="0.2"
/>
<path
d="M5.0625 9.12348L9.28012 11.572V7.24023L5.0625 9.12348Z"
fill="white"
fillOpacity="0.602"
/>
</svg>
</span>
<span className="text-base text-dark-gray dark:text-white font-medium whitespace-nowrap">
7473 ETH
</span>
</div>
</td>
<td className="text-center py-4 px-2">
<div className="flex space-x-1 items-center justify-center">
<span>
<svg
width="16"
height="16"
viewBox="0 0 16 16"
fill="none"
xmlns="http://www.w3.org/2000/svg"
>
<path
d="M7.55225 0C7.8457 0 8.13914 0 8.43205 0C8.44829 0.026534 8.47537 0.0151623 8.49756 0.0162453C9.28966 0.0649812 10.0606 0.220936 10.8013 0.505229C12.7699 1.26172 14.2323 2.58354 15.183 4.46638C15.5999 5.29218 15.8506 6.16997 15.9561 7.08891C15.9691 7.201 15.9621 7.3158 16 7.42465C16 7.80858 16 8.19251 16 8.57698C15.9778 8.5916 15.9854 8.61543 15.9838 8.63546C15.9475 9.10387 15.8744 9.56686 15.7515 10.0206C15.1787 12.1342 13.9524 13.7603 12.0818 14.8942C11.1516 15.4579 10.1397 15.8002 9.06064 15.941C8.89497 15.9626 8.72875 15.98 8.56308 15.9995C8.17217 15.9995 7.78127 15.9995 7.39036 16C7.3752 15.9789 7.35138 15.9865 7.33135 15.9848C6.96752 15.9545 6.60639 15.9009 6.25068 15.8197C4.77639 15.4829 3.48998 14.793 2.4131 13.7311C0.998917 12.3372 0.204656 10.6461 0.0270709 8.66687C0.0205739 8.59431 0.033568 8.51904 0 8.44972C0 8.15081 0 7.85244 0 7.55352C0.0265295 7.53403 0.0151597 7.50479 0.016784 7.47988C0.0730915 6.64162 0.251218 5.83044 0.564158 5.05066C1.10179 3.71043 1.93774 2.59058 3.07634 1.70142C4.33839 0.715876 5.77098 0.159745 7.36762 0.0270755C7.4288 0.0216604 7.49432 0.0341151 7.55225 0ZM7.24635 9.86252C7.24635 10.2383 7.24526 10.6147 7.24743 10.9905C7.24797 11.0457 7.23389 11.0679 7.17596 11.0593C7.09691 11.0479 7.01678 11.0446 6.93774 11.0338C6.26746 10.9461 5.63563 10.7371 5.03952 10.4192C5.00379 10.4002 4.97834 10.3802 4.9621 10.4425C4.81375 11.0176 4.66324 11.5926 4.51164 12.1666C4.50027 12.2094 4.51272 12.2278 4.54954 12.2473C4.66486 12.3096 4.78235 12.3665 4.90309 12.4152C5.5961 12.6968 6.31998 12.8408 7.06497 12.8842C7.14131 12.8885 7.16134 12.9112 7.1608 12.9865C7.15701 13.4159 7.16026 13.8453 7.15809 14.2747C7.15755 14.3397 7.17488 14.3619 7.2431 14.3614C7.69085 14.3581 8.13914 14.3576 8.5869 14.3614C8.66432 14.3619 8.67731 14.3359 8.67731 14.2666C8.67461 13.8026 8.67677 13.3385 8.67461 12.8744C8.67407 12.8089 8.68544 12.7786 8.76015 12.765C9.09962 12.7049 9.4288 12.6058 9.74228 12.4607C10.3498 12.1802 10.8408 11.7703 11.1603 11.1724C11.4288 10.6699 11.51 10.1327 11.4618 9.56957C11.4158 9.03239 11.2366 8.55207 10.8787 8.14431C10.5506 7.77121 10.1402 7.51129 9.69843 7.29522C9.39145 7.14523 9.07363 7.02284 8.75041 6.91129C8.7098 6.89721 8.67407 6.88693 8.67407 6.82736C8.67623 6.14993 8.67569 5.4725 8.67461 4.79507C8.67461 4.75121 8.68489 4.73117 8.73308 4.73767C8.87547 4.75717 9.01895 4.77016 9.16134 4.79236C9.634 4.86493 10.0796 5.02467 10.5116 5.22395C10.5717 5.25157 10.5945 5.24886 10.6123 5.17684C10.7434 4.6467 10.8771 4.1171 11.0162 3.58913C11.0379 3.5079 11.0244 3.47541 10.948 3.44076C10.2799 3.13751 9.57282 3.01025 8.8457 2.97614C8.78018 2.97289 8.76123 2.95556 8.76178 2.88896C8.76503 2.50232 8.76232 2.11568 8.76448 1.72904C8.76503 1.66785 8.75041 1.64727 8.68489 1.64727C8.23173 1.64998 7.77802 1.64998 7.32485 1.64727C7.26151 1.64673 7.24418 1.66406 7.24418 1.72742C7.24689 2.1433 7.24256 2.55972 7.24797 2.9756C7.24905 3.06116 7.2209 3.08661 7.14239 3.10285C6.73579 3.18679 6.34651 3.32271 5.98646 3.53281C5.20628 3.98822 4.72117 4.64724 4.61938 5.5586C4.51597 6.48837 4.83812 7.2427 5.57661 7.81778C6.05739 8.19251 6.60639 8.43781 7.1738 8.64683C7.2274 8.66633 7.24743 8.68907 7.24689 8.7481C7.24472 9.12066 7.24635 9.49159 7.24635 9.86252Z"
fill="#59BE59"
/>
<path
d="M7.2452 9.86252C7.2452 9.49158 7.24358 9.12119 7.24683 8.75026C7.24737 8.69123 7.22734 8.66903 7.17374 8.64899C6.60687 8.43997 6.05787 8.19467 5.57655 7.81994C4.8386 7.24486 4.51591 6.49053 4.61933 5.56076C4.72057 4.6494 5.20568 3.99092 5.98641 3.53497C6.34645 3.32486 6.73519 3.18894 7.14233 3.10501C7.22084 3.08876 7.24899 3.06277 7.24791 2.97775C7.2425 2.56187 7.24683 2.14545 7.24412 1.72957C7.24358 1.66621 7.2609 1.64888 7.32479 1.64943C7.77796 1.65213 8.23167 1.65213 8.68483 1.64943C8.7498 1.64888 8.76442 1.66946 8.76442 1.73119C8.76171 2.11783 8.76496 2.50447 8.76171 2.89111C8.76117 2.95717 8.78012 2.97504 8.84563 2.97829C9.57276 3.01295 10.2793 3.13966 10.948 3.44291C11.0243 3.47757 11.0373 3.51006 11.0162 3.59128C10.877 4.11926 10.7433 4.64885 10.6123 5.17899C10.5944 5.25156 10.5717 5.25372 10.5116 5.2261C10.079 5.02683 9.63394 4.86708 9.16128 4.79452C9.01889 4.77286 8.87595 4.75932 8.73302 4.73983C8.68483 4.73333 8.67455 4.75337 8.67455 4.79723C8.67563 5.47466 8.67617 6.15209 8.674 6.82952C8.674 6.88908 8.70974 6.89937 8.75034 6.91345C9.07303 7.02446 9.39138 7.14684 9.69837 7.29738C10.1396 7.51344 10.5506 7.77283 10.8787 8.14647C11.2365 8.55369 11.4157 9.03455 11.4618 9.57173C11.51 10.1349 11.4287 10.6726 11.1602 11.1746C10.8408 11.7724 10.3497 12.1818 9.74222 12.4629C9.42874 12.608 9.09956 12.7071 8.76009 12.7672C8.68483 12.7802 8.674 12.811 8.67455 12.8766C8.67671 13.3406 8.67455 13.8047 8.67725 14.2688C8.67779 14.3381 8.66426 14.3646 8.58684 14.3636C8.13908 14.3598 7.69079 14.3608 7.24304 14.3636C7.17536 14.3641 7.15803 14.3424 7.15803 14.2769C7.1602 13.8475 7.15695 13.4181 7.16074 12.9887C7.16128 12.9128 7.14179 12.8906 7.06491 12.8863C6.31992 12.843 5.59658 12.699 4.90303 12.4174C4.78229 12.3681 4.66426 12.3112 4.54948 12.2495C4.51321 12.23 4.50075 12.2116 4.51158 12.1688C4.66318 11.5943 4.81369 11.0197 4.96204 10.4446C4.97829 10.3824 5.00373 10.4024 5.03947 10.4214C5.63557 10.7387 6.2674 10.9477 6.93768 11.036C7.01672 11.0463 7.09685 11.0501 7.1759 11.0614C7.23383 11.0695 7.24737 11.0479 7.24737 10.9927C7.24412 10.6147 7.2452 10.2383 7.2452 9.86252ZM8.68537 9.36325C8.67942 9.37245 8.67455 9.37678 8.67455 9.38112C8.674 9.83978 8.67401 10.2984 8.67292 10.7571C8.67292 10.8177 8.70216 10.7928 8.72598 10.7755C8.82452 10.7046 8.90736 10.619 8.96691 10.5123C9.17698 10.1333 9.05679 9.63725 8.68537 9.36325ZM7.23871 6.11147C7.23871 5.75354 7.23871 5.40589 7.23871 5.05174C6.92522 5.33982 6.92522 5.77249 7.23871 6.11147Z"
fill="#FEFEFE"
/>
<path
d="M8.68433 9.36328C9.05574 9.63729 9.17539 10.1333 8.96586 10.5118C8.90631 10.619 8.82347 10.7046 8.72493 10.775C8.70111 10.7918 8.67188 10.8172 8.67188 10.7566C8.67242 10.2979 8.67296 9.83927 8.6735 9.38061C8.67404 9.37682 8.67891 9.37249 8.68433 9.36328Z"
fill="#59BE59"
/>
<path
d="M7.23882 6.11149C6.92533 5.77305 6.92587 5.33984 7.23882 5.05176C7.23882 5.40591 7.23882 5.75355 7.23882 6.11149Z"
fill="#59BE59"
/>
</svg>
</span>
<span className="text-base text-dark-gray dark:text-white font-medium">
6392.99$
</span>
</div>
</td>
const getRecentActivities = async () => {
setRecentActivitiesData((prev) => ({ ...prev, loading: true }));
let res;
try {
res = await _apiCall.getRecentActivities(null);
res = res.data;
if (res?.internal_return >= 0) {
setRecentActivitiesData((prev) => ({
...prev,
loading: false,
status: true,
data: res.result_list,
}));
} else {
setRecentActivitiesData((prev) => ({
...prev,
loading: false,
status: false,
msg: "Something went wrong",
}));
}
} catch (error) {
setRecentActivitiesData((prev) => ({
...prev,
loading: false,
status: false,
msg: "Unable to see this",
}));
throw new Error(`Unable to see this because: ${error}`);
}
};
<td className="text-right py-4">
<button
type="button"
className="text-sm text-white bg-purple px-2.5 py-1.5 rounded-full"
>
Active
</button>
</td>
</tr>
<tr className="bg-white dark:bg-dark-white border-b dark:border-[#5356fb29] hover:bg-gray-50">
<td className=" py-4">
<div className="flex space-x-2 items-center">
<div className="w-[60px] h-[60px] rounded-full overflow-hidden flex justify-center items-center">
<img
src={dataImage2}
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">
Mullican Computer Joy
</h1>
<span className="text-sm text-thin-light-gray">
Owned by <span className="text-purple">Xoeyam</span>
</span>
</div>
</div>
</td>
<td className="text-center py-4 px-2">
<div className="flex space-x-1 items-center justify-center">
<span>
<svg
width="18"
height="18"
viewBox="0 0 18 18"
fill="none"
xmlns="http://www.w3.org/2000/svg"
>
<path
d="M9 18C13.9706 18 18 13.9706 18 9C18 4.02944 13.9706 0 9 0C4.02944 0 0 4.02944 0 9C0 13.9706 4.02944 18 9 18Z"
fill="#627EEA"
/>
<path
d="M9.28125 2.25V7.23937L13.4983 9.12375L9.28125 2.25Z"
fill="white"
fillOpacity="0.602"
/>
<path
d="M9.28012 2.25L5.0625 9.12375L9.28012 7.23937V2.25Z"
fill="white"
/>
<path
d="M9.28125 12.3582V15.7483L13.5011 9.91016L9.28125 12.3582Z"
fill="white"
fillOpacity="0.602"
/>
<path
d="M9.28012 15.7483V12.3576L5.0625 9.91016L9.28012 15.7483Z"
fill="white"
/>
<path
d="M9.28125 11.572L13.4983 9.12348L9.28125 7.24023V11.572Z"
fill="white"
fillOpacity="0.2"
/>
<path
d="M5.0625 9.12348L9.28012 11.572V7.24023L5.0625 9.12348Z"
fill="white"
fillOpacity="0.602"
/>
</svg>
</span>
<span className="text-base text-dark-gray dark:text-white font-medium whitespace-nowrap">
7473 ETH
</span>
</div>
</td>
<td className="text-center py-4 px-2">
<div className="flex space-x-1 items-center justify-center">
<span>
<svg
width="16"
height="16"
viewBox="0 0 16 16"
fill="none"
xmlns="http://www.w3.org/2000/svg"
>
<path
d="M7.55225 0C7.8457 0 8.13914 0 8.43205 0C8.44829 0.026534 8.47537 0.0151623 8.49756 0.0162453C9.28966 0.0649812 10.0606 0.220936 10.8013 0.505229C12.7699 1.26172 14.2323 2.58354 15.183 4.46638C15.5999 5.29218 15.8506 6.16997 15.9561 7.08891C15.9691 7.201 15.9621 7.3158 16 7.42465C16 7.80858 16 8.19251 16 8.57698C15.9778 8.5916 15.9854 8.61543 15.9838 8.63546C15.9475 9.10387 15.8744 9.56686 15.7515 10.0206C15.1787 12.1342 13.9524 13.7603 12.0818 14.8942C11.1516 15.4579 10.1397 15.8002 9.06064 15.941C8.89497 15.9626 8.72875 15.98 8.56308 15.9995C8.17217 15.9995 7.78127 15.9995 7.39036 16C7.3752 15.9789 7.35138 15.9865 7.33135 15.9848C6.96752 15.9545 6.60639 15.9009 6.25068 15.8197C4.77639 15.4829 3.48998 14.793 2.4131 13.7311C0.998917 12.3372 0.204656 10.6461 0.0270709 8.66687C0.0205739 8.59431 0.033568 8.51904 0 8.44972C0 8.15081 0 7.85244 0 7.55352C0.0265295 7.53403 0.0151597 7.50479 0.016784 7.47988C0.0730915 6.64162 0.251218 5.83044 0.564158 5.05066C1.10179 3.71043 1.93774 2.59058 3.07634 1.70142C4.33839 0.715876 5.77098 0.159745 7.36762 0.0270755C7.4288 0.0216604 7.49432 0.0341151 7.55225 0ZM7.24635 9.86252C7.24635 10.2383 7.24526 10.6147 7.24743 10.9905C7.24797 11.0457 7.23389 11.0679 7.17596 11.0593C7.09691 11.0479 7.01678 11.0446 6.93774 11.0338C6.26746 10.9461 5.63563 10.7371 5.03952 10.4192C5.00379 10.4002 4.97834 10.3802 4.9621 10.4425C4.81375 11.0176 4.66324 11.5926 4.51164 12.1666C4.50027 12.2094 4.51272 12.2278 4.54954 12.2473C4.66486 12.3096 4.78235 12.3665 4.90309 12.4152C5.5961 12.6968 6.31998 12.8408 7.06497 12.8842C7.14131 12.8885 7.16134 12.9112 7.1608 12.9865C7.15701 13.4159 7.16026 13.8453 7.15809 14.2747C7.15755 14.3397 7.17488 14.3619 7.2431 14.3614C7.69085 14.3581 8.13914 14.3576 8.5869 14.3614C8.66432 14.3619 8.67731 14.3359 8.67731 14.2666C8.67461 13.8026 8.67677 13.3385 8.67461 12.8744C8.67407 12.8089 8.68544 12.7786 8.76015 12.765C9.09962 12.7049 9.4288 12.6058 9.74228 12.4607C10.3498 12.1802 10.8408 11.7703 11.1603 11.1724C11.4288 10.6699 11.51 10.1327 11.4618 9.56957C11.4158 9.03239 11.2366 8.55207 10.8787 8.14431C10.5506 7.77121 10.1402 7.51129 9.69843 7.29522C9.39145 7.14523 9.07363 7.02284 8.75041 6.91129C8.7098 6.89721 8.67407 6.88693 8.67407 6.82736C8.67623 6.14993 8.67569 5.4725 8.67461 4.79507C8.67461 4.75121 8.68489 4.73117 8.73308 4.73767C8.87547 4.75717 9.01895 4.77016 9.16134 4.79236C9.634 4.86493 10.0796 5.02467 10.5116 5.22395C10.5717 5.25157 10.5945 5.24886 10.6123 5.17684C10.7434 4.6467 10.8771 4.1171 11.0162 3.58913C11.0379 3.5079 11.0244 3.47541 10.948 3.44076C10.2799 3.13751 9.57282 3.01025 8.8457 2.97614C8.78018 2.97289 8.76123 2.95556 8.76178 2.88896C8.76503 2.50232 8.76232 2.11568 8.76448 1.72904C8.76503 1.66785 8.75041 1.64727 8.68489 1.64727C8.23173 1.64998 7.77802 1.64998 7.32485 1.64727C7.26151 1.64673 7.24418 1.66406 7.24418 1.72742C7.24689 2.1433 7.24256 2.55972 7.24797 2.9756C7.24905 3.06116 7.2209 3.08661 7.14239 3.10285C6.73579 3.18679 6.34651 3.32271 5.98646 3.53281C5.20628 3.98822 4.72117 4.64724 4.61938 5.5586C4.51597 6.48837 4.83812 7.2427 5.57661 7.81778C6.05739 8.19251 6.60639 8.43781 7.1738 8.64683C7.2274 8.66633 7.24743 8.68907 7.24689 8.7481C7.24472 9.12066 7.24635 9.49159 7.24635 9.86252Z"
fill="#59BE59"
/>
<path
d="M7.2452 9.86252C7.2452 9.49158 7.24358 9.12119 7.24683 8.75026C7.24737 8.69123 7.22734 8.66903 7.17374 8.64899C6.60687 8.43997 6.05787 8.19467 5.57655 7.81994C4.8386 7.24486 4.51591 6.49053 4.61933 5.56076C4.72057 4.6494 5.20568 3.99092 5.98641 3.53497C6.34645 3.32486 6.73519 3.18894 7.14233 3.10501C7.22084 3.08876 7.24899 3.06277 7.24791 2.97775C7.2425 2.56187 7.24683 2.14545 7.24412 1.72957C7.24358 1.66621 7.2609 1.64888 7.32479 1.64943C7.77796 1.65213 8.23167 1.65213 8.68483 1.64943C8.7498 1.64888 8.76442 1.66946 8.76442 1.73119C8.76171 2.11783 8.76496 2.50447 8.76171 2.89111C8.76117 2.95717 8.78012 2.97504 8.84563 2.97829C9.57276 3.01295 10.2793 3.13966 10.948 3.44291C11.0243 3.47757 11.0373 3.51006 11.0162 3.59128C10.877 4.11926 10.7433 4.64885 10.6123 5.17899C10.5944 5.25156 10.5717 5.25372 10.5116 5.2261C10.079 5.02683 9.63394 4.86708 9.16128 4.79452C9.01889 4.77286 8.87595 4.75932 8.73302 4.73983C8.68483 4.73333 8.67455 4.75337 8.67455 4.79723C8.67563 5.47466 8.67617 6.15209 8.674 6.82952C8.674 6.88908 8.70974 6.89937 8.75034 6.91345C9.07303 7.02446 9.39138 7.14684 9.69837 7.29738C10.1396 7.51344 10.5506 7.77283 10.8787 8.14647C11.2365 8.55369 11.4157 9.03455 11.4618 9.57173C11.51 10.1349 11.4287 10.6726 11.1602 11.1746C10.8408 11.7724 10.3497 12.1818 9.74222 12.4629C9.42874 12.608 9.09956 12.7071 8.76009 12.7672C8.68483 12.7802 8.674 12.811 8.67455 12.8766C8.67671 13.3406 8.67455 13.8047 8.67725 14.2688C8.67779 14.3381 8.66426 14.3646 8.58684 14.3636C8.13908 14.3598 7.69079 14.3608 7.24304 14.3636C7.17536 14.3641 7.15803 14.3424 7.15803 14.2769C7.1602 13.8475 7.15695 13.4181 7.16074 12.9887C7.16128 12.9128 7.14179 12.8906 7.06491 12.8863C6.31992 12.843 5.59658 12.699 4.90303 12.4174C4.78229 12.3681 4.66426 12.3112 4.54948 12.2495C4.51321 12.23 4.50075 12.2116 4.51158 12.1688C4.66318 11.5943 4.81369 11.0197 4.96204 10.4446C4.97829 10.3824 5.00373 10.4024 5.03947 10.4214C5.63557 10.7387 6.2674 10.9477 6.93768 11.036C7.01672 11.0463 7.09685 11.0501 7.1759 11.0614C7.23383 11.0695 7.24737 11.0479 7.24737 10.9927C7.24412 10.6147 7.2452 10.2383 7.2452 9.86252ZM8.68537 9.36325C8.67942 9.37245 8.67455 9.37678 8.67455 9.38112C8.674 9.83978 8.67401 10.2984 8.67292 10.7571C8.67292 10.8177 8.70216 10.7928 8.72598 10.7755C8.82452 10.7046 8.90736 10.619 8.96691 10.5123C9.17698 10.1333 9.05679 9.63725 8.68537 9.36325ZM7.23871 6.11147C7.23871 5.75354 7.23871 5.40589 7.23871 5.05174C6.92522 5.33982 6.92522 5.77249 7.23871 6.11147Z"
fill="#FEFEFE"
/>
<path
d="M8.68433 9.36328C9.05574 9.63729 9.17539 10.1333 8.96586 10.5118C8.90631 10.619 8.82347 10.7046 8.72493 10.775C8.70111 10.7918 8.67188 10.8172 8.67188 10.7566C8.67242 10.2979 8.67296 9.83927 8.6735 9.38061C8.67404 9.37682 8.67891 9.37249 8.68433 9.36328Z"
fill="#59BE59"
/>
<path
d="M7.23882 6.11149C6.92533 5.77305 6.92587 5.33984 7.23882 5.05176C7.23882 5.40591 7.23882 5.75355 7.23882 6.11149Z"
fill="#59BE59"
/>
</svg>
</span>
<span className="text-base text-dark-gray dark:text-white font-medium">
6392.99$
</span>
</div>
</td>
useEffect(() => {
getRecentActivities();
}, []);
<td className="text-right py-4">
<button
type="button"
className="text-sm text-white bg-light-green px-2.5 py-1.5 rounded-full"
>
Complated
</button>
</td>
</tr>
</tbody>
</table>
</div>
console.log(recentActivitiesData);
return (
<div
className={`update-table w-full p-8 bg-white dark:bg-dark-white overflow-hidden rounded-2xl section-shadow relative min-h-[520px] ${
className || ""
}`}
>
<div className="header w-full sm:flex justify-between items-center mb-5">
<div className="flex space-x-2 items-center mb-4 sm:mb-0">
<h1 className="text-center text-2xl font-bold text-dark-gray dark:text-white tracking-wide">
Recent Activities
</h1>
</div>
);
</div>
<div className="relative w-full overflow-x-auto sm:rounded-lg">
<table className="w-full text-sm text-left text-gray-500 dark:text-gray-400">
<tbody>
{/* table heading */}
{/*<tr className="text-base text-thin-light-gray whitespace-nowrap px-2 border-b dark:border-[#5356fb29] default-border-b dark:border-[#5356fb29] ottom ">*/}
{/* <td className="py-4 w-[300px] block whitespace-nowrap">*/}
{/* All Product*/}
{/* </td>*/}
{/* <td className="py-4 whitespace-nowrap text-center">.</td>*/}
{/* <td className="py-4 whitespace-nowrap text-center"></td>*/}
{/* <td className="py-4 whitespace-nowrap text-right"></td>*/}
{/*</tr>*/}
{recentActivitiesData.loading ? (
<tr>
<td>
<div className="h-[100px] w-full flex justify-center items-center">
<LoadingSpinner color="sky-blue" size="16" />
</div>
</td>
</tr>
) : recentActivitiesData.data ? (
recentActivitiesData.data?.map((item) => {
let addedDate = item?.added?.split(" ")[0];
return (
<tr
className="bg-white dark:bg-dark-white border-b dark:border-[#5356fb29] hover:bg-gray-50"
key={item.uid}
>
<td className="py-3">
<div className="flex space-x-2 items-center">
{/* <div className="w-[60px] 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 ? item.title : "Title"}
</h1>
<span className="text-sm text-thin-light-gray">
{item.description ? item.description : ""}
</span>
</div>
</div>
</td>
<td className="text-right py-3">
<div className="flex space-x-1 items-center justify-end">
<span className="text-base text-dark-gray dark:text-white font-medium">
{item.added ? addedDate : ""}
</span>
</div>
</td>
</tr>
);
})
) : null}
</tbody>
</table>
</div>
</div>
);
}
+14 -9
View File
@@ -7,28 +7,30 @@ import FamilyDash from "./FamilyDash";
import FullAccountDash from "./FullAccountDash";
export default function Home(props) {
console.log("PROPS IN HOME->", props);
// console.log("PROPS IN HOME->", props);
const userApi = new usersService();
const { commonHeadBanner } = useSelector((state) => state.commonHeadBanner);
let [nextDueTask, setNextDueTask] = useState({});
const [MyOffersList, setMyOffersList] = useState([]);
const [MyOffersList, setMyOffersList] = useState({loading: true, data: []});
const { userDetails } = useSelector((state) => state?.userDetails);
const [MyActiveJobList, setMyActiveJobList] = useState([]); // STATE TO HOLD ACTIVE/CURRENT TASKS
const [MyActiveJobList, setMyActiveJobList] = useState({loading:true, data:[]}); // STATE TO HOLD ACTIVE/CURRENT TASKS
// const [MyActiveJobList, setMyActiveJobList] = useState([]); // STATE TO HOLD ACTIVE/CURRENT TASKS
const getMyActiveJobList = async () => { // FUNCTION TO POPULATE ACTIVE/CURRENT TASK LIST
try {
const res = await userApi.getMyActiveTaskList();
setMyActiveJobList(res?.data?.result_list);
setMyActiveJobList({loading:false, data:res?.data?.result_list});
// setMyActiveJobList(res?.data?.result_list);
} catch (error) {
setMyActiveJobList([]);
setMyActiveJobList({loading:false, data:[]});
// setMyActiveJobList([]);
console.log("Error getting tasks");
}
};
// FUNCTION TO GET DASH DATA TO DETERMINE CURRENT TASK DUE TIME
const getHomeDate = () => {
userApi
@@ -47,8 +49,9 @@ export default function Home(props) {
const getMyOffersList = async () => {
try {
const res = await userApi.getOffersList();
setMyOffersList(res.data?.result_list);
setMyOffersList({loading:false, data:res.data});
} catch (error) {
setMyOffersList({loading:false, data:[]});
console.log("Error getting offers", error);
}
};
@@ -72,13 +75,15 @@ export default function Home(props) {
<FamilyDash
account={userDetails}
commonHeadData={props.bannerList}
familyOffers={MyOffersList}
MyActiveJobList={MyActiveJobList}
familyOffers={MyOffersList?.data?.result_list}
MyActiveJobList={MyActiveJobList?.data}
/>
) : userDetails && userDetails?.account_type == "FULL" ? (
<FullAccountDash
nextDueTask={nextDueTask}
bannerList={props.bannerList}
offersList={MyOffersList}
MyActiveJobList={MyActiveJobList}
/>
) : (
<div>
+3 -3
View File
@@ -1,8 +1,8 @@
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 ListView from "../../assets/images/list-view.png";
import AvailableJobsCard from "../Cards/AvailableJobsCard";
import DataIteration from "../Helpers/DataIteration";
import SelectBox from "../Helpers/SelectBox";
export default function MainSection({
@@ -7,6 +7,10 @@ import LoadingSpinner from "../../Spinners/LoadingSpinner";
const MarketPopUp = ({ details, onClose, situation, marketInt }) => {
const [textValue, setTextValue] = useState("");
const [errMsg, setErrMsg] = useState({
market: false,
manage: false,
});
const apiCall = useMemo(() => new usersService(), []);
const handleInputChange = ({ target: { value } }) => {
@@ -48,20 +52,17 @@ const MarketPopUp = ({ details, onClose, situation, marketInt }) => {
return;
}
toast.success("Message sent", {
autoClose: 2500,
hideProgressBar: true,
});
setMarketMsg({ data: marketMessageRes, state: true });
setTimeout(() => onClose(), 2000);
} catch (error) {
setErrMsg({ market: true });
setMarketMsg({ loading: false, state: false });
throw new Error(error);
} finally {
setTextValue("");
setTimeout(() => {
setTextValue("");
setMarketMsg({ loading: false });
}, 2000);
setMarketMsg({ loading: false, state: false });
setErrMsg({ market: false });
}, 5000);
}
};
@@ -117,7 +118,7 @@ const MarketPopUp = ({ details, onClose, situation, marketInt }) => {
return (
<ModalCom action={onClose} situation={situation} className="edit-popup">
<div className="logout-modal-wrapper md:w-[750px] md:h-[660px] h-full bg-white dark:bg-dark-white lg:rounded-2xl overflow-y-auto">
<div className="logout-modal-wrapper md:w-[650px] md:h-[580px] h-full bg-white dark:bg-dark-white lg:rounded-2xl overflow-y-auto">
<div className="logout-modal-header w-full flex items-center justify-between lg:p-6 px-[30px] py-[23px]">
<h1 className="text-26 font-bold text-dark-gray dark:text-white tracking-wide">
{details.offer_code}
@@ -125,10 +126,10 @@ const MarketPopUp = ({ details, onClose, situation, marketInt }) => {
<CloseIcon onClose={onClose} />
</div>
<div className="md:flex bg-white rounded-lg">
<div className="md:flex bg-white dark:bg-dark-white text-slate-900 dark:text-white rounded-lg">
<div className="p-4 w-full md:w-[75%] md:border-r-1">
<div className="min-h-[263px]">
<h2 className="font-semibold text-slate-900 dark:text-black tracking-wide">
<div className="max-h-[240px] h-full">
<h2 className="font-semibold text-slate-900 dark:text-white tracking-wide">
{details?.title}
</h2>
@@ -152,32 +153,32 @@ const MarketPopUp = ({ details, onClose, situation, marketInt }) => {
},
].map(({ name, content, danger }, idx) => (
<div className={`my-3 md:flex items-center`} key={idx}>
<label className="w-full md:w-[19%] text-slate-900 tracking-wide font-semibold whitespace-pre-wrap">
<label className="w-full md:w-[19%] tracking-wide font-semibold whitespace-pre-wrap">
{name}
</label>
<div
className={`w-full md:w-3/4 text-slate-900 market-pop ${
className={`w-full md:w-3/4 text-slate-900 dark:text-white market-pop ${
name !== "Delivery Detail"
? " h-full max-h-28 flex items-center"
: " overflow-y-auto max-h-[100px]"
}`}
? " h-full flex items-center"
: " overflow-y-auto max-h-[80px]"
} ${name === "Description" && "max-h-14 h-full overflow-auto"}`}
>
{danger ? (
<p
className={``}
className={`dark:text-white`}
dangerouslySetInnerHTML={{
__html: danger && content,
}}
/>
) : (
<p className={`w-full text-slate-900`}>
<p className={`w-full text-slate-900 dark:text-white`}>
{name !== "Delivery Detail" ? (
<>
{typeof content !== "object" ? content : null}
{typeof content === "object" && (
<>
<hr className="mb-1" />
<span className="flex items-center gap-2">
<span className="flex items-center gap-2 dark:text-white">
{content?.text}
<strong>{thePrice}</strong>
</span>
@@ -195,40 +196,46 @@ const MarketPopUp = ({ details, onClose, situation, marketInt }) => {
))}
</div>
<hr />
<div className="my-3 w-full flex flex-col gap-3">
<div className="w-full flex flex-col gap-3">
<div className="w-full">
<label className="w-full text-slate-900 tracking-wide">
<label className="w-full text-slate-900 dark:text-white tracking-wide font-semibold">
If you have any questions about this task:
</label>
<textarea
className={`p-1 w-full text-sm text-slate-900 outline-none border border-slate-300 rounded-md`}
rows="5"
className={`p-1 w-full text-sm text-slate-900 dark:text-white ${
marketMsg.loading && "italic text-[#9CA3AF]"
} bg-transparent outline-none border-2 border-slate-300 rounded-md`}
rows="3"
style={{ resize: "none" }}
placeholder="Enter message here ..."
value={textValue}
value={marketMsg.loading ? "Sending..." : textValue}
onChange={handleInputChange}
/>
</div>
<button
className="self-end w-[150px] h-[52px] rounded-md text-base bg-yellow-500 text-white"
name="market-message"
onClick={MarketDetail}
>
{marketMsg.loading ? (
<LoadingSpinner size={5} color="white" />
) : !marketMsg.state ? (
"Send Message"
) : (
"Message Sent"
)}
</button>
<div className="relative flex w-full justify-end ">
<span className="text-sm text-[#57cd89] absolute left-[8rem] top-4">
{marketMsg.state && "Message Sent!"}
{errMsg.market && "Something went wrong"}
</span>
<button
className="self-end w-[150px] h-[48px] rounded-full text-base bg-yellow-500 text-white"
name="market-message"
onClick={MarketDetail}
>
{marketMsg.loading ? (
<LoadingSpinner size={5} color="white" />
) : (
"Send Message"
)}
</button>
</div>
</div>
</div>
<div className="w-full md:w-[23%] h-full ">
<div className="mx-auto bg-[#f1f8ff] p-4 rounded-md md:min-h-[498px] flex flex-col justify-between">
<div className="w-full flex flex-col justify-center py-4 gap-2">
<p className="w-full text-slate-900 tracking-wide my-1">
<div className="w-full md:w-[23%] h-full flex flex-col">
<div className="mx-auto bg-[#f1f8ff] dark:bg-[#C2C8D3] px-4 rounded-md md:min-h-[420px] flex flex-col justify-between">
<div className="w-full flex flex-col justify-center pb-4 gap-2">
<p className="w-full text-slate-900 tracking-wide my-1 font-semibold">
Interested in the task?
</p>
<hr />
@@ -261,7 +268,7 @@ const MarketPopUp = ({ details, onClose, situation, marketInt }) => {
</>
</div>
<div className="">
<div className="text-slate-900">
<p className="flex items-center tracking-wide">
Interest: <b className="ml-1">{details.interest_count}</b>
</p>
@@ -270,7 +277,7 @@ const MarketPopUp = ({ details, onClose, situation, marketInt }) => {
</div>
</div>
<button
className="self-end w-[150px] mt-2 h-[52px] rounded-md text-base bg-transparent border border-red-500 text-red-500 mx-auto"
className="self-center w-[150px] mt-2 h-[48px] rounded-full text-base bg-transparent border border-red-500 text-red-500 mx-auto"
name="cancel"
onClick={onClose}
>
+4 -4
View File
@@ -1,7 +1,7 @@
import Layout from "../Partials/Layout";
import MainSection from "./MainSection";
import CommonHead from "../UserHeader/CommonHead";
import { useSelector } from "react-redux";
import Layout from "../Partials/Layout";
import CommonHead from "../UserHeader/CommonHead";
import MainSection from "./MainSection";
export default function MarketPlace({ commonHeadData }) {
let { jobLists } = useSelector((state) => state.jobLists);
@@ -20,4 +20,4 @@ export default function MarketPlace({ commonHeadData }) {
</Layout>
</>
);
}
}
@@ -35,7 +35,11 @@ export default function ActiveJobMessage({ activeJobMesList }) {
<td>
<div className={`msg_box ${item.who}`}>
<div className="msg_header">{item.msg_date} {item.msg_firstname}</div>
{item.msg_type == 'FILE' ?
<a href='' className="p-2" dangerouslySetInnerHTML={{__html: item.message}}></a>
:
<span className="p-2" dangerouslySetInnerHTML={{__html: item.message}}></span>
}
</div>
</td>
+26 -12
View File
@@ -10,24 +10,25 @@ import ActiveJobMessage from "./ActiveJobMessage";
import IndexJobActions from "./JobActions/IndexJobActions";
import usersService from "../../services/UsersService";
import { PriceFormatter } from "../Helpers/PriceFormatter";
function ActiveJobs(props) {
const ApiCall = new usersService();
let navigate = useNavigate();
const navigate = useNavigate();
let { userDetails } = useSelector((state) => state.userDetails);
const { userDetails } = useSelector((state) => state.userDetails);
let [passDue, setPassDue] = useState(
const [passDue, setPassDue] = useState(
new Date() > new Date(props.details?.delivery_date)
); // STATE TO KNOW IF TASK IS PASSED DUE TIME
let [messageToSend, setMessageToSend] = useState(""); // State to hold the value of message to be sent
const [messageToSend, setMessageToSend] = useState(""); // State to hold the value of message to be sent
let [filesToSend, setFilesToSend] = useState([]); // State to hold the value of files to be sent
const [filesToSend, setFilesToSend] = useState([]); // State to hold the value of files to be sent
let [tab, setTab] = useState("message");
const [tab, setTab] = useState("message");
let [requestStatus, setRequestStatus] = useState({
const [requestStatus, setRequestStatus] = useState({
loading: false,
status: false,
message: "",
@@ -248,7 +249,11 @@ function ActiveJobs(props) {
}
}, [passDue]);
console.log("AC JOBS >>", props);
let thePrice = PriceFormatter(
props.details?.price * 0.01,
props.details?.currency_code,
props.details?.currency
);
return (
<Layout>
@@ -311,10 +316,11 @@ function ActiveJobs(props) {
<span className="font-semibold">Due: </span>
{props?.details && props.details.delivery_date.split(" ")[0]}
</p>
<p className="py-2 text-base text-slate-700">
{props?.delivery_date &&
props.details.delivery_date.split(" ")[1]}
</p>
{props?.delivery_date && (
<p className="py-2 text-base text-slate-700">
{props.details.delivery_date.split(" ")[1]}
</p>
)}
</div>
) : (
<div className="my-1 flex items-start gap-3">
@@ -334,6 +340,13 @@ function ActiveJobs(props) {
</div>
)}
<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">
Price:{" "}
</span>
<span className="">{thePrice}</span>
</div>
<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:{" "}
@@ -405,6 +418,7 @@ function ActiveJobs(props) {
name="message"
onChange={handleMessageChange}
value={messageToSend}
autoFocus
/>
) : (
<div className="p-4 w-full h-[300px] text-base text-slate-600 border border-slate-300">
@@ -118,7 +118,11 @@ function PastDueJobAction({jobDetails}) {
<tr>
<td>
<div className="flex justify-center items-center">
<button type="button" onClick={popUpHandler} className="w-[180px] h-11 flex justify-center items-center btn-gradient text-base rounded-full text-white">
<button
type="button"
onClick={popUpHandler}
className="px-4 h-[48px] flex justify-center items-center btn-gradient text-base rounded-full text-white"
>
Cancel or Extend Timeline
</button>
</div>
@@ -230,8 +234,12 @@ function PastDueJobAction({jobDetails}) {
{/* cancel btn */}
<div className='flex justify-end items-center'>
<button onClick={popUpHandler} type="button" className="w-20 h-11 flex justify-center items-center border-gradient text-base rounded-full text-white">
<span className='text-gradient'>Cancel</span>
<button onClick={popUpHandler} type="button"
// className="w-20 h-11 flex justify-center items-center border-gradient text-base rounded-full text-white"
className='w-[150px] mt-2 h-[48px] rounded-full text-base bg-transparent border border-red-500 text-red-500'
>
Cancel
{/* <span className='text-gradient'>Cancel</span> */}
</button>
</div>
</div>
@@ -1,10 +1,9 @@
import React, { useState } from "react";
import { useLocation, useNavigate } from "react-router-dom";
import dataImage2 from "../../assets/images/data-table-user-2.png";
import localImgLoad from "../../lib/localImgLoad";
import { PriceFormatter } from "../Helpers/PriceFormatter";
import { handlePagingFunc } from "../Pagination/HandlePagination";
import PaginatedList from "../Pagination/PaginatedList";
import { PriceFormatter } from "../Helpers/PriceFormatter";
import localImgLoad from "../../lib/localImgLoad";
export default function MyActiveJobTable({ MyJobList, className }) {
const navigate = useNavigate();
@@ -25,7 +24,7 @@ export default function MyActiveJobTable({ MyJobList, className }) {
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={`update-table w-full p-3 sm:p-8 bg-white dark:bg-dark-white overflow-hidden rounded-2xl section-shadow min-h-[520px] ${
className || ""
}`}
>
@@ -33,11 +32,6 @@ export default function MyActiveJobTable({ MyJobList, className }) {
<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>
{/*<tr className="text-base text-thin-light-gray border-b dark:border-[#5356fb29] default-border-b dark:border-[#5356fb29] ottom ">*/}
{/* <td className="py-4">All Product</td>*/}
{/* <td className="py-4 text-right">.</td>*/}
{/*</tr>*/}
{
<>
{MyJobList &&
@@ -59,7 +53,9 @@ export default function MyActiveJobTable({ MyJobList, className }) {
<div className="flex space-x-2 items-center w-full">
<div className="w-[60px] h-[60px] p-2 bg-alice-blue rounded-full overflow-hidden flex justify-center items-center">
<img
src={localImgLoad(`images/taskbanners/${value.banner}`)}
src={localImgLoad(
`images/taskbanners/${value.banner}`
)}
alt="data"
className="w-full h-full rounded-full"
/>
@@ -75,7 +71,7 @@ export default function MyActiveJobTable({ MyJobList, className }) {
{thePrice}
</span>
</span>
<div className="flex gap-4 items-center">
<div className="flex flex-col sm:flex-row items-start gap-1 md:gap-4 md:items-center">
<span className="text-sm text-thin-light-gray">
Duration:{" "}
<span className="text-purple">
@@ -113,9 +109,11 @@ export default function MyActiveJobTable({ MyJobList, className }) {
state: { ...value, pathname },
});
}}
className="w-20 h-11 flex justify-center items-center btn-gradient text-base rounded-full text-white"
className="px-4 h-11 flex justify-center items-center btn-gradient text-base rounded-full text-white"
>
View
{value.owner_status == "OWNER"
? "Manage"
: "Send Updates"}
</button>
</div>
</td>
+28 -31
View File
@@ -1,42 +1,39 @@
import React, { useState } from "react";
import { Link } from "react-router-dom";
import Layout from "../Partials/Layout";
import CommonHead from "../UserHeader/CommonHead";
import MyActiveJobTable from "./MyActiveJobTable";
export default function MyReviewDueJobs(props) {
const [selectTab, setValue] = useState("today");
const filterHandler = (value) => {
setValue(value);
};
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">
const [selectTab, setValue] = useState("today");
const filterHandler = (value) => {
setValue(value);
};
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"}`}
className={`${selectTab === "today" ? "block" : "hidden"}`}
>
Review Due Job(s)
Review Due Job(s)
</span>
</h1>
</div>
<div className="slider-btns flex space-x-4">
<div
onClick={() => filterHandler("today")}
className="relative"
></div>
</div>
</div>
<MyActiveJobTable MyJobList={props.MyJobList} />
</div>
</h1>
</div>
</Layout>
);
<div className="slider-btns flex space-x-4">
<div
onClick={() => filterHandler("today")}
className="relative"
></div>
</div>
</div>
<MyActiveJobTable MyJobList={props.myJobList} />
</div>
</div>
</Layout>
);
}
+2 -1
View File
@@ -26,7 +26,8 @@ const CouponPopup = ({ popUpHandler, data }) => {
setStatusMsg({ error: "An error occurred" });
else setStatusMsg({ success: res.data?.status_text });
dispatch(tableReload({ type: "COUPONTABLE" }));
// dispatch(tableReload({ type: "COUPONTABLE" }));
dispatch(tableReload({ type: "WALLETTABLE" }));
setTimeout(() => {
popUpHandler();
setLoader(false);
+2 -2
View File
@@ -7,7 +7,7 @@ import { useSelector } from "react-redux";
export default function MyCoupons() {
const apiCall = useMemo(() => new usersService(), []);
const {couponTable} = useSelector(state => state.tableReload)
const {couponTable, walletTable} = useSelector(state => state.tableReload)
let [couponHistory, setCouponHistory] = useState({
// FOR COUPON HISTORY
loading: true,
@@ -38,7 +38,7 @@ export default function MyCoupons() {
useEffect(() => {
getCouponHistory();
}, [couponTable]);
}, [couponTable, walletTable]);
return (
<>
@@ -0,0 +1,154 @@
import React, { useState } from "react";
import ModalCom from "../Helpers/ModalCom";
import { useNavigate } from "react-router-dom";
import usersService from "../../services/UsersService";
import LoadingSpinner from "../Spinners/LoadingSpinner";
import { useDispatch } from "react-redux";
function DeleteTaskModal({ details, onClose, situation, setReloadList }) {
let dispatch = useDispatch();
const navigate = useNavigate();
const ApiCall = new usersService();
let [requestStatus, setRequestStatus] = useState({
laoding: false,
status: false,
message: "",
}); // STATE FOR KNOWING WHEN A REQUEST IS MADE TO THE SERVER
// FUNCTION TO DELETE TASK
const deleteTask = () => {
setRequestStatus({loading:true, status:false, message: ''})
let reqData = { // REQUEST PAYLOAD
suggest_uid: details.uid,
suggest_action: 555,
offset: 0
}
ApiCall.suggestStatus(reqData).then((response)=>{ // API CALL TO DELETE SUGGESTED TASK
let {data} = response
if(data.internal_return < 0){
setRequestStatus({loading:false, status:false, message: 'Unable to delete, Try again'})
return setTimeout(()=>{
setRequestStatus({loading:false, status:false, message: ''})
},3000)
}
setRequestStatus({loading:false, status:true, message: 'Family Suggest Deleted'})
setReloadList(prev => !prev) // RELOADS THE FAMILY SUGGEST LIST TABLE
setTimeout(()=>{
setRequestStatus({loading:false, status:false, message: ''})
onClose()
},3000)
}).catch(error => {
setRequestStatus({loading:false, status:false, message: 'Unable to delete, Try again'})
setTimeout(()=>{
setRequestStatus({loading:false, status:false, message: ''})
},3000)
})
}
return (
<ModalCom action={onClose} situation={situation}>
<div className="logout-modal-wrapper lg:w-[500px] h-full lg:h-auto bg-white dark:bg-dark-white lg:rounded-2xl">
<div className="logout-modal-header w-full flex items-center justify-between lg:px-10 lg:py-8 px-[30px] py-[23px] border-b border-light-purple dark:border-[#5356fb29] ">
<h1 className="text-26 font-bold text-dark-gray dark:text-white tracking-wide">
Delete Task
</h1>
<button
type="button"
className="text-[#374557] dark:text-red-500"
onClick={onClose}
>
<svg
width="36"
height="36"
viewBox="0 0 36 36"
fill="none"
className="fill-current"
xmlns="http://www.w3.org/2000/svg"
>
<path
d="M36 16.16C36 17.4399 36 18.7199 36 20.0001C35.7911 20.0709 35.8636 20.2554 35.8385 20.4001C34.5321 27.9453 30.246 32.9248 22.9603 35.2822C21.9006 35.6251 20.7753 35.7657 19.6802 35.9997C18.4003 35.9997 17.1204 35.9997 15.8401 35.9997C15.5896 35.7086 15.2189 35.7732 14.9034 35.7093C7.77231 34.2621 3.08728 30.0725 0.769671 23.187C0.435002 22.1926 0.445997 21.1199 0 20.1599C0 18.7198 0 17.2798 0 15.8398C0.291376 15.6195 0.214408 15.2656 0.270759 14.9808C1.71321 7.69774 6.02611 2.99691 13.0428 0.700951C14.0118 0.383805 15.0509 0.386897 15.9999 0C17.2265 0 18.4532 0 19.6799 0C19.7156 0.124041 19.8125 0.136067 19.9225 0.146719C27.3 0.868973 33.5322 6.21922 35.3801 13.427C35.6121 14.3313 35.7945 15.2484 36 16.16ZM33.011 18.0787C33.0433 9.77105 26.3423 3.00309 18.077 2.9945C9.78479 2.98626 3.00344 9.658 2.98523 17.8426C2.96667 26.1633 9.58859 32.9601 17.7602 33.0079C26.197 33.0577 32.9787 26.4186 33.011 18.0787Z"
fill=""
fillOpacity="0.6"
/>
<path
d="M15.9309 18.023C13.9329 16.037 12.007 14.1207 10.0787 12.2072C9.60071 11.733 9.26398 11.2162 9.51996 10.506C9.945 9.32677 11.1954 9.0811 12.1437 10.0174C13.9067 11.7585 15.6766 13.494 17.385 15.2879C17.9108 15.8401 18.1633 15.7487 18.6375 15.258C20.3586 13.4761 22.1199 11.7327 23.8822 9.99096C24.8175 9.06632 26.1095 9.33639 26.4967 10.517C26.7286 11.2241 26.3919 11.7413 25.9133 12.2178C24.1757 13.9472 22.4477 15.6855 20.7104 17.4148C20.5228 17.6018 20.2964 17.7495 20.0466 17.9485C22.0831 19.974 24.0372 21.8992 25.9689 23.8468C26.9262 24.8119 26.6489 26.1101 25.4336 26.4987C24.712 26.7292 24.2131 26.3441 23.7455 25.8757C21.9945 24.1227 20.2232 22.3892 18.5045 20.6049C18.0698 20.1534 17.8716 20.2269 17.4802 20.6282C15.732 22.4215 13.9493 24.1807 12.1777 25.951C11.7022 26.4262 11.193 26.7471 10.4738 26.4537C9.31345 25.9798 9.06881 24.8398 9.98589 23.8952C11.285 22.5576 12.6138 21.2484 13.9387 19.9355C14.5792 19.3005 15.2399 18.6852 15.9309 18.023Z"
fill="#"
fillOpacity="0.6"
/>
</svg>
</button>
</div>
<div className="logout-modal-body w-full flex flex-col items-center px-10 py-8">
<div className="what-icon mb-6 cursor-pointer">
<svg
width="136"
height="136"
viewBox="0 0 136 136"
fill="none"
xmlns="http://www.w3.org/2000/svg"
>
<circle cx="68" cy="68" r="68" fill="#4687ba" />
<path
d="M69.8844 35.7891C71.1588 36.0357 72.4569 36.1967 73.7044 36.5423C81.5447 38.7098 87.2705 45.5378 87.9574 53.6156C88.5113 60.1147 86.3075 65.6006 81.5043 70.0195C79.8359 71.5545 78.0497 72.9604 76.3408 74.4534C76.127 74.6397 75.9654 75.0037 75.9604 75.2872C75.9284 77.2752 75.9435 79.2649 75.9435 81.2965C70.8895 81.2965 65.8758 81.2965 60.7915 81.2965C60.7915 81.0616 60.7915 80.8385 60.7915 80.6137C60.7915 76.5454 60.7999 72.4772 60.7797 68.4106C60.778 67.9392 60.9312 67.649 61.2831 67.3537C64.5643 64.5957 67.8271 61.8175 71.1033 59.0545C72.2616 58.0781 72.9215 56.8702 72.9081 55.3419C72.8878 52.916 70.8608 50.9146 68.423 50.8911C65.9701 50.8693 63.9145 52.8053 63.832 55.2328C63.8084 55.8988 63.8286 56.5665 63.8286 57.2695C58.7745 57.2695 53.7744 57.2695 48.6917 57.2695C48.6917 56.3149 48.6462 55.3385 48.6984 54.3655C49.222 44.699 56.7442 36.8745 66.4331 35.8914C66.5762 35.8763 66.7142 35.8243 66.854 35.7891C67.8641 35.7891 68.8742 35.7891 69.8844 35.7891Z"
fill="white"
/>
<path
d="M67.485 100.21C66.1617 99.9268 64.9041 99.5091 63.803 98.6787C61.3804 96.8484 60.2877 93.7699 61.0386 90.7888C61.7726 87.8747 64.2138 85.6703 67.2089 85.2157C71.273 84.6 75.2024 87.3681 75.8135 91.277C76.4937 95.6153 73.8202 99.3782 69.544 100.103C69.4429 100.12 69.3487 100.172 69.2527 100.209C68.6635 100.21 68.0742 100.21 67.485 100.21Z"
fill="white"
/>
</svg>
</div>
<div className="mb-6">
<p className="text-xl tracking-wide text-dark-gray dark:text-white">
{details.title}
</p>
</div>
<div className="flex space-x-2.5">
<button
onClick={onClose}
type="button"
className=" border-gradient text-18 tracking-wide px-4 py-3 rounded-full"
>
<span className="text-gradient">Cancel</span>
</button>
{requestStatus.loading ? (
<LoadingSpinner size="8" color="sky-blue" />
) : (
<button
onClick={() => deleteTask(details)}
type="button"
className="text-white primary-gradient text-18 tracking-wide px-4 py-3 rounded-full"
>
Confirm Delete
</button>
)}
</div>
{/* ERROR DISPLAY AND SUBMIT BUTTON */}
{requestStatus.message != "" &&
(!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>
)
))}
{/* End of error or success display */}
</div>
</div>
</ModalCom>
);
}
export default DeleteTaskModal;
@@ -18,7 +18,7 @@ export default function MyPendingJobTable({ MyJobList, className }) {
indexOfFirstItem,
indexOfLastItem
);
const handlePagination = (e) => {
handlePagingFunc(e, setCurrentPage);
};
@@ -33,11 +33,6 @@ export default function MyPendingJobTable({ MyJobList, className }) {
<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>
{/*<tr className="text-base text-thin-light-gray border-b dark:border-[#5356fb29] default-border-b dark:border-[#5356fb29] ottom ">*/}
{/* <td className="py-4">All Product</td>*/}
{/* <td className="py-4 text-right">.</td>*/}
{/*</tr>*/}
{
<>
{MyJobList &&
@@ -108,9 +103,9 @@ export default function MyPendingJobTable({ MyJobList, className }) {
onClick={() => {
setJobPopout({ show: true, data: value });
}}
className="w-20 h-11 flex justify-center items-center btn-gradient text-base rounded-full text-white"
className="px-4 h-11 flex justify-center items-center btn-gradient text-base rounded-full text-white"
>
View
{value.owner_status == 'OWNER' ? 'Manage' : 'Send Updates'}
</button>
</td>
</tr>
@@ -7,7 +7,9 @@ import LoadingSpinner from "../Spinners/LoadingSpinner";
import { handlePagingFunc } from "../Pagination/HandlePagination";
import PaginatedList from "../Pagination/PaginatedList";
import usersService from "../../services/UsersService";
import DeleteIcon from '../../assets/images/icon-delete.svg'
import DeleteTaskModal from "./DeleteTaskModal";
import SendReminderModal from "./SendReminderModal";
export default function ParentWaitingTable() {
// const transationFilterData = [
@@ -34,6 +36,11 @@ export default function ParentWaitingTable() {
const apiCall = new usersService()
let [reloadList, setReloadList] = useState(false) // STATE TO DETERMINE WHEN TO RELOAD THE FAMILY SUGGEST LIST
let [deleteTaskPopout, setDeleteTaskPopout] = useState({show:false, data:{}}) // HOLDS THE INFO OF DELETE TASK POPOUT
let [sendReminderPopout, setSendReminderPopout] = useState({show:false, data:{}}) // HOLDS THE INFO OF SEND REMINDER POPOUT
let [familySuggestList, setFamilySuggestList] = useState({loading: true, data:[]})
const [currentPage, setCurrentPage] = useState(0);
@@ -53,7 +60,7 @@ export default function ParentWaitingTable() {
setFamilySuggestList({loading: false, data:[]})
console.log('ERROR==>Familysuggestlist', err)
})
},[])
},[reloadList])
return (
<div className="rounded-2xl bg-white dark:bg-dark-white flex flex-col justify-between">
@@ -97,7 +104,7 @@ export default function ParentWaitingTable() {
<div className="icon w-14 h-14 flex justify-center items-center">
<img src={Image} alt="" className="w-full h-full" />
</div>
<div className="">
<div className="w-full">
<p className="text-xl font-bold text-dark-gray dark:text-white mb-2 capitalize line-clamp-1">
{item.title}
</p>
@@ -107,12 +114,35 @@ export default function ParentWaitingTable() {
</p>
</div>
</div>
<div className="px-2 self-end">
<p className="text-sm font-bold text-dark-gray dark:text-white">
{new Date(item.added).toLocaleString().split(',')[0]}
</p>
<p className="text-xs py-1.5 w-[50px] tracking-wide rounded-full bg-gold text-white flex justify-center items-center">{item.status_text}</p>
<div className="min-w-[120px] flex justify-start items-end px-2 gap-1">
<button
type="button"
className="p-1 border-2 border-red-400 rounded-md"
onClick={() => {
setDeleteTaskPopout({
show: true,
data: { ...item },
});
}}
>
<img className="min-w-[21px] h-[21px]" src={DeleteIcon} alt='delete-icon' />
</button>
<div className="">
<p className="text-sm font-bold text-dark-gray dark:text-white">
{new Date(item.added).toLocaleString().split(',')[0]}
</p>
<p
className="text-xs py-1.5 w-[50px] tracking-wide rounded-full bg-gold text-white flex justify-center items-center cursor-pointer"
onClick={() => {
setSendReminderPopout({
show: true,
data: { ...item },
});
}}
>{item.status_text}</p>
</div>
</div>
</div>
</li>
@@ -122,6 +152,32 @@ export default function ParentWaitingTable() {
:
<p className="w-full flex items-center justify-center text-xl text-dark-gray dark:text-white">No List Found!</p>
}
{/* Delete Task Popout */}
{deleteTaskPopout.show && (
<DeleteTaskModal
details={deleteTaskPopout.data}
onClose={() => {
setDeleteTaskPopout({ show: false, data: {} });
}}
situation={deleteTaskPopout.show}
setReloadList={setReloadList}
/>
)}
{/* END of Delete Task Popout */}
{/* Send Reminder Popout */}
{sendReminderPopout.show && (
<SendReminderModal
details={sendReminderPopout.data}
onClose={() => {
setSendReminderPopout({ show: false, data: {} });
}}
situation={sendReminderPopout.show}
setReloadList={setReloadList}
/>
)}
{/* END of Send Reminder Popout */}
</div>
{/* PAGINATION BUTTON */}
@@ -0,0 +1,152 @@
import React, { useState } from "react";
import ModalCom from "../Helpers/ModalCom";
import { useNavigate } from "react-router-dom";
import usersService from "../../services/UsersService";
import LoadingSpinner from "../Spinners/LoadingSpinner";
import { useDispatch } from "react-redux";
function SendReminderModal({ details, onClose, situation, setReloadList }) {
let dispatch = useDispatch();
const navigate = useNavigate();
const ApiCall = new usersService();
let [requestStatus, setRequestStatus] = useState({
laoding: false,
status: false,
message: "",
}); // STATE FOR KNOWING WHEN A REQUEST IS MADE TO THE SERVER
// FUNCTION TO SEND REMINDER
const sendReminder = () => {
setRequestStatus({loading:true, status:false, message: ''})
let reqData = { // REQUEST PAYLOAD
suggest_uid: details.uid,
suggest_action: 222,
offset: 0
}
ApiCall.suggestStatus(reqData).then((response)=>{ // API CALL TO DELETE SUGGESTED TASK
let {data} = response
if(data.internal_return < 0){
setRequestStatus({loading:false, status:false, message: 'Unable to send reminder, Try again1111'})
return setTimeout(()=>{
setRequestStatus({loading:false, status:false, message: ''})
},3000)
}
setRequestStatus({loading:false, status:true, message: 'Reminder Sent'})
setReloadList(prev => !prev) // RELOADS THE FAMILY SUGGEST LIST TABLE
setTimeout(()=>{
setRequestStatus({loading:false, status:false, message: ''})
onClose()
},3000)
}).catch(error => {
setRequestStatus({loading:false, status:false, message: 'Unable to send reminder, Try againNETWORK'})
setTimeout(()=>{
setRequestStatus({loading:false, status:false, message: ''})
},3000)
})
}
return (
<ModalCom action={onClose} situation={situation}>
<div className="logout-modal-wrapper lg:w-[500px] h-full lg:h-auto bg-white dark:bg-dark-white lg:rounded-2xl">
<div className="logout-modal-header w-full flex items-center justify-between lg:px-10 lg:py-8 px-[30px] py-[23px] border-b border-light-purple dark:border-[#5356fb29] ">
<h1 className="text-26 font-bold text-dark-gray dark:text-white tracking-wide">
Send Reminder
</h1>
<button
type="button"
className="text-[#374557] dark:text-red-500"
onClick={onClose}
>
<svg
width="36"
height="36"
viewBox="0 0 36 36"
fill="none"
className="fill-current"
xmlns="http://www.w3.org/2000/svg"
>
<path
d="M36 16.16C36 17.4399 36 18.7199 36 20.0001C35.7911 20.0709 35.8636 20.2554 35.8385 20.4001C34.5321 27.9453 30.246 32.9248 22.9603 35.2822C21.9006 35.6251 20.7753 35.7657 19.6802 35.9997C18.4003 35.9997 17.1204 35.9997 15.8401 35.9997C15.5896 35.7086 15.2189 35.7732 14.9034 35.7093C7.77231 34.2621 3.08728 30.0725 0.769671 23.187C0.435002 22.1926 0.445997 21.1199 0 20.1599C0 18.7198 0 17.2798 0 15.8398C0.291376 15.6195 0.214408 15.2656 0.270759 14.9808C1.71321 7.69774 6.02611 2.99691 13.0428 0.700951C14.0118 0.383805 15.0509 0.386897 15.9999 0C17.2265 0 18.4532 0 19.6799 0C19.7156 0.124041 19.8125 0.136067 19.9225 0.146719C27.3 0.868973 33.5322 6.21922 35.3801 13.427C35.6121 14.3313 35.7945 15.2484 36 16.16ZM33.011 18.0787C33.0433 9.77105 26.3423 3.00309 18.077 2.9945C9.78479 2.98626 3.00344 9.658 2.98523 17.8426C2.96667 26.1633 9.58859 32.9601 17.7602 33.0079C26.197 33.0577 32.9787 26.4186 33.011 18.0787Z"
fill=""
fillOpacity="0.6"
/>
<path
d="M15.9309 18.023C13.9329 16.037 12.007 14.1207 10.0787 12.2072C9.60071 11.733 9.26398 11.2162 9.51996 10.506C9.945 9.32677 11.1954 9.0811 12.1437 10.0174C13.9067 11.7585 15.6766 13.494 17.385 15.2879C17.9108 15.8401 18.1633 15.7487 18.6375 15.258C20.3586 13.4761 22.1199 11.7327 23.8822 9.99096C24.8175 9.06632 26.1095 9.33639 26.4967 10.517C26.7286 11.2241 26.3919 11.7413 25.9133 12.2178C24.1757 13.9472 22.4477 15.6855 20.7104 17.4148C20.5228 17.6018 20.2964 17.7495 20.0466 17.9485C22.0831 19.974 24.0372 21.8992 25.9689 23.8468C26.9262 24.8119 26.6489 26.1101 25.4336 26.4987C24.712 26.7292 24.2131 26.3441 23.7455 25.8757C21.9945 24.1227 20.2232 22.3892 18.5045 20.6049C18.0698 20.1534 17.8716 20.2269 17.4802 20.6282C15.732 22.4215 13.9493 24.1807 12.1777 25.951C11.7022 26.4262 11.193 26.7471 10.4738 26.4537C9.31345 25.9798 9.06881 24.8398 9.98589 23.8952C11.285 22.5576 12.6138 21.2484 13.9387 19.9355C14.5792 19.3005 15.2399 18.6852 15.9309 18.023Z"
fill="#"
fillOpacity="0.6"
/>
</svg>
</button>
</div>
<div className="logout-modal-body w-full flex flex-col items-center p-8">
<div className="mb-6 w-full flex gap-4 items-center">
<div className="icon max-w-[150px] min-w-[150px] max-h-[150px] min-h-[150px] flex justify-center items-center">
<img src={require(`../../assets/images/family/${details.banner || "default.jpg"}`)} alt="" className="w-full h-full" />
</div>
<div className="w-full">
<p className="text-xl font-bold text-dark-gray dark:text-white mb-2 capitalize line-clamp-1">
{details.title}
</p>
<p className="text-sm mb-2 text-thin-light-gray font-medium">
{details.description}
</p>
{
details.remind &&
<p className="text-xl font-bold text-dark-gray dark:text-white mb-2 capitalize line-clamp-1">
Last Remind: {new Date(details.remind).toLocaleString().split(',')[0]}
</p>
}
</div>
</div>
<div className="w-full flex justify-end">
{/* <button
onClick={onClose}
type="button"
className=" border-gradient text-18 tracking-wide px-4 py-3 rounded-full"
>
<span className="text-gradient">Cancel</span>
</button> */}
{requestStatus.loading ? (
<LoadingSpinner size="8" color="sky-blue" />
) : (
<button
onClick={() => sendReminder(details)}
type="button"
className="text-white bg-sky-blue text-18 tracking-wide px-4 py-3 rounded-full"
>
Send Reminder
</button>
)}
</div>
{/* ERROR DISPLAY AND SUBMIT BUTTON */}
{requestStatus.message != "" &&
(!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>
)
))}
{/* End of error or success display */}
</div>
</div>
</ModalCom>
);
}
export default SendReminderModal;
+12 -13
View File
@@ -55,15 +55,14 @@ export default function MyJobTable({ className, ActiveJobList, Account }) {
{!ActiveJobList?.data.length && accountType && (
<div className="absolute inset-0 bg-black opacity-30"></div>
)}
{ActiveJobList?.data.length > 0 && ActiveJobList.loading && (
{ActiveJobList.loading ?
<div className="w-full h-[520px] flex items-center justify-center">
<LoadingSpinner size="16" color="sky-blue" />
</div>
)}
:
<div className="relative w-full sm:rounded-lg">
<div className="h-auto w-full">
{ActiveJobList?.data?.length > 0 &&
{ActiveJobList?.data?.length > 0 ?
currentTask?.map((task, idx) => {
// find due date
const dueDate = task?.delivery_date.split(" ")[0];
@@ -97,7 +96,7 @@ export default function MyJobTable({ className, ActiveJobList, Account }) {
Price:
<span className="text-purple ml-1">{thePrice}</span>
</span>
<div className="flex gap-4 items-center">
<div className="flex flex-col sm:flex-row items-start gap-1 md:gap-4 md:items-center">
<span className="text-sm text-thin-light-gray">
Duration:
<span className="text-purple ml-1">
@@ -129,16 +128,15 @@ export default function MyJobTable({ className, ActiveJobList, Account }) {
state: { ...task, pathname },
});
}}
className="w-20 h-11 flex justify-center items-center btn-gradient text-base rounded-full text-white"
className="px-4 h-11 flex justify-center items-center btn-gradient text-base rounded-full text-white"
>
Manage
{task.owner_status == 'OWNER' ? 'Manage' : 'Send Updates'}
</button>
</div>
</div>
);
})}
{ActiveJobList?.data?.length <= 0 && (
})
:
<div
className={`flex flex-col ${
accountType ? "items-center" : "items-end"
@@ -175,12 +173,12 @@ export default function MyJobTable({ className, ActiveJobList, Account }) {
</button>
</div>
</div>
)}
{ActiveJobList?.internal_return < 0 && (
}
{/* {ActiveJobList?.internal_return < 0 && (
<div className="font-bold text-xl text-dark-gray dark:text-white whitespace-nowrap">
<p className="p-2">Error Occurred! Unable to display Tasks!</p>
</div>
)}
)} */}
</div>
{/* PAGINATION BUTTON */}
@@ -199,6 +197,7 @@ export default function MyJobTable({ className, ActiveJobList, Account }) {
/>
{/* END OF PAGINATION BUTTON */}
</div>
}
</div>
);
}
@@ -38,8 +38,7 @@ export default function MyOffersFamilyTable({ className, familyOffers }) {
},
],
};
console.log("YES WE SEE OFFERS", familyOffers);
// console.log("YES WE SEE OFFERS", familyOffers);
const trendingSlider = useRef(null);
const prevHandler = () => {
+233 -223
View File
@@ -64,6 +64,8 @@ const initialValues = {
};
function AddFundDollars(props) {
let MaxNoOfCards = process.env.REACT_APP_MAX_CREDIT_CARDS // HOLDS THE VALUE OF THE MAX NUMBER OF CARDS USER CAN ADD
const apiCall = new usersService();
let countryWallet = props.walletItem.country;
const [tab, setTab] = useState("previous");
@@ -240,7 +242,7 @@ function AddFundDollars(props) {
<label
onClick={() => setTab("new")}
htmlFor="new"
className="cursor-pointer flex items-center gap-1"
className={`cursor-pointer flex items-center gap-1 ${payListCards.data.length >= MaxNoOfCards ? 'pointer-events-none':''}`}
>
<input
id="new"
@@ -251,7 +253,7 @@ function AddFundDollars(props) {
tab == "new" ? "" : ""
} tracking-wide transition duration-200`}
/>
Add New Card
Add New Card {payListCards.data.length >= MaxNoOfCards && <span className="text-[14px] text-red-500">Max Reached</span>}
</label>
</div>
</form>
@@ -314,245 +316,253 @@ function AddFundDollars(props) {
{tab === "new" && (
<div className="new-details w-full max-h-[22rem]">
<div className="w-full flex flex-col justify-between">
<Formik
initialValues={initialValues}
validationSchema={validationSchema}
onSubmit={handleSubmit}
>
{(props) => {
return (
<Form className="md:pl-8">
<div className="flex flex-col-reverse sm:flex-row">
<div className="flex-1 sm:mr-10">
<div className="fields w-full">
{/* Inputs */}
{/* Name */}
<div className="flex items-center field w-full my-2 flex-[0.4] gap-3">
<label className="input-label text-[#181c32] dark:text-white text-[13.975px] leading-[20.9625px] font-semibold flex items-center gap-1">
Name:
</label>
<p className="input-label text-[#181c32] dark:text-white text-[16px] leading-[20.9625px] font-semibold flex items-center gap-1">{`${firstname} ${lastname}`}</p>
</div>
<div className="flex items-center flex-1 gap-3 my-2">
{/* Card Number */}
<div className="field w-full flex-[0.6]">
<InputCom
fieldClass="px-6"
spanTag="*"
iconName={cardIcons}
label="Card Number"
type="text"
name="cardNum"
onInput={handleCards}
placeholder="Enter Card Number"
value={handleCardNumberChange(
props.values.cardNum
)}
inputHandler={props.handleChange}
blurHandler={props.handleBlur}
error={props.errors.cardNum}
/>
{payListCards.loading ?
<div className="pt-10 flex w-full h-full justify-center items-center">
<LoadingSpinner size='10' color='sky-blue' />
</div>
:payListCards.data.length < MaxNoOfCards ?
<div className="w-full flex flex-col justify-between">
<Formik
initialValues={initialValues}
validationSchema={validationSchema}
onSubmit={handleSubmit}
>
{(props) => {
return (
<Form className="md:pl-8">
<div className="flex flex-col-reverse sm:flex-row">
<div className="flex-1 sm:mr-10">
<div className="fields w-full">
{/* Inputs */}
{/* Name */}
<div className="flex items-center field w-full my-2 flex-[0.4] gap-3">
<label className="input-label text-[#181c32] dark:text-white text-[13.975px] leading-[20.9625px] font-semibold flex items-center gap-1">
Name:
</label>
<p className="input-label text-[#181c32] dark:text-white text-[16px] leading-[20.9625px] font-semibold flex items-center gap-1">{`${firstname} ${lastname}`}</p>
</div>
{/* Expire Year, Year */}
<div className="sm:grid gap-5 grid-cols-2 flex-[0.4]">
<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 line-clamp-3 flex items-center"
htmlFor="expiration"
>
Exp Month{" "}
<span className="text-red-700 text-sm italic">
*
</span>
<span className="text-[12px] text-red-500 ml-1">
{props.errors.expirationMonth && "**"}
</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-[#fafafa] focus:ring-0 focus:outline-none`}
value={props.values.expirationMonth}
onChange={props.handleChange}
onBlur={props.handleBlur}
name="expirationMonth"
>
<option
value=""
className="text-dark-gray"
>
Month
</option>
{expireMonth?.length &&
expireMonth.map((item, index) => (
<option
key={index}
value={
Number(item.value) < 10
? "0" + item.value
: item.value
}
>
{item.name}
</option>
))}
</select>
</div>
</div>
<div className="flex items-center flex-1 gap-3 my-2">
{/* Card Number */}
<div className="field w-full flex-[0.6]">
<InputCom
fieldClass="px-6"
spanTag="*"
iconName={cardIcons}
label="Card Number"
type="text"
name="cardNum"
onInput={handleCards}
placeholder="Enter Card Number"
value={handleCardNumberChange(
props.values.cardNum
)}
inputHandler={props.handleChange}
blurHandler={props.handleBlur}
error={props.errors.cardNum}
/>
</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 flex items-center line-clamp-3"
htmlFor="expiration"
{/* Expire Year, Year */}
<div className="sm:grid gap-5 grid-cols-2 flex-[0.4]">
<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`}
>
Exp Year{" "}
<span className="text-red-700 text-sm tracking-wide">
*
</span>
<span className="text-[12px] text-red-500 italic">
{props.errors.expirationYear && "**"}
</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-[#fafafa] focus:ring-0 focus:outline-none`}
value={props.values.expirationYear}
onChange={props.handleChange}
onBlur={props.handleBlur}
name="expirationYear"
>
<option
value=""
className="text-dark-gray"
<label
className="input-label text-[#181c32] dark:text-white text-[13.975px] leading-[20.9625px] font-semibold line-clamp-3 flex items-center"
htmlFor="expiration"
>
Year
</option>
{expireYear?.length &&
expireYear.map((item, index) => (
<option key={index} value={item}>
{item}
</option>
))}
</select>
Exp Month{" "}
<span className="text-red-700 text-sm italic">
*
</span>
<span className="text-[12px] text-red-500 ml-1">
{props.errors.expirationMonth && "**"}
</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-[#fafafa] focus:ring-0 focus:outline-none`}
value={props.values.expirationMonth}
onChange={props.handleChange}
onBlur={props.handleBlur}
name="expirationMonth"
>
<option
value=""
className="text-dark-gray"
>
Month
</option>
{expireMonth?.length &&
expireMonth.map((item, index) => (
<option
key={index}
value={
Number(item.value) < 10
? "0" + item.value
: item.value
}
>
{item.name}
</option>
))}
</select>
</div>
</div>
</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 flex items-center line-clamp-3"
htmlFor="expiration"
>
Exp Year{" "}
<span className="text-red-700 text-sm tracking-wide">
*
</span>
<span className="text-[12px] text-red-500 italic">
{props.errors.expirationYear && "**"}
</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-[#fafafa] focus:ring-0 focus:outline-none`}
value={props.values.expirationYear}
onChange={props.handleChange}
onBlur={props.handleBlur}
name="expirationYear"
>
<option
value=""
className="text-dark-gray"
>
Year
</option>
{expireYear?.length &&
expireYear.map((item, index) => (
<option key={index} value={item}>
{item}
</option>
))}
</select>
</div>
</div>
</div>
</div>
</div>
</div>
{/* Address and CVV */}
<div className="flex items-center flex-1 gap-3 my-2">
<div className="field w-full col-span-1 flex-[0.4]">
<InputCom
fieldClass="px-6"
spanTag="*"
iconName={cardIcons}
label="CVV"
type="text"
name="cvv"
placeholder="CVV"
maxLength={3}
value={props.values.cvv}
inputHandler={props.handleChange}
blurHandler={props.handleBlur}
error={props.errors.cvv}
/>
{/* Address and CVV */}
<div className="flex items-center flex-1 gap-3 my-2">
<div className="field w-full col-span-1 flex-[0.4]">
<InputCom
fieldClass="px-6"
spanTag="*"
iconName={cardIcons}
label="CVV"
type="text"
name="cvv"
placeholder="CVV"
maxLength={3}
value={props.values.cvv}
inputHandler={props.handleChange}
blurHandler={props.handleBlur}
error={props.errors.cvv}
/>
</div>
<div className="field w-full flex-[0.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}
error={props.errors.address}
/>
</div>
</div>
<div className="field w-full flex-[0.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}
error={props.errors.address}
/>
</div>
</div>
{/* Postal Code and State */}
<div className="sm:grid gap-5 grid-cols-3 my-2">
<div className="field w-full xl:mb-0 col-span-1">
<InputCom
fieldClass="px-6"
spanTag="*"
label="Postal Code"
type="number"
name="code"
placeholder="Postal Code"
value={props.values.code}
maxLength={6}
inputHandler={props.handleChange}
blurHandler={props.handleBlur}
error={props.errors.code}
/>
</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.toUpperCase()}
inputHandler={props.handleChange}
blurHandler={props.handleBlur}
error={props.errors.state}
/>
{/* Postal Code and State */}
<div className="sm:grid gap-5 grid-cols-3 my-2">
<div className="field w-full xl:mb-0 col-span-1">
<InputCom
fieldClass="px-6"
spanTag="*"
label="Postal Code"
type="number"
name="code"
placeholder="Postal Code"
value={props.values.code}
maxLength={6}
inputHandler={props.handleChange}
blurHandler={props.handleBlur}
error={props.errors.code}
/>
</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.toUpperCase()}
inputHandler={props.handleChange}
blurHandler={props.handleBlur}
error={props.errors.state}
/>
</div>
</div>
</div>
</div>
</div>
</div>
<div className="add-fund-btn flex justify-end items-center gap-2 mt-4">
<button
className="px-4 py-1 h-11 max-w-[100px] w-full flex justify-center bg-[#f5a430] text-black items-center text-base rounded-full"
onClick={handleClose}
>
Cancel
</button>
<button
type="submit"
className="px-4 py-1 h-11 max-w-[115px] w-full flex justify-center items-center btn-gradient text-base rounded-full text-white"
>
{props.confirmCredit?.show?.awaitConfirm?.loader ? (
<LoadingSpinner size="6" color="sky-blue" />
) : (
<>
<span className="text-white">Continue</span>{" "}
<Icons name="chevron-right" />
</>
)}
</button>
</div>
</Form>
);
}}
</Formik>
</div>
<div className="add-fund-btn flex justify-end items-center gap-2 mt-4">
<button
className="px-4 py-1 h-11 max-w-[100px] w-full flex justify-center bg-[#f5a430] text-black items-center text-base rounded-full"
onClick={handleClose}
>
Cancel
</button>
<button
type="submit"
className="px-4 py-1 h-11 max-w-[115px] w-full flex justify-center items-center btn-gradient text-base rounded-full text-white"
>
{props.confirmCredit?.show?.awaitConfirm?.loader ? (
<LoadingSpinner size="6" color="sky-blue" />
) : (
<>
<span className="text-white">Continue</span>{" "}
<Icons name="chevron-right" />
</>
)}
</button>
</div>
</Form>
);
}}
</Formik>
</div>
:
null
}
</div>
)}
</div>
+53 -37
View File
@@ -26,46 +26,59 @@ function AddFundPop({
};
const handleSubmit = async () => {
setInputError("");
setConfirmCredit((prev) => ({
...prev,
show: { awaitConfirm: { loader: true } },
}));
if (!input || input === "0") {
setConfirmCredit((prev) => ({
...prev,
show: { awaitConfirm: { loader: false } },
}));
setInputError("Please Enter Amount");
setTimeout(() => setInputError(""), 5000);
return;
}
if (Number(input) * 100 > Number(walletItem?.transfer_limit)) {
setInputError("Credit limit has been exceeded");
setTimeout(() => setInputError(""), 5000);
return;
}
if (isNaN(input)) {
setConfirmCredit((prev) => ({
...prev,
show: { awaitConfirm: { loader: false } },
}));
setInputError("Amount must be a Number");
setTimeout(() => setInputError(""), 5000);
return;
}
let stateData = {
amount: Number(input) * 100,
currency: walletItem?.code,
};
try {
// Clear any previous input error and set the loading spinner to be shown
setInputError("");
setConfirmCredit((prev) => ({
...prev,
show: { awaitConfirm: { loader: true } },
}));
// Perform validation checks on the input amount
if (!input || input === "0") {
// Handle input validation error
setConfirmCredit((prev) => ({
...prev,
show: { awaitConfirm: { loader: false } },
}));
setInputError("Please Enter Amount");
setTimeout(() => setInputError(""), 5000);
return;
}
if (Number(input) * 100 > Number(walletItem?.transfer_limit)) {
// Handle credit limit exceeded error
setConfirmCredit((prev) => ({
...prev,
show: { awaitConfirm: { loader: false } },
}));
setInputError("Credit limit has been exceeded");
setTimeout(() => setInputError(""), 5000);
return;
}
if (isNaN(input)) {
// Handle invalid input error
setConfirmCredit((prev) => ({
...prev,
show: { awaitConfirm: { loader: false } },
}));
setInputError("Amount must be a Number");
setTimeout(() => setInputError(""), 5000);
return;
}
// Prepare state data for API call
let stateData = {
amount: Number(input) * 100,
currency: walletItem?.code,
};
// Make API call to start credit process
const res = await apiCall.getStartCredit(stateData);
if (res.data.internal_return < 0) {
// Handle API error
setConfirmCredit((prev) => ({
...prev,
show: { awaitConfirm: { loader: false } },
@@ -75,6 +88,7 @@ function AddFundPop({
return;
}
// Update state with response data
const _response = res.data;
stateData.amount = Number(input);
stateData.currency = currency;
@@ -91,6 +105,7 @@ function AddFundPop({
}));
}, 1500);
} catch (error) {
// Handle API call error
setConfirmCredit((prev) => ({
...prev,
show: { awaitConfirm: { loader: false } },
@@ -116,6 +131,7 @@ function AddFundPop({
placeholder="0"
value={input}
inputHandler={handleChange}
tabIndex={0}
/>
<p className="text-base text-red-500 italic h-5">
{inputError && inputError}
@@ -1,7 +1,12 @@
import React from "react";
/**
* Renders a modal with information about a credit transaction.
* @returns {JSX.Element} - The rendered modal component.
*/
function CompleteConfirmCredit({ onClose, confirmCredit }) {
const { data } = confirmCredit;
const isSuccess =
data?.result === "Charge success" || data?.status === "successful";
return (
<div className="logout-modal-body w-full flex flex-col items-center">
<div className="content-wrapper w-full h-[32rem]">
@@ -11,91 +16,82 @@ function CompleteConfirmCredit({ onClose, confirmCredit }) {
<div className="field w-full mb-3 min-h-[45px]">
<div
className={`flex flex-col gap-4 ${
data?.result !== "Charge success" &&
"h-[328px] items-center justify-center"
!isSuccess && "h-[328px] items-center justify-center"
}`}
>
{/* Success Icon for now */}
<div className="flex items-center w-full justify-center">
{data?.result == "Charge success" ||
data?.status == "successful" ? (
<svg
xmlns="http://www.w3.org/2000/svg"
width="100"
height="100"
viewBox="0 0 24 24"
fill="none"
stroke="green"
stroke-width="2"
stroke-linecap="round"
stroke-linejoin="round"
className="feather feather-check-circle"
>
<path d="M22 11.08V12a10 10 0 1 1-5.93-9.14"></path>
<polyline points="22 4 12 14.01 9 11.01"></polyline>
</svg>
) : (
<svg
xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 24 24"
fill="none"
width="100"
height="100"
stroke="red"
stroke-width="2"
stroke-linecap="round"
stroke-linejoin="round"
className="feather feather-x-circle"
>
<circle cx="12" cy="12" r="10"></circle>
<line x1="15" y1="9" x2="9" y2="15"></line>
<line x1="9" y1="9" x2="15" y2="15"></line>
</svg>
)}
<svg
xmlns="http://www.w3.org/2000/svg"
width="100"
height="100"
viewBox="0 0 24 24"
fill="none"
stroke={isSuccess ? "green" : "red"}
strokeWidth="2"
strokeLinecap="round"
strokeLinejoin="round"
className={`feather ${
isSuccess ? "feather-check-circle" : "feather-x-circle"
}`}
>
{isSuccess ? (
<>
<path d="M22 11.08V12a10 10 0 1 1-5.93-9.14"></path>
<polyline points="22 4 12 14.01 9 11.01"></polyline>
</>
) : (
<>
<circle cx="12" cy="12" r="10"></circle>
<line x1="15" y1="9" x2="9" y2="15"></line>
<line x1="9" y1="9" x2="15" y2="15"></line>
</>
)}
</svg>
</div>
<div className={`flex items-center`}>
<div className="flex items-center">
<h1 className="text-xl font-semibold text-dark-gray dark:text-white tracking-tighter my-1">
{data?.result == "Charge success" ||
data?.status == "successful"
{isSuccess
? "Credit was Successful!"
: "Credit was Unsuccessful"}
</h1>
</div>
{data?.internal_return >= 0 &&
data?.result !== "Charge failed" ? (
<>
<div className="flex items-center gap-8">
<h1 className="text-xl font-bold text-dark-gray dark:text-white tracking-tighter my-1">
Amount({data?.currency || ""})
</h1>
<span className="text-xl font-bold text-dark-gray dark:text-white tracking-tighter my-1">
{`${data?.symbol || ""} ${
Number(data?.amount * 0.01).toLocaleString() || ""
}`}
</span>
</div>
data?.result !== "Charge failed" && (
<>
<div className="flex items-center gap-8">
<h1 className="text-xl font-bold text-dark-gray dark:text-white tracking-tighter my-1">
Amount({data?.currency || ""})
</h1>
<span className="text-xl font-bold text-dark-gray dark:text-white tracking-tighter my-1">
{`${data?.symbol || ""} ${
Number(data?.amount * 0.01).toLocaleString() || ""
}`}
</span>
</div>
<div className="flex items-center gap-8">
<h1 className="text-xl font-bold text-dark-gray dark:text-white tracking-tighter my-1">
Wallet Balance
</h1>
<span className="text-xl font-bold text-dark-gray dark:text-white tracking-tighter my-1">
{data?.curr_balance}
</span>
</div>
<div className="flex items-center gap-8">
<h1 className="text-xl font-bold text-dark-gray dark:text-white tracking-tighter my-1">
Wallet Balance
</h1>
<span className="text-xl font-bold text-dark-gray dark:text-white tracking-tighter my-1">
{data?.curr_balance * 0.01}
</span>
</div>
<div className="flex items-center gap-8">
<h1 className="text-xl font-bold text-dark-gray dark:text-white tracking-tighter my-1">
Confirmation Number
</h1>
<span className="text-xl font-bold text-dark-gray dark:text-white tracking-tighter my-1">
{data?.confirmation}
</span>
</div>
</>
) : null}
{isSuccess && (
<div className="flex items-center gap-8">
<h1 className="text-xl font-bold text-dark-gray dark:text-white tracking-tighter my-1">
Confirmation Number
</h1>
<span className="text-xl font-bold text-dark-gray dark:text-white tracking-tighter my-1">
{data?.confirmation}
</span>
</div>
)}
</>
)}
</div>
</div>
</div>
+123 -83
View File
@@ -1,84 +1,102 @@
import { FlutterWaveButton, closePaymentModal } from "flutterwave-react-v3";
import React, { useState } from "react";
import { useDispatch, useSelector } from "react-redux";
import { useSelector } from "react-redux";
import { toast } from "react-toastify";
import debounce from "../../../hooks/debounce";
import usersService from "../../../services/UsersService";
import { tableReload } from "../../../store/TableReloads";
import LoadingSpinner from "../../Spinners/LoadingSpinner";
/**
* Renders a React component that displays the description and last four digits of a payment card.
*/
function ThePaymentText({ value, type }) {
const cardDetails = value;
value.description =
type === "new"
? cardDetails.cardNum[0] === "4"
? "Visa"
: cardDetails.cardNum[0] == "5"
? "Master"
: "ATM"
: value.description;
value.digits = type === "new" ? cardDetails.cardNum.slice(-4) : value.digits;
const { cardNum } = value;
let description = value.description;
let digits = value.digits;
if (type === "new") {
const firstDigit = cardNum[0];
if (firstDigit === "4") {
description = "Visa";
} else if (firstDigit === "5") {
description = "Master";
} else {
description = "ATM";
}
digits = cardNum.slice(-4);
}
return (
<div className="my-2 flex items-center gap-5">
<div className="card-details flex items-center gap-3">
<h1 className="text-xl font-bold text-dark-gray dark:text-white tracking-tighter my-1 space-x-1">
{value.description} Card
<h1 className="text-xl font-normal text-dark-gray dark:text-white tracking-tighter my-1 space-x-1">
{description} Card
</h1>
<p className="text-base font-bold text-dark-gray dark:text-white tracking-wide">
Bank **************{value.digits}
<p className="text-xl font-normal text-dark-gray dark:text-white tracking-wide">
Bank **************{digits}
</p>
</div>
</div>
);
}
/**
* Renders the amount of a transaction in a specific currency.
* @returns {JSX.Element} - The rendered component.
*/
function AmountSection({ currency, amount, country }) {
const formattedAmount = Number(amount).toFixed(2);
const formattedAmount = amount?.toFixed(2);
const gapClassName = country === "US" ? "gap-14" : "gap-4";
return (
<div
className={`flex items-center ${country == "US" ? "gap-14" : "gap-4"}`}
>
<div className={`flex items-center ${gapClassName}`}>
<h1 className="text-xl font-bold text-dark-gray dark:text-white tracking-tighter my-1">
Amount({currency})
</h1>
<span className="text-xl font-bold text-dark-gray dark:text-white tracking-tighter my-1">
<span className="text-xl font-normal text-dark-gray dark:text-white tracking-tighter my-1">
{formattedAmount}
</span>
</div>
);
}
/**
* Renders the transaction fee for a payment.
* @returns {JSX.Element} - Rendered JSX displaying the transaction fee with the label "Transaction Fee".
*/
function TransactionFeeSection({ currency, fee, country }) {
const formattedFee = Number(fee).toFixed(2);
const formattedFee = (+fee).toFixed(2);
const gapClass = country === "US" ? "gap-[2.7rem]" : "gap-4";
return (
<div
className={`flex items-center border-b border-gray-600 ${
country == "US" ? "gap-[2.7rem]" : "gap-4"
}`}
>
<div className={`flex items-center border-b border-gray-600 ${gapClass}`}>
<h1 className="text-xl font-bold text-dark-gray dark:text-white tracking-tighter my-1">
Transaction Fee
</h1>
<span className="text-xl font-bold text-dark-gray dark:text-white tracking-tighter my-1">
<span className="text-xl font-normal text-dark-gray dark:text-white tracking-tighter my-1">
{formattedFee}
</span>
</div>
);
}
/**
* Calculates the total amount by adding the `amount` and `fee` values together.
* Formats the total amount to two decimal places and displays it.
* @returns {JSX.Element} - The TotalSection component.
*/
function TotalSection({ currency, amount, fee, country }) {
const total = Number(amount) + Number(fee);
const formattedTotal = total.toFixed(2);
const formattedTotal = total?.toFixed(2);
const gap = country === "US" ? "gap-[8rem]" : "gap-[6.3rem]";
return (
<div
className={`flex items-center ${
country == "US" ? "gap-[8rem]" : "gap-[6.3rem]"
}`}
>
<div className={`flex items-center ${gap}`}>
<h1 className="text-xl font-bold text-dark-gray dark:text-white tracking-tighter my-1">
Total
</h1>
<span className="text-xl font-bold text-dark-gray dark:text-white tracking-tighter my-1">
<span className="text-xl font-normal text-dark-gray dark:text-white tracking-tighter my-1">
{formattedTotal}
</span>
</div>
@@ -102,8 +120,6 @@ function ConfirmAddFund({
const { userDetails } = useSelector((state) => state?.userDetails);
const dispatch = useDispatch();
const [requestStatus, setRequestStatus] = useState({
message: "",
loading: false,
@@ -127,7 +143,7 @@ function ConfirmAddFund({
logo: "https://www.wrenchboard.com/assets/images/wrench-500-500-icon.png",
},
};
//debugger;
const fwConfig = {
...config,
text: "Proceed",
@@ -162,8 +178,6 @@ function ConfirmAddFund({
status: false,
});
}
return dispatch(tableReload({ type: "WALLETTABLE" }));
})
.catch((err) => {
setRequestStatus({
@@ -188,27 +202,40 @@ function ConfirmAddFund({
const debouncedSuccessPayment = debounce(onSuccessPayment, 5000);
/**
* Handles the process of making a payment using a previously saved card.
* Updates the state to show a loader while the payment is being processed,
* sends a request to the server to make the payment, and updates the state with the response.
* If the payment is successful, it also dispatches an action to reload the wallet table.
*/
const handlePrevCard = async () => {
const { amount, credit_reference, currency } = __confirmData;
const { card_uid } = __confirmCardDetails;
const reqData = {
amount: amount * 100,
card_uid,
credit_reference,
currency,
};
try {
// Show loader while the payment is being processed
setConfirmCredit((prev) => ({
...prev,
show: {
acceptConfirm: { loader: true },
},
}));
// Extract necessary data from confirmCredit and confirmCardDetails objects
const { amount, credit_reference, currency } = __confirmData;
const { card_uid } = __confirmCardDetails;
// Create request data object with required parameters for making the payment
const reqData = {
amount: amount * 100,
card_uid,
credit_reference,
currency,
};
// Send request to server to make the payment using getPaidPrevCard method of usersService
const res = await apiURL.getPaidPrevCard(reqData);
const _response = res.data;
if (res.data.internal_return < 0) {
// If internal_return value in the response is less than 0, hide the loader and return
if (_response.internal_return < 0) {
setConfirmCredit((prev) => ({
...prev,
show: {
@@ -218,6 +245,7 @@ function ConfirmAddFund({
return;
}
// Update state to show the acceptConfirm state and the response data
setTimeout(() => {
setConfirmCredit((prev) => ({
...prev,
@@ -227,9 +255,9 @@ function ConfirmAddFund({
},
data: _response,
}));
dispatch(tableReload({ type: "WALLETTABLE" }));
}, 1500);
} catch (error) {
// Handle error and hide the loader
setConfirmCredit((prev) => ({
...prev,
show: {
@@ -240,45 +268,45 @@ function ConfirmAddFund({
}
};
/**
* Handles the payment process when a new card is used.
* @async
*/
const handleNewCard = async () => {
const { amount, credit_reference, uid } = __confirmData;
const { address, cardNum, cvv, expirationMonth, expirationYear } =
__confirmCardDetails;
const reqData = {
amount: amount * 100,
cardnumber: cardNum.replace(/\s/g, ""),
credit_reference,
cvc: cvv,
description: address,
exp_month: expirationMonth,
exp_year: expirationYear,
paymenttype: 100,
uid,
};
try {
// Extract necessary data from __confirmData and __confirmCardDetails
const { amount, credit_reference, uid } = __confirmData;
const { address, cardNum, cvv, expirationMonth, expirationYear } =
__confirmCardDetails;
// Set loading state to indicate payment is being processed
setConfirmCredit((prev) => ({
...prev,
show: {
acceptConfirm: { loader: true },
},
}));
// Prepare request data
const reqData = {
amount: amount * 100,
cardnumber: cardNum.replace(/\s/g, ""),
credit_reference,
cvc: cvv,
description: address,
exp_month: expirationMonth,
exp_year: expirationYear,
paymenttype: 100,
uid,
};
// Send request to server to process payment
const res = await apiURL.getPaidNewCard(reqData);
const _response = res.data;
if (res.data.internal_return < 0) {
setConfirmCredit((prev) => ({
...prev,
show: {
awaitConfirm: { loader: false, state: false },
acceptConfirm: { loader: false, state: true },
},
data: _response,
}));
return;
}
setTimeout(() => {
// Handle response from server
if (res.data.internal_return < 0) {
// Payment could not be completed
setConfirmCredit((prev) => ({
...prev,
show: {
@@ -287,9 +315,21 @@ function ConfirmAddFund({
},
data: _response,
}));
dispatch(tableReload({ type: "WALLETTABLE" }));
}, 1500);
} else {
// Payment was successful
setTimeout(() => {
setConfirmCredit((prev) => ({
...prev,
show: {
awaitConfirm: { loader: false, state: false },
acceptConfirm: { loader: false, state: true },
},
data: _response,
}));
}, 1500);
}
} catch (error) {
// Handle error during payment process
setConfirmCredit((prev) => ({
...prev,
show: {
@@ -362,7 +402,7 @@ function ConfirmAddFund({
<h1 className="text-xl font-bold text-dark-gray dark:text-white tracking-tighter my-1">
Reference No
</h1>
<span className="text-xl font-bold text-dark-gray dark:text-white tracking-tighter my-1">
<span className="text-xl font-normal text-dark-gray dark:text-white tracking-tighter my-1">
{__confirmData?.credit_reference}
</span>
</div>
+18 -18
View File
@@ -6,7 +6,7 @@ import CompleteConfirmCredit from "./CompleteConfirmCredit";
import ConfirmAddFund from "./ConfirmAddFund";
const CreditPopup = ({ details, onClose, situation, walletItem }) => {
let [input, setInput] = useState("");
const [input, setInput] = useState("");
const [confirmCredit, setConfirmCredit] = useState({
show: {
awaitConfirm: { loader: false, state: false },
@@ -15,6 +15,20 @@ const CreditPopup = ({ details, onClose, situation, walletItem }) => {
data: {},
});
const getTitle = () => {
if (confirmCredit?.show?.acceptConfirm?.state) {
if (confirmCredit?.data?.internal_return <= 0) {
return "Credit Unsuccessful";
} else {
return "Credit Add Completed";
}
} else if (confirmCredit?.show?.awaitConfirm?.state) {
return "Confirm Credit Add";
} else {
return "Add Credit";
}
};
return (
<ModalCom
action={onClose}
@@ -24,23 +38,9 @@ const CreditPopup = ({ details, onClose, situation, walletItem }) => {
<div className="logout-modal-wrapper lw-[90%] md:w-[768px] h-full lg:h-auto bg-white dark:bg-dark-white lg:rounded-2xl overflow-y-auto">
<div className="logout-modal-header w-full flex items-center justify-between lg:p-6 px-[30px] py-[23px] border-b dark:border-[#5356fb29] border-light-purple">
<h1 className="text-26 font-bold text-dark-gray dark:text-white tracking-wide">
{confirmCredit?.show?.acceptConfirm?.state &&
(confirmCredit?.data?.internal_return < 0
// ||
// confirmCredit?.data?.status !== "successful"
) ? (
"Credit Unsuccessful"
) : (
<>
{confirmCredit?.show?.acceptConfirm?.loader
? "Confirming Credit..."
: confirmCredit?.show?.awaitConfirm?.state
? "Confirm Credit Add"
: confirmCredit?.show?.acceptConfirm?.state
? "Credit Add Completed"
: "Add Credit"}
</>
)}
{confirmCredit?.show?.acceptConfirm?.loader
? "Confirming Credit..."
: getTitle()}
</h1>
<button
type="button"
+274 -273
View File
@@ -12,6 +12,7 @@ function NairaWithdraw({
state,
setShowConfirmNairaWithdraw,
}) {
let MaxNoOfBanks = process.env.REACT_APP_MAX_CREDIT_BANK_ACCOUNT; // HOLDS THE VALUE OF THE MAX NUMBER OF BANKS USER CAN ADD
const apiCall = new usersService();
const [tab, setTab] = useState("previous");
let [requestStatus, setRequestStatus] = useState(false);
@@ -23,7 +24,9 @@ function NairaWithdraw({
recipientID: state?.previousAccount?.recipientID || "",
},
newAccount: {
country: state?.newAccount?.amount || "",
country: wallet.walletCountry
? wallet.walletCountry[0][0]
: wallet.country,
bank: state?.newAccount?.amount || "",
accountNumber: state?.newAccount?.amount || "",
accountType: state?.newAccount?.amount || "",
@@ -65,10 +68,14 @@ function NairaWithdraw({
// Handling card change
const handleBankOptions = (event) => {
const { value } = event.target;
let bankCountry = wallet.walletCountry
? wallet.walletCountry[0][0]
: wallet.country
setBankName((prev) => ({ loading: true, data: [] }));
apiCall
.getCountryBank({ country: value })
.getCountryBank({
country: bankCountry
})
.then((res) => {
if (res.data.internal_return < 0) {
setBankName((prev) => ({ loading: false, data: [] }));
@@ -138,6 +145,7 @@ function NairaWithdraw({
};
useEffect(() => {
handleBankOptions()
getCountry(); // TO LOAD LIST COUNTRY
getAccountTypes(); // TO LOAD LIST ACCOUNT TYPES
}, []);
@@ -224,7 +232,7 @@ function NairaWithdraw({
setShowConfirmNairaWithdraw({ show: true, state: stateData });
}, 1000);
}
if (tab === "new") {
const { accountNumber, accountType, bank, city, country, state } =
values?.newAccount;
@@ -279,6 +287,8 @@ function NairaWithdraw({
getRecipients();
}, []);
console.log("Testing Wallet Country", wallet?.walletCountry[0][0]);
return (
<ModalCom action={action} situation={situation} className="edit-popup">
<div className="logout-modal-wrapper lw-[90%] md:w-[768px] h-full lg:h-auto bg-white dark:bg-dark-white lg:rounded-2xl">
@@ -422,7 +432,11 @@ function NairaWithdraw({
<label
onClick={() => setTab("new")}
htmlFor="new"
className="cursor-pointer flex items-center gap-1"
className={`cursor-pointer flex items-center gap-1 ${
recipients.data.length >= MaxNoOfBanks
? "pointer-events-none"
: ""
}`}
>
<input
id="new"
@@ -435,6 +449,11 @@ function NairaWithdraw({
} tracking-wide transition duration-200`}
/>
New Account{" "}
{recipients.data.length >= MaxNoOfBanks && (
<span className="text-[14px] text-red-500">
Max Reached
</span>
)}
</label>
</div>
</div>
@@ -521,275 +540,257 @@ function NairaWithdraw({
</>
)}
{tab == "new" && (
<div className="w-full mt-3 rounded-md bg-slate-100">
<div className="relative fields w-full flex flex-col p-4">
<div className="flex flex-[2] min-h-[52px]">
{/* country */}
<div className="add-recipient w-full flex items-center flex-1 xl:mb-0">
<label
htmlFor="newAccount.country"
className="input-label text-[#181c32] dark:text-white text-base font-semibold inline-flex flex-[0.3]"
>
Country{" "}
<span className="text-red-500">*</span>
</label>
<select
className="w-full text-base p-2 text-dark-gray dark:text-white border border-slate-300 outline-0 flex-[0.6] rounded-full h-[42px] overflow-hidden relative font-medium leading-6 bg-clip-padding tracking-[1.5px]"
name="newAccount.country"
value={props.values.newAccount?.country}
onChange={(e) => {
props.handleChange(e);
handleBankOptions(e);
}}
>
{allCountries.loading ? (
<option
className="text-slate-500 text-lg"
value=""
>
Loading...
</option>
) : allCountries.data?.length ? (
<>
<option
className="text-slate-500 text-lg"
value=""
>
{errorMsgs.newAccount?.country
? errorMsgs.newAccount.country
: "Select..."}
</option>
{allCountries.data.map((item, index) => (
<option
key={index}
className="text-slate-500 text-lg"
value={item[0]}
>
{item[1]}
</option>
))}
</>
) : (
<option
className="text-slate-500 text-lg"
value=""
>
No Options Found!
</option>
)}
</select>
{/* {props.errors.country &&
props.touched.country && (
<p className="text-sm text-red-500">
{props.errors.country}
</p>
)} */}
</div>
{/* bank name */}
<div className="add-recipient w-full flex items-center flex-1">
<label
htmlFor="newAccount.bank"
className="input-label text-[#181c32] dark:text-white text-base font-semibold inline-flex flex-[0.4] tracking-[1.5px]"
>
Bank Name{" "}
<span className="text-red-500">*</span>
</label>
<select
className="w-full text-base p-2 text-dark-gray dark:text-white border border-slate-300 outline-0 flex-[0.6] rounded-full h-[42px] overflow-hidden relative font-medium leading-6 bg-clip-padding "
name="newAccount.bank"
value={props.values.newAccount?.bank}
onChange={props.handleChange}
onBlur={props.handleBlur}
>
{bankName.loading ? (
<option
className="text-slate-500 text-lg"
value=""
>
Loading...
</option>
) : bankName.data?.length ? (
<>
<option
className="text-slate-500 text-lg"
value=""
>
Select...
</option>
{bankName.data?.map((item, index) => (
<option
key={index}
className="text-slate-500 text-lg"
value={item.bank_uid}
>
{item.name}
</option>
))}
</>
) : (
<option
className="text-slate-500 text-lg"
value=""
>
{allCountries.data?.length
? "Select..."
: "No Options Found!"}
</option>
)}
</select>
{/* {props.errors.bank && props.touched.bank && (
<p className="text-sm text-red-500">
{props.errors.bank}
</p>
)} */}
</div>
</div>
<div className="flex flex-[2] gap-4">
{/* ACCOUNT NUMBER */}
<div className="field w-full flex-[1.4] flex items-center gap-2">
<label
htmlFor="newAccount.accountNumber"
className="input-label text-[#181c32] dark:text-white text-base font-semibold inline-flex items-center flex-1"
>
Account Number{" "}
<span className="text-red-500">*</span>
</label>
<InputCom
fieldClass="px-6 tracking-[1.5px]"
inputClass="flex items-center max-w-[15rem]"
type="text"
name="newAccount.accountNumber"
placeholder="Account No"
maxLength={10}
value={props.values.newAccount?.accountNumber}
inputHandler={props.handleChange}
blurHandler={props.handleBlur}
/>
{/* {props.errors.accountNumber &&
props.touched.accountNumber && (
<p className="text-sm text-red-500">
{props.errors.accountNumber}
</p>
)} */}
</div>
{/* Account Type */}
<div className="add-recipient w-full flex flex-1 items-center">
<label
htmlFor="newAccount.accountType"
className="input-label text-[#181c32] dark:text-white text-base font-semibold inline-flex flex-[0.3]"
>
Type <span className="text-red-500">*</span>
</label>
<select
className="w-full text-base p-2 text-dark-gray dark:text-white border border-slate-300 outline-0 flex-[0.6] rounded-full h-[42px] overflow-hidden relative font-medium leading-6 bg-clip-padding flex-grow tracking-[1.5px]"
name="newAccount.accountType"
value={props.values.newAccount?.accountType}
onChange={props.handleChange}
onBlur={props.handleBlur}
>
{accType.loading ? (
<option
className="text-slate-500 text-lg"
value=""
>
Loading...
</option>
) : accType.data?.length ? (
<>
<option
className="text-slate-500 text-lg"
value=""
>
Select...
</option>
{accType.data?.map((item, index) => (
<option
key={index}
className="text-slate-500 text-lg"
value={item.value}
>
{item.name}
</option>
))}
</>
) : (
<option
className="text-slate-500 text-lg"
value=""
>
No Options Found!
</option>
)}
</select>
{/* {props.errors.accountType &&
props.touched.accountType && (
<p className="text-sm text-red-500">
{props.errors.accountType}
</p>
)} */}
</div>
</div>
<div className="flex items-center flex-1">
{/* state */}
<div className="field w-full flex items-center gap-4 flex-[0.4]">
<label
htmlFor="newAccount.state"
className="input-label text-[#181c32] dark:text-white text-base font-semibold inline-flex flex-[0.4]"
>
State <span className="text-red-500">*</span>
</label>
<InputCom
fieldClass="px-6 tracking-[1.5px]"
inputClass="max-w-[10rem]"
type="text"
name="newAccount.state"
placeholder="State/Province"
value={props.values.newAccount?.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>
{/* city */}
<div className="field w-full flex items-center flex-[0.4]">
<label
htmlFor="newAccount.city"
className="input-label text-[#181c32] dark:text-white text-base font-semibold inline-flex flex-[0.4]"
>
City <span className="text-red-500">*</span>
</label>
<InputCom
fieldClass="px-6 tracking-[1.5px]"
type="text"
inputClass="max-w-[10rem]"
name="newAccount.city"
placeholder="City"
value={props.values.newAccount?.city}
inputHandler={props.handleChange}
blurHandler={props.handleBlur}
/>
{/* {props.errors.city && props.touched.city && (
<p className="text-sm text-red-500">
{props.errors.city}
</p>
)} */}
</div>
</div>
{/* end of inputs for new accounts */}
{tab == "new" &&
(recipients.loading ? (
<div className="mt-3 flex flex-col w-full h-[188px] justify-center items-center">
<LoadingSpinner size="10" color="sky-blue" />
</div>
</div>
)}
) : recipients.data.length < MaxNoOfBanks ? (
<div className="w-full mt-3 rounded-md bg-slate-100">
<div className="relative fields w-full flex flex-col p-4">
<div className="flex flex-[2] min-h-[52px]">
{/* country */}
<div className="add-recipient w-full flex items-center flex-1 xl:mb-0">
<label
htmlFor="newAccount.country"
className="input-label text-[#181c32] dark:text-white text-base font-semibold inline-flex flex-[0.3]"
>
Country{" "}
<span className="text-red-500">*</span>
</label>
<div className="w-full text-base p-2 text-dark-gray dark:text-white border border-slate-300 outline-0 flex-[0.6] rounded-full h-[42px] overflow-hidden relative font-medium leading-6 bg-clip-padding tracking-[1.5px] flex items-center pointer-events-none">
<span className="text-slate-500 text-lg italic">
{wallet.walletCountry[0][1]}
</span>
</div>
{/* <select
className="w-full text-base p-2 text-dark-gray dark:text-white border border-slate-300 outline-0 flex-[0.6] rounded-full h-[42px] overflow-hidden relative font-medium leading-6 bg-clip-padding tracking-[1.5px]"
name="newAccount.country"
value={props.values.newAccount?.country}
onChange={(e) => {
props.handleChange(e);
handleBankOptions(e);
}}
>
{allCountries.loading ? (
<option
className="text-slate-500 text-lg"
value=""
>
Loading...
</option>
) : allCountries.data?.length ? (
<>
<option
className="text-slate-500 text-lg"
value=""
>
{errorMsgs.newAccount?.country
? errorMsgs.newAccount.country
: "Select..."}
</option>
{allCountries.data.map((item, index) => (
<option
key={index}
className="text-slate-500 text-lg"
value={item[0]}
>
{item[1]}
</option>
))}
</>
) : (
<option
className="text-slate-500 text-lg"
value=""
>
No Options Found!
</option>
)}
</select> */}
</div>
{/* bank name */}
<div className="add-recipient w-full flex items-center flex-1">
<label
htmlFor="newAccount.bank"
className="input-label text-[#181c32] dark:text-white text-base font-semibold inline-flex flex-[0.4] tracking-[1.5px]"
>
Bank Name{" "}
<span className="text-red-500">*</span>
</label>
<select
className="w-full text-base p-2 text-dark-gray dark:text-white border border-slate-300 outline-0 flex-[0.6] rounded-full h-[42px] overflow-hidden relative font-medium leading-6 bg-clip-padding "
name="newAccount.bank"
value={props.values.newAccount?.bank}
onChange={props.handleChange}
onBlur={props.handleBlur}
>
{bankName.loading ? (
<option
className="text-slate-500 text-lg"
value=""
>
Loading...
</option>
) : bankName.data?.length ? (
<>
<option
className="text-slate-500 text-lg"
value=""
>
Select...
</option>
{bankName.data?.map((item, index) => (
<option
key={index}
className="text-slate-500 text-lg"
value={item.bank_uid}
>
{item.name}
</option>
))}
</>
) : (
<option
className="text-slate-500 text-lg"
value=""
>
{allCountries.data?.length
? "Select..."
: "No Options Found!"}
</option>
)}
</select>
</div>
</div>
<div className="flex flex-[2] gap-4">
{/* ACCOUNT NUMBER */}
<div className="field w-full flex-[1.4] flex items-center gap-2">
<label
htmlFor="newAccount.accountNumber"
className="input-label text-[#181c32] dark:text-white text-base font-semibold inline-flex items-center flex-1"
>
Account Number{" "}
<span className="text-red-500">*</span>
</label>
<InputCom
fieldClass="px-6 tracking-[1.5px]"
inputClass="flex items-center max-w-[15rem]"
type="text"
name="newAccount.accountNumber"
placeholder="Account No"
maxLength={10}
value={
props.values.newAccount?.accountNumber
}
inputHandler={props.handleChange}
blurHandler={props.handleBlur}
/>
</div>
{/* Account Type */}
<div className="add-recipient w-full flex flex-1 items-center">
<label
htmlFor="newAccount.accountType"
className="input-label text-[#181c32] dark:text-white text-base font-semibold inline-flex flex-[0.3]"
>
Type <span className="text-red-500">*</span>
</label>
<select
className="w-full text-base p-2 text-dark-gray dark:text-white border border-slate-300 outline-0 flex-[0.6] rounded-full h-[42px] overflow-hidden relative font-medium leading-6 bg-clip-padding flex-grow tracking-[1.5px]"
name="newAccount.accountType"
value={props.values.newAccount?.accountType}
onChange={props.handleChange}
onBlur={props.handleBlur}
>
{accType.loading ? (
<option
className="text-slate-500 text-lg"
value=""
>
Loading...
</option>
) : accType.data?.length ? (
<>
<option
className="text-slate-500 text-lg"
value=""
>
Select...
</option>
{accType.data?.map((item, index) => (
<option
key={index}
className="text-slate-500 text-lg"
value={item.value}
>
{item.name}
</option>
))}
</>
) : (
<option
className="text-slate-500 text-lg"
value=""
>
No Options Found!
</option>
)}
</select>
</div>
</div>
<div className="flex items-center flex-1">
{/* state */}
<div className="field w-full flex items-center gap-4 flex-[0.4]">
<label
htmlFor="newAccount.state"
className="input-label text-[#181c32] dark:text-white text-base font-semibold inline-flex flex-[0.4]"
>
State{" "}
<span className="text-red-500">*</span>
</label>
<InputCom
fieldClass="px-6 tracking-[1.5px]"
inputClass="max-w-[10rem]"
type="text"
name="newAccount.state"
placeholder="State/Province"
value={props.values.newAccount?.state}
inputHandler={props.handleChange}
blurHandler={props.handleBlur}
/>
</div>
{/* city */}
<div className="field w-full flex items-center flex-[0.4]">
<label
htmlFor="newAccount.city"
className="input-label text-[#181c32] dark:text-white text-base font-semibold inline-flex flex-[0.4]"
>
City <span className="text-red-500">*</span>
</label>
<InputCom
fieldClass="px-6 tracking-[1.5px]"
type="text"
inputClass="max-w-[10rem]"
name="newAccount.city"
placeholder="City"
value={props.values.newAccount?.city}
inputHandler={props.handleChange}
blurHandler={props.handleBlur}
/>
</div>
</div>
{/* end of inputs for new accounts */}
</div>
</div>
) : (
<div className="mt-3 flex w-full h-[188px] justify-center items-center"></div>
))}
</div>
<div className="transfer-fund-btn flex justify-end items-center gap-2 py-4">
+42 -38
View File
@@ -1,11 +1,4 @@
import React, {
Suspense,
lazy,
useCallback,
useEffect,
useMemo,
useState,
} from "react";
import React, { Suspense, lazy, useEffect, useState } from "react";
import { useSelector } from "react-redux";
import usersService from "../../services/UsersService";
import Layout from "../Partials/Layout";
@@ -14,31 +7,19 @@ const WalletBox = lazy(() => import("./WalletBox"));
const WalletRoutes = () => {
const apiCall = new usersService();
const { walletDetails } = useSelector((state) => state?.walletDetails); // WALLET STORE
const { walletTable } = useSelector((state) => state.tableReload);
const [walletList, setWalletList] = useState({
loading: true,
data: [],
reload: false,
});
const [paymentHistory, setPaymentHistory] = useState({
loading: true,
data: [],
});
const getWalletList = () => {
apiCall
.getUserWallets()
.then((res) => {
if (res.data.internal_return < 0) {
setWalletList({ loading: false, data: [] });
} else {
setWalletList({ loading: false, data: res.data?.result_list });
}
})
.catch(() => {
setWalletList({ loading: false, data: [] });
});
}
const [allCountries, setAllCountries] = useState({
// STATE TO HOLD LIST OF COUNTRIES
loading: true,
data: [],
});
const getPaymentHistory = () => {
apiCall
@@ -53,24 +34,47 @@ const WalletRoutes = () => {
.catch(() => {
setPaymentHistory({ loading: false, data: [] });
});
}
};
// FUNCTION TO GET COUNTRIES
const getCountry = () => {
apiCall
.getSignupCountryData()
.then((res) => {
if (res.data.internal_return < 0) {
setAllCountries((prev) => ({ loading: false, data: [] }));
return;
}
setAllCountries((prev) => ({
loading: false,
data: res.data.signup_country,
}));
})
.catch((error) => {
setAllCountries((prev) => ({ loading: false, data: [] }));
});
};
useEffect(() => {
// const fetchData = async () => {
// await Promise.all([getWalletList(), getPaymentHistory()]);
// };
// if (walletList.loading) {
// fetchData();
// }
getWalletList()
getPaymentHistory()
getCountry();
getPaymentHistory();
}, [walletTable]);
console.log(
"Testing all country: ",
allCountries,
"Testing wallet: ",
walletDetails
);
return (
<Layout>
<Suspense fallback={<LoadingSpinner size="16" color="sky-blue" />}>
<WalletBox wallet={walletList} payment={paymentHistory} />
<WalletBox
wallet={walletDetails}
payment={paymentHistory}
countries={allCountries.data}
/>
</Suspense>
</Layout>
);
+2 -2
View File
@@ -1,4 +1,4 @@
import React, { useCallback, useState } from "react";
import React, { useEffect, useState } from "react";
import usersService from "../../services/UsersService";
import ConfirmNairaWithdraw from "./Popup/ConfirmNairaWithdraw";
import NairaWithdraw from "./Popup/NairaWithdraw";
@@ -8,7 +8,7 @@ function WalletAction({ walletItem, payment, openPopUp }) {
show: false,
state: {},
}); // DETERMINES WHEN NAIRA WITHDRAWAL POPS UP
const [showConfirmNairaWithdraw, setShowConfirmNairaWithdraw] = useState({
show: false,
state: {},
+20 -17
View File
@@ -1,26 +1,29 @@
import LoadingSpinner from "../Spinners/LoadingSpinner";
import WalletItemCard from "./WalletItemCard";
export default function WalletBox({ wallet, payment }) {
/**
* Renders a list of wallet items or a loading spinner depending on the state of the `wallet` object.
*/
export default function WalletBox({ wallet, payment, countries }) {
const { loading, data } = wallet;
return (
<>
<div className="my-wallet-wrapper w-full mb-10">
<div className="main-wrapper w-full">
<div className="balance-inquery w-full lg:grid grid-cols-[repeat(auto-fill,_minmax(325px,_1fr))] gap-5 mb-11 h-[22rem]">
{wallet.loading ? (
<div className="w-full h-full flex items-center justify-center">
<LoadingSpinner size="16" color="sky-blue" />
<div className="my-wallet-wrapper w-full mb-10">
<div className="main-wrapper w-full">
<div className="balance-inquery w-auto grid sm:grid-cols-2 lg:grid-cols-2 xl:grid-cols-[repeat(auto-fill,_minmax(354px,_1fr))] min-[1440px]:grid-cols-[repeat(auto-fill,_minmax(415px,_1fr))] gap-5 mb-11 h-auto">
{loading ? (
<div className="w-full h-full flex items-center justify-center">
<LoadingSpinner size="16" color="sky-blue" />
</div>
) : (
data.length > 0 && data.map((item) => (
<div key={item.wallet_uid} className="lg:w-full h-full mb-10 lg:mb-0">
<WalletItemCard walletItem={item} payment={payment} countries={countries} />
</div>
) : wallet.data.length ? (
wallet.data.map((item, index) => (
<div className="lg:w-full h-full mb-10 lg:mb-0">
<WalletItemCard walletItem={item} payment={payment} />
</div>
))
) : null}
</div>
))
)}
</div>
</div>
</>
</div>
);
}
@@ -16,45 +16,36 @@ function PurchasesTable({purchase}) {
return (
<div className='flex flex-col justify-between min-h-[500px]'>
<table className="wallet-activity w-full table-auto border-collapse text-left">
<thead className='border-b-2'>
<tr className='text-slate-600'>
<th className="p-2">Trx.</th>
<th className="p-2">Amount</th>
<th className="p-2">Fee</th>
</tr>
</thead>
{purchase.data.length ?
(
<tbody>
{currentPurchase.map((item, index) => (
<tr key={index} className='text-slate-500'>
<td className="p-2">{item.added_date}<br />
<b>{item.confirmation} </b>
</td>
<td className="p-2">{item.amount}</td>
<td className="p-2">{item.fee}</td>
</tr>
))}
</tbody>
)
<table className="wallet-activity w-full table-auto border-collapse text-left">
<thead className='border-b-2'>
<tr className='text-slate-600'>
<th className="p-2">Trx.</th>
<th className="p-2">Amount</th>
<th className="p-2">Fee</th>
</tr>
</thead>
<tbody>
{currentPurchase.map((item, index) => (
<tr key={index} className='text-slate-500'>
<td className="p-2">{item.added_date}<br />
<b>{item.confirmation} </b>
</td>
<td className="p-2">{item.amount}</td>
<td className="p-2">{item.fee}</td>
</tr>
))}
</tbody>
</table>
:purchase.error ?
<div className="p-2 text-slate-500 flex flex-col grow justify-center items-center">
<span>Opps! an error occurred. Please try again!</span>
</div>
:
purchase.error ?
(
<tbody>
<tr className='text-slate-500'>
<td className="p-2" colSpan={4}>Opps! an error occurred. Please try again!</td>
</tr>
</tbody>
)
:
<tbody>
<tr className='text-slate-500'>
<td className="p-2" colSpan={4}>No Purchase History Found!</td>
</tr>
</tbody>
<div className="p-2 text-slate-500 flex flex-col grow justify-center items-center">
<span>No Purchase History Found!</span>
</div>
}
</table>
{/* PAGINATION BUTTON */}
<PaginatedList onClick={handlePagination} prev={currentPage == 0 ? true : false} next={currentPage+Number(process.env.REACT_APP_ITEM_PER_PAGE) >= purchase?.data?.length ? true : false} data={purchase?.data} start={indexOfFirstItem} stop={indexOfLastItem} />
@@ -19,16 +19,16 @@ function RecentActivityTable({ payment }) {
return (
<div className="flex flex-col justify-between min-h-[500px]">
<table className="wallet-activity w-full table-auto border-collapse text-left">
<thead className="border-b-2">
<tr className="text-slate-600">
<th className="p-2">Date</th>
<th className="p-4">Trx.</th>
<th className="p-2">Amnt./Fee</th>
<th className="p-2">Status</th>
</tr>
</thead>
{payment?.data?.length > 0 ? (
{payment?.data?.length > 0 ?
<table className="wallet-activity w-full table-auto border-collapse text-left">
<thead className="border-b-2">
<tr className="text-slate-600">
<th className="p-2">Date</th>
<th className="p-4">Trx.</th>
<th className="p-2">Amnt./Fee</th>
<th className="p-2">Status</th>
</tr>
</thead>
<tbody>
{currentActivity.map((item, index) => (
<tr key={index} className="text-slate-500">
@@ -46,24 +46,16 @@ function RecentActivityTable({ payment }) {
</tr>
))}
</tbody>
) : payment?.error ? (
<tbody>
<tr className="text-slate-500">
<td className="p-2" colSpan={4}>
Opps! an error occurred. Please try again!
</td>
</tr>
</tbody>
) : (
<tbody>
<tr className="text-slate-500">
<td className="p-2" colSpan={4}>
No Payment History Found!
</td>
</tr>
</tbody>
)}
</table>
</table>
:payment.error ?
<div className="p-2 text-slate-500 flex flex-col grow justify-center items-center">
<span>Opps! an error occurred. Please try again!</span>
</div>
:
<div className="p-2 text-slate-500 flex flex-col grow justify-center items-center">
<span>No Payment History Found!</span>
</div>
}
{/* PAGINATION BUTTON */}
<PaginatedList
+11 -8
View File
@@ -13,6 +13,12 @@ export default function WalletHeader(props) {
//props.myWalletList.result_list
let { pathname } = useLocation();
let navigate = useNavigate();
const onWalletClick = () => {
if (pathname == "/my-wallet")
props.setBalanceDropdown.toggle();
else navigate("/my-wallet", { replace: true });
}
return (
<>
<div className="lg:flex hidden user-balance cursor-pointer lg:w-[152px] w-[150px] h-[48px] items-center rounded-full relative bg-sky-blue pr-1.5 pl-4">
@@ -41,14 +47,15 @@ export default function WalletHeader(props) {
<div className="content px-7 pb-7">
<ul>
{props.myWalletList &&
props.myWalletList?.result_list?.length > 0 &&
props.myWalletList.result_list.map((value, index) =>
props.myWalletList?.length > 0 &&
props.myWalletList.map((value, index) =>
{
let image = value.code ? `${value.code.toLocaleLowerCase()}.svg` : 'default.png'
return(
<li
key={index}
className="content-item py-4 border-b dark:border-[#5356fb29] border-light-purple hover:border-purple dark:hover:border-purple"
onClick={onWalletClick}
>
<div className="sm:flex justify-between items-center">
<div className="account-name flex space-x-4 items-center mb-2 sm:mb-0">
@@ -56,7 +63,7 @@ export default function WalletHeader(props) {
<img src={localImgLoad(`images/currency/${image}`)} className="w-14 h-14" alt="" />
</div>
<div className="name">
<p className="text-base text-dark-gray dark:text-white font-medium">
<p className="text-2xl font-bold text-dark-gray dark:text-white">
{value.description}
</p>
</div>
@@ -178,11 +185,7 @@ export default function WalletHeader(props) {
</button> */}
<Link
to="/my-wallet"
onClick={() => {
if (pathname == "/my-wallet")
props.setBalanceDropdown.toggle();
else navigate("/my-wallet", { replace: true });
}}
onClick={onWalletClick}
className="w-[122px] h-11 flex justify-center items-center btn-gradient text-base rounded-full text-white"
>
Manage
+38 -26
View File
@@ -1,21 +1,25 @@
import React, { useState } from "react";
import { useSelector } from "react-redux";
import { useDispatch, useSelector } from "react-redux";
import background from "../../assets/images/bg-sky-blue.jpg"; //shape/balance-bg.svg";
import localImgLoad from "../../lib/localImgLoad";
import { tableReload } from "../../store/TableReloads";
import { PriceFormatter } from "../Helpers/PriceFormatter";
import CreditPopup from "./Popup/CreditPopup";
import WalletAction from "./WalletAction";
export default function WalletItemCard({ walletItem, payment }) {
// const [eth] = useState(90);
// const [btc] = useState(85);
// const [ltc] = useState(20);
const { userDetails } = useSelector((state) => state?.userDetails);
let accountType = userDetails?.account_type == "FAMILY";
// Credit popup
/**
* Renders a card displaying information about a wallet item.
*/
export default function WalletItemCard({ walletItem, payment, countries }) {
const { userDetails } = useSelector((state) => state.userDetails);
const accountType = userDetails?.account_type === "FAMILY";
const dispatch = useDispatch();
const [creditPopup, setCreditPopup] = useState({ show: false, data: {} });
/**
* Opens the credit popup.
* @param {Object} value - The value object.
*/
const openPopUp = (value) => {
setCreditPopup({
show: true,
@@ -23,37 +27,45 @@ export default function WalletItemCard({ walletItem, payment }) {
});
};
/**
* Closes the credit popup and dispatches a table reload action.
*/
const closePopUp = () => {
setCreditPopup({ show: false, data: {} });
dispatch(tableReload({ type: "WALLETTABLE" }));
};
let image = walletItem.code
? `${walletItem.code.toLocaleLowerCase()}.svg`
: "default.png"; // HOLDS THE VALUE NAME PROPERTY FOR IMAGE ICON
const currentWalletCurrency = countries
.map((country) => country)
.filter((country) => country[0] === walletItem.country);
const image = walletItem.code
? `${walletItem.code.toLowerCase()}.svg`
: "default.png";
return (
<>
<div
className={`current-balance-widget w-full h-full rounded-2xl overflow-hidden flex flex-col items-center gap-2 px-8 pt-9 pb-20`}
className="current-balance-widget w-full h-full rounded-2xl overflow-hidden flex flex-col items-center gap-2 p-8 justify-between"
style={{
background: `url(${background}) 0% 0% / cover no-repeat`,
}}
>
{/* <div className="w-[350px]"> */}
<div className="wallet w-full flex justify-between items-start gap-3">
<div className="min-w-[100px] min-h-[100px] max-w-[100px] max-h-[100px] rounded-full bg-[#e3e3e3] flex justify-center items-center">
<div className="min-w-[100px] min-h-[100px] max-w-min md:max-w-[150px] max-h-min md:max-h-[150px] rounded-full bg-[#e3e3e3] flex justify-center items-center">
<img
src={localImgLoad(`images/currency/${image}`)}
className="w-full h-full"
alt="curreny-icon"
alt="currency-icon"
/>
</div>
<div className="balance w-full mt-2 flex justify-center">
<div className="">
<p className="text-lg text-white opacity-[70%] tracking-wide mb-6">
<p className="text-base sm:text-lg text-white opacity-[70%] tracking-wide mb-2 sm:mb-6">
Current Balance
</p>
<p className="text-[44px] font-bold text-white tracking-wide leading-10 mb-2">
<p className="text-[44px] lg:text-[62px] font-bold text-white tracking-wide leading-10 xxs:scale-100 lg:scale-100 xl:scale-125">
{PriceFormatter(
walletItem.amount * 0.01,
walletItem.code,
@@ -65,9 +77,9 @@ export default function WalletItemCard({ walletItem, payment }) {
</div>
</div>
<p className="my-5 text-lg text-white tracking-wide flex justify-center items-center gap-2">
<p className="text-lg text-white tracking-wide flex justify-center items-center gap-8">
HOLDINGS :{" "}
<span className="mt-1">
<span className="xxs:scale-100 lg:scale-100 xl:scale-125">
{PriceFormatter(
walletItem.escrow * 0.01,
walletItem.code,
@@ -76,18 +88,18 @@ export default function WalletItemCard({ walletItem, payment }) {
)}
</span>
</p>
{/* for white underline */}
<div className="my-2 w-full h-[1px] bg-white"></div>
{!accountType ? (
{!accountType && (
<WalletAction
walletItem={walletItem}
walletItem={{ ...walletItem, walletCountry: currentWalletCurrency }}
payment={payment}
openPopUp={openPopUp}
/>
) : null}
{/* </div> */}
)}
</div>
{creditPopup.show && (
<CreditPopup
details={creditPopup.data}
+3 -15
View File
@@ -15,6 +15,7 @@ import WalletHeader from "../MyWallet/WalletHeader";
import { useDispatch, useSelector } from "react-redux";
import Flag from "../../assets/images/united-states.svg";
import siteLogo from "../../assets/images/wrenchboard-logo-text.png";
// import { updateWalletDetails } from "../../store/walletDetails";
import TimeDifference from "../Helpers/TimeDifference";
const DEFAULT_PROFILE_IMAGE = require("../../assets/images/profile.jpg");
@@ -33,19 +34,7 @@ export default function Header({ logoutModalHandler, sidebarHandler }) {
const navigate = useNavigate();
const { notifications } = useSelector((state) => state?.notifications); // NOTIFICATION STORE
const getMyWalletList = async () => {
try {
const res = await api.getUserWallets(null);
console.log("wallet - > ", res.data);
setMyWalletList(res.data);
} catch (error) {
console.log("Error getting mode");
}
};
useEffect(() => {
getMyWalletList();
}, [walletTable]);
const { walletDetails } = useSelector((state) => state?.walletDetails); // WALLET STORE
const handlerBalance = () => {
setbalanceValue.toggle();
@@ -104,7 +93,6 @@ export default function Header({ logoutModalHandler, sidebarHandler }) {
let userEmail = email?.split("@")[0];
const userProfileImage = profile_pic_url || DEFAULT_PROFILE_IMAGE;
return (
<>
<div className="header-wrapper backdrop-blur-sm bg-[#efedfe5e]/60 dark:bg-transparent w-full h-full flex items-center xl:px-0 md:px-10 px-5">
@@ -241,7 +229,7 @@ export default function Header({ logoutModalHandler, sidebarHandler }) {
{/*<div className="lg:hidden block"></div>*/}
<WalletHeader
myWalletList={myWalletList}
myWalletList={walletDetails.data}
handlerBalance={handlerBalance}
balanceDropdown={balanceDropdown}
addMoneyHandler={addMoneyHandler}
+22 -91
View File
@@ -55,59 +55,29 @@ export default function Layout({ children }) {
return (
<>
<div className="nft-main-wrapper-layout">
<div className="nft-wrapper-layout-container 2xl:pr-20 md:pr-10 pr-2 pl-2 md:pl-0 w-full min-h-screen flex">
<div className={`nft-wrapper-layout-container 2xl:pr-20 md:pr-10 pr-2 pl-2 md:pl-0 w-full min-h-screen flex`}>
{/* sidebar */}
<div
className={`nft-sidebar xl:block hidden section-shadow ${
drawer
? "2xl:w-[335px] w-[280px] 2xl:pl-20 pl-10 pr-6 "
: "w-[70px]"
} bg-white dark:bg-dark-white h-full overflow-y-scroll overflow-style-none fixed left-0 top-0 pt-[30px]`}
>
<Sidebar
logoutModalHandler={logoutModalHandler}
sidebar={drawer}
action={() => dispatch(drawerToggle())}
myJobList={userJobList}
/>
<div className={`nft-sidebar xl:block hidden section-shadow ${drawer ? "2xl:w-[335px] w-[280px] 2xl:pl-20 pl-10 pr-6 " : "w-[70px]"} bg-white dark:bg-dark-white h-full overflow-y-scroll overflow-style-none fixed left-0 top-0 pt-[30px]`}>
<Sidebar logoutModalHandler={logoutModalHandler} sidebar={drawer} action={() => dispatch(drawerToggle())} myJobList={userJobList} />
</div>
{MobileSideBar && (
<div
onClick={() => setMobileSidebar.toggle()}
className="bg-black bg-opacity-20 fixed left-0 top-0 w-full h-full z-[50] block xl:hidden"
></div>
<div onClick={() => setMobileSidebar.toggle()} className="bg-black bg-opacity-20 fixed left-0 top-0 w-full h-full z-[50] block xl:hidden"></div>
)}
<div
className={`nft-sidebar block xl:hidden section-shadow w-[280px] pl-3 bg-white dark:bg-dark-white h-full overflow-y-scroll overflow-style-none fixed z-[60] top-0 pt-8 ${
MobileSideBar ? "left-0" : "-left-[290px]"
}`}
>
<MobileSidebar
logoutModalHandler={logoutModalHandler}
sidebar={MobileSideBar}
action={() => setMobileSidebar.toggle()}
myJobList={userJobList}
/>
<div className={`nft-sidebar block xl:hidden section-shadow w-[280px] pl-3 bg-white dark:bg-dark-white h-full overflow-y-scroll overflow-style-none fixed z-[60] top-0 pt-8 ${MobileSideBar ? "left-0" : "-left-[290px]"}`}>
<MobileSidebar logoutModalHandler={logoutModalHandler} sidebar={MobileSideBar} action={() => setMobileSidebar.toggle()} myJobList={userJobList} />
</div>
{/* end sidebar */}
<div
className={`nft-header-container-wrapper flex-1 md:ml-10 ${
drawer ? "2xl:ml-[375px] xl:ml-[310px]" : "xl:ml-[110px]"
} h-full`}
>
<div className={`nft-header-container-wrapper flex-1 md:ml-10 ${drawer ? "2xl:ml-[375px] xl:ml-[310px]" : "xl:ml-[110px]"} h-full`}>
{/* header */}
<div className="nft-header w-full lg:h-[100px] h-[70px] default-border-bottom dark:border-[#292967] z-40 xl:sticky fixed top-0 left-0 ">
<Header
sidebarHandler={() => setMobileSidebar.toggle()}
logoutModalHandler={logoutModalHandler}
/>
<div className="nft-header w-full lg:h-[100px] h-[70px] default-border-bottom dark:border-[#292967] z-40 xl:sticky fixed top-0 left-0 ">
<Header sidebarHandler={() => setMobileSidebar.toggle()} logoutModalHandler={logoutModalHandler} />
</div>
{/* container */}
<div className="nft-container 2xl:flex 2xl:space-x-8 h-full mb-12 lg:mt-[140px] mt-24 xl:mt-10">
<div className="nft-main-container flex-1">
<div className="nft-container 2xl:flex 2xl:space-x-8 h-full mb-12 lg:mt-[140px] mt-24 xl:mt-10 flex flex-col xl:flex-row items-start justify-center gap-4">
<div className="nft-main-container flex-[80%] w-full">
{children && children}
</div>
<div className="nft-right-side-content 2xl:w-[270px] w-full h-full 2xl:flex justify-center relative">
<div className="nft-right-side-content 2xl:w-[270px] w-full h-full 2xl:flex justify-center relative flex-[20%]">
<RightSideBar />
</div>
</div>
@@ -121,50 +91,19 @@ export default function Layout({ children }) {
<h1 className="text-26 font-bold text-dark-gray dark:text-white tracking-wide">
Confirm
</h1>
<button
type="button"
className="text-[#374557] dark:text-red-500"
onClick={logoutModalHandler}
>
<svg
width="36"
height="36"
viewBox="0 0 36 36"
fill="none"
className="fill-current"
xmlns="http://www.w3.org/2000/svg"
>
<path
d="M36 16.16C36 17.4399 36 18.7199 36 20.0001C35.7911 20.0709 35.8636 20.2554 35.8385 20.4001C34.5321 27.9453 30.246 32.9248 22.9603 35.2822C21.9006 35.6251 20.7753 35.7657 19.6802 35.9997C18.4003 35.9997 17.1204 35.9997 15.8401 35.9997C15.5896 35.7086 15.2189 35.7732 14.9034 35.7093C7.77231 34.2621 3.08728 30.0725 0.769671 23.187C0.435002 22.1926 0.445997 21.1199 0 20.1599C0 18.7198 0 17.2798 0 15.8398C0.291376 15.6195 0.214408 15.2656 0.270759 14.9808C1.71321 7.69774 6.02611 2.99691 13.0428 0.700951C14.0118 0.383805 15.0509 0.386897 15.9999 0C17.2265 0 18.4532 0 19.6799 0C19.7156 0.124041 19.8125 0.136067 19.9225 0.146719C27.3 0.868973 33.5322 6.21922 35.3801 13.427C35.6121 14.3313 35.7945 15.2484 36 16.16ZM33.011 18.0787C33.0433 9.77105 26.3423 3.00309 18.077 2.9945C9.78479 2.98626 3.00344 9.658 2.98523 17.8426C2.96667 26.1633 9.58859 32.9601 17.7602 33.0079C26.197 33.0577 32.9787 26.4186 33.011 18.0787Z"
fill=""
fillOpacity="0.6"
/>
<path
d="M15.9309 18.023C13.9329 16.037 12.007 14.1207 10.0787 12.2072C9.60071 11.733 9.26398 11.2162 9.51996 10.506C9.945 9.32677 11.1954 9.0811 12.1437 10.0174C13.9067 11.7585 15.6766 13.494 17.385 15.2879C17.9108 15.8401 18.1633 15.7487 18.6375 15.258C20.3586 13.4761 22.1199 11.7327 23.8822 9.99096C24.8175 9.06632 26.1095 9.33639 26.4967 10.517C26.7286 11.2241 26.3919 11.7413 25.9133 12.2178C24.1757 13.9472 22.4477 15.6855 20.7104 17.4148C20.5228 17.6018 20.2964 17.7495 20.0466 17.9485C22.0831 19.974 24.0372 21.8992 25.9689 23.8468C26.9262 24.8119 26.6489 26.1101 25.4336 26.4987C24.712 26.7292 24.2131 26.3441 23.7455 25.8757C21.9945 24.1227 20.2232 22.3892 18.5045 20.6049C18.0698 20.1534 17.8716 20.2269 17.4802 20.6282C15.732 22.4215 13.9493 24.1807 12.1777 25.951C11.7022 26.4262 11.193 26.7471 10.4738 26.4537C9.31345 25.9798 9.06881 24.8398 9.98589 23.8952C11.285 22.5576 12.6138 21.2484 13.9387 19.9355C14.5792 19.3005 15.2399 18.6852 15.9309 18.023Z"
fill="#"
fillOpacity="0.6"
/>
<button type="button" className="text-[#374557] dark:text-red-500" onClick={logoutModalHandler}>
<svg width="36" height="36" viewBox="0 0 36 36" fill="none" className="fill-current" xmlns="http://www.w3.org/2000/svg">
<path d="M36 16.16C36 17.4399 36 18.7199 36 20.0001C35.7911 20.0709 35.8636 20.2554 35.8385 20.4001C34.5321 27.9453 30.246 32.9248 22.9603 35.2822C21.9006 35.6251 20.7753 35.7657 19.6802 35.9997C18.4003 35.9997 17.1204 35.9997 15.8401 35.9997C15.5896 35.7086 15.2189 35.7732 14.9034 35.7093C7.77231 34.2621 3.08728 30.0725 0.769671 23.187C0.435002 22.1926 0.445997 21.1199 0 20.1599C0 18.7198 0 17.2798 0 15.8398C0.291376 15.6195 0.214408 15.2656 0.270759 14.9808C1.71321 7.69774 6.02611 2.99691 13.0428 0.700951C14.0118 0.383805 15.0509 0.386897 15.9999 0C17.2265 0 18.4532 0 19.6799 0C19.7156 0.124041 19.8125 0.136067 19.9225 0.146719C27.3 0.868973 33.5322 6.21922 35.3801 13.427C35.6121 14.3313 35.7945 15.2484 36 16.16ZM33.011 18.0787C33.0433 9.77105 26.3423 3.00309 18.077 2.9945C9.78479 2.98626 3.00344 9.658 2.98523 17.8426C2.96667 26.1633 9.58859 32.9601 17.7602 33.0079C26.197 33.0577 32.9787 26.4186 33.011 18.0787Z" fill="" fillOpacity="0.6" />
<path d="M15.9309 18.023C13.9329 16.037 12.007 14.1207 10.0787 12.2072C9.60071 11.733 9.26398 11.2162 9.51996 10.506C9.945 9.32677 11.1954 9.0811 12.1437 10.0174C13.9067 11.7585 15.6766 13.494 17.385 15.2879C17.9108 15.8401 18.1633 15.7487 18.6375 15.258C20.3586 13.4761 22.1199 11.7327 23.8822 9.99096C24.8175 9.06632 26.1095 9.33639 26.4967 10.517C26.7286 11.2241 26.3919 11.7413 25.9133 12.2178C24.1757 13.9472 22.4477 15.6855 20.7104 17.4148C20.5228 17.6018 20.2964 17.7495 20.0466 17.9485C22.0831 19.974 24.0372 21.8992 25.9689 23.8468C26.9262 24.8119 26.6489 26.1101 25.4336 26.4987C24.712 26.7292 24.2131 26.3441 23.7455 25.8757C21.9945 24.1227 20.2232 22.3892 18.5045 20.6049C18.0698 20.1534 17.8716 20.2269 17.4802 20.6282C15.732 22.4215 13.9493 24.1807 12.1777 25.951C11.7022 26.4262 11.193 26.7471 10.4738 26.4537C9.31345 25.9798 9.06881 24.8398 9.98589 23.8952C11.285 22.5576 12.6138 21.2484 13.9387 19.9355C14.5792 19.3005 15.2399 18.6852 15.9309 18.023Z" fill="#" fillOpacity="0.6" />
</svg>
</button>
</div>
<div className="logout-modal-body w-full flex flex-col items-center px-10 py-8">
<div className="what-icon mb-6 cursor-pointer">
<svg
width="136"
height="136"
viewBox="0 0 136 136"
fill="none"
xmlns="http://www.w3.org/2000/svg"
>
<circle cx="68" cy="68" r="68" fill="#5356FB" />
<path
d="M69.8844 35.7891C71.1588 36.0357 72.4569 36.1967 73.7044 36.5423C81.5447 38.7098 87.2705 45.5378 87.9574 53.6156C88.5113 60.1147 86.3075 65.6006 81.5043 70.0195C79.8359 71.5545 78.0497 72.9604 76.3408 74.4534C76.127 74.6397 75.9654 75.0037 75.9604 75.2872C75.9284 77.2752 75.9435 79.2649 75.9435 81.2965C70.8895 81.2965 65.8758 81.2965 60.7915 81.2965C60.7915 81.0616 60.7915 80.8385 60.7915 80.6137C60.7915 76.5454 60.7999 72.4772 60.7797 68.4106C60.778 67.9392 60.9312 67.649 61.2831 67.3537C64.5643 64.5957 67.8271 61.8175 71.1033 59.0545C72.2616 58.0781 72.9215 56.8702 72.9081 55.3419C72.8878 52.916 70.8608 50.9146 68.423 50.8911C65.9701 50.8693 63.9145 52.8053 63.832 55.2328C63.8084 55.8988 63.8286 56.5665 63.8286 57.2695C58.7745 57.2695 53.7744 57.2695 48.6917 57.2695C48.6917 56.3149 48.6462 55.3385 48.6984 54.3655C49.222 44.699 56.7442 36.8745 66.4331 35.8914C66.5762 35.8763 66.7142 35.8243 66.854 35.7891C67.8641 35.7891 68.8742 35.7891 69.8844 35.7891Z"
fill="white"
/>
<path
d="M67.485 100.21C66.1617 99.9268 64.9041 99.5091 63.803 98.6787C61.3804 96.8484 60.2877 93.7699 61.0386 90.7888C61.7726 87.8747 64.2138 85.6703 67.2089 85.2157C71.273 84.6 75.2024 87.3681 75.8135 91.277C76.4937 95.6153 73.8202 99.3782 69.544 100.103C69.4429 100.12 69.3487 100.172 69.2527 100.209C68.6635 100.21 68.0742 100.21 67.485 100.21Z"
fill="white"
/>
<svg width="136" height="136" viewBox="0 0 136 136" fill="none" xmlns="http://www.w3.org/2000/svg">
<circle cx="68" cy="68" r="68" fill="#4687ba" />
<path d="M69.8844 35.7891C71.1588 36.0357 72.4569 36.1967 73.7044 36.5423C81.5447 38.7098 87.2705 45.5378 87.9574 53.6156C88.5113 60.1147 86.3075 65.6006 81.5043 70.0195C79.8359 71.5545 78.0497 72.9604 76.3408 74.4534C76.127 74.6397 75.9654 75.0037 75.9604 75.2872C75.9284 77.2752 75.9435 79.2649 75.9435 81.2965C70.8895 81.2965 65.8758 81.2965 60.7915 81.2965C60.7915 81.0616 60.7915 80.8385 60.7915 80.6137C60.7915 76.5454 60.7999 72.4772 60.7797 68.4106C60.778 67.9392 60.9312 67.649 61.2831 67.3537C64.5643 64.5957 67.8271 61.8175 71.1033 59.0545C72.2616 58.0781 72.9215 56.8702 72.9081 55.3419C72.8878 52.916 70.8608 50.9146 68.423 50.8911C65.9701 50.8693 63.9145 52.8053 63.832 55.2328C63.8084 55.8988 63.8286 56.5665 63.8286 57.2695C58.7745 57.2695 53.7744 57.2695 48.6917 57.2695C48.6917 56.3149 48.6462 55.3385 48.6984 54.3655C49.222 44.699 56.7442 36.8745 66.4331 35.8914C66.5762 35.8763 66.7142 35.8243 66.854 35.7891C67.8641 35.7891 68.8742 35.7891 69.8844 35.7891Z" fill="white" />
<path d="M67.485 100.21C66.1617 99.9268 64.9041 99.5091 63.803 98.6787C61.3804 96.8484 60.2877 93.7699 61.0386 90.7888C61.7726 87.8747 64.2138 85.6703 67.2089 85.2157C71.273 84.6 75.2024 87.3681 75.8135 91.277C76.4937 95.6153 73.8202 99.3782 69.544 100.103C69.4429 100.12 69.3487 100.172 69.2527 100.209C68.6635 100.21 68.0742 100.21 67.485 100.21Z" fill="white" />
</svg>
</div>
<div className="mb-6">
@@ -173,18 +112,10 @@ export default function Layout({ children }) {
</p>
</div>
<div className="flex space-x-2.5">
<button
onClick={logOut}
type="button"
className="text-white primary-gradient text-18 tracking-wide px-4 py-3 rounded-full"
>
<button onClick={logOut} type="button" className="text-white primary-gradient text-18 tracking-wide px-4 py-3 rounded-full">
{`Yes ${process.env.REACT_APP_LOGOUT_TEXT}`}
</button>
<button
onClick={logoutModalHandler}
type="button"
className=" border-gradient text-18 tracking-wide px-4 py-3 rounded-full"
>
<button onClick={logoutModalHandler} type="button" className=" border-gradient text-18 tracking-wide px-4 py-3 rounded-full">
<span className="text-gradient">Not Now</span>
</button>
</div>
+81 -29
View File
@@ -1,6 +1,6 @@
import React, { useState } from "react";
import { NavLink } from "react-router-dom";
import { useSelector } from "react-redux";
import { NavLink } from "react-router-dom";
//import SideStatistics from "./SideStatistics";
export default function RightSideBar() {
@@ -35,8 +35,8 @@ export default function RightSideBar() {
return (
<>
<div className="right-sidebar-wrapper overflow-y-scroll overflow-style-none 2xl:fixed 2xl:grid-cols-none 2xl:block grid lg:grid-cols-2 grid-cols-1 xl:gap-7 gap-4 h-full 2xl:pb-96">
<div className="top-platform bg-white dark:bg-dark-white rounded-2xl p-8 2xl:w-[268px] w-full 2xl:mb-10 2xl:border-none border ">
<div className="right-sidebar-wrapper overflow-y-scroll overflow-style-none 2xl:fixed h-full 2xl:pb-96">
<div className="top-platform bg-white dark:bg-dark-white rounded-2xl p-8 2xl:w-[268px] w-full 2xl:mb-10 2xl:border-none border ">
{/* heading */}
<div className="heading flex justify-between items-center mb-3.5">
<h3 className="text-xl font-bold text-dark-gray dark:text-white">
@@ -47,31 +47,62 @@ export default function RightSideBar() {
<div className="platform-list">
{userDetails && userDetails?.account_type !== "FAMILY" && (
<>
<div className="item flex space-x-3 items-center mb-4">
{/* image */}
<div className="w-8 h-8 rounded-full">
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" id="history">
<g data-name="14"><circle cx="9" cy="14" r="7" fill="#ffd54f"></circle>
<path fill="#ef6c00" d="M21 9H17a1 1 0 0 1 0-2h4a1 1 0 0 1 0 2zM21 5H3A1 1 0 0 1 3 3H21a1 1 0 0 1 0 2zM21 13H19a1 1 0 0 1 0-2h2a1 1 0 0 1 0 2zM21 17H19a1 1 0 0 1 0-2h2a1 1 0 0 1 0 2zM21 21H17a1 1 0 0 1 0-2h4a1 1 0 0 1 0 2z"></path><path fill="#ff8f00" d="M10,10a1,1,0,0,0-2,0v3.59L6.29,15.29a1,1,0,1,0,1.41,1.41l2-2A1,1,0,0,0,10,14Z">
</path>
</g>
</svg>
</div>
{/* name */}
<div>
<p className="text-thin-light-gray text-base font-medium">
<NavLink to="/history">History</NavLink>
</p>
</div>
{/* action */}
</div>
<>
<div className="item flex space-x-3 items-center mb-4">
{/* image */}
<div className="w-8 h-8 rounded-full">
<svg
xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 24 24"
id="history"
>
<g data-name="14">
<circle cx="9" cy="14" r="7" fill="#ffd54f"></circle>
<path
fill="#ef6c00"
d="M21 9H17a1 1 0 0 1 0-2h4a1 1 0 0 1 0 2zM21 5H3A1 1 0 0 1 3 3H21a1 1 0 0 1 0 2zM21 13H19a1 1 0 0 1 0-2h2a1 1 0 0 1 0 2zM21 17H19a1 1 0 0 1 0-2h2a1 1 0 0 1 0 2zM21 21H17a1 1 0 0 1 0-2h4a1 1 0 0 1 0 2z"
></path>
<path
fill="#ff8f00"
d="M10,10a1,1,0,0,0-2,0v3.59L6.29,15.29a1,1,0,1,0,1.41,1.41l2-2A1,1,0,0,0,10,14Z"
></path>
</g>
</svg>
</div>
{/* name */}
<div>
<p className="text-thin-light-gray text-base font-medium">
<NavLink to="/history">History</NavLink>
</p>
</div>
{/* action */}
</div>
<div className="item flex space-x-3 items-center mb-4">
{/* image */}
<div className="w-8 h-8 rounded-full">
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" id="add-user">
<path fill="#9bcb5b" d="M12 13.4c-2 0-3.7-1.6-3.7-3.6s1.6-3.7 3.6-3.7 3.7 1.6 3.7 3.6-1.6 3.6-3.6 3.7zm0-6.2c-1.4 0-2.6 1.1-2.6 2.6 0 1.4 1.1 2.6 2.6 2.6s2.6-1.1 2.6-2.6c-.1-1.5-1.2-2.6-2.6-2.6z"></path><path fill="#9bcb5b" d="M16.6 17.9c-.3 0-.5-.2-.6-.5 0-2.2-1.8-4-4-4s-4 1.8-4 4c0 .3-.3.5-.6.5-.2 0-.4-.2-.5-.5 0-2.8 2.3-5.1 5.1-5.1s5.1 2.3 5.1 5.1c0 .3-.2.5-.5.5z"></path><path fill="#0376bc" d="M12 23.7C5.5 23.7.3 18.4.3 12 .3 5.5 5.6.3 12 .3c2.6 0 5.1.9 7.2 2.5.2.2.2.6 0 .8-.2.2-.4.2-.7.1-1.9-1.4-4.1-2.2-6.5-2.2C6.2 1.4 1.4 6.2 1.4 12S6.2 22.6 12 22.6 22.6 17.8 22.6 12c0-2.4-.8-4.6-2.2-6.5-.2-.3-.1-.6.2-.8.2-.1.5-.1.7.1 1.6 2 2.5 4.6 2.4 7.2 0 6.4-5.3 11.7-11.7 11.7z"></path><circle cx="20.2" cy="20.3" r="2.4" fill="#fff"></circle><path fill="#9bcb5b" d="M18 18.1c.6-.6 1.4-.9 2.2-.9.8 0 1.6.3 2.2.9s1 1.4.9 2.2c0 .8-.3 1.6-.9 2.2s-1.4 1-2.2.9c-.8 0-1.6-.3-2.2-.9s-1-1.4-.9-2.2c-.1-.8.3-1.7.9-2.2zm3.8 2.5V20h-1.3v-1.3h-.6V20h-1.3v.6h1.3v1.3h.6v-1.3h1.3z"></path>
<svg
xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 24 24"
id="add-user"
>
<path
fill="#9bcb5b"
d="M12 13.4c-2 0-3.7-1.6-3.7-3.6s1.6-3.7 3.6-3.7 3.7 1.6 3.7 3.6-1.6 3.6-3.6 3.7zm0-6.2c-1.4 0-2.6 1.1-2.6 2.6 0 1.4 1.1 2.6 2.6 2.6s2.6-1.1 2.6-2.6c-.1-1.5-1.2-2.6-2.6-2.6z"
></path>
<path
fill="#9bcb5b"
d="M16.6 17.9c-.3 0-.5-.2-.6-.5 0-2.2-1.8-4-4-4s-4 1.8-4 4c0 .3-.3.5-.6.5-.2 0-.4-.2-.5-.5 0-2.8 2.3-5.1 5.1-5.1s5.1 2.3 5.1 5.1c0 .3-.2.5-.5.5z"
></path>
<path
fill="#0376bc"
d="M12 23.7C5.5 23.7.3 18.4.3 12 .3 5.5 5.6.3 12 .3c2.6 0 5.1.9 7.2 2.5.2.2.2.6 0 .8-.2.2-.4.2-.7.1-1.9-1.4-4.1-2.2-6.5-2.2C6.2 1.4 1.4 6.2 1.4 12S6.2 22.6 12 22.6 22.6 17.8 22.6 12c0-2.4-.8-4.6-2.2-6.5-.2-.3-.1-.6.2-.8.2-.1.5-.1.7.1 1.6 2 2.5 4.6 2.4 7.2 0 6.4-5.3 11.7-11.7 11.7z"
></path>
<circle cx="20.2" cy="20.3" r="2.4" fill="#fff"></circle>
<path
fill="#9bcb5b"
d="M18 18.1c.6-.6 1.4-.9 2.2-.9.8 0 1.6.3 2.2.9s1 1.4.9 2.2c0 .8-.3 1.6-.9 2.2s-1.4 1-2.2.9c-.8 0-1.6-.3-2.2-.9s-1-1.4-.9-2.2c-.1-.8.3-1.7.9-2.2zm3.8 2.5V20h-1.3v-1.3h-.6V20h-1.3v.6h1.3v1.3h.6v-1.3h1.3z"
></path>
</svg>
</div>
{/* name */}
@@ -81,16 +112,38 @@ export default function RightSideBar() {
</p>
</div>
</div>
</>
)}
<div className="item flex space-x-3 items-center mb-4">
{/* image */}
<div className="w-8 h-8 rounded-full">
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 48 48" id="InternationalUser">
<path fill="#e6e7f9" d="M38.5 31.1c2.1-1.1 3.9-2.5 5.4-4.4h-3.2c-.6 1.6-1.3 3.1-2.2 4.4zm-5.8 1.4c2-.7 3.7-2.8 4.9-5.8h-4.9v5.8zm8.8-8.6h4.2c.9-1.8 1.5-3.7 1.7-5.8h-5.1c-.1 2-.4 4-.8 5.8zm2.4-17.3c-1.5-1.8-3.3-3.3-5.4-4.4.8 1.2 1.6 2.7 2.2 4.4h3.2zm-5.3 2.9h-5.9v5.8h6.7c-.1-2.1-.4-4.1-.8-5.8zm3.7 5.8h5.1c-.2-2.1-.8-4-1.7-5.8h-4.2c.4 1.8.7 3.7.8 5.8zM32.7.8v5.8h4.9c-1.2-2.9-2.9-5.1-4.9-5.8zm6.7 17.3h-6.7v5.8h5.9c.4-1.8.7-3.8.8-5.8zM25 6.6h4.9V.8c-2 .7-3.7 2.9-4.9 5.8zm-1.8 8.7h6.7V9.5H24c-.4 1.7-.7 3.7-.8 5.8zm6.7 17.2v-5.8H25c1.2 3 2.9 5.1 4.9 5.8zM24.1 2.3c-2.1 1.1-3.9 2.5-5.4 4.4H22c.5-1.7 1.3-3.2 2.1-4.4zM24 23.9h5.9v-5.8h-6.7c.1 2 .4 4 .8 5.8z" className="colorc1e5ff svgShape"></path><path fill="#ff6699" d="M3.7 44.7c0 1.6 1.2 2.8 2.8 2.8 1.3 0 2.4-.9 2.7-2.2.3 1.3 1.4 2.2 2.7 2.2 1.5 0 2.8-1.3 2.8-2.8V30.1h3.1V18.8c0-4.2-3.3-7.5-7.4-7.5H8c-4.1 0-7.4 3.4-7.4 7.5v11.3h3.1v14.6z" className="colorff99b0 svgShape"></path><path fill="#998da0" d="M9.2 10.3c2.4 0 4.4-2.2 4.4-4.9S11.6.5 9.2.5C6.8.5 4.8 2.7 4.8 5.4s2 4.9 4.4 4.9z" className="colorffd499 svgShape"></path><path fill="#e6e7f9" d="M16.9 9.5c-.3.6-.5 1.2-.8 1.8 1.3 1 2.3 2.4 2.9 4h1.3c.1-2 .3-4 .8-5.8h-4.2zm2.9 9.3v5.1h1.4c-.4-1.8-.7-3.8-.8-5.8h-.6c-.1.2 0 .4 0 .7zm2.2 7.9h-2.2v1.2c1.3 1.3 2.7 2.4 4.4 3.2-.9-1.3-1.7-2.8-2.2-4.4z" className="colorc1e5ff svgShape"></path>
</svg>
<svg
xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 48 48"
id="InternationalUser"
>
<path
fill="#e6e7f9"
d="M38.5 31.1c2.1-1.1 3.9-2.5 5.4-4.4h-3.2c-.6 1.6-1.3 3.1-2.2 4.4zm-5.8 1.4c2-.7 3.7-2.8 4.9-5.8h-4.9v5.8zm8.8-8.6h4.2c.9-1.8 1.5-3.7 1.7-5.8h-5.1c-.1 2-.4 4-.8 5.8zm2.4-17.3c-1.5-1.8-3.3-3.3-5.4-4.4.8 1.2 1.6 2.7 2.2 4.4h3.2zm-5.3 2.9h-5.9v5.8h6.7c-.1-2.1-.4-4.1-.8-5.8zm3.7 5.8h5.1c-.2-2.1-.8-4-1.7-5.8h-4.2c.4 1.8.7 3.7.8 5.8zM32.7.8v5.8h4.9c-1.2-2.9-2.9-5.1-4.9-5.8zm6.7 17.3h-6.7v5.8h5.9c.4-1.8.7-3.8.8-5.8zM25 6.6h4.9V.8c-2 .7-3.7 2.9-4.9 5.8zm-1.8 8.7h6.7V9.5H24c-.4 1.7-.7 3.7-.8 5.8zm6.7 17.2v-5.8H25c1.2 3 2.9 5.1 4.9 5.8zM24.1 2.3c-2.1 1.1-3.9 2.5-5.4 4.4H22c.5-1.7 1.3-3.2 2.1-4.4zM24 23.9h5.9v-5.8h-6.7c.1 2 .4 4 .8 5.8z"
className="colorc1e5ff svgShape"
></path>
<path
fill="#ff6699"
d="M3.7 44.7c0 1.6 1.2 2.8 2.8 2.8 1.3 0 2.4-.9 2.7-2.2.3 1.3 1.4 2.2 2.7 2.2 1.5 0 2.8-1.3 2.8-2.8V30.1h3.1V18.8c0-4.2-3.3-7.5-7.4-7.5H8c-4.1 0-7.4 3.4-7.4 7.5v11.3h3.1v14.6z"
className="colorff99b0 svgShape"
></path>
<path
fill="#998da0"
d="M9.2 10.3c2.4 0 4.4-2.2 4.4-4.9S11.6.5 9.2.5C6.8.5 4.8 2.7 4.8 5.4s2 4.9 4.4 4.9z"
className="colorffd499 svgShape"
></path>
<path
fill="#e6e7f9"
d="M16.9 9.5c-.3.6-.5 1.2-.8 1.8 1.3 1 2.3 2.4 2.9 4h1.3c.1-2 .3-4 .8-5.8h-4.2zm2.9 9.3v5.1h1.4c-.4-1.8-.7-3.8-.8-5.8h-.6c-.1.2 0 .4 0 .7zm2.2 7.9h-2.2v1.2c1.3 1.3 2.7 2.4 4.4 3.2-.9-1.3-1.7-2.8-2.2-4.4z"
className="colorc1e5ff svgShape"
></path>
</svg>
</div>
{/* name */}
<div>
@@ -99,7 +152,6 @@ export default function RightSideBar() {
</p>
</div>
</div>
</div>
</div>
{/*<SideStatistics />*/}
+1
View File
@@ -8,6 +8,7 @@ import {
import DarkModeContext from "../Contexts/DarkModeContext";
import Icons from "../Helpers/Icons";
export default function Sidebar({
sidebar,
action,
+12 -7
View File
@@ -158,13 +158,18 @@ export default function Resources(props) {
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 === "")}
/>
))}
tabCategories?.map((tabValue, idx) => {
if(tabValue.enabled){
return (
<TabItem
key={tabValue.id}
tabValue={tabValue}
isActive={tab === tabValue.name || (idx === 0 && tab === "")}
/>
)
}
}
)}
</ul>
);
};
@@ -1,5 +1,7 @@
import React, { useState } from "react";
import { Link } from "react-router-dom";
import defaultImg from "../../../assets/images/myfiles/default.svg";
import localImgLoad from "../../../lib/localImgLoad";
import { PaginatedList, handlePagingFunc } from "../../Pagination";
export default function MyUploadedFiles({ uploadedFiles }) {
@@ -50,6 +52,17 @@ export default function MyUploadedFiles({ uploadedFiles }) {
>
<td className=" py-4">
<div className="flex space-x-2 items-center w-full">
<div className="w-[60px] h-[60px] p-2 bg-alice-blue rounded-full overflow-hidden flex justify-center items-center">
<img
src={
localImgLoad(
`images/myfiles/${value.banner}`
) || defaultImg
}
alt="data"
className="w-full h-full rounded-full"
/>
</div>
<div className="flex flex-col flex-[0.9]">
<h1 className="font-bold text-xl text-dark-gray dark:text-white">
{value.title || "Dummy Text"}
@@ -87,6 +100,21 @@ export default function MyUploadedFiles({ uploadedFiles }) {
</div>
</div>
</td>
<td className="text-right py-4 px-2">
<div className="flex justify-center items-center">
<button
type="button"
// onClick={() => {
// navigate("/manage-active-job", {
// state: { ...value, pathname },
// });
// }}
className="w-20 h-11 flex justify-center items-center btn-gradient text-base rounded-full text-white"
>
View
</button>
</div>
</td>
</tr>
);
})
@@ -69,7 +69,7 @@ function DeleteCardPopout({action, situation, data, setReloadCardList}) {
fill="none"
xmlns="http://www.w3.org/2000/svg"
>
<circle cx="68" cy="68" r="68" fill="#5356FB" />
<circle cx="68" cy="68" r="68" fill="#4687ba" />
<path
d="M69.8844 35.7891C71.1588 36.0357 72.4569 36.1967 73.7044 36.5423C81.5447 38.7098 87.2705 45.5378 87.9574 53.6156C88.5113 60.1147 86.3075 65.6006 81.5043 70.0195C79.8359 71.5545 78.0497 72.9604 76.3408 74.4534C76.127 74.6397 75.9654 75.0037 75.9604 75.2872C75.9284 77.2752 75.9435 79.2649 75.9435 81.2965C70.8895 81.2965 65.8758 81.2965 60.7915 81.2965C60.7915 81.0616 60.7915 80.8385 60.7915 80.6137C60.7915 76.5454 60.7999 72.4772 60.7797 68.4106C60.778 67.9392 60.9312 67.649 61.2831 67.3537C64.5643 64.5957 67.8271 61.8175 71.1033 59.0545C72.2616 58.0781 72.9215 56.8702 72.9081 55.3419C72.8878 52.916 70.8608 50.9146 68.423 50.8911C65.9701 50.8693 63.9145 52.8053 63.832 55.2328C63.8084 55.8988 63.8286 56.5665 63.8286 57.2695C58.7745 57.2695 53.7744 57.2695 48.6917 57.2695C48.6917 56.3149 48.6462 55.3385 48.6984 54.3655C49.222 44.699 56.7442 36.8745 66.4331 35.8914C66.5762 35.8763 66.7142 35.8243 66.854 35.7891C67.8641 35.7891 68.8742 35.7891 69.8844 35.7891Z"
fill="white"
@@ -51,6 +51,7 @@ export default function PersonalInfoTab({
coverImgInput,
browseCoverImg,
coverImgChangHandler,
uploadStatus
}) {
let { userDetails } = useSelector((state) => state.userDetails);
@@ -68,7 +69,7 @@ export default function PersonalInfoTab({
pref_email: 0,
pref_phone: 0,
accept_promo: false,
online_name: "",
online_name: userDetails?.online_name,
};
let [profile, setProfile] = useState({
@@ -158,7 +159,7 @@ export default function PersonalInfoTab({
fieldClass="px-6"
label="User Name"
type="text"
name="online_name"
name="username"
placeholder="Username"
value={userDetails?.username}
disable={true}
@@ -361,63 +362,67 @@ export default function PersonalInfoTab({
{/* inputs ends here */}
</div>
</div>
<div className="w-[232px] mb-10">
<div className="update-profile w-full mb-9">
<h1 className="text-xl tracking-wide font-bold text-dark-gray dark:text-white flex items-center mb-2">
Update Profile
<span className="ml-1">
<Icons name="block-question" />
</span>
</h1>
<p className="text-base text-thin-light-gray mb-5">
Profile of at least Size
<span className="ml-1 text-dark-gray dark:text-white">
300x300
</span>
. Gifs work too.
<span className="ml-1 text-dark-gray dark:text-white">
Max 5mb
</span>
.
</p>
<div className="flex justify-center">
<div className="w-full relative">
<img
src={profileImg}
alt="profile"
className="sm:w-[198px] sm:h-[198px] w-[120px] h-[120px] rounded-full overflow-hidden object-contain object-center"
/>
<input
ref={profileImgInput}
onChange={(e) => profileImgChangHandler(e)}
type="file"
className="hidden"
/>
<div
onClick={browseProfileImg}
className="w-[32px] h-[32px] absolute bottom-7 sm:right-10 right-[105px] hover:bg-pink bg-dark-gray rounded-full cursor-pointer"
>
<svg
width="32"
height="32"
viewBox="0 0 32 32"
fill="none"
xmlns="http://www.w3.org/2000/svg"
{/* {process.env.REACT_APP_SHOW_UPLOAD_PROFILE_PICTURE != 0 && */}
<div className="w-[232px] mb-10">
<div className="update-profile w-full mb-9">
<h1 className="text-xl tracking-wide font-bold text-dark-gray dark:text-white flex items-center mb-2">
Update Profile
<span className="ml-1">
<Icons name="block-question" />
</span>
</h1>
<p className="text-base text-thin-light-gray mb-5">
Profile of at least Size
<span className="ml-1 text-dark-gray dark:text-white">
300x300
</span>
. Gifs work too.
<span className="ml-1 text-dark-gray dark:text-white">
Max 5mb
</span>
.
</p>
<div className="flex justify-center">
<div className="w-full relative">
<img
src={profileImg}
alt="profile"
className="sm:w-[198px] sm:h-[198px] w-[120px] h-[120px] rounded-full overflow-hidden object-contain object-center"
/>
<input
ref={profileImgInput}
onChange={(e) => profileImgChangHandler(e)}
type="file"
className="hidden"
/>
<div
onClick={browseProfileImg}
className="w-[32px] h-[32px] absolute bottom-7 sm:right-10 right-[105px] hover:bg-pink bg-dark-gray rounded-full cursor-pointer"
>
<path
d="M16.5147 11.5C17.7284 12.7137 18.9234 13.9087 20.1296 15.115C19.9798 15.2611 19.8187 15.4109 19.6651 15.5683C17.4699 17.7635 15.271 19.9587 13.0758 22.1539C12.9334 22.2962 12.7948 22.4386 12.6524 22.5735C12.6187 22.6034 12.5663 22.6296 12.5213 22.6296C11.3788 22.6334 10.2362 22.6297 9.09365 22.6334C9.01498 22.6334 9 22.6034 9 22.536C9 21.4009 9 20.2621 9.00375 19.1271C9.00375 19.0746 9.02997 19.0109 9.06368 18.9772C10.4123 17.6249 11.7609 16.2763 13.1095 14.9277C14.2295 13.8076 15.3459 12.6913 16.466 11.5712C16.4884 11.5487 16.4997 11.5187 16.5147 11.5Z"
fill="white"
/>
<path
d="M20.9499 14.2904C19.7436 13.0842 18.5449 11.8854 17.3499 10.6904C17.5634 10.4694 17.7844 10.2446 18.0054 10.0199C18.2639 9.76139 18.5261 9.50291 18.7884 9.24443C19.118 8.91852 19.5713 8.91852 19.8972 9.24443C20.7251 10.0611 21.5492 10.8815 22.3771 11.6981C22.6993 12.0165 22.7105 12.4698 22.3996 12.792C21.9238 13.2865 21.4443 13.7772 20.9686 14.2717C20.9648 14.2792 20.9536 14.2867 20.9499 14.2904Z"
fill="white"
/>
</svg>
<svg
width="32"
height="32"
viewBox="0 0 32 32"
fill="none"
xmlns="http://www.w3.org/2000/svg"
>
<path
d="M16.5147 11.5C17.7284 12.7137 18.9234 13.9087 20.1296 15.115C19.9798 15.2611 19.8187 15.4109 19.6651 15.5683C17.4699 17.7635 15.271 19.9587 13.0758 22.1539C12.9334 22.2962 12.7948 22.4386 12.6524 22.5735C12.6187 22.6034 12.5663 22.6296 12.5213 22.6296C11.3788 22.6334 10.2362 22.6297 9.09365 22.6334C9.01498 22.6334 9 22.6034 9 22.536C9 21.4009 9 20.2621 9.00375 19.1271C9.00375 19.0746 9.02997 19.0109 9.06368 18.9772C10.4123 17.6249 11.7609 16.2763 13.1095 14.9277C14.2295 13.8076 15.3459 12.6913 16.466 11.5712C16.4884 11.5487 16.4997 11.5187 16.5147 11.5Z"
fill="white"
/>
<path
d="M20.9499 14.2904C19.7436 13.0842 18.5449 11.8854 17.3499 10.6904C17.5634 10.4694 17.7844 10.2446 18.0054 10.0199C18.2639 9.76139 18.5261 9.50291 18.7884 9.24443C19.118 8.91852 19.5713 8.91852 19.8972 9.24443C20.7251 10.0611 21.5492 10.8815 22.3771 11.6981C22.6993 12.0165 22.7105 12.4698 22.3996 12.792C21.9238 13.2865 21.4443 13.7772 20.9686 14.2717C20.9648 14.2792 20.9536 14.2867 20.9499 14.2904Z"
fill="white"
/>
</svg>
</div>
</div>
</div>
{uploadStatus.message && !uploadStatus.loading && <p className={`text-center ${uploadStatus.status ? 'text-green-500':'text-red-500'}`}>{uploadStatus.message}</p>}
{uploadStatus.loading && <p className="text-center">{uploadStatus.message}</p>}
</div>
</div>
</div>
{/* } */}
</div>
<div className="content-footer w-full">
+178 -150
View File
@@ -8,13 +8,16 @@ export default function TermsConditionTab() {
<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] ">
<p className="text-base text-thin-light-gray leading-[28px]">
(updated August 10, 2023)
</p>
<p className="text-base text-thin-light-gray leading-[28px]">
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.
use of www.wrenchboard.com,users.wrenchboard.com (the Site),
native apps, including all software, data, reports, text, images,
sounds, video, and all contents made available through any portion
of the Site (collectively, the Content). The range includes all
such elements as whole, individual, and parts.
</p>
</div>
<hr />
@@ -37,103 +40,138 @@ export default function TermsConditionTab() {
<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] ">
<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 />
<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>
<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] ">
<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>
<ul className="ml-8">
<li className="text-base text-thin-light-gray leading-[28px]">
<h1 className="text-lg tracking-wide font-bold text-dark-gray dark:text-white my-4">
Authorization to Access and Use Site and Content.
</h1>
<p className="text-base font-bold text-thin-light-gray leading-[28px]">
Subject to your compliance with these T&Cs and the provisions
hereof, you may access or use the Site and Content solely to
evaluate WRENCHBOARD and WRENCHBOARDs products and services.
You may only link to the Site or Content, or any portion
thereof, as expressly permitted by WRENCHBOARD.
</p>
</li>
<li className="text-base font-bold text-thin-light-gray leading-[28px]">
<h1 className="text-lg tracking-wide font-bold text-dark-gray dark:text-white my-4">
Ownership and Restrictions
</h1>
<p className="text-base text-thin-light-gray leading-[28px]">
All rights, title, and interest in and to the Site and Content
will remain exclusive to WRENCHBOARD. You will not:
</p>
<ol className="ml-8 list-decimal">
<li className="my-2 text-base font-bold text-thin-light-gray leading-[28px]">
Sublicense, resell, rent, lease, transfer, assign, timeshare,
or commercially exploit or make the Site and any Content
available to any third party.
</li>
<li className="my-2 text-base font-bold text-thin-light-gray leading-[28px]">
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 way that interferes with or
disrupts the integrity or performance of the Site and Content
or their related components.
</li>
<li className="my-2 text-base font-bold text-thin-light-gray leading-[28px]">
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).
</li>
</ol>
<p className="my-4 text-base font-bold text-thin-light-gray leading-[28px]">
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 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. You keep intact all copyright,
trademark, and other proprietary notices.
</p>
<p className="my-4 text-base font-bold text-thin-light-gray leading-[28px]">
You agree not to access the Site or Content by any means other
than through the interface that WRENCHBOARD provides 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.
</p>
<p className="my-4 text-base font-bold text-thin-light-gray leading-[28px]">
WRENCHBOARD reserves the right to take lawful measures to
prevent such activity. You may not forge headers or otherwise
manipulate identifiers 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.
</p>
</li>
<li className="text-base text-thin-light-gray leading-[28px]">
<h1 className="text-lg tracking-wide font-bold text-dark-gray dark:text-white my-4">
Responsibility for Your Data
</h1>
<p className="text-base font-bold text-thin-light-gray leading-[28px]">
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. 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, 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. <br />
WRENCHBOARD may remove or turn off any User Content at any time
for any reason or no reason. 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>
</li>
<li className="text-base text-thin-light-gray leading-[28px]">
<h1 className="text-lg tracking-wide font-bold text-dark-gray dark:text-white my-4">
Feedback
</h1>
<p className="text-base font-bold 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:{" "}
</p>
<ol className="ml-8 list-decimal">
<li className="my-2 text-base font-bold text-thin-light-gray leading-[28px]">
Your Feedback is provided by you voluntarily, and WRENCHBOARD
may, without any obligations or limitations, use and exploit
such Feedback in any manner and for any purpose.
</li>
<li className="my-2 text-base font-bold text-thin-light-gray leading-[28px]">
You will not seek and are not entitled to any money or other
form of compensation, consideration, or attribution concerning
your Feedback, regardless of whether WRENCHBOARD considered or
used your Feedback in any manner.
</li>
<li className="my-2 text-base font-bold text-thin-light-gray leading-[28px]">
Your Feedback is not confidential or proprietary information
of you or any third party.
</li>
</ol>
</li>
</ul>
</div>
<hr />
@@ -141,36 +179,32 @@ export default function TermsConditionTab() {
<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] ">
<p className="font-bold 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
terminate your access to the Site and 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.
the Site. 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):
</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>
<ol className="ml-8 list-decimal font-bold">
<li className="my-2 text-base font-bold text-thin-light-gray leading-[28px]">
Requests by law enforcement or other government agencies
</li>
<li className="my-2 text-base font-bold text-thin-light-gray leading-[28px]">
Discontinuance or material modification of the Site or any service
offered on or through the Site
</li>
<li className="my-2 text-base font-bold text-thin-light-gray leading-[28px]">
Unexpected technical issues or problems.T&Cs Updates
</li>
</ol>
<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
changes to these T&Cs on the Site and 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
@@ -193,42 +227,36 @@ export default function TermsConditionTab() {
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
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
RESULTS THAT MAY BE OBTAINED FROM THE 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] ">
<p className="font-bold 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
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
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..
non-routine maintenance, error correction, or other changes. Changes
to the Policy
</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 className="my-4 text-base text-thin-light-gray leading-[28px] ">
We reserve the right to update and change this Privacy policy at any
time. 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>
+62 -4
View File
@@ -24,10 +24,12 @@ import {
import RecipientAccountTab from "./Tabs/RecipientAccountTab";
export default function Settings({ faq }) {
const apiCall = new usersService();
const { userDetails } = useSelector((state) => state?.userDetails);
const [profileImg, setProfileImg] = useState(
userDetails?.profile_pic_url ? userDetails.profile_pic_url : profile
);
let [uploadStatus, setUploadStatus] = useState({loading: false, status: false, message:''}) // HOLDS STATE FOR UPLOAD PROFILE PICTURE STATUS
const [coverImg, setCoverImg] = useState(cover);
const [reloadCardList, setReloadCardList] = useState(false); // STATE TO DETERMINE WHEN CARD LIST RELOADS. EG: WHEN USER DELETES A CARD
@@ -36,12 +38,68 @@ export default function Settings({ faq }) {
const browseProfileImg = () => {
profileImgInput.current.click();
};
const profileImgChangHandler = (e) => {
// if (e.target.value !== "") {
// const imgReader = new FileReader();
// imgReader.onload = (event) => {
// setProfileImg(event.target.result);
// };
// imgReader.readAsDataURL(e.target.files[0]);
// }
setUploadStatus({loading: false, status: false, message:''})
let acceptedFormat = ["jpeg", "jpg", "png", "bmp", "gif"] // ARRAY OF SUPPORTED FORMATS
let uploadedFile = e.target.files[0] //UPLOADED FILE
if(!acceptedFormat.includes(uploadedFile?.type?.split("/")[1]?.toLowerCase())){ //CHECKING FOR CORRECT UPLOAD FORMAT
let msg = 'Please select '
for(let i=0; i<=acceptedFormat.length-1; i++){
if(i == acceptedFormat.length-1){
msg+=`or ${acceptedFormat[i]}`
}else{
msg+=`${acceptedFormat[i]}, `
}
}
setUploadStatus({loading: false, status: false, message:msg})
return setTimeout(()=>{
profileImgInput.current.value = '' // clear the input
setUploadStatus({loading: false, status: false, message:''})
},5000)
}
if(uploadedFile.size > 5*1048576){ // CHECKING FOR CORRECT FILE SIZE
setUploadStatus({loading: false, status: false, message:'File must not exceed 5MB'})
return setTimeout(()=>{
profileImgInput.current.value = '' // clear the input
setUploadStatus({loading: false, status: false, message:''})
},5000)
}
if (e.target.value !== "") {
const imgReader = new FileReader();
imgReader.onload = (event) => {
setProfileImg(event.target.result);
let reqData = { // PAYLOAD FOR API CALL
file_name: uploadedFile?.name,
file_size: uploadedFile?.size,
file_type: uploadedFile?.type?.split("/")[0]?.toLowerCase(),
file_data: event?.target?.result,
msg_type: 'FILE',
action: 'WRENCHBOARD_PICTURE_PROFILE',
// action: 11307
}
setUploadStatus({loading: true, status: false, message:'Loading...'})
apiCall.sendFiles(reqData).then(res=>{
if(res.status != 200 || res.data.internal_return < 0){
return setUploadStatus({loading: false, status: false, message: 'Something went wrong, try again'})
}
setUploadStatus({loading: false, status: true, message: 'Uploaded successfully'})
setProfileImg(event.target.result);
}).catch(error=>{
setUploadStatus({loading: false, status: false, message: 'Network error, try again'})
}).finally(()=>{
setTimeout(()=>{
setUploadStatus({loading: false, status: false, message: ''})
},5000)
})
};
imgReader.readAsDataURL(e.target.files[0]);
}
@@ -61,7 +119,6 @@ export default function Settings({ faq }) {
}
};
const apiCall = useMemo(() => new usersService(), []);
// Tabs Handling
const tabs = [
{
@@ -113,7 +170,7 @@ export default function Settings({ faq }) {
iconName: "page-right",
},
{
id: 8,
id: 9,
name: "terms",
title: "Terms and Conditions",
iconName: "page-right",
@@ -217,6 +274,7 @@ export default function Settings({ faq }) {
coverImgChangHandler={coverImgChangHandler}
browseCoverImg={browseCoverImg}
coverImgInput={coverImgInput}
uploadStatus={uploadStatus}
/>
</div>
)}
+31 -13
View File
@@ -81,22 +81,28 @@ export default function UploadProduct({uploadTypes}) {
const [imgDetails, setImgDetails] = useState('')
const changeFile = (e, file) => {
if (e) {
let uploadedFileType = e.target.files[0].type.split("/")[0].toLowerCase();
setSelectedFile(e.target.files[0].name);
setImgDetails(e?.target?.files[0])
const imgRead = new FileReader();
imgRead.onload = (event) => {
setImg(event.target.result);
// setImg(event.target.result);
setImg({ type: uploadedFileType, file: event.target.result });
};
// most importend
imgRead.readAsDataURL(e.target.files[0]);
}
if (file?.length) {
let uploadedFileType = file[0].type.split("/")[0].toLowerCase();
if (file[0].name) {
setSelectedFile(file?.[0].name);
setImgDetails(file?.[0])
const imgRead = new FileReader();
imgRead.onload = (event) => {
setImg(event.target.result);
// setImg(event.target.result);
setImg({ type: uploadedFileType, file: event.target.result });
};
// most importend
imgRead.readAsDataURL(file[0]);
@@ -141,7 +147,7 @@ export default function UploadProduct({uploadTypes}) {
file_name: selectedFile.substring(0,21).replace(/ /gi, ""),//selectedFile.replace(/[ -]/gi, ""),
file_size: imgDetails.size,
file_type: imgDetails.type,
file_data: img,
file_data: img.file,
title: itemName,
description: description,
msg_type: 'FILE',
@@ -164,7 +170,7 @@ export default function UploadProduct({uploadTypes}) {
},5000)
}
//API CALL TO UPLOAD COMES HERE
// API CALL TO UPLOAD COMES HERE
setRequestStatus({loading: true, status: null, message: ''}) // SETS REQUEST LOADING TRUE
apiCall.sendFiles(reqData).then(res=>{
if(res.status != 200 || res.data.internal_return < 0){
@@ -248,7 +254,6 @@ export default function UploadProduct({uploadTypes}) {
setPurchase(false);
cancelUploadModalHandler();
setImg(null);
navigate('/resources', {state:{tab:'created'}})
};
return (
<>
@@ -292,17 +297,29 @@ export default function UploadProduct({uploadTypes}) {
>
{img ? (
<div className="w-full h-full">
<img
src={img}
alt=""
className="w-full h-full object-cover"
/>
{img.type === "image" ? (
<img
src={img.file}
alt=""
className="w-full h-full object-cover"
/>
) : img.type === "video" ? (
<video
type=""
src={img.file}
className="w-full h-full object-cover"
controls
></video>
) : img.type != null ? (
<p>{selectedFile}</p>
) : null}
<input
type="file"
id="fileElem"
name="file"
className="hidden"
accept="image/*"
// accept="image/*"
ref={fileRef}
value={selectedFile.name}
onChange={(e) => changeFile(e)}
@@ -359,7 +376,7 @@ export default function UploadProduct({uploadTypes}) {
id="fileElem"
name="file"
className="hidden"
accept="image/*"
// accept="image/*"
ref={fileRef}
value={selectedFile.name}
onChange={(e) => changeFile(e)}
@@ -393,7 +410,8 @@ export default function UploadProduct({uploadTypes}) {
<div className="flex sm:space-x-4 space-x-2 sm:mr-9 mr-2">
<button
type="button"
onClick={cancelUploadModalHandler}
// onClick={cancelUploadModalHandler}
onClick={()=>navigate('/resources', {state:{tab:'created'}})}
className="sm:text-18 text-sm text-light-red tracking-wide "
>
<span className="border-b dark:border-[#5356fb29] border-light-red">
+1 -1
View File
@@ -103,7 +103,7 @@ function DeleteJobPopout({ details, onClose, situation }) {
fill="none"
xmlns="http://www.w3.org/2000/svg"
>
<circle cx="68" cy="68" r="68" fill="#5356FB" />
<circle cx="68" cy="68" r="68" fill="#4687ba" />
<path
d="M69.8844 35.7891C71.1588 36.0357 72.4569 36.1967 73.7044 36.5423C81.5447 38.7098 87.2705 45.5378 87.9574 53.6156C88.5113 60.1147 86.3075 65.6006 81.5043 70.0195C79.8359 71.5545 78.0497 72.9604 76.3408 74.4534C76.127 74.6397 75.9654 75.0037 75.9604 75.2872C75.9284 77.2752 75.9435 79.2649 75.9435 81.2965C70.8895 81.2965 65.8758 81.2965 60.7915 81.2965C60.7915 81.0616 60.7915 80.8385 60.7915 80.6137C60.7915 76.5454 60.7999 72.4772 60.7797 68.4106C60.778 67.9392 60.9312 67.649 61.2831 67.3537C64.5643 64.5957 67.8271 61.8175 71.1033 59.0545C72.2616 58.0781 72.9215 56.8702 72.9081 55.3419C72.8878 52.916 70.8608 50.9146 68.423 50.8911C65.9701 50.8693 63.9145 52.8053 63.832 55.2328C63.8084 55.8988 63.8286 56.5665 63.8286 57.2695C58.7745 57.2695 53.7744 57.2695 48.6917 57.2695C48.6917 56.3149 48.6462 55.3385 48.6984 54.3655C49.222 44.699 56.7442 36.8745 66.4331 35.8914C66.5762 35.8763 66.7142 35.8243 66.854 35.7891C67.8641 35.7891 68.8742 35.7891 69.8844 35.7891Z"
fill="white"
+1 -1
View File
@@ -351,7 +351,7 @@ const EditJobPopOut = ({
className="w-[120px] h-[40px] flex justify-center items-center btn-gradient text-base rounded-full text-white"
// className='w-20 h-11 flex justify-center items-center btn-gradient text-base rounded-full text-white'
>
Edit Job
Save
</button>
)}
</div>
+30 -17
View File
@@ -1,11 +1,13 @@
import { Field, Form, Formik } from "formik";
import React, { useCallback, useEffect, useMemo, useState } from "react";
import Detail from "./popoutcomponent/Detail";
import ModalCom from "../Helpers/ModalCom";
import InputCom from "../Helpers/Inputs/InputCom/index";
import SiteService from "../../services/SiteService";
import { Form, Formik, Field } from "formik";
import * as Yup from "yup";
import usersService from "../../services/UsersService";
import InputCom from "../Helpers/Inputs/InputCom/index";
import ModalCom from "../Helpers/ModalCom";
import LoadingSpinner from "../Spinners/LoadingSpinner";
import Detail from "./popoutcomponent/Detail";
import { tableReload } from "../../store/TableReloads";
import { useDispatch } from "react-redux";
const validationSchema = Yup.object().shape({
family: Yup.string().required("This is required "),
@@ -21,6 +23,8 @@ const validationSchema = Yup.object().shape({
});
function JobListPopout({ details, onClose, situation }) {
const dispatch = useDispatch()
const [familyList, setFamilyList] = useState([]);
let [loader, setLoader] = useState({
member: false,
@@ -32,7 +36,7 @@ function JobListPopout({ details, onClose, situation }) {
},
});
const apiCall = useMemo(() => new SiteService(), []);
const apiCall = useMemo(() => new usersService(), []);
// member listing
const memberList = useCallback(async () => {
@@ -159,6 +163,7 @@ function JobListPopout({ details, onClose, situation }) {
const res = await apiCall.assignJobTask(reqData);
let { data } = await res;
setLoader({ jobFields: false });
dispatch(tableReload({ type: "JOBTABLE" }));
onClose();
throw new Response(data);
} catch (error) {
@@ -168,7 +173,6 @@ function JobListPopout({ details, onClose, situation }) {
};
// console.log("Job List P >> ", details)
return (
<ModalCom action={onClose} situation={situation} className="job-popup">
<div className="logout-modal-wrapper lw-[90%] md:w-[768px] h-full lg:h-auto bg-white dark:bg-dark-white lg:rounded-2xl overflow-y-auto">
@@ -202,7 +206,7 @@ function JobListPopout({ details, onClose, situation }) {
</svg>
</button>
</div>
<div className="md:flex bg-white rounded-lg shadow-lg">
<div className="md:flex bg-white dark:bg-dark-white rounded-lg shadow-lg">
<div className="p-4 w-full md:w-2/4 md:border-r-2">
{/* <p className='text-lg font-semibold text-slate-900 tracking-wide'>{details.title}</p> */}
@@ -225,17 +229,17 @@ function JobListPopout({ details, onClose, situation }) {
<div className="my-3 md:flex">
<Detail
label="Created"
value={`Dummy, no value found for created!`}
/>
value={new Date(details?.created).toDateString()}
/>
</div>
<div className="my-3">
<label className="w-full text-slate-900 tracking-wide font-semibold">
<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"
className={`p-2 w-full text-sm text-slate-900 dark:text-white bg-transparent outline-none border border-slate-300 rounded-md`}
rows="7"
style={{ resize: "none" }}
value={textArea}
onChange={handleInputChange}
@@ -322,7 +326,7 @@ function JobListPopout({ details, onClose, situation }) {
input={true}
inputName="individual"
value={props?.values.individual}
placeholder="enter email of individual"
placeholder="Enter email of individual"
inputHandler={props?.handleChange}
btnText="Send Offer to Individual"
loader={loader?.jobFields.individual}
@@ -338,7 +342,8 @@ function JobListPopout({ details, onClose, situation }) {
);
}}
</Formik>
{ process.env.REACT_APP_SHOW_OFFER_GROUP_JOB != 0 &&
<Formik
initialValues={initialValues}
validationSchema={validationSchema.fields.group}
@@ -368,6 +373,7 @@ function JobListPopout({ details, onClose, situation }) {
);
}}
</Formik>
}
</div>
{/* END OF ACTION SECTION */}
</div>
@@ -396,7 +402,7 @@ const JobFieldInput = ({
data,
}) => {
return (
<div className="field w-full p-3 mb-2 bg-red-50 rounded-md">
<div className="field w-full p-3 mb-2 bg-red-50 dark:bg-[#D85A5A] rounded-md">
{select && (
<>
<div className={`input-com ${parentClass}`}>
@@ -468,7 +474,14 @@ const JobFieldInput = ({
type="submit"
name={inputName}
onClick={errorHandler}
className="px-2 py-1 text-sm text-white btn-gradient tracking-wide rounded-md"
// className={`px-2 py-1 text-sm text-white btn-gradient tracking-wide rounded-md ${
// !value && "disabled:grayscale-[65%] transition duration-300"
// }`}
className={`px-4 h-11 flex justify-center items-center btn-gradient text-base rounded-full text-white ${
!value && ""
}
`}
// disabled={!value}
>
{loader ? <LoadingSpinner size={5} /> : btnText}
</button>
+89 -75
View File
@@ -1,11 +1,11 @@
import React, { useMemo, useState } from "react";
import React, { useState } from "react";
import { useNavigate } from "react-router-dom";
import { toast } from "react-toastify";
import usersService from "../../services/UsersService";
import ModalCom from "../Helpers/ModalCom";
import { PriceFormatter } from "../Helpers/PriceFormatter";
import LoadingSpinner from "../Spinners/LoadingSpinner";
import Detail from "./popoutcomponent/Detail";
import { useNavigate } from "react-router-dom";
import { useDispatch } from "react-redux";
import { tableReload } from "../../store/TableReloads";
@@ -18,85 +18,95 @@ const showSuccessToast = (message) => {
};
function PendingJobsPopout({ details, onClose, situation }) {
const dispatch = useDispatch()
const navigate = useNavigate()
const dispatch = useDispatch();
const navigate = useNavigate();
const [pendingJobLoader, setPendingJobLoader] = useState({
extend: false,
offer: false,
});
let [requestMessage, setRequestMessage] = useState({status: false, message: ''})
let [requestMessage, setRequestMessage] = useState({
status: false,
message: "",
});
const apiCall = new usersService();
const handlePendingJobsBtn = ({ target: { name } }) => {
let { job_uid, offer_code } = details;
let { job_uid, offer_code } = details;
let reqData;
let reqData;
let pendingData = { job_uid, offer_code };
if(name=='extend'){ // RUNS THIS TO EXTEND JOB EXPIRY BY ONE WEEK
reqData = { ...pendingData };
setPendingJobLoader({ extend: true });
apiCall.pendingJobExtend(reqData).then(res => {
setRequestMessage({status: true, message: res.data.status})
let pendingData = { job_uid, offer_code };
if (name == "extend") {
// RUNS THIS TO EXTEND JOB EXPIRY BY ONE WEEK
reqData = { ...pendingData };
setPendingJobLoader({ extend: true });
apiCall
.pendingJobExtend(reqData)
.then((res) => {
setRequestMessage({ status: true, message: res.data.status });
setTimeout(() => {
setPendingJobLoader({ extend: false, offer: false });
setRequestMessage({status: false, message: ''})
setRequestMessage({ status: false, message: "" });
onClose();
dispatch(tableReload({ type: "PENDINGTABLE" }));
}, 4000);
}).catch(error => {
setRequestMessage({status: false, message:'Try Again'})
setTimeout(() => {
setPendingJobLoader({ extend: false, offer: false });
setRequestMessage({status: false, message: ''})
}, 3000);
})
}else if(name=='offer'){ // RUNS THIS IF JOB IS SENT TO USER
reqData = { ...pendingData };
setPendingJobLoader({ offer: true });
apiCall.pendingJobSendTome(reqData).then(res => {
setRequestMessage({status: true, message: res.data.status})
.catch((error) => {
setRequestMessage({ status: false, message: "Try Again" });
setTimeout(() => {
setPendingJobLoader({ extend: false, offer: false });
setRequestMessage({status: false, message: ''})
setRequestMessage({ status: false, message: "" });
}, 3000);
});
} else if (name == "offer") {
// RUNS THIS IF JOB IS SENT TO USER
reqData = { ...pendingData };
setPendingJobLoader({ offer: true });
apiCall
.pendingJobSendTome(reqData)
.then((res) => {
setRequestMessage({ status: true, message: res.data.status });
setTimeout(() => {
setPendingJobLoader({ extend: false, offer: false });
setRequestMessage({ status: false, message: "" });
}, 4000);
}).catch(error => {
setRequestMessage('Try Again')
})
.catch((error) => {
setRequestMessage("Try Again");
setTimeout(() => {
setPendingJobLoader({ extend: false, offer: false });
setRequestMessage({status: false, message: ''})
setRequestMessage({ status: false, message: "" });
}, 3000);
})
} else return
// try {
// if (name === "extend") {
// setPendingJobLoader({ extend: true });
// reqData = { ...pendingData };
// // let { data } =
// await apiCall.pendingJobExtend(reqData);
// showSuccessToast("Job has been extended by a week!");
// dispatch(tableReload({ type: "PENDINGTABLE" }));
// } else if (name === "offer") {
// setPendingJobLoader({ offer: true });
// reqData = { ...pendingData };
// // let { data } =
// await apiCall.pendingJobSendTome(reqData);
// showSuccessToast("Offer sent, check your email");
// } else return;
});
} else return;
// try {
// if (name === "extend") {
// setPendingJobLoader({ extend: true });
// reqData = { ...pendingData };
// // let { data } =
// await apiCall.pendingJobExtend(reqData);
// showSuccessToast("Job has been extended by a week!");
// dispatch(tableReload({ type: "PENDINGTABLE" }));
// } else if (name === "offer") {
// setPendingJobLoader({ offer: true });
// reqData = { ...pendingData };
// // let { data } =
// await apiCall.pendingJobSendTome(reqData);
// showSuccessToast("Offer sent, check your email");
// } else return;
// setTimeout(() => {
// setPendingJobLoader({ extend: false, offer: false });
// onClose();
// }, 2700);
// } catch (error) {
// setPendingJobLoader({ extend: false, offer: false });
// throw new Error(error);
// }
}
// setTimeout(() => {
// setPendingJobLoader({ extend: false, offer: false });
// onClose();
// }, 2700);
// } catch (error) {
// setPendingJobLoader({ extend: false, offer: false });
// throw new Error(error);
// }
};
return (
<ModalCom action={onClose} situation={situation} className="edit-popup">
@@ -131,9 +141,9 @@ function PendingJobsPopout({ details, onClose, situation }) {
</svg>
</button>
</div>
<div className="md:flex bg-white rounded-lg shadow-lg">
<div className="md:flex bg-white dark:bg-dark-white rounded-lg shadow-lg">
<div className="p-4 w-full md:w-3/4 md:border-r-2">
<p className="text-base font-semibold text-slate-900 tracking-wide">
<p className="text-base font-semibold text-slate-900 dark:text-white tracking-wide">
{details.title}
</p>
<div className="my-2 p-2 flex justify-start items-center space-x-2 bg-red-100 border-2 border-red-300">
@@ -177,7 +187,11 @@ function PendingJobsPopout({ details, onClose, situation }) {
<Detail
label="Price"
// value={`${details.price * 0.01} ${details.currency}`}
value={PriceFormatter(details.price * 0.01, details?.currency_code, details.currency)}
value={PriceFormatter(
details.price * 0.01,
details?.currency_code,
details.currency
)}
/>
</div>
@@ -194,19 +208,11 @@ function PendingJobsPopout({ details, onClose, situation }) {
value={details.job_description || details.description}
/>
</div>
{/* <div className="my-2 md:flex">
<Detail
label="Public Link"
value="https://work.wrenchboard.com/plb/viewjob/218B4BWB83"
bg="bg-slate-200"
/>
</div> */}
</div>
{/* ACTION SECTION */}
<div className="p-4 w-full md:w-1/4 h-full">
<p className="mb-6 text-sm">Actions</p>
<p className="mb-6 text-sm dark:text-white">Actions</p>
<div className="mb-3">
<p className="px-2 py-1 text-sm bg-slate-100">
@@ -229,11 +235,15 @@ function PendingJobsPopout({ details, onClose, situation }) {
)}
</button>
</div>
{pendingJobLoader.extend && requestMessage.message &&
<div className={`p-1 ${requestMessage.status ? 'bg-green-500':'bg-red-500'} text-white rounded-[0.475rem] text-md font-light leading-[19.5px] text-[13px]`}>
{pendingJobLoader.extend && requestMessage.message && (
<div
className={`p-1 ${
requestMessage.status ? "bg-green-500" : "bg-red-500"
} text-white rounded-[0.475rem] text-md font-light leading-[19.5px] text-[13px]`}
>
<p>{requestMessage.message}</p>
</div>
}
)}
<div className="my-3">
<button
@@ -251,11 +261,15 @@ function PendingJobsPopout({ details, onClose, situation }) {
</button>
</div>
{pendingJobLoader.offer && requestMessage.message &&
<div className={`p-1 ${requestMessage.status ? 'bg-green-500':'bg-red-500'} text-white rounded-[0.475rem] text-md font-light leading-[19.5px] text-[13px]`}>
{pendingJobLoader.offer && requestMessage.message && (
<div
className={`p-1 ${
requestMessage.status ? "bg-green-500" : "bg-red-500"
} text-white rounded-[0.475rem] text-md font-light leading-[19.5px] text-[13px]`}
>
<p>{requestMessage.message}</p>
</div>
}
)}
<div className="mt-10 md:mt-32 md:flex md:justify-center">
<button
@@ -284,4 +298,4 @@ function PendingJobsPopout({ details, onClose, situation }) {
);
}
export default PendingJobsPopout;
export default PendingJobsPopout;
+174 -76
View File
@@ -3,7 +3,7 @@
font-family: "Product Sans";
src: url("./assets/fonts/Product Sans Regular.ttf");
}
.nft-main-container{
.nft-main-container {
max-width: 1200px;
}
/* Bold Weight */
@@ -11,57 +11,59 @@
font-family: "Product Sans";
src: url("./assets/fonts/Product Sans Bold.ttf");
}
.SENDER{
.SENDER {
margin-left: 60px !important;
background-color: azure;
}
.RECIPIENT{
.RECIPIENT {
margin-right: 60px !important;
background-color: #add8e6 !important;
}
.wallet-box{
.wallet-box {
background-color: aliceblue;
border-radius: 20px;
}
.bal-col1{
.bal-col1 {
width: 110px;
}
.bg-green{
.bg-green {
background-color: darkgreen;
}
.referral{
margin-bottom: 20px
.referral {
margin-bottom: 20px;
}
.task_action_panel{
font-family: sans; color: white;
.task_action_panel {
font-family: sans;
color: white;
font-weight: bolder;
font-size: 14px;
font-family: Circular, Helvetica Neue, Helvetica, Roboto, Arial, sans-serif;
padding: 0px 10px 5px 10px
padding: 0px 10px 5px 10px;
}
.heroSilderTitle{
.heroSilderTitle {
text-shadow: -1px 0 black, 0 1px black, 1px 0 black, 0 -1px black;
font-family: sans; color: white;
font-family: sans;
color: white;
font-family: Circular, Helvetica Neue, Helvetica, Roboto, Arial, sans-serif;
}
.back-dark1{
background-color: #193F5F;
.back-dark1 {
background-color: #193f5f;
min-width: 280px !important;
}
.job-action{
.job-action {
background-color: #4687ba;
height: 100px;
border-radius: 15px;
padding: 5px;
}
.msg_box{
.msg_box {
background-color: aliceblue;
margin: 5px;
padding: 5px;
border-radius: 15px;
}
.msg_header{
.msg_header {
background-color: white;
color: black;
font-weight: bold;
@@ -69,7 +71,7 @@
font-size: 14px;
font-family: Circular, Helvetica Neue, Helvetica, Roboto, Arial, sans-serif;
}
.siderCardDescription{
.siderCardDescription {
background-color: aliceblue;
padding: 5px;
border-radius: 5px;
@@ -78,7 +80,7 @@
font-size: 1.125rem;
line-height: 1.56;
}
.siderCardButton{
.siderCardButton {
margin-top: 10px;
width: 100%;
text-align: center;
@@ -91,33 +93,39 @@
padding: 10px;
}
.short_style{
.short_style {
background-color: transparent;
border-color: #a2d7f1;
border-width: 3px;
}
.lr{
.lr {
background-color: #e1cace;
}
.lg{
.lg {
background-color: #a7dca7;
}
.lb{
.lb {
background-color: #b3ccd7;
}
.ly{
.ly {
background-color: #eeee67;
}
.offer-slide-item{
background: rgb(2,0,36);
background: radial-gradient(circle, rgba(2,0,36,1) 0%, rgba(3,51,2,0.782125350140056) 0%, rgba(0,212,255,0.07904411764705888) 0%, rgba(153,182,201,1) 99%);
.offer-slide-item {
background: rgb(2, 0, 36);
background: radial-gradient(
circle,
rgba(2, 0, 36, 1) 0%,
rgba(3, 51, 2, 0.782125350140056) 0%,
rgba(0, 212, 255, 0.07904411764705888) 0%,
rgba(153, 182, 201, 1) 99%
);
margin: 5px;
border-radius: 15px;
padding: 15px;
height: 250px;
border-color: beige;
}
.banner-630-340{
.banner-630-340 {
width: 630px;
height: 340px;
background-color: aliceblue;
@@ -133,6 +141,7 @@
font-family: "Product Sans";
src: url("./assets/fonts/Product Sans Regular.ttf");
}
@tailwind base;
@tailwind components;
@tailwind utilities;
@@ -144,9 +153,8 @@
--toastify-color-success: #f539f8;
}
/* ===================== EXTRA ===================== */
.bottomMargin{
.bottomMargin {
margin-bottom: 15px;
}
/* TODO: =================================default================================ */
@@ -159,8 +167,13 @@ html {
font-family: "Product Sans";
}
.primary-gradient {
background: linear-gradient(134.38deg, #f539f8 0%, #284f64 43.55%, #1a3544 104.51%);
/* background-image: url("./assets/images/left-myft.jpg");
background: linear-gradient(
134.38deg,
#f539f8 0%,
#284f64 43.55%,
#1a3544 104.51%
);
/* background-image: url("./assets/images/left-myft.jpg");
background-repeat: no-repeat;
background-size: cover; */
}
@@ -199,7 +212,12 @@ html {
top: 0;
width: 100%;
height: 100%;
background: linear-gradient(134.38deg, #5356fb 0%, #c342f9 43.55%, #f539f8 104.51%);
background: linear-gradient(
134.38deg,
#5356fb 0%,
#c342f9 43.55%,
#f539f8 104.51%
);
opacity: 30%;
}
/* Chrome, Safari, Edge, Opera */
@@ -229,10 +247,20 @@ input[type="text"][dir="rtl"] {
scrollbar-width: none; /* Firefox */
}
.btn-gradient {
background: linear-gradient(134.38deg, #f539f8 0%, #c342f9 43.55%, #5356fb 104.51%);
background: linear-gradient(
134.38deg,
#f539f8 0%,
#c342f9 43.55%,
#5356fb 104.51%
);
}
.btn-gradient:hover {
background: linear-gradient(134.38deg, #5356fb 0%, #c342f9 43.55%, #f539f8 104.51%);
background: linear-gradient(
134.38deg,
#5356fb 0%,
#c342f9 43.55%,
#f539f8 104.51%
);
}
.text-26 {
font-size: 26px;
@@ -246,7 +274,12 @@ input[type="text"][dir="rtl"] {
}
.text-gradient {
background: linear-gradient(134.38deg, #f539f8 0%, #c342f9 43.55%, #5356fb 104.51%);
background: linear-gradient(
134.38deg,
#f539f8 0%,
#c342f9 43.55%,
#5356fb 104.51%
);
-webkit-background-clip: text;
-webkit-text-fill-color: transparent;
}
@@ -271,6 +304,12 @@ input[type="text"][dir="rtl"] {
transform: rotate(360deg);
}
}
.my-custom-bg-class{
background: url("./assets/images/wrench-page-notfound.jpg") center/cover;
position: relative;
}
/* TODO: =================================default end================================ */
/* TODO: =================================update password================================ */
.content-wrapper.thankyou-section {
@@ -305,6 +344,16 @@ input[type="text"][dir="rtl"] {
transform: scale(1);
width: 80%;
}
@media screen and (min-width: 25rem) {
.sidebar-logo.enter {
width: 65%;
}
.sidebar-logo.enter + span{
position: revert;
}
}
.sidebar-logo {
transform: scale(0);
width: 0;
@@ -437,7 +486,12 @@ input[type="text"][dir="rtl"] {
.home-page-wrapper .hero-slider .slick-slider .slick-dots li {
margin: 0;
}
.home-page-wrapper .hero-slider .slick-slider .slick-dots li.slick-active button {
.home-page-wrapper
.hero-slider
.slick-slider
.slick-dots
li.slick-active
button {
background: white;
}
.home-page-wrapper .hero-slider .slick-slider .slick-dots li button {
@@ -460,7 +514,12 @@ input[type="text"][dir="rtl"] {
top: 0;
width: 100%;
height: 100%;
background: linear-gradient(134.38deg, #5356fb 0%, #c342f9 43.55%, #f539f8 104.51%);
background: linear-gradient(
134.38deg,
#5356fb 0%,
#c342f9 43.55%,
#f539f8 104.51%
);
z-index: -1;
opacity: 30%;
}
@@ -476,8 +535,10 @@ input[type="text"][dir="rtl"] {
margin: 0 16px;
}
.transfer-field, .transfer-field:focus, .transfer-field:focus-within{
background: #FFF !important;
.transfer-field,
.transfer-field:focus,
.transfer-field:focus-within {
background: #fff !important;
border: none !important;
outline: none !important;
box-shadow: none !important;
@@ -583,12 +644,12 @@ input[type="text"][dir="rtl"] {
background-size: cover;
}
.content{
.content {
overflow: hidden;
}
.job-items{
.job-sub-menu{
.job-items {
.job-sub-menu {
background-color: yellow;
}
}
@@ -597,7 +658,7 @@ input[type="text"][dir="rtl"] {
top: 0;
right: -100%;
opacity: 0;
transition: all .5s;
transition: all 0.5s;
background-color: white;
}
@@ -612,7 +673,12 @@ input[type="text"][dir="rtl"] {
.notification-page .content-item .notifications {
@apply flex space-x-4 items-center;
}
.notification-page .content-item .notifications .notification-page .content-item .notifications {
.notification-page
.content-item
.notifications
.notification-page
.content-item
.notifications {
@apply mb-0;
}
.notification-setting-tab .notification-settings-items li:last-child {
@@ -621,7 +687,11 @@ input[type="text"][dir="rtl"] {
.faq-tab .accordion-items .accordion-item:first-child .accordion-title-bar {
padding-top: 0;
}
.faq-tab .accordion-items .accordion-item:last-child .accordion-body .accordion-body-content {
.faq-tab
.accordion-items
.accordion-item:last-child
.accordion-body
.accordion-body-content {
padding-bottom: 20px;
}
.faq-tab .accordion-title-bar {
@@ -665,14 +735,15 @@ input[type="text"][dir="rtl"] {
}
@media print {
body .modal-com{
body .modal-com {
height: 100%;
overflow: hidden;
}
.job-action-modal-body button, .message-modal-header button {
.job-action-modal-body button,
.message-modal-header button {
display: none;
}
.message-modal-wrapper .message-table{
.message-modal-wrapper .message-table {
height: 100%;
overflow-y: hidden;
}
@@ -839,30 +910,40 @@ TODO: Responsive ===========================
}
}
/* LoginPage */
.main-wrapper.login-wrapper{
background-image: url('./assets/images/login-dots.jpg');
.main-wrapper.login-wrapper {
background-image: url("./assets/images/login-dots.jpg");
background-attachment: fixed;
background-size: contain;
background-position-y: bottom;
background-repeat: no-repeat;
}
.layout-wrapper.login{
background: rgb(236,237,241);
background: linear-gradient(90deg, rgba(236,237,241,1) 0%, rgba(252,252,252,1) 31%, rgba(255,255,255,0.9416141456582633) 41%, rgba(255,255,255,0.9752275910364145) 61%, rgba(252,252,252,1) 71%, rgba(236,237,241,1) 100%);
.layout-wrapper.login {
background: rgb(236, 237, 241);
background: linear-gradient(
90deg,
rgba(236, 237, 241, 1) 0%,
rgba(252, 252, 252, 1) 31%,
rgba(255, 255, 255, 0.9416141456582633) 41%,
rgba(255, 255, 255, 0.9752275910364145) 61%,
rgba(252, 252, 252, 1) 71%,
rgba(236, 237, 241, 1) 100%
);
font-family: Circular, Helvetica Neue, Helvetica, Roboto, Arial, sans-serif;
font-weight: 400;
font-size: 1.125rem;
line-height: 1.56;
}
.content-wrapper.login{
--bg-color: 255,255,255;
background: linear-gradient(90deg, rgba(236,237,240,1) 0%, rgba(255,255,255,1) 50%, rgba(236,237,240,1) 100%);
.content-wrapper.login {
--bg-color: 255, 255, 255;
background: linear-gradient(
90deg,
rgba(236, 237, 240, 1) 0%,
rgba(255, 255, 255, 1) 50%,
rgba(236, 237, 240, 1) 100%
);
}
.content-wrapper select {
@@ -880,51 +961,68 @@ TODO: Responsive ===========================
}
/* Update table scrollbar */
.update-table::-webkit-scrollbar-track, .update-table > *::-webkit-scrollbar-track,
.update-table::-webkit-scrollbar-track,
.update-table > *::-webkit-scrollbar-track,
.market-pop::-webkit-scrollbar-track,
.wallet.coupon::-webkit-scrollbar-track{
.wallet.coupon::-webkit-scrollbar-track {
-webkit-box-shadow: inset 0 0 6px rgba(0, 0, 0, 0.1);
background-color: transparent;
border-radius: 10px;
}
.update-table::-webkit-scrollbar, .update-table > *::-webkit-scrollbar,
.update-table::-webkit-scrollbar,
.update-table > *::-webkit-scrollbar,
.market-pop::-webkit-scrollbar,
.wallet.coupon::-webkit-scrollbar {
width: 10px;
background-color: transparent;
}
.update-table::-webkit-scrollbar-thumb, .update-table > *::-webkit-scrollbar-thumb, .wallet.coupon::-webkit-scrollbar-thumb {
.update-table::-webkit-scrollbar-thumb,
.update-table > *::-webkit-scrollbar-thumb,
.wallet.coupon::-webkit-scrollbar-thumb {
border-radius: 10px;
border-top-right-radius: 50px;
border-bottom-right-radius: 50px;
background-color: #fff;
background: linear-gradient(134.38deg, #f539f8 0%, #c342f9 43.55%, #5356fb 104.51%);
background: linear-gradient(
134.38deg,
#f539f8 0%,
#c342f9 43.55%,
#5356fb 104.51%
);
}
.market-pop::-webkit-scrollbar-thumb {
border-radius: 100px;
/* background-color: #fafafa; */
/* background: linear-gradient(134.38deg, #f539f8 0%, #c342f9 43.55%, #5356fb 104.51%); */
background: rgb(236,237,241);
background: linear-gradient(90deg, rgba(236,237,241,1) 0%, rgba(252,252,252,1) 31%, rgba(255,255,255,0.9416141456582633) 41%, rgba(255,255,255,0.9752275910364145) 61%, rgba(252,252,252,1) 71%, rgba(236,237,241,1) 100%);
background: rgb(236, 237, 241);
background: linear-gradient(
90deg,
rgba(236, 237, 241, 1) 0%,
rgba(252, 252, 252, 1) 31%,
rgba(255, 255, 255, 0.9416141456582633) 41%,
rgba(255, 255, 255, 0.9752275910364145) 61%,
rgba(252, 252, 252, 1) 71%,
rgba(236, 237, 241, 1) 100%
);
}
.input-curve.lg{
.input-curve.lg {
border-radius: 35px !important;
}
.edit-popup{
.edit-popup {
top: 75px;
}
.job-popup{
.job-popup {
top: 55px;
}
.addJob-popup{
.addJob-popup {
top: 30px;
height: 55rem !important;
}
@@ -952,10 +1050,10 @@ TODO: Responsive ===========================
}
/* TO REMOVE SLIDER COMPONENT FROM CENTRALIZED */
.slider-left .slick-slider .slick-track{
.slider-left .slick-slider .slick-track {
margin: 0;
}
.assign-task-popup{
.assign-task-popup {
top: 75px;
}
}
+24 -1
View File
@@ -1,4 +1,4 @@
export default function formattedDate(dateString) {
export function formattedDate(dateString) {
const parts = dateString.split(" ");
const datePart = parts[0];
const timePart = parts[1];
@@ -15,3 +15,26 @@ export default function formattedDate(dateString) {
return new Date(year, month - 1, day, hour, minute);
}
export function formatDateString(inputDateString) {
// Parse the input date string
const parsedDate = new Date(inputDateString);
// Get day, month, year, and time components
const day = parsedDate.toLocaleDateString(undefined, { weekday: "long" });
const month = parsedDate.toLocaleDateString(undefined, { month: "short" });
const date = parsedDate.toLocaleDateString(undefined, { day: "numeric" });
const year = parsedDate.toLocaleDateString(undefined, { year: "numeric" });
// Get the time in 12-hour format with 'AM' or 'PM'
const hours = parsedDate.getHours();
const minutes = parsedDate.getMinutes();
const time = `${hours % 12 || 12}:${minutes.toString().padStart(2, "0")} ${
hours < 12 ? "AM" : "PM"
}`;
// Combine the components into the desired format
const formattedDate = `${day}, ${month} ${date} ${year} - ${time}`;
return formattedDate;
}
+2 -1
View File
@@ -1,12 +1,13 @@
import ClearCookies from "./ClearCookies";
import checkAndSetError from "./checkAndSetError";
import formattedDate from "./fomattedDate";
import { formatDateString, formattedDate } from "./fomattedDate";
import getTimeAgo from "./getTimeAgo";
import localImgLoad from "./localImgLoad";
export {
ClearCookies,
checkAndSetError,
formatDateString,
formattedDate,
getTimeAgo,
localImgLoad,
+24 -7
View File
@@ -2,7 +2,6 @@ import { useCallback, useEffect, useMemo, useState } from "react";
import { useDispatch, useSelector } from "react-redux";
import { Navigate, Outlet, useNavigate } from "react-router-dom";
import LoadingSpinner from "../components/Spinners/LoadingSpinner";
import formattedDate from "../lib/fomattedDate";
import usersService from "../services/UsersService";
import { commonHeadBanner } from "../store/CommonHeadBanner";
import { recentActivitiesData } from "../store/RecentActivitiesData";
@@ -10,6 +9,9 @@ import { updateUserDetails } from "../store/UserDetails";
import { updateJobs } from "../store/jobLists";
import { updateNotifications } from "../store/notifications";
import { updateUserJobList } from "../store/userJobList";
import { updateWalletDetails } from "../store/walletDetails";
import { formattedDate } from "../lib";
import { tableReload } from "../store/TableReloads";
const AuthRoute = ({ redirectPath = "/login", children }) => {
const apiCall = useMemo(() => new usersService(), []);
@@ -19,13 +21,13 @@ const AuthRoute = ({ redirectPath = "/login", children }) => {
const [loadProfileDetails, setLoadProfileDetails] = useState([]);
const navigate = useNavigate();
const { jobListTable } = useSelector((state) => state.tableReload);
const { jobListTable, walletTable } = useSelector((state) => state.tableReload);
const {
userDetails: { username, uid },
userDetails: { username, uid, session },
} = useSelector((state) => state?.userDetails); // CHECKS IF USER Details are avaliable, to determine if user is active
let loggedIn = username && uid ? true : false; // variable to determine if user is logged in
let loggedIn = username && session && uid ? true : false; // variable to determine if user is logged in
useEffect(() => {
//Removing Data stored at localStorage after session expires
@@ -149,7 +151,7 @@ const AuthRoute = ({ redirectPath = "/login", children }) => {
// sort: _sorted,
// header: _header,
// },
data: _sorted
data: _sorted,
})
);
})
@@ -177,6 +179,21 @@ const AuthRoute = ({ redirectPath = "/login", children }) => {
getMyJobList();
}, [jobListTable]);
useEffect(() => {
const getMyWalletList = async () => {
dispatch(updateWalletDetails({ loading: true, data:[] }));
try {
const res = await apiCall.getUserWallets();
console.log("wallet - >", res.data);
dispatch(updateWalletDetails({ loading: false, data:res.data?.result_list }));
} catch (error) {
dispatch(updateWalletDetails({ loading: false, data:[] }));
console.log("Error getting mode");
}
};
getMyWalletList();
}, [walletTable]);
useEffect(() => {
// Getting market data
const getMarketActiveJobList = async () => {
@@ -188,7 +205,7 @@ const AuthRoute = ({ redirectPath = "/login", children }) => {
}
};
getMarketActiveJobList();
}, [apiCall, dispatch]);
}, [apiCall, dispatch, jobListTable]);
//FUNCTION TO GET COMMON HEAD DATA
useEffect(() => {
@@ -211,7 +228,7 @@ const AuthRoute = ({ redirectPath = "/login", children }) => {
.getRecentActivitiedData()
.then((res) => {
// debugger;
if (res.data?.internal_return < 0) {
if (res?.data?.internal_return < 0) {
return;
}
dispatch(recentActivitiesData(res.data));
+98 -92
View File
@@ -1,107 +1,113 @@
import React from "react";
import Axios from "axios";
class SiteService {
constructor() {
console.log("Er are here anyway");
}
// Blog Data {Get}
blogData() {
return this.getAuxEnd("/blogdata", null);
}
constructor() {
console.log("Er are here anyway");
}
// Blog Data {Get}
blogData() {
return this.getAuxEnd("/blogdata", null);
}
// Country Data {GET}
countryData() {
return this.getAuxEnd("/country", null);
}
// Country Data {GET}
countryData() {
return this.getAuxEnd("/country", null);
}
// Contact Data{POST}
contactData() {
return this.postAuxEnd("/contact", null)
}
// Contact Data{POST}
contactData() {
return this.postAuxEnd("/contact", null);
}
faqData() {
return this.getAuxEnd("/faq", null);
}
faqData() {
return this.getAuxEnd("/faq", null);
}
priceData() {
return this.getAuxEnd("/pricing", null);
}
priceData() {
return this.getAuxEnd("/pricing", null);
}
addFamily(reqData) {
return this.postAuxEnd('/familyadd', reqData)
}
addFamily(reqData) {
var postData = {
uid: localStorage.getItem("uid"),
member_id: localStorage.getItem("member_id"),
sessionid: localStorage.getItem("session_token"),
action: 22015,
...reqData,
};
return this.postAuxEnd("/familyadd", postData);
}
familyListings(reqData) {
var postData = {
uid: localStorage.getItem("uid"),
member_id: localStorage.getItem("member_id"),
sessionid: localStorage.getItem("session_token"),
...reqData
};
return this.postAuxEnd('/familylist', postData)
}
familyListings(reqData) {
var postData = {
uid: localStorage.getItem("uid"),
member_id: localStorage.getItem("member_id"),
sessionid: localStorage.getItem("session_token"),
...reqData,
};
return this.postAuxEnd("/familylist", postData);
}
assignJobTask(reqData) {
var postData = {
uid: localStorage.getItem("uid"),
member_id: localStorage.getItem("member_id"),
sessionid: localStorage.getItem("session_token"),
...reqData
};
return this.postAuxEnd('/assigntask', postData)
}
assignJobTask(reqData) {
var postData = {
uid: localStorage.getItem("uid"),
member_id: localStorage.getItem("member_id"),
sessionid: localStorage.getItem("session_token"),
...reqData,
};
return this.postAuxEnd("/assigntask", postData);
}
//---------------------------------------- -----
//---------------------------------------- -----
// Unified call below
//---------------------------------------- -----
//---------------------------------------- -----
getAuxEnd(uri, reqData) {
const endPoint = process.env.REACT_APP_AUX_ENDPOINT + uri;
return Axios.get(endPoint)
.then((response) => {
// console.log(response);
// res = response;
// console.log("~~~~~~~ Toks2 GET ~~~~~~~~");
return response;
})
.catch((error) => {
if (error.response) {
//response status is an error code
console.log(error.response.status);
} else if (error.request) {
//response not received though the request was sent
console.log(error.request);
} else {
//an error occurred when setting up the request
console.log(error.message);
}
});
}
//---------------------------------------- -----
//---------------------------------------- -----
// Unified call below
//---------------------------------------- -----
//---------------------------------------- -----
getAuxEnd(uri, reqData) {
const endPoint = process.env.REACT_APP_AUX_ENDPOINT + uri;
return Axios.get(endPoint)
.then((response) => {
// console.log(response);
// res = response;
// console.log("~~~~~~~ Toks2 GET ~~~~~~~~");
return response;
})
.catch((error) => {
if (error.response) {
//response status is an error code
console.log(error.response.status);
} else if (error.request) {
//response not received though the request was sent
console.log(error.request);
} else {
//an error occurred when setting up the request
console.log(error.message);
}
});
}
postAuxEnd(uri, reqData) {
const endPoint = process.env.REACT_APP_AUX_ENDPOINT + uri;
return Axios.post(endPoint, reqData)
.then((response) => {
console.log(response);
// res = response;
console.log("~~~~~~~ Toks2 POST ~~~~~~~~");
return response;
})
.catch((error) => {
if (error.response) {
//response status is an error code
console.log(error.response.status);
} else if (error.request) {
//response not received though the request was sent
console.log(error.request);
} else {
//an error occurred when setting up the request
console.log(error.message);
}
});
}
postAuxEnd(uri, reqData) {
const endPoint = process.env.REACT_APP_AUX_ENDPOINT + uri;
return Axios.post(endPoint, reqData)
.then((response) => {
console.log(response);
// res = response;
console.log("~~~~~~~ Toks2 POST ~~~~~~~~");
return response;
})
.catch((error) => {
if (error.response) {
//response status is an error code
console.log(error.response.status);
} else if (error.request) {
//response not received though the request was sent
console.log(error.request);
} else {
//an error occurred when setting up the request
console.log(error.message);
}
});
}
}
export default SiteService;
+88 -1
View File
@@ -30,6 +30,16 @@ class usersService {
return this.postAuxEnd("/dashdata", postData);
}
getRecentActivities(){
var postData = {
uid: localStorage.getItem("uid"),
member_id: localStorage.getItem("member_id"),
sessionid: localStorage.getItem("session_token"),
action: 11202
};
return this.postAuxEnd("/recentactivities", postData);
}
logInUser(reqData) {
localStorage.setItem("session_token", ``);
return this.postAuxEnd("/userlogin", reqData);
@@ -358,6 +368,7 @@ class usersService {
page: 0,
offset: 0,
limit: 100,
allstatus: 0
};
return this.postAuxEnd("/activetaskslist", postData);
}
@@ -485,6 +496,19 @@ class usersService {
return this.postAuxEnd("/purchasehx", postData);
}
// API FUNCTION TO GET FAMILY REWARD HISTORY
getFamilyRewardHx() {
var postData = {
uid: localStorage.getItem("uid"),
member_id: localStorage.getItem("member_id"),
sessionid: localStorage.getItem("session_token"),
offset: 1,
limit: 20,
action: 22011,
};
return this.postAuxEnd("/familyrewardhx", postData);
}
// API FUNCTION TO GET PAYMENT HISTORY
getPaymentHx() {
var postData = {
@@ -567,13 +591,14 @@ class usersService {
return this.postAuxEnd("/familyadd", postData);
}
getFamilyUpdate() {
getFamilyUpdate(reqdata) {
var postData = {
uuid: localStorage.getItem("uid"),
member_id: localStorage.getItem("member_id"),
sessionid: localStorage.getItem("session_token"),
page: 0,
limit: 100,
...reqdata
};
return this.postAuxEnd("/familyupdate", postData);
}
@@ -1030,6 +1055,17 @@ class usersService {
return this.postAuxEnd("/payremcard", postData);
}
getHomeRecent() {
var postData = {
uid: localStorage.getItem("uid"),
member_id: localStorage.getItem("member_id"),
sessionid: localStorage.getItem("session_token"),
limit: 30,
offset: 0,
action: 11202,
};
return this.postAuxEnd("/recentactivities", postData);
}
// FUNCTION TO GET SINGLE BLOG ITEM
getSingleBlogData(reqData) {
var postData = {
@@ -1042,6 +1078,53 @@ class usersService {
return this.postAuxEnd("/blogdata", postData);
}
// FUNCTION TO CANCEL TASK OR SEND REMINDER BY FAMILY MEMBER
suggestStatus(reqData) {
var postData = {
uid: localStorage.getItem("uid"),
member_id: localStorage.getItem("member_id"),
sessionid: localStorage.getItem("session_token"),
action: 22026,
...reqData,
};
return this.postAuxEnd("/suggeststatus", postData);
}
// FUNCTION TO GET FAMILY WALLET
getFamilyWallet(reqData) {
var postData = {
uid: localStorage.getItem("uid"),
member_id: localStorage.getItem("member_id"),
sessionid: localStorage.getItem("session_token"),
action: 22012,
...reqData,
};
return this.postAuxEnd("/familywallet", postData);
}
// FUNCTION TO START FAMILY TRANSFER
familyTransferStart(reqData) {
var postData = {
uid: localStorage.getItem("uid"),
member_id: localStorage.getItem("member_id"),
sessionid: localStorage.getItem("session_token"),
...reqData,
};
return this.postAuxEnd("/familytransferstart", postData);
}
// FUNCTION TO PERFORM FAMILY TRANSFER
familyTransfer(reqData) {
var postData = {
uid: localStorage.getItem("uid"),
member_id: localStorage.getItem("member_id"),
sessionid: localStorage.getItem("session_token"),
...reqData,
};
return this.postAuxEnd("/familytransfer", postData);
}
/*
- 20:27:30.118 FLOG_MAX [757411]: REQ_STRING(username)
- 20:27:30.118 FLOG_MAX [757411]: REQ_STRING(password)
@@ -1154,6 +1237,10 @@ class usersService {
console.log(response);
// res = response;
console.log("~~~~~~~ Toks2 POST ~~~~~~~~");
if(response.data.internal_return == '-9999'){
localStorage.clear()
window.location.href=`/login?sessionExpired=true`
}
return response;
})
.catch((error) => {
+4 -4
View File
@@ -1,15 +1,15 @@
import { createSlice } from "@reduxjs/toolkit";
const initialState = {
userDetails: {}
userDetails: {},
};
export const userSlice = createSlice({
name: "userDetails",
initialState,
reducers: {
updateUserDetails: (state,action) => {
state.userDetails = {...action.payload}
updateUserDetails: (state, action) => {
state.userDetails = { ...action.payload };
},
},
});
@@ -17,4 +17,4 @@ export const userSlice = createSlice({
// Action creators are generated for each case reducer function
export const { updateUserDetails } = userSlice.actions;
export default userSlice.reducer;
export default userSlice.reducer;
+2
View File
@@ -7,6 +7,7 @@ import userDetailReducer from "./UserDetails";
import jobReducer from "./jobLists";
import notificationsReducer from "./notifications";
import userJobListReducer from "./userJobList";
import walletDetails from "./walletDetails";
export default configureStore({
reducer: {
@@ -17,5 +18,6 @@ export default configureStore({
userJobList: userJobListReducer,
commonHeadBanner: commonHeadBannerReducer,
notifications: notificationsReducer,
walletDetails: walletDetails
},
});
+21
View File
@@ -0,0 +1,21 @@
import { createSlice } from "@reduxjs/toolkit";
const initialState = {
walletDetails: {
loading: true,
data: []
},
};
export const walletSlice = createSlice({
name: "walletDetails",
initialState,
reducers: {
updateWalletDetails: (state, action) => {
state.walletDetails = { ...action.payload };
},
},
});
export const { updateWalletDetails } = walletSlice.actions;
export default walletSlice.reducer;

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