Compare commits

..

268 Commits

Author SHA1 Message Date
victorAnumudu 1ce154cc06 cookie bug fixing 2023-07-05 17:48:40 +01:00
victorAnumudu 67eb142ae2 cookie bug fixing 2023-07-05 17:47:05 +01:00
ameye e784d84699 Merge branch 'cookie-value-reading' of WrenchBoard/Users-Wrench into master 2023-07-05 16:10:01 +00:00
CHIEFSOFT\ameye cd6d540c4b Family Activer Job 2023-07-05 11:12:08 -04:00
victorAnumudu b2beaa7c2b changed how the value of login type is read from the cookie 2023-07-05 15:40:05 +01:00
ameye b0db896f6b Merge branch 'resources-page' of WrenchBoard/Users-Wrench into master 2023-07-05 14:15:03 +00:00
CHIEFSOFT\ameye fafea8b1ad Task banners 2023-07-05 10:13:19 -04:00
Ebube bbb099d51f Modified status for family waitlist 2023-07-05 14:51:24 +01:00
Ebube bfc0521e54 Merge branch 'master' of https://gitlab.chiefsoft.net/WrenchBoard/Users-Wrench into resources-page 2023-07-05 14:45:51 +01:00
Ebube 6870c145d7 Added banner name to suggested task payload 2023-07-05 14:45:29 +01:00
ameye c18373f9db Merge branch 'resources-page' of WrenchBoard/Users-Wrench into master 2023-07-05 13:34:21 +00:00
CHIEFSOFT\ameye 24ac0daf29 status_text 2023-07-05 08:58:08 -04:00
Ebube 4dd58b16d8 Merge branch 'master' of https://gitlab.chiefsoft.net/WrenchBoard/Users-Wrench into resources-page 2023-07-05 13:52:39 +01:00
Ebube cc66ebbbde Family waitlist complete 2023-07-05 13:51:10 +01:00
CHIEFSOFT\ameye 3d61b3259d Status label 2023-07-05 08:32:00 -04:00
CHIEFSOFT\ameye 40b614356f active task spread 2023-07-05 08:17:57 -04:00
ameye dbba6c4014 Merge branch 'resources-page' of WrenchBoard/Users-Wrench into master 2023-07-05 11:56:34 +00:00
Ebube ae93de5f25 Merge branch 'master' of https://gitlab.chiefsoft.net/WrenchBoard/Users-Wrench into resources-page 2023-07-05 12:53:41 +01:00
Ebube 4904f1d398 Added waiting component and rendered the offerlist 2023-07-05 12:53:19 +01:00
ameye 880b084547 Merge branch 'cookie-bug-fix' of WrenchBoard/Users-Wrench into master 2023-07-05 10:26:39 +00:00
victorAnumudu c807a2657b adjusted cookie to check for login type before components mounts 2023-07-05 10:24:01 +01:00
ameye 37a0ffed27 Merge branch 'cookies-family-dash-changes' of WrenchBoard/Users-Wrench into master 2023-07-05 08:17:28 +00:00
victorAnumudu 067d9ca5d1 suggest image position adjusted 2023-07-05 08:50:56 +01:00
victorAnumudu 6e3061b9db cookie bug fix and family dash style changes made 2023-07-05 04:39:39 +01:00
CHIEFSOFT\ameye 4f9ca918c9 more graphics 2023-07-04 18:16:32 -04:00
ameye 12f805f0a7 Merge branch 'login-page-box' of WrenchBoard/Users-Wrench into master 2023-07-04 19:43:14 +00:00
CHIEFSOFT\ameye 175ac7e2b6 coffee added 2023-07-04 15:25:05 -04:00
victorAnumudu bb0796d1e6 login input text changed and padding on login page reduced 2023-07-04 19:22:36 +01:00
CHIEFSOFT\ameye c0d01e05fe defualt 2023-07-04 13:51:38 -04:00
CHIEFSOFT\ameye 38a888a8a1 clean dishes 2023-07-04 13:48:55 -04:00
CHIEFSOFT\ameye 8b0aedcbf2 wash cloth 2023-07-04 13:29:13 -04:00
CHIEFSOFT\ameye dfdccc0287 Clean Room 2023-07-04 13:10:46 -04:00
CHIEFSOFT\ameye 12512cef97 wash cars image 2023-07-04 13:00:28 -04:00
ameye d5d0e64070 Merge branch 'resources-page' of WrenchBoard/Users-Wrench into master 2023-07-04 15:27:51 +00:00
Ebube d55f7ce175 Correct layout and button 2023-07-04 15:45:19 +01:00
ameye f81b687f9f Merge branch 'resources-page' of WrenchBoard/Users-Wrench into master 2023-07-04 14:04:17 +00:00
Ebube 3de1ef71c9 Fixed the empty params and fixed broken offer link 2023-07-04 15:00:00 +01:00
ameye fe7d23837c Merge branch 'enable-job-bg-color' of WrenchBoard/Users-Wrench into master 2023-07-04 13:07:44 +00:00
victorAnumudu bdc000fb8e Enable job background color changed to alice blue 2023-07-04 13:57:12 +01:00
ameye 01f8c7b49b Merge branch 'family-dashboard' of WrenchBoard/Users-Wrench into master 2023-07-04 12:26:17 +00:00
ameye 05384fe001 Merge branch 'family-task-page' of WrenchBoard/Users-Wrench into master 2023-07-04 12:26:09 +00:00
ameye 66a43ac636 Merge branch 'resources-page' of WrenchBoard/Users-Wrench into master 2023-07-04 12:25:36 +00:00
victorAnumudu 0ff4e5cf1e btn color 2023-07-04 10:56:53 +01:00
Ebube 6c0a107919 . 2023-07-04 10:39:00 +01:00
Ebube 5c8d1e72dd . 2023-07-04 10:38:35 +01:00
Ebube 4b11d7ec77 . 2023-07-04 10:38:22 +01:00
victorAnumudu baf2ca155f common header removed for family account on my task page 2023-07-04 10:33:37 +01:00
CHIEFSOFT\ameye fffc51d77a defualt added 2023-07-04 03:23:40 -04:00
ameye c622d73058 Merge branch 'family-suggest-list' of WrenchBoard/Users-Wrench into master 2023-07-04 07:02:18 +00:00
victorAnumudu d0237872ad added family suggest list API 2023-07-04 07:32:40 +01:00
ameye 4403c8650e Merge branch 'resources-page' of WrenchBoard/Users-Wrench into master 2023-07-04 04:29:04 +00:00
Ebube 2afffb1dc9 Added Popup for suggest task 2023-07-04 03:37:56 +01:00
ameye 3a72ebc7a0 Merge branch 'resources-page' of WrenchBoard/Users-Wrench into master 2023-07-03 19:26:19 +00:00
Ebube ecfef0d6e1 modified validation rules 2023-07-03 20:13:45 +01:00
Ebube 7abf4fb7f7 Merge branch 'master' of https://gitlab.chiefsoft.net/WrenchBoard/Users-Wrench into resources-page 2023-07-03 19:40:55 +01:00
ameye 6c601a605a Merge branch 'input-error-changed' of WrenchBoard/Users-Wrench into master 2023-07-03 18:38:30 +00:00
Ebube 4480e2ebd6 removed validation 2023-07-03 19:34:38 +01:00
victorAnumudu 9f11e8b415 input error position changed 2023-07-03 19:32:18 +01:00
CHIEFSOFT\ameye a1d74b773d family pictures 2023-07-02 19:30:48 -04:00
ameye 7969199584 Merge branch 'blog-count' of WrenchBoard/Users-Wrench into master 2023-07-02 21:44:55 +00:00
victorAnumudu 9dd8f49ff8 blog count implemented 2023-07-02 22:12:28 +01:00
ameye fa5e9b8107 Merge branch 'resources-page' of WrenchBoard/Users-Wrench into master 2023-07-02 16:56:36 +00:00
Ebube 3e7ab11e62 Merge branch 'master' of https://gitlab.chiefsoft.net/WrenchBoard/Users-Wrench into resources-page 2023-07-02 17:40:38 +01:00
Ebube 6f259ad8ad . 2023-07-02 16:24:49 +01:00
ameye a31a20652f Merge branch 'resources-blog-tab' of WrenchBoard/Users-Wrench into master 2023-07-02 11:52:54 +00:00
victorAnumudu e3a5952675 made blog component to show first when resources link is hit 2023-07-02 08:10:10 +01:00
ameye f48297c5c0 Merge branch 'resources-page' of WrenchBoard/Users-Wrench into master 2023-07-02 02:07:31 +00:00
Ebube 23e5a9aaa4 Merge branch 'master' of https://gitlab.chiefsoft.net/WrenchBoard/Users-Wrench into resources-page 2023-07-02 03:03:08 +01:00
Ebube 02d90ebb14 Family market added 2023-07-02 03:02:29 +01:00
ameye 38b979a2e9 Merge branch 'resources-page' of WrenchBoard/Users-Wrench into master 2023-07-02 00:49:02 +00:00
Ebube 93d9afa417 Moved Offer Table to Dash 2023-07-02 01:34:27 +01:00
ameye 1e2219fb00 Merge branch 'banner-feedback' of WrenchBoard/Users-Wrench into master 2023-07-01 23:07:43 +00:00
victorAnumudu 560897f6f9 banner feedback fixed 2023-07-01 22:29:28 +01:00
ameye 630a029c6e Merge branch 'radio-btn-fix' of WrenchBoard/Users-Wrench into master 2023-07-01 20:26:41 +00:00
ameye 6e2fdc46d1 Merge branch 'add-task-btn-position' of WrenchBoard/Users-Wrench into master 2023-07-01 20:26:37 +00:00
victorAnumudu d3b2cddd6c radio btn size fixed 2023-07-01 21:24:25 +01:00
CHIEFSOFT\ameye f6bd70fdca Change text 2023-07-01 16:17:48 -04:00
victorAnumudu de3bfa2541 add task btn position changed 2023-07-01 19:36:26 +01:00
CHIEFSOFT\ameye cd60831f76 Blog Respurces 2023-07-01 12:44:31 -04:00
ameye 71152f7a05 Merge branch 'manage-family-active-task' of WrenchBoard/Users-Wrench into master 2023-07-01 15:28:38 +00:00
victorAnumudu 8cbdb1b8a6 manage family active task forward and back btn fixed 2023-07-01 16:25:34 +01:00
ameye abbf60ad48 Merge branch 'resources-page' of WrenchBoard/Users-Wrench into master 2023-07-01 10:51:21 +00:00
Ebube c956befed9 Merge branch 'master' of https://gitlab.chiefsoft.net/WrenchBoard/Users-Wrench into resources-page 2023-07-01 10:38:29 +01:00
Ebube a5dbeaecbf Fixed Offer Popup 2023-07-01 10:37:59 +01:00
CHIEFSOFT\ameye 0b0b563dda parennt waiting 2023-07-01 00:22:08 -04:00
CHIEFSOFT\ameye f8a3e42fe6 Kids waiting 2023-06-30 23:41:09 -04:00
CHIEFSOFT\ameye ced88fa497 no task image 2023-06-30 21:19:59 -04:00
ameye 89e2527ba6 Merge branch 'assign-task-bug-fix' of WrenchBoard/Users-Wrench into master 2023-07-01 00:06:19 +00:00
victorAnumudu 9e6b59624f Delivery detail bug fixed 2023-07-01 01:01:37 +01:00
ameye 1b6b4f17b0 Merge branch 'assign-task-revamp' of WrenchBoard/Users-Wrench into master 2023-06-30 23:12:55 +00:00
victorAnumudu 8a9ec35994 Assign family task API consumed 2023-07-01 00:06:22 +01:00
tokslaw fa2102eb61 Merge branch 'resources-page' of WrenchBoard/Users-Wrench into master 2023-06-30 18:21:38 +00:00
tokslaw bf73461c64 Merge branch 'price-fix' of WrenchBoard/Users-Wrench into master 2023-06-30 18:21:25 +00:00
victorAnumudu 5ffa6eb691 price fix 2023-06-30 16:01:11 +01:00
Ebube 5a0d8aebdb 1280 width fixed 2023-06-30 13:48:52 +01:00
Ebube e8ed10ddbf Added link to Family Market 2023-06-30 13:28:44 +01:00
ameye cca423a41c Merge branch 'family-assign-task' of WrenchBoard/Users-Wrench into master 2023-06-30 11:08:57 +00:00
victorAnumudu 6a6900a62b family assign task popout added 2023-06-30 11:57:47 +01:00
ameye c6b4fcc43d Merge branch 'delete-edit-icon' of WrenchBoard/Users-Wrench into master 2023-06-29 18:25:39 +00:00
ameye 29510c3b85 Merge branch 'resources-page' of WrenchBoard/Users-Wrench into master 2023-06-29 18:25:34 +00:00
victorAnumudu 1d8e54c57d delete and edit icon seperator width reduced 2023-06-29 13:56:14 +01:00
victorAnumudu 426599dd1f delete and edit icons added 2023-06-29 13:52:33 +01:00
Ebube c8d6d3d7d4 Removed Bg Img 2023-06-29 13:39:07 +01:00
Ebube 593ea74388 Merge branch 'master' of https://gitlab.chiefsoft.net/WrenchBoard/Users-Wrench into resources-page 2023-06-29 13:37:04 +01:00
Ebube d81b9970b3 Improved folder structure for resources and clean up 2023-06-29 13:18:31 +01:00
ameye 777ff54220 Merge branch 'resources-page' of WrenchBoard/Users-Wrench into master 2023-06-29 10:37:20 +00:00
Ebube 382a266b9a Resource Page Data Mapping 2023-06-29 09:09:30 +01:00
ameye 2d80dd9450 Merge branch 'no-interest-table-hidden' of WrenchBoard/Users-Wrench into master 2023-06-28 15:56:41 +00:00
victorAnumudu 0eaa72a5ec made others interest table hidden if its empty 2023-06-28 16:42:16 +01:00
CHIEFSOFT\ameye a2e2df867d resource props 2023-06-28 10:41:08 -04:00
ameye 10d4e169d3 Merge branch 'manage-active-job-delivery-date' of WrenchBoard/Users-Wrench into master 2023-06-28 13:38:48 +00:00
Ebube 79ed578483 Merge branch 'master' of https://gitlab.chiefsoft.net/WrenchBoard/Users-Wrench into manage-active-job-delivery-date 2023-06-28 14:10:42 +01:00
Ebube 25440a3c06 fixed minor bug 2023-06-28 14:10:14 +01:00
ameye 5f4c40a318 Merge branch 'manage-active-job-delivery-date' of WrenchBoard/Users-Wrench into master 2023-06-28 01:06:13 +00:00
Ebube ff7e8ea1ab Merge branch 'master' of https://gitlab.chiefsoft.net/WrenchBoard/Users-Wrench into manage-active-job-delivery-date 2023-06-28 02:02:27 +01:00
Ebube cba14f4265 Added error borders 2023-06-28 02:01:53 +01:00
ameye 9c342f87f7 Merge branch 'manage-active-job-delivery-date' of WrenchBoard/Users-Wrench into master 2023-06-27 21:01:38 +00:00
Ebube 498966dd23 Fixed bugs in market popup and made changes to Add/edit 2023-06-27 21:57:39 +01:00
ameye b282295924 Merge branch 'manage-active-job-delivery-date' of WrenchBoard/Users-Wrench into master 2023-06-27 18:21:46 +00:00
Ebube 7222a4d750 Merge branch 'master' of https://gitlab.chiefsoft.net/WrenchBoard/Users-Wrench into manage-active-job-delivery-date 2023-06-27 17:12:02 +01:00
Ebube 271f5635a0 Fixed Api Parameter, Banner Button Style and Slider header 2023-06-27 17:11:30 +01:00
ameye 86c4283507 Merge branch 'manage-active-job-delivery-date' of WrenchBoard/Users-Wrench into master 2023-06-27 14:19:36 +00:00
Ebube 5e5d953769 Add Job button Fixed 2023-06-27 15:15:41 +01:00
ameye aa7065c5b4 Merge branch 'manage-active-job-delivery-date' of WrenchBoard/Users-Wrench into master 2023-06-27 14:10:27 +00:00
Ebube 29fee11ec3 Merge branch 'master' of https://gitlab.chiefsoft.net/WrenchBoard/Users-Wrench into manage-active-job-delivery-date 2023-06-27 13:49:24 +01:00
Ebube 264d7b8501 . 2023-06-27 13:48:54 +01:00
ameye 8f90bcdf10 Merge branch 'manage-active-job-delivery-date' of WrenchBoard/Users-Wrench into master 2023-06-27 10:52:36 +00:00
Ebube 4b897cb3a9 . 2023-06-27 10:06:51 +01:00
ameye 0977650bf4 Merge branch 'faq-display' of WrenchBoard/Users-Wrench into master 2023-06-26 18:07:41 +00:00
Ebube 1f76dd0db8 Merge branch 'master' of https://gitlab.chiefsoft.net/WrenchBoard/Users-Wrench into faq-display 2023-06-26 19:01:49 +01:00
Ebube f772cf0a68 . 2023-06-26 18:57:41 +01:00
CHIEFSOFT\ameye c4c5c7967e Resource Starter 2023-06-26 13:36:43 -04:00
Ebube 45ecec24ac Merge branch 'master' of https://gitlab.chiefsoft.net/WrenchBoard/Users-Wrench into faq-display 2023-06-26 17:48:25 +01:00
Ebube 16fd35df83 . 2023-06-26 17:48:00 +01:00
CHIEFSOFT\ameye 3f6a391d30 witing Jobs 2023-06-26 09:23:28 -04:00
CHIEFSOFT\ameye 5eb64f49c0 text style 2023-06-26 07:50:49 -04:00
CHIEFSOFT\ameye 1761150bd3 My wait jobs 2023-06-26 07:44:03 -04:00
ameye a4b15dd06e Merge branch 'faq-display' of WrenchBoard/Users-Wrench into master 2023-06-25 21:38:00 +00:00
Ebube 1a15410e4c . 2023-06-25 20:07:35 +01:00
Ebube da1133ed43 Merge branch 'master' of https://gitlab.chiefsoft.net/WrenchBoard/Users-Wrench into faq-display 2023-06-25 20:00:49 +01:00
Ebube 23605bc358 . 2023-06-25 19:58:54 +01:00
ameye 2092682be6 Merge branch 'faq-display' of WrenchBoard/Users-Wrench into master 2023-06-25 11:19:58 +00:00
Ebube 0e270d8efa . 2023-06-25 07:09:17 +01:00
CHIEFSOFT\ameye 3bb4fe6a02 banner waiting 2023-06-25 00:01:44 -04:00
CHIEFSOFT\ameye fa728d3879 Waiting Jobs 2023-06-24 15:43:59 -04:00
ameye a9ce76123b Merge branch 'privacy-policy' of WrenchBoard/Users-Wrench into master 2023-06-24 11:17:42 +00:00
ameye 3ac1be9b89 Merge branch 'AddJob-Link-Fix' of WrenchBoard/Users-Wrench into master 2023-06-24 11:17:37 +00:00
ameye e4be117c90 Merge branch 'offer-interest-msg-list' of WrenchBoard/Users-Wrench into master 2023-06-24 11:17:11 +00:00
Ebube 58834cd7ca privacy policy 2023-06-24 08:52:33 +01:00
Ebube 59945c28e4 . 2023-06-24 07:23:39 +01:00
victorAnumudu 0feaf42f49 offer interest message list API added 2023-06-23 23:47:46 +01:00
ameye 5a623dd147 Merge branch 'AddJob-Link-Fix' of WrenchBoard/Users-Wrench into master 2023-06-23 21:52:50 +00:00
ameye 9265fde343 Merge branch 'offer-interest-msg-sending' of WrenchBoard/Users-Wrench into master 2023-06-23 21:52:39 +00:00
Ebube 452bb73bef AddJob_Link Fix 2023-06-23 21:42:26 +01:00
victorAnumudu cfec230ce3 Added API for sending offer interest message 2023-06-23 20:51:37 +01:00
ameye 87430f530d Merge branch 'manage-offer-reformat' of WrenchBoard/Users-Wrench into master 2023-06-22 23:48:41 +00:00
victorAnumudu 52cb0cb2da manage offer reformatted 2023-06-22 20:41:17 +01:00
ameye a19df45997 Merge branch 'terms-and-conditions' of WrenchBoard/Users-Wrench into master 2023-06-22 19:36:07 +00:00
Ebube 7868e7d689 Done 2023-06-22 18:34:33 +01:00
ameye 08f1ae1a9f Merge branch 'wallet-bal-format' of WrenchBoard/Users-Wrench into master 2023-06-22 14:17:09 +00:00
victorAnumudu c4cc27490b wallet balance formatted 2023-06-22 15:14:43 +01:00
ameye 90b609d457 Merge branch 'new-price-formatter' of WrenchBoard/Users-Wrench into master 2023-06-22 13:40:47 +00:00
victorAnumudu a6c6c36fbc price formatter style changed 2023-06-22 14:24:56 +01:00
ameye 03b79f0e0c Merge branch 'Cookies-addition-for-family' of WrenchBoard/Users-Wrench into master 2023-06-22 10:43:50 +00:00
Ebube 0af52df1d2 Merge branch 'master' of https://gitlab.chiefsoft.net/WrenchBoard/Users-Wrench into Cookies-addition-for-family 2023-06-22 11:42:20 +01:00
ameye 9e1a68f81a Merge branch 'JobList-Categories' of WrenchBoard/Users-Wrench into master 2023-06-22 10:37:26 +00:00
Ebube 12e4b7824a Added Cookies for family 2023-06-22 10:01:39 +01:00
Ebube 5769332e74 . 2023-06-22 08:42:42 +01:00
ameye bab0296f4f Merge branch 'offer-interest-format' of WrenchBoard/Users-Wrench into master 2023-06-21 15:06:12 +00:00
victorAnumudu d457550d58 offer interest page formatted 2023-06-21 15:58:40 +01:00
ameye f106e17ce6 Merge branch 'market-toggle-position' of WrenchBoard/Users-Wrench into master 2023-06-21 10:52:13 +00:00
victorAnumudu 8f1d2b6584 market category toggle position changed 2023-06-21 10:02:47 +01:00
ameye 3a3503447a Merge branch 'market-list-toggle' of WrenchBoard/Users-Wrench into master 2023-06-20 22:34:26 +00:00
ameye e46d2bea8d Merge branch 'JobList-Categories' of WrenchBoard/Users-Wrench into master 2023-06-20 22:34:15 +00:00
victorAnumudu b05c519571 adjusted market toggle position 2023-06-20 22:48:55 +01:00
victorAnumudu 48ab2d80ad market list toggle added, thumbnail images added in add pay card page 2023-06-20 22:37:18 +01:00
Ebube 47876875cf done 2023-06-20 21:33:18 +01:00
ameye d78ad0b648 Merge branch 'JobList-Categories' of WrenchBoard/Users-Wrench into master 2023-06-20 19:52:15 +00:00
Ebube d63690a43c Merge branch 'master' of https://gitlab.chiefsoft.net/WrenchBoard/Users-Wrench into JobList-Categories 2023-06-20 20:47:36 +01:00
Ebube 3ce97a4b76 . 2023-06-20 20:46:51 +01:00
ameye 6e9af99d46 Merge branch 'paycard-listing' of WrenchBoard/Users-Wrench into master 2023-06-20 19:44:35 +00:00
victorAnumudu 4ce8f813c4 added svg images 2023-06-20 20:22:08 +01:00
victorAnumudu 9163c42d77 API for paycard listing added 2023-06-20 20:15:05 +01:00
CHIEFSOFT\ameye 3b4ce9c99e Production to orion 2023-06-20 12:00:57 -04:00
ameye cfa6117a07 Merge branch 'history-new-layout' of WrenchBoard/Users-Wrench into master 2023-06-20 15:01:40 +00:00
victorAnumudu 6f26e2e88f history layout changed 2023-06-20 15:50:47 +01:00
ameye b7927a9d6a Merge branch 'JobList-Categories' of WrenchBoard/Users-Wrench into master 2023-06-19 22:43:57 +00:00
Ebube 2d366cd103 . 2023-06-19 22:32:18 +01:00
Ebube 6eed7bf1f3 Added Price Formatter to this files 2023-06-19 22:27:01 +01:00
Ebube 3c87a67581 Changed the status and moved the payload to the users-services 2023-06-19 21:47:40 +01:00
Ebube e31ee86299 Merge branch 'master' of https://gitlab.chiefsoft.net/WrenchBoard/Users-Wrench into JobList-Categories 2023-06-19 21:25:18 +01:00
Ebube 4d3fe6a799 Joblist cart 2023-06-19 21:24:57 +01:00
CHIEFSOFT\ameye 3e8c8e88ea Correct endpoint settings 2023-06-19 08:24:19 -04:00
ameye feca65eb80 Merge branch 'add-dollar-fund' of WrenchBoard/Users-Wrench into master 2023-06-19 11:45:43 +00:00
victorAnumudu 9ea846bc56 add dollar fund component added 2023-06-19 10:21:50 +01:00
CHIEFSOFT\ameye 8c31eb3145 banner description 2023-06-18 20:05:11 -04:00
ameye d049d1b2b0 Merge branch 'referral_page_payload_fix' of WrenchBoard/Users-Wrench into master 2023-06-18 22:17:02 +00:00
Ebube d34895c64c . 2023-06-18 23:09:18 +01:00
Ebube 9a3fa2a4a4 added fix 2023-06-18 23:06:16 +01:00
Ebube a9d982386c . 2023-06-18 22:46:42 +01:00
ameye 2341d2a17d Merge branch 'offer-interval-cleanup' of WrenchBoard/Users-Wrench into master 2023-06-18 16:45:26 +00:00
ameye 076df9438d Merge branch 'Market-Categories' of WrenchBoard/Users-Wrench into master 2023-06-18 16:45:21 +00:00
Ebube bbf03b2b10 Merge branch 'master' of https://gitlab.chiefsoft.net/WrenchBoard/Users-Wrench into Market-Categories 2023-06-18 15:52:19 +01:00
Ebube afead464b0 Done! 2023-06-18 15:51:59 +01:00
victorAnumudu dcdf03e9c5 redirect interval cleared from memory once the page redirects 2023-06-18 13:41:43 +01:00
ameye 44e2402cb3 Merge branch 'offer-interest-process' of WrenchBoard/Users-Wrench into master 2023-06-18 11:58:58 +00:00
victorAnumudu 5c223ba641 margin added to offer interest table 2023-06-18 08:20:29 +01:00
victorAnumudu cc0e2c2c6b offer interest process API added 2023-06-18 08:14:22 +01:00
ameye 3df97c0760 Merge branch 'return-to-wallet-fixed' of WrenchBoard/Users-Wrench into master 2023-06-17 22:29:15 +00:00
ameye 6d742e92b5 Merge branch 'wallet-add-icon' of WrenchBoard/Users-Wrench into master 2023-06-17 22:29:08 +00:00
victorAnumudu 44090c94a1 return to wallet added and add fund style fixed 2023-06-17 23:06:46 +01:00
victorAnumudu 63f4658449 Icon padding fixed 2023-06-17 22:19:57 +01:00
CHIEFSOFT\ameye 552224c489 refer banner 2023-06-17 12:45:17 -04:00
CHIEFSOFT\ameye 134be6a1f0 Add fund page 2023-06-17 08:55:12 -04:00
CHIEFSOFT\ameye fcca050ff6 Add crerdikt style 2023-06-17 08:01:14 -04:00
ameye 197dc2e0bc Merge branch 'interest-page-reformat' of WrenchBoard/Users-Wrench into master 2023-06-17 09:45:35 +00:00
ameye e0f8e8df12 Merge branch 'Move-AddJob-to-Popup' of WrenchBoard/Users-Wrench into master 2023-06-17 09:45:24 +00:00
victorAnumudu b5625ab799 margin adjusted 2023-06-17 08:01:52 +01:00
victorAnumudu 9dc8acc584 manage offer interest reformat 2023-06-17 07:55:26 +01:00
Ebube 6d51fdfc19 popup closes 2023-06-17 06:33:08 +01:00
Ebube 800c62d76f Merge branch 'master' of https://gitlab.chiefsoft.net/WrenchBoard/Users-Wrench into Move-AddJob-to-Popup 2023-06-17 06:21:49 +01:00
Ebube ae23195e0e Changed add job to modal 2023-06-17 06:20:09 +01:00
ameye 4ef9b2f20e Merge branch 'Family-page-Tasks-cleanup' of WrenchBoard/Users-Wrench into master 2023-06-16 14:06:58 +00:00
ameye 709b8ea8f2 Merge branch 'coupon-page-cleanup' of WrenchBoard/Users-Wrench into master 2023-06-16 14:06:51 +00:00
ameye 549af89a43 Merge branch 'faq-page' of WrenchBoard/Users-Wrench into master 2023-06-16 14:06:30 +00:00
Ebube 123ed2056a Family page Tasks 2023-06-16 14:23:54 +01:00
Ebube f04f4c713e . 2023-06-16 13:04:51 +01:00
victorAnumudu 6d16e7f63f faq content add from API 2023-06-16 12:15:08 +01:00
ameye 153bc7ab7d Merge branch 'refferal-style' of WrenchBoard/Users-Wrench into master 2023-06-15 20:37:21 +00:00
victorAnumudu 054688af8b referral layout and style fixed 2023-06-15 21:02:49 +01:00
CHIEFSOFT\ameye 9994ccc26a History page 2023-06-15 12:11:06 -04:00
ameye f5c24ffb0c Merge branch 'other-interest-amendment' of WrenchBoard/Users-Wrench into master 2023-06-15 15:43:20 +00:00
victorAnumudu d5ce5d758a Others interested offer table modified to show only list of persons interest in an offer 2023-06-15 16:31:28 +01:00
ameye 0b14c7675b Merge branch 'pagination-dark-mode' of WrenchBoard/Users-Wrench into master 2023-06-15 10:34:23 +00:00
victorAnumudu 6a3662d69e dark mode style changed for pagination and manage active job 2023-06-15 07:01:41 +01:00
CHIEFSOFT\ameye 8a6c8badbe home banner 2023-06-14 21:19:23 -04:00
ameye 99be0961a9 Merge branch 'interest-processing-page' of WrenchBoard/Users-Wrench into master 2023-06-14 20:00:06 +00:00
victorAnumudu 22bfcbf0c6 interest processing page added 2023-06-14 20:54:14 +01:00
ameye 7975bd3d11 Merge branch 'client-name' of WrenchBoard/Users-Wrench into master 2023-06-14 16:40:19 +00:00
victorAnumudu 5475719e7e client name added and job price formatted to 2 decimal places 2023-06-14 17:36:52 +01:00
ameye 36420a4b47 Merge branch 'offer-interest' of WrenchBoard/Users-Wrench into master 2023-06-14 14:47:11 +00:00
victorAnumudu 6df489a0c2 price and client name added 2023-06-14 15:36:16 +01:00
victorAnumudu 15adddb0ed offer interest list page added 2023-06-14 15:22:54 +01:00
ameye 83e6cef40a Merge branch 'coupon-page-cleanup' of WrenchBoard/Users-Wrench into master 2023-06-14 11:53:42 +00:00
Ebube 4e275da916 Adding a price formatter 2023-06-14 12:44:52 +01:00
ameye 610768f4a5 Merge branch 'pastdue-extension' of WrenchBoard/Users-Wrench into master 2023-06-14 10:23:39 +00:00
victorAnumudu 39e1b05461 duration changed to extension in extend time function payload 2023-06-14 03:42:45 +01:00
CHIEFSOFT\ameye b89cf9a6bc finterest table 2023-06-13 21:17:58 -04:00
ameye 549d61d81b Merge branch 'owner-review-popout' of WrenchBoard/Users-Wrench into master 2023-06-13 23:35:40 +00:00
victorAnumudu 0ba8bbd2e7 owner job cancel action added to payload 2023-06-13 23:20:37 +01:00
victorAnumudu 9bdde5376c popout title renamed 2023-06-13 23:17:09 +01:00
victorAnumudu 8e35839ddf owner review job API added and API name changed 2023-06-13 23:09:55 +01:00
CHIEFSOFT\ameye 4abd8a911b Style fix 2023-06-13 16:24:20 -04:00
ameye a2a9fb2842 Merge branch 'coupon-page-cleanup' of WrenchBoard/Users-Wrench into master 2023-06-13 20:02:53 +00:00
Ebube 9a2fd54bd5 added the redeem coupon api 2023-06-13 20:49:29 +01:00
Ebube d7dbacc69e Merge branch 'master' of https://gitlab.chiefsoft.net/WrenchBoard/Users-Wrench into coupon-page-cleanup 2023-06-13 20:17:42 +01:00
ameye 464969530d Merge branch 'Missing-data-on-Active-Job-Page' of WrenchBoard/Users-Wrench into master 2023-06-13 18:51:24 +00:00
Ebube 7074347289 New Changes to ActiveJobs 2023-06-13 18:26:41 +01:00
Ebube 6a79d6369f Merge branch 'master' of https://gitlab.chiefsoft.net/WrenchBoard/Users-Wrench into Missing-data-on-Active-Job-Page 2023-06-13 18:01:06 +01:00
Ebube 984cdeaa24 Data added 2023-06-13 18:00:40 +01:00
ameye 7946ab12a1 Merge branch 'owner-extend-timeline' of WrenchBoard/Users-Wrench into master 2023-06-13 15:55:46 +00:00
victorAnumudu 392e0394ed API for extend job by owner added 2023-06-13 16:36:28 +01:00
ameye a5d534f364 Merge branch 'worker-task-completed' of WrenchBoard/Users-Wrench into master 2023-06-13 14:30:56 +00:00
ameye 01416ff42c Merge branch 'pastdue-owner-component' of WrenchBoard/Users-Wrench into master 2023-06-13 14:30:44 +00:00
victorAnumudu 976b4afbbf API call for worker task completed added 2023-06-13 15:28:12 +01:00
victorAnumudu 3818e05d2f pastdue popout component added 2023-06-13 14:33:01 +01:00
ameye 5f0364df10 Merge branch 'no-task-background' of WrenchBoard/Users-Wrench into master 2023-06-13 10:46:50 +00:00
Ebube 9adc7eaf1a added redeem api 2023-06-13 09:01:02 +01:00
177 changed files with 8835 additions and 3701 deletions
+5 -5
View File
@@ -9,15 +9,15 @@ REACT_APP_APPSITE="https://myfitapp.mermsemr.com"
# REACT_APP_AUX_ENDPOINT="http://10.20.30.32:9083/svs/user"
# REACT_APP_USERS_ENDPOINT="http://10.20.30.32:9083/svs/user"
REACT_APP_AUX_ENDPOINT="https://apigate.lotus.g1.wrenchboard.com/svs/user"
REACT_APP_USERS_ENDPOINT="https://apigate.lotus.g1.wrenchboard.com/svs/user"
#REACT_APP_AUX_ENDPOINT="https://apigate.lotus.g1.wrenchboard.com/svs/user"
#REACT_APP_USERS_ENDPOINT="https://apigate.lotus.g1.wrenchboard.com/svs/user"
#REACT_APP_AUX_ENDPOINT="https://apigate.lotus.g1.wrenchboard.com/en/wrench/api/v1"
#REACT_APP_USERS_ENDPOINT="https://apigate.lotus.g1.wrenchboard.com/en/wrench/api/v1"
REACT_APP_AUX_ENDPOINT="https://apigate.lotus.g1.wrenchboard.com/en/wrench/api/v1"
REACT_APP_USERS_ENDPOINT="https://apigate.lotus.g1.wrenchboard.com/en/wrench/api/v1"
#"https://devapi.mermsemr.com/en/desktop/api/v2/myfituser"
REACT_APP_SESSION_EXPIRE_MINUTES=300000
REACT_APP_SESSION_EXPIRE_MINUTES=600000
REACT_APP_SESSION_EXPIRE_MINUTES_FAMILY=600000
REACT_APP_SESSION_EXPIRE_CHECKER=60000
+5 -5
View File
@@ -9,15 +9,15 @@ REACT_APP_APPSITE="https://myfitapp.mermsemr.com"
# REACT_APP_AUX_ENDPOINT="http://10.20.30.32:9083/svs/user"
# REACT_APP_USERS_ENDPOINT="http://10.20.30.32:9083/svs/user"
REACT_APP_AUX_ENDPOINT="https://apigate.lotus.g1.wrenchboard.com/svs/user"
REACT_APP_USERS_ENDPOINT="https://apigate.lotus.g1.wrenchboard.com/svs/user"
# REACT_APP_AUX_ENDPOINT="https://apigate.lotus.g1.wrenchboard.com/svs/user"
# REACT_APP_USERS_ENDPOINT="https://apigate.lotus.g1.wrenchboard.com/svs/user"
#REACT_APP_AUX_ENDPOINT="https://apigate.lotus.g1.wrenchboard.com/en/wrench/api/v1"
#REACT_APP_USERS_ENDPOINT="https://apigate.lotus.g1.wrenchboard.com/en/wrench/api/v1"
REACT_APP_AUX_ENDPOINT="https://apigate.lotus.g1.wrenchboard.com/en/wrench/api/v1"
REACT_APP_USERS_ENDPOINT="https://apigate.lotus.g1.wrenchboard.com/en/wrench/api/v1"
#"https://devapi.mermsemr.com/en/desktop/api/v2/myfituser"
REACT_APP_SESSION_EXPIRE_MINUTES=300000
REACT_APP_SESSION_EXPIRE_MINUTES=600000
REACT_APP_SESSION_EXPIRE_MINUTES_FAMILY=600000
REACT_APP_SESSION_EXPIRE_CHECKER=60000
+5 -5
View File
@@ -9,15 +9,15 @@ REACT_APP_APPSITE="https://myfitapp.mermsemr.com"
# REACT_APP_AUX_ENDPOINT="http://10.20.30.32:9083/svs/user"
# REACT_APP_USERS_ENDPOINT="http://10.20.30.32:9083/svs/user"
REACT_APP_AUX_ENDPOINT="https://apigate.orion.g1.wrenchboard.com/svs/user"
REACT_APP_USERS_ENDPOINT="https://apigate.orion.g1.wrenchboard.com/svs/user"
#REACT_APP_AUX_ENDPOINT="https://apigate.orion.g1.wrenchboard.com/svs/user"
#REACT_APP_USERS_ENDPOINT="https://apigate.orion.g1.wrenchboard.com/svs/user"
#REACT_APP_AUX_ENDPOINT="https://apigate.orion.g1.wrenchboard.com/en/wrench/api/v1"
#REACT_APP_USERS_ENDPOINT="https://apigate.orion.g1.wrenchboard.com/en/wrench/api/v1"
REACT_APP_AUX_ENDPOINT="https://apigate.orion.g1.wrenchboard.com/en/wrench/api/v1"
REACT_APP_USERS_ENDPOINT="https://apigate.orion.g1.wrenchboard.com/en/wrench/api/v1"
#"https://devapi.mermsemr.com/en/desktop/api/v2/myfituser"
REACT_APP_SESSION_EXPIRE_MINUTES=300000
REACT_APP_SESSION_EXPIRE_MINUTES=600000
REACT_APP_SESSION_EXPIRE_MINUTES_FAMILY=600000
REACT_APP_SESSION_EXPIRE_CHECKER=60000
+7 -1
View File
@@ -44,6 +44,9 @@ import MyPastDueJobsPage from "./views/MyPastDueJobsPage";
import BlogPage from "./views/BlogPage";
import MyReviewDueJobsPage from "./views/MyReviewDueJobsPage";
import OffersInterestPage from "./views/OffersInterestPage";
import ManageInterestOfferPage from './views/ManageInterestOfferPage'
import MyWaitingJobsPage from "./views/MyWaitingJobsPage";
import FamilyMarketPage from "./views/FamilyMarketPage";
export default function Routers() {
return (
@@ -85,13 +88,15 @@ export default function Routers() {
<Route exact path="/notification" element={<Notification />} />
<Route exact path="/market-place" element={<MarketPlacePage />} />
<Route exact path="/market" element={<MarketPlacePage />} />
<Route exact path="/familymarket" element={<FamilyMarketPage />} />
<Route exact path="/notification" element={<Notification />} />
<Route exact path="/mytask" element={<MyTaskPage />} />
<Route exact path="/myjobs" element={<MyJobsPage />} />
<Route exact path="/add-job" element={<AddJobPage />} />
{/* <Route exact path="/add-job" element={<AddJobPage />} /> */}
<Route exact path="/my-active-jobs" element={<MyActiveJobsPage />} />
<Route exact path="/my-pastdue-jobs" element={<MyPastDueJobsPage />} />
<Route exact path="/my-pending-jobs" element={<MyPendingJobsPage />} />
<Route exact path="/pend-interest" element={<MyWaitingJobsPage />} />
<Route exact path="/my-review-jobs" element={<MyReviewDueJobsPage />} />
<Route exact path="/acc-family" element={<FamilyAccPage />} />
<Route exact path="/manage-family" element={<FamilyManagePage />} />
@@ -99,6 +104,7 @@ export default function Routers() {
<Route exact path="/manage-active-job" element={<ManageActiveJobs />} />
<Route exact path="/blog-page" element={<BlogPage />} />
<Route exact path="/offer-interest" element={<OffersInterestPage />} />
<Route exact path="/manage-offer" element={<ManageInterestOfferPage />} />
<Route
Binary file not shown.

Before

Width:  |  Height:  |  Size: 77 KiB

After

Width:  |  Height:  |  Size: 69 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 72 KiB

After

Width:  |  Height:  |  Size: 82 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 64 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 68 KiB

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

After

Width:  |  Height:  |  Size: 2.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 61 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 25 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 20 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 19 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 18 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 16 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 28 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 24 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 14 KiB

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

After

Width:  |  Height:  |  Size: 1.1 KiB

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

After

Width:  |  Height:  |  Size: 841 B

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

After

Width:  |  Height:  |  Size: 2.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 78 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.9 KiB

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 236 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 159 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 25 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 20 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 19 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 18 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 16 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 28 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 24 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 14 KiB

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 5.6 KiB

+154 -145
View File
@@ -1,67 +1,66 @@
import React, { useState, useEffect } from "react";
import { Link, useNavigate } from "react-router-dom";
import { Field, Form, Formik } from "formik";
import React, { useEffect, useState } from "react";
import { useDispatch } from "react-redux";
import * as Yup from "yup";
import usersService from "../../services/UsersService";
import { tableReload } from "../../store/TableReloads";
import InputCom from "../Helpers/Inputs/InputCom";
import LoadingSpinner from "../Spinners/LoadingSpinner";
import usersService from "../../services/UsersService";
import { useSelector, useDispatch } from "react-redux";
import { tableReload } from '../../store/TableReloads'
import { Field, Form, Formik } from "formik";
import * as Yup from "yup";
const validationSchema = Yup.object().shape({
country: Yup.string()
.min(1, "Minimum 3 characters")
.max(25, "Maximum 25 characters")
.required("Country is required"),
price: Yup.number()
.typeError("you must specify a number")
price: Yup.string()
.typeError("Invalid number")
.min(1, "Price must be greater than 0")
.test("no-e", "Invalid number", (value) => {
if (value && /\d+e/.test(value)) {
return false;
}
return true;
})
.required("Price is required"),
title: Yup.string()
.min(3, "Minimum 3 characters")
.max(100, "Maximum 25 characters")
.min(5, "Minimum 5 characters")
.max(149, "Maximum 149 characters")
.required("Title is required"),
description: Yup.string()
.min(3, "Minimum 3 characters")
.max(250, "Maximum 250 characters")
.min(5, "Minimum 5 characters")
.max(299, "Maximum 299 characters")
.required("Description is required"),
job_detail: Yup.string()
.min(3, "Minimum 3 characters")
.max(250, "Maximum 250 characters")
.max(1440, "Maximum 1440 characters")
.required("Details is required"),
timeline_days: Yup.number()
.typeError("you must specify a number")
.min(1, "Price must be greater than 0")
.required("Timeline is required"),
category: Yup.array().min(1, "Select at least one checkbox"),
});
function AddJob() {
function AddJob({ popUpHandler, categories }) {
const ApiCall = new usersService();
const navigate = useNavigate();
let dispatch = useDispatch()
let dispatch = useDispatch();
let { userDetails } = useSelector((state) => state.userDetails);
let [pageLoading, setPageLoading] = useState(true); // State used for knowing when the page is mounting
let [country, setCountry] = useState({
let [currency, setCurrency] = useState({
loading: true,
status: false,
data: [],
}); // To Hold the array of country getUserCountry returns
data: null,
}); // To Hold the array of currency getUserCurrency returns
let initialValues = {
// initial values for formik
country: userDetails.country,
country: "",
price: "",
title: "",
description: "",
job_detail: "",
timeline_days: "",
category: [],
};
let [requestStatus, setRequestStatus] = useState({
@@ -70,30 +69,42 @@ function AddJob() {
message: "",
}); // Holds state when submit button is pressed
// FUNCTION TO GET COUNTRY
const getUserCountry = () => {
setCountry((prev) => ({ ...prev, loading: true }));
ApiCall.getSignupCountryData()
// FUNCTION TO GET Currency
const getUserCurrency = () => {
setCurrency((prev) => ({ ...prev, loading: true }));
ApiCall.getUserWallets()
.then((res) => {
if (res.data.internal_return < 1) {
setCountry({ loading: false, status: true, data: [] });
if (res.data.internal_return < 0) {
setCurrency({ loading: false, status: true, data: [] });
return;
}
setCountry({
console.log("Res for currency >> ", res);
setCurrency({
loading: false,
status: true,
data: res.data.signup_country,
data: res.data.result_list,
});
})
.catch((err) => {
setCountry({ loading: false, status: false, data: [] });
setCurrency({ loading: false, status: false, data: [] });
});
};
// FUNCTION TO HANDLE ADD JOB FORM
const handleAddJob = (values, helpers) => {
let reqData = {
country: values?.country,
price: Number(values.price) * 100,
title: values?.title,
description: values?.description,
job_detail: values?.job_detail,
timeline_days: values?.timeline_days,
category: values.category?.join("@"),
};
setRequestStatus({ loading: true, status: false, message: "" });
ApiCall.jobManagerCreateJob(values)
ApiCall.jobManagerCreateJob(reqData)
.then((res) => {
if (res.data.internal_return < 1) {
setRequestStatus({
@@ -101,6 +112,9 @@ function AddJob() {
status: false,
message: "Could not complete your request at the moment",
});
setTimeout(() => {
popUpHandler();
}, 1500);
return;
}
setRequestStatus({
@@ -109,15 +123,15 @@ function AddJob() {
message: "Job Added Successfully",
});
setTimeout(() => {
dispatch(tableReload({type:'JOBTABLE'}))
navigate("/myjobs", { replace: true });
dispatch(tableReload({ type: "JOBTABLE" }));
popUpHandler();
}, 1000);
})
.catch((err) => {
setRequestStatus({
loading: false,
status: false,
message: "Opps! soemthing went wrong. Try Again",
message: "Opps! something went wrong. Try Again",
});
})
.finally(() => {
@@ -128,17 +142,10 @@ function AddJob() {
};
useEffect(() => {
getUserCountry();
setPageLoading(false);
getUserCurrency();
}, []);
return pageLoading.loading ? (
<div className="personal-info-tab w-full flex flex-col justify-between">
<div className="p-3">
<LoadingSpinner size="32" color="sky-blue" />
</div>
</div>
) : (
return (
<div className="add-job p-5 w-full bg-white rounded-md flex flex-col justify-between">
<Formik
initialValues={initialValues}
@@ -148,64 +155,44 @@ function AddJob() {
{(props) => {
return (
<Form>
<h1 className="py-2 my-4 text-lg md:text-xl font-bold tracking-wide">
Create New Job
</h1>
<div className="flex flex-col-reverse sm:flex-row">
<div className="fields w-full">
{/* inputs starts here */}
{/* country */}
<div className="xl:flex xl:space-x-7 mb-6">
<div className="xl:flex xl:space-x-7 mb-[5px]">
<div className="field w-full mb-6 xl:mb-0">
{/* <InputCom
fieldClass="px-6 cursor-not-allowed"
label="Country"
labelClass='tracking-wide'
inputBg = 'bg-slate-100'
type="text"
name="country"
disable={true}
value={country.loading ? 'loading' : country.data ? country.data : 'no country found!'}
inputHandler={(e)=> setCountry((prev) => ({...prev, data:e.target.value}))}
/> */}
<label
htmlFor="country"
className="input-label text-[#181c32] dark:text-white text-[13.975px] leading-[20.9625px] font-semibold block"
className="input-label text-[#181c32] dark:text-white text-[13.975px] leading-[20.9625px] font-semibold flex item-center gap-1"
>
Country
Currency
{props.errors.country && props.touched.country && <span className="text-[12px] text-red-500">{props.errors.country}</span>}
</label>
<select
id="country"
name="country"
disabled
value={props.values.country}
className={`input-field p-2 mt-3 rounded-md placeholder:text-base text-dark-gray dark:text-white w-full h-10 bg-slate-100 dark:bg-[#11131F] focus:ring-0 focus:outline-none`}
onChange={props.handleChange}
onBlur={props.handleBlur}
>
{country.loading ? (
{currency.loading ? (
<option className="text-slate-500 text-lg" value="">
Loading...
</option>
) : country.data.length ? (
) : currency.data.length ? (
<>
<option className="text-slate-500 text-lg" value="">
Select...
Select a currency
</option>
{country.data.map((item, index) => {
if (item[0] == userDetails.country) {
return (
<option
key={index}
className="text-slate-500 text-lg"
value={item[0]}
>
{item[1]}
</option>
);
}
})}
{currency.data?.map((item, index) => (
<option
key={index}
className="text-slate-500 text-lg"
value={item?.country}
>
{item?.description}
</option>
))}
</>
) : (
<option className="text-slate-500 text-lg" value="">
@@ -213,11 +200,6 @@ function AddJob() {
</option>
)}
</select>
{props.errors.country && props.touched.country && (
<p className="text-sm text-red-500">
{props.errors.country}
</p>
)}
</div>
{/* Price */}
@@ -233,18 +215,14 @@ function AddJob() {
value={props.values.price}
inputHandler={props.handleChange}
blurHandler={props.handleBlur}
error={props.errors.price && props.touched.price && props.errors.price}
/>
{props.errors.price && props.touched.price && (
<p className="text-sm text-red-500">
{props.errors.price}
</p>
)}
</div>
</div>
{/* Title */}
<div className="field w-full mb-6">
<div className="field w-full mb-[5px]">
<InputCom
fieldClass="px-6"
label="Title"
@@ -252,20 +230,15 @@ function AddJob() {
inputBg="bg-slate-100"
type="text"
name="title"
// placeholder="Enter Job Title"
value={props.values.title}
inputHandler={props.handleChange}
blurHandler={props.handleBlur}
error={props.errors.title && props.touched.title && props.errors.title}
/>
{props.errors.title && props.touched.title && (
<p className="text-sm text-red-500">
{props.errors.title}
</p>
)}
</div>
{/* Description */}
<div className="field w-full mb-6">
<div className="field w-full mb-[5px]">
<InputCom
fieldClass="px-6"
label="Description"
@@ -273,44 +246,71 @@ function AddJob() {
inputBg="bg-slate-100"
type="text"
name="description"
// placeholder="Enter a description"
value={props.values.description}
inputHandler={props.handleChange}
blurHandler={props.handleBlur}
error={props.errors.description && props.touched.description && props.errors.description}
/>
{props.errors.description && props.touched.description && (
<p className="text-sm text-red-500">
{props.errors.description}
</p>
)}
</div>
{/* Details */}
<div className="field w-full mb-6">
<label
htmlFor="Job Delivery Details"
className='className="input-label text-[#181c32] dark:text-white text-[13.975px] leading-[20.9625px] font-semibold block"'
>
Job Delivery Details
</label>
<textarea
id="Job Delivery Details"
rows="7"
className={`input-field p-6 mt-3 rounded-md placeholder:text-base text-dark-gray dark:text-white w-full h-full bg-slate-100 dark:bg-[#11131F] focus:ring-0 focus:outline-none`}
style={{ resize: "none" }}
name="job_detail"
value={props.values.job_detail}
onChange={props.handleChange}
onBlur={props.handleBlur}
/>
{props.errors.job_detail && props.touched.job_detail && (
<p className="text-sm text-red-500">
{props.errors.job_detail}
</p>
)}
<div className="field flex flex-col sm:flex-row w-full mb-[5px] gap-2">
<div className="sm:w-[60%] w-full">
<label
htmlFor="Job Delivery Details"
className='className="input-label text-[#181c32] dark:text-white text-[13.975px] leading-[20.9625px] font-semibold flex items-center gap-1'
>
Job Delivery Details
{props.errors.job_detail && props.touched.job_detail && <span className="text-[12px] text-red-500">{props.errors.job_detail}</span>}
</label>
<textarea
id="Job Delivery Details"
rows="5"
className={`input-field px-3 py-2 placeholder:text-base text-dark-gray dark:text-white w-full h-[100px] bg-slate-100 dark:bg-[#11131F] focus:ring-0 focus:outline-[#dce4e9] rounded-[10px]`}
style={{ resize: "none" }}
name="job_detail"
value={props.values.job_detail}
onChange={props.handleChange}
onBlur={props.handleBlur}
/>
</div>
<div className="sm:w-[35%] w-full">
<div
htmlFor="Job Categories"
className='className="input-label text-[#181c32] dark:text-white text-[13.975px] leading-[20.9625px] font-semibold block"'
id="checked-group"
>
Categories
</div>
<div
className="sm:flex-col flex flex-wrap px-3 mt-3"
role="group"
aria-labelledby="checked-group"
>
{Object?.entries(categories).map(([key, value]) => (
<label
key={key}
className="flex gap-1 w-full items-center"
>
<Field
type="checkbox"
name="category"
value={key}
/>
<span className="text-[13.975px]">{value}</span>
</label>
))}
<span className="h-5 text-sm italic text-[#cf3917]">
{props.errors.category &&
props.touched.category &&
"please select a category"}
</span>
</div>
</div>
</div>
<div className="field w-full mb-6">
<div className="field w-full mb-[5px]">
<div className={`flex items-center justify-between mb-2.5`}>
<label
className="input-label text-[#181c32] dark:text-white text-[13.975px] leading-[20.9625px] font-semibold block"
@@ -326,7 +326,12 @@ function AddJob() {
<Field
component="select"
name="timeline_days"
className="input-field p-2 mt-3 rounded-md placeholder:text-base text-dark-gray dark:text-white w-full h-10 bg-slate-100 dark:bg-[#11131F] focus:ring-0 focus:outline-none"
className={`input-field p-2 mt-3 rounded-md placeholder:text-base text-dark-gray dark:text-white w-full h-10 bg-slate-100 dark:bg-[#11131F] focus:ring-0 focus:outline-none ${
props.errors.timeline_days &&
props.touched.timeline_days
? "border-[#ff0a0a63] shadow-red-500 border-[0.5px] animate-shake"
: "border border-[#f5f8fa] dark:border-[#5e6278]"
}`}
value={props.values.timeline_days}
>
<option value="">Select Duration</option>
@@ -339,12 +344,6 @@ function AddJob() {
</option>
))}
</Field>
{props.errors.timeline_days &&
props.touched.timeline_days && (
<p className="text-sm text-red-500">
{props.errors.timeline_days}
</p>
)}
</div>
{/* inputs ends here */}
</div>
@@ -371,17 +370,20 @@ function AddJob() {
))}
{/* End of error or success display */}
<div className="w-full h-[120px] border-t border-light-purple dark:border-[#5356fb29] flex justify-end items-center">
<div className="w-full h-[70px] border-t border-light-purple dark:border-[#5356fb29] flex justify-end items-center">
<div className="flex items-center space-x-4 mr-9">
<Link
to="/myjobs"
<button
type="button"
className="text-18 text-light-red tracking-wide "
>
<span className="border-b dark:border-[#5356fb29] border-light-red">
<span
className="border-b dark:border-[#5356fb29] border-light-red"
onClick={popUpHandler}
>
{" "}
Cancel
</span>
</Link>
</button>
{requestStatus.loading ? (
<LoadingSpinner size="8" color="sky-blue" />
@@ -419,3 +421,10 @@ const publicArray = [
{ duration: 21, name: "3 weeks" },
{ duration: 28, name: "4 weeks" },
];
// .test("no-e", "Invalid number", (value) => {
// if (value && /\d+e/.test(value)) {
// return false;
// }
// return true;
// })
+3 -3
View File
@@ -4,12 +4,12 @@ import { Link } from "react-router-dom";
export default function LoginLayout({ slogan, children }) {
return (
<div className={`layout-wrapper login`}>
<div className={`main-wrapper login-wrapper w-full h-screen overflow-y-auto p-2 sm:p-20`}>
<div className={`main-wrapper login-wrapper w-full h-screen overflow-y-auto p-2 sm:px-20 sm:py-5`}>
<div className="w-full h-full">
<div className="flex-1 flex justify-center items-center">
{children && children}
</div>
<div className="flex-1 flex justify-center items-center p-10">
<div className="flex-1 flex justify-center items-center px-10 pt-10">
<div className="flex items-center">
<a
href="https://www.wrenchboard.com/about-us"
@@ -37,7 +37,7 @@ export default function LoginLayout({ slogan, children }) {
</a>
</div>
</div>
<div className="flex-1 flex justify-center items-center p-10">
<div className="flex-1 flex justify-center items-center px-10 py-1">
<p className="text-black text-[15px] px-2 font-medium flex items-center">
<span className="text-3xl mt-2 mr-1">©</span> 2023 - {" "}
<Link to="/" className="text-[#009ef7] ml-1">
@@ -43,7 +43,7 @@ export default function CollectionTab({ className, products }) {
<DataIteration
datas={products}
startLength={process.env.REACT_APP_ZERO_STATE}
endLength={products.length}
endLength={products?.length}
>
{({ datas }) => (
<CollectionCard key={datas.uniqKey} collectionData={datas} />
@@ -42,7 +42,9 @@ export default function CreateSaleSlider({
{/* heading */}
<div className="flex justify-between items-center mb-6">
<div>
<h1 className="text-26 font-bold text-dark-gray dark:text-white">Create for Sell</h1>
<h1 className="text-26 font-bold text-dark-gray dark:text-white">
Create for Sell
</h1>
</div>
<div className="slider-btns flex space-x-4">
<button onClick={nextHandler} type="button">
@@ -89,7 +91,7 @@ export default function CreateSaleSlider({
<div className="trending-products relative w-full">
<SliderCom selector={trendingSlider} settings={settings}>
{products &&
products.length > 0 &&
products?.length > 0 &&
products.map((item) => (
<ProductCardStyleTwo
key={item.id}
@@ -49,7 +49,9 @@ export default function CreatedBidsSlider({
{/* heading */}
<div className="flex justify-between items-center mb-6">
<div>
<h1 className="text-26 font-bold text-dark-gray dark:text-white">Create for Bits</h1>
<h1 className="text-26 font-bold text-dark-gray dark:text-white">
Create for Bits
</h1>
</div>
<div className="slider-btns flex space-x-4">
<button onClick={nextHandler} type="button">
@@ -96,7 +98,7 @@ export default function CreatedBidsSlider({
<div className="trending-products relative w-full">
<SliderCom selector={trendingSlider} settings={settings}>
{products &&
products.length > 0 &&
products?.length > 0 &&
products.map((item) => (
<ProductCardStyleOne
key={item.id}
@@ -43,7 +43,7 @@ export default function OnSaleTab({ className, products }) {
<DataIteration
datas={products}
startLength={process.env.REACT_APP_ZERO_STATE}
endLength={products.length}
endLength={products?.length}
>
{({ datas }) => (
<ProductCardStyleTwo key={datas.id} datas={datas} />
@@ -43,7 +43,7 @@ export default function OwnTab({ className, products }) {
<DataIteration
datas={products}
startLength={process.env.REACT_APP_ZERO_STATE}
endLength={products.length}
endLength={products?.length}
>
{({ datas }) => (
<ProductCardStyleOne key={datas.id} datas={datas} />
+56 -21
View File
@@ -1,4 +1,4 @@
import React, { useEffect, useState } from "react";
import React, { useEffect, useLayoutEffect, useState } from "react";
import { Link, useNavigate } from "react-router-dom";
import linkedInLogo from "../../../assets/images/Linkedin.png";
import appleLogo from "../../../assets/images/apple-black.svg";
@@ -17,7 +17,7 @@ import { updateUserDetails } from "../../../store/UserDetails";
export default function Login() {
const dispatch = useDispatch();
let [loginType, setLoginType] = useState({ full: true, family: false });
let [loginType, setLoginType] = useState('');
const [checked, setValue] = useState(false);
const [loginLoading, setLoginLoading] = useState(false);
@@ -33,11 +33,12 @@ export default function Login() {
//FUNCTION TO DETERMINE/CHANGE LOGIN COMPONENT
const handleLoginType = ({ target: { name } }) => {
if (name == "full") {
setLoginType({ [name]: true, family: false });
} else if ((name = "family")) {
setLoginType({ [name]: false, family: true });
}
setLoginType(name);
let currentDate = new Date();
let expirationDate = new Date(currentDate.getTime() + (24 * 60 * 60 * 1000));
// Convert the expiration date to the appropriate format
let expirationDateString = expirationDate.toUTCString();
document.cookie = `loginType=${name}; expires=${expirationDateString}; path=/;`;
};
// email
@@ -68,7 +69,7 @@ export default function Login() {
return;
}
if (name == "loginfull") {
if (name == "full") {
// Post Data Info for normal Login
postData = {
username: email,
@@ -77,7 +78,7 @@ export default function Login() {
login_mode: 1100,
action: 11025,
};
} else if (name == "loginfamily") {
} else if (name == "family") {
// Post Data Info for family Login
postData = {
username: email,
@@ -127,25 +128,59 @@ export default function Login() {
};
const googleLogin = useGoogleLogin({
flow: 'auth-code',
ux_mode:'redirect',
flow: "auth-code",
ux_mode: "redirect",
redirect_uri: process.env.REACT_APP_GOOGLE_REDIRECT_URL,
onSuccess: async (codeResponse) => {
console.log(codeResponse);
console.log("GOOGLE LOGIN GOOD --- ", codeResponse);
},
onError: (errorResponse) => console.log(errorResponse),
});
// In order to update the selected login type whenever the component renders
// useEffect(() => {
// Clear the loginType cookie if the user switches to loginfull
// document.cookie ="loginType=; expires=Thu, 01 Jan 1970 00:00:00 UTC; path=/;";
// }, []);
useLayoutEffect(()=>{ // checks the cookie in order to set the login type before components mounts
// if(document.cookie.includes("loginType=family")){
// setLoginType('family')
// }else if(document.cookie.includes("loginType=full")){
// setLoginType('full')
// }else{
// setLoginType('full')
// }
function readCookie(cname) { // checks the cookie in order to set the login type before components mounts
let name = cname + "=";
let decoded_cookie = decodeURIComponent(document.cookie);
let carr = decoded_cookie.split(';');
for(let i=0; i<carr.length;i++){
let c = carr[i];
while(c.charAt(0)==' '){
c=c.substring(1);
}
if(c.indexOf(name) == 0) {
return c.substring(name.length, c.length);
}
}
return 'full'
}
let loginValue = readCookie('loginType')
setLoginType(loginValue)
},[])
useEffect(() => {
setMail("");
setPassword("");
}, [loginType.full, loginType.family]);
}, [loginType]);
return (
<>
<AuthLayout slogan="Welcome to WrenchBoard">
<div className="w-full">
<div className="mb-12">
<div className="mb-5">
<Link to="#">
<img
src={WrenchBoard}
@@ -176,7 +211,7 @@ export default function Login() {
<button
name="full"
className={`px-2 py-1 w-[100px] text-left h-[40px] text-lg font-bold text-[#4687ba] hover:text-[#009ef7] tracking-wide transition outline-none border-2 border-b-0 border-r-0 border-[#4687ba] ${
loginType.full && "border-r-2 h-[45px]"
loginType=='full' && "border-r-2 h-[45px]"
}`}
onClick={handleLoginType}
>
@@ -185,7 +220,7 @@ export default function Login() {
<button
name="family"
className={`px-2 py-1 w-[100px] text-left h-[40px] text-lg font-bold text-[#4687ba] hover:text-[#009ef7] tracking-wide transition outline-none border-2 border-b-0 border-l-0 border-[#4687ba] ${
loginType.family && "border-l-2 h-[45px]"
loginType=='family' && "border-l-2 h-[45px]"
}`}
onClick={handleLoginType}
>
@@ -197,7 +232,7 @@ export default function Login() {
{/* for login component */}
{
loginType.full ? (
loginType == 'full' ? (
//user login component
<div className="p-2 input-area border-2 border-[#4687ba]">
<div className="input-item mb-5">
@@ -206,7 +241,7 @@ export default function Login() {
fieldClass="sm:px-6 px-2"
value={email}
inputHandler={handleEmail}
placeholder="support@mermsemr.com"
placeholder="Your Email"
label="Email"
name="email"
type="email"
@@ -248,7 +283,7 @@ export default function Login() {
<div className="signin-area mb-3.5">
<div className="flex justify-center">
<button
name="loginfull"
name="full"
onClick={doLogin}
type="button"
disabled={loginLoading}
@@ -294,7 +329,7 @@ export default function Login() {
fieldClass="px-6"
value={email}
inputHandler={handleEmail}
placeholder="support@mermsemr.com"
placeholder="Account ID"
label="Username"
name="email"
type="email"
@@ -336,7 +371,7 @@ export default function Login() {
<div className="signin-area mb-1.5">
<div className="flex justify-center">
<button
name="loginfamily"
name="family"
onClick={doLogin}
disabled={loginLoading}
type="button"
+3 -3
View File
@@ -80,7 +80,7 @@ export default function SignUp() {
if (res.status === 200) {
const { data } = res;
if (data.status === -1 && data.acc === "DULPICATE") {
if (data && data.acc === "DULPICATE") {
setMsgError("This account has been already created");
setSignUpLoading(false);
}
@@ -108,12 +108,12 @@ export default function SignUp() {
useEffect(() => {
getCountryList();
});
}, []);
return (
<>
<div className="layout-wrapper login">
<div className="main-wrapper w-full xl:h-screen h-full xl:py-10 py-12 overflow-y-auto">
<div className="main-wrapper login-wrapper w-full xl:h-screen h-full xl:py-10 py-12 overflow-y-auto">
<div className=" h-full">
<div className="flex-1 flex justify-center items-center">
<div className="w-full">
@@ -43,7 +43,7 @@ export default function CollectionTab({ className, products }) {
<DataIteration
datas={products}
startLength={process.env.REACT_APP_ZERO_STATE}
endLength={products.length}
endLength={products?.length}
>
{({ datas }) => (
<CollectionCard key={datas.uniqKey} collectionData={datas} />
@@ -42,7 +42,9 @@ export default function CreateSaleSlider({
{/* heading */}
<div className="flex justify-between items-center mb-6">
<div>
<h1 className="text-26 font-bold text-dark-gray dark:text-white">Create for Sell</h1>
<h1 className="text-26 font-bold text-dark-gray dark:text-white">
Create for Sell
</h1>
</div>
<div className="slider-btns flex space-x-4">
<button onClick={nextHandler} type="button">
@@ -89,7 +91,7 @@ export default function CreateSaleSlider({
<div className="trending-products relative w-full">
<SliderCom selector={trendingSlider} settings={settings}>
{products &&
products.length > 0 &&
products?.length > 0 &&
products.map((item) => (
<ProductCardStyleTwo
key={item.id}
@@ -49,7 +49,9 @@ export default function CreatedBidsSlider({
{/* heading */}
<div className="flex justify-between items-center mb-6">
<div>
<h1 className="text-26 font-bold text-dark-gray dark:text-white">Create for Bits</h1>
<h1 className="text-26 font-bold text-dark-gray dark:text-white">
Create for Bits
</h1>
</div>
<div className="slider-btns flex space-x-4">
<button onClick={nextHandler} type="button">
@@ -96,7 +98,7 @@ export default function CreatedBidsSlider({
<div className="trending-products relative w-full">
<SliderCom selector={trendingSlider} settings={settings}>
{products &&
products.length > 0 &&
products?.length > 0 &&
products.map((item) => (
<ProductCardStyleOne
key={item.id}
@@ -43,7 +43,7 @@ export default function OnSaleTab({ className, products }) {
<DataIteration
datas={products}
startLength={process.env.REACT_APP_ZERO_STATE}
endLength={products.length}
endLength={products?.length}
>
{({ datas }) => (
<ProductCardStyleTwo key={datas.id} datas={datas} />
@@ -43,7 +43,7 @@ export default function OwnTab({ className, products }) {
<DataIteration
datas={products}
startLength={process.env.REACT_APP_ZERO_STATE}
endLength={products.length}
endLength={products?.length}
>
{({ datas }) => (
<ProductCardStyleOne key={datas.id} datas={datas} />
+96 -48
View File
@@ -1,57 +1,33 @@
import React, { useCallback, useEffect, useMemo, useState } from "react";
import { Link, useNavigate, } from "react-router-dom";
import { toast } from "react-toastify";
import localImgLoad from "../../lib/localImgLoad";
import Icons from "../Helpers/Icons";
import { useCallback, useEffect, useMemo, useState } from "react";
import { Link, useNavigate } from "react-router-dom";
import MarketPopUp from "../MarketPlace/PopUp/MarketPopUp";
import usersService from "../../services/UsersService";
import { PriceFormatter } from "../Helpers/PriceFormatter";
import dataImage2 from "../../assets/images/data-table-user-2.png";
export default function AvailableJobsCard({
className,
datas,
hidden = false,
contentDisplay
}) {
//debugger;
const [addFavorite, setValue] = useState(datas.whishlisted);
const [marketPopUp, setMarketPopUp] = useState({ show: false, data: {} });
const [manageInt, setManageInt] = useState(null)
const [imageUrl, setImageUrl] = useState("");
const navigate = useNavigate();
const apiCall = useMemo(() => new usersService(), []);
const favoriteHandler = () => {
if (!addFavorite) {
setValue(true);
toast.success("Added to Favorite List");
} else {
setValue(false);
toast.warn("Remove to Favorite List");
}
};
const marketInterestData = useCallback(async() => {
let { offer_code } = datas;
let reqData = { offer_code };
try {
const manageInt = await apiCall.MarketInterest(reqData);
const manageIntRes = await manageInt?.data;
setManageInt(manageIntRes)
} catch (error) {
throw new Error(error)
}
}, [])
let thePrice = PriceFormatter(
datas?.price * 0.01,
datas?.currency_code,
datas?.currency
);
useEffect(() => {
if (!datas) {
navigate("/market", { replace: true });
}
marketInterestData()
}, [marketInterestData, datas])
const imagePath = require(`../../assets/images/${datas.thumbnil}`); // Replace with your directory path for local images
setImageUrl(imagePath);
}, []);
return (
<>
{contentDisplay == 'grid' ?
<div
className={`card-style-two w-full h-[426px] p-[20px] bg-white dark:bg-dark-white rounded-2xl section-shadow ${
className || ""
@@ -87,11 +63,9 @@ export default function AvailableJobsCard({
</div>
<div className="thumbnail-area w-full">
<div
className="w-full h-[236px] p-6 rounded-xl overflow-hidden"
className="w-full h-[236px] p-6 rounded-xl overflow-hidden bg-center bg-cover bg-no-repeat"
style={{
background: `url(${localImgLoad(
`images/${datas.thumbnil}`
)}) 0% 0% / cover no-repeat`,
backgroundImage: `url('${imageUrl}')`,
}}
>
<div className="flex justify-center">{datas.description}</div>
@@ -124,11 +98,14 @@ export default function AvailableJobsCard({
<div className="flex items-center space-x-2">
<div>
<p className="font-bold text-xl tracking-wide line-clamp-1 text-dark-gray dark:text-white">
{datas.price * 0.01}
{datas.currency} | {datas.timeline_days} day(s)
{/* {thePrice} | {datas.timeline_days} day(s) */}
{thePrice}
</p>
<p className="text-sm text-lighter-gray">
( {datas.offer_code})
( {datas.offer_code}) |
<span className="italic ml-1">
{datas.timeline_days} day(s)
</span>
</p>
</div>
</div>
@@ -137,7 +114,7 @@ export default function AvailableJobsCard({
type="button"
className="px-4 py-2.5 text-white text-sm bg-pink rounded-full tracking-wide"
onClick={() => {
setMarketPopUp({show: true, data: datas})
setMarketPopUp({ show: true, data: datas });
}}
>
View
@@ -147,6 +124,78 @@ export default function AvailableJobsCard({
</div>
</div>
</div>
:
<div className="card-style-two w-full p-8 my-2 flex items-center gap-4 bg-white dark:bg-dark-white rounded-2xl section-shadow">
<div className="flex gap-5 items-center w-full">
<div className="w-full h-[60px] rounded-full overflow-hidden flex justify-center items-center flex-[0.1] min-w-[60px] max-w-[60px]">
<img src={dataImage2} alt="data" className="w-full h-full" />
</div>
<div className="flex flex-col flex-[0.9]">
<Link to="/shop-details" className="">
<h1 className="font-bold text-xl tracking-wide line-clamp-1 text-dark-gray dark:text-white capitalize">
{datas?.title}
</h1>
</Link>
<div className="my-2">
<p className="text-dark-gray dark:text-white">{datas?.description}</p>
</div>
{/* <div className="card-two-info flex gap-2 items-center">
<div className="owned-by flex space-x-2 items-center">
<div>
<p className="text-thin-light-gray text-sm leading-3">Added</p>
<p className="text-base text-dark-gray dark:text-white">
{datas.offer_added}
</p>
</div>
</div>
<div className="w-[1px] bg-light-purple dark:bg-dark-light-purple h-7"></div>
<div className="created-by flex space-x-2 items-center flex-row-reverse">
<div>
<p className="text-thin-light-gray text-sm leading-3 text-right">
Expires
</p>
<p className="text-base text-dark-gray dark:text-white text-right">
{datas.expire}
</p>
</div>
</div>
</div> */}
<div className="block sm:flex flex-wrap gap-4">
<p className="text-sm text-thin-light-gray flex flext-start gap-1">
Price: <span className="text-purple">{thePrice}</span>
</p>
<p className="text-sm text-thin-light-gray">
Duration:{" "}
<span className="text-purple italic">
{" "}
{datas?.timeline_days} day(s)
</span>
</p>
<p className="text-sm text-thin-light-gray">
Code:{" "}
<span className="text-purple">
{" "}
{datas?.offer_code}
</span>
</p>
</div>
</div>
</div>
<div className="">
<button
type="button"
className="px-4 py-2.5 text-white text-sm bg-pink rounded-full tracking-wide"
onClick={() => {
setMarketPopUp({ show: true, data: datas });
}}
>
View
</button>
</div>
</div>
}
{marketPopUp.show && (
<MarketPopUp
details={datas}
@@ -154,7 +203,6 @@ export default function AvailableJobsCard({
setMarketPopUp({ show: false, data: {} });
}}
situation={marketPopUp.show}
marketInt={manageInt}
/>
)}
</>
@@ -0,0 +1,115 @@
import { useState } from "react";
import { Link } from "react-router-dom";
import { toast } from "react-toastify";
import localImgLoad from "../../lib/localImgLoad";
import CountDown from "../Helpers/CountDown";
import Icons from "../Helpers/Icons";
export default function FamilyActiveJobsCard({ datas, hidden = false }) {
const [imageUrl, setImageUrl] = useState("");
const [addFavorite, setValue] = useState(false);
const [options, setOption] = useState(false);
const favoriteHandler = () => {
if (!addFavorite) {
setValue(true);
toast.success("Added to Favorite List");
} else {
setValue(false);
toast.warn("Remove to Favorite List");
}
};
return (
<div className="card-style-one flex flex-col justify-between w-full h-[387px] bg-white dark:bg-dark-white p-3 pb rounded-2xl">
<div className="content">
{/* thumbnail */}
<div className="w-full h-40">
{/* thumbnail image */}
<div
className="thumbnail w-full h-full rounded-xl overflow-hidden px-4 pt-4"
style={{
background: `url(${localImgLoad(
`images/${datas.thumbnil}`
)}) 0% 0% / cover no-repeat`,
}}
>
{/* <div className="product-options flex justify-between relative">*/}
{/*<span*/}
{/* onClick={favoriteHandler}*/}
{/* className={`w-7 h-7 bg-white rounded-full flex justify-center items-center cursor-pointer ${*/}
{/* addFavorite ? "text-pink" : " text-dark-gray"*/}
{/* }`}*/}
{/*>*/}
{/* <Icons name="love" />*/}
{/*</span>*/}
{/* <span*/}
{/* onClick={() => setOption(!options)}*/}
{/* className="w-7 h-7 flex justify-center items-center bg-white rounded-full cursor-pointer"*/}
{/* >*/}
{/* <Icons name="dots" />*/}
{/*</span>*/}
{/* {options && (*/}
{/* <div*/}
{/* onClick={() => setOption(!options)}*/}
{/* className="w-full h-screen fixed top-0 left-0 z-10"*/}
{/* ></div>*/}
{/* )}*/}
{/* <div*/}
{/* style={{ boxShadow: "0px 4px 87px 0px #0000002B" }}*/}
{/* className={`drop-down-content w-[80px] bg-white dark:bg-dark-white rounded-[4px] p-2.5 absolute right-0 top-[100%] z-20 ${*/}
{/* options ? "active" : ""*/}
{/* }`}*/}
{/* >*/}
{/* </div>*/}
{/* </div>*/}
{hidden && (
<div className="flex justify-center">
</div>
)}
</div>
</div>
{/* details */}
<div className="details">
{/* product title */}
<Link
to="/active-bids"
className="text-xl font-bold text-dark-gray dark:text-white mb-2 capitalize line-clamp-1"
>
{datas.title}
</Link>
{/* countdown */}
<div className="w-full h-[54px] flex justify-evenly items-center p-2 rounded-lg border border-[#E3E4FE] dark:border-[#a7a9b533] ">
<div className="flex flex-col justify-between">
<p className="text-sm text-thin-light-gray dark:text-white tracking-wide">
Task Code
</p>
<p className="text-base font-bold tracking-wide text-dark-gray dark:text-white">
{datas.price}
</p>
</div>
<div className="w-[1px] h-full bg-[#E3E4FE] dark:bg-[#a7a9b533] "></div>
<div className="flex flex-col justify-between">
<p className="text-sm text-thin-light-gray dark:text-white tracking-wide">
Remaining Time
</p>
<p className="text-base font-bold tracking-wide text-dark-gray dark:text-white">
<CountDown lastDate={datas.remaing} />
</p>
</div>
</div>
</div>
</div>
<div className="card-buttons flex items-center space-x-2">
<Link
to="/active-bids"
className="btn-shine w-[98px] h-[33px] text-white rounded-full text-sm bg-pink flex justify-center items-center"
>
View
</Link>
</div>
</div>
);
}
+78
View File
@@ -0,0 +1,78 @@
import { useState } from "react";
import SuggestTask from "../FamilyPopup/SuggestTask";
export default function FamilyMarketCard({ className, datas, hidden = false }) {
// debugger;
const [popUp, setPopUp] = useState(false);
const popUpHandler = () => {
setPopUp((prev) => !prev);
};
// Image
let selectedImage = require(`../../assets/images/family/${
datas.banner || "default.jpg"
}`);
return (
<>
<div
className={`card-style-two w-full h-[336px] p-[20px] bg-white dark:bg-dark-white rounded-2xl section-shadow ${
className || ""
}`}
key={datas?.uid}
>
<div className="flex flex-col justify-between w-full h-full">
<div className="thumbnail-area w-full">
<div
className="w-full h-[236px] p-6 rounded-xl overflow-hidden"
onClick={popUpHandler}
style={{
// background: `url(${selectedImage}) 0% 0% / cover no-repeat`,
background: `url(${selectedImage}) center / contain no-repeat`,
}}
>
<div className="product-two-options flex justify-between mb-5 relative">
<div className="status">
{/* <span className="text-xs px-3 py-1.5 tracking-wide rounded-full bg-gold text-white">
Active
</span> */}
</div>
</div>
{hidden && <div className="flex justify-center"></div>}
</div>
</div>
<div className="details-area mt-12 flex justify-between items-center gap-2">
{/* title */}
<button onClick={popUpHandler}>
<h1 className="font-bold text-xl tracking-wide line-clamp-1 text-dark-gray dark:text-white capitalize">
{datas?.title}
</h1>
</button>
<div className="flex justify-between">
{/* <div className="flex items-center space-x-2">
<p className="italic text-gray-400">
{getTimeAgo(datas?.added)}
</p>
</div> */}
<div>
<button
type="button"
onClick={popUpHandler}
className="px-4 py-2.5 text-white text-sm bg-pink rounded-full tracking-wide"
>
View
</button>
</div>
</div>
</div>
</div>
</div>
{popUp && (
<SuggestTask
onClose={popUpHandler}
situation={popUp}
details={{ ...datas, selectedImage }}
/>
)}
</>
);
}
@@ -9,7 +9,6 @@ export default function HomeBannerOffersCard(props) {
let { banner, banner_location } = props?.itemData;
if (banner_location === "LOCAL") {
const imagePath = require(`../../assets/images/${banner}`); // Replace with your directory path for local images
console.log("This is local");
setImageUrl(imagePath);
} else if (banner_location === "URL") setImageUrl(banner);
else return null;
@@ -24,21 +23,20 @@ export default function HomeBannerOffersCard(props) {
}}
>
<div className="flex flex-col justify-between h-full">
<div className="content flex justify-between items-center mb-5">
<div className="content flex justify-between items-center">
<div className="siderCardHeader">
<h1 className="text-2xl font-bold text-dark-gray dark:text-white tracking-wide">
<>{props.itemData.title}</>
<span className="heroSilderTitle">{props.itemData.title}</span>
</h1>
</div>
{/*<SelectBox datas={filterDatas} action={dataSetHandler} />*/}
</div>
<div className="h-[233px]">
<div className="flex flex-col justify-around items-center flex-1">
<div className="siderCardDescription">
{props.itemData.description}
</div>
<div className="siderCardButton">
[ {props.itemData.button_text} ]
</div>
<button className="w-[150px] h-11 flex justify-center items-center btn-gradient text-base rounded-full text-white">
{props.itemData.button_text}
</button>
</div>
</div>
</Link>
+4 -1
View File
@@ -1,4 +1,4 @@
import React, { useState } from "react";
import { useState } from "react";
import { Link } from "react-router-dom";
import { toast } from "react-toastify";
import localImgLoad from "../../lib/localImgLoad";
@@ -6,6 +6,7 @@ import CountDown from "../Helpers/CountDown";
import Icons from "../Helpers/Icons";
export default function ProductCardStyleOne({ datas, hidden = false }) {
const [imageUrl, setImageUrl] = useState("");
const [addFavorite, setValue] = useState(false);
const [options, setOption] = useState(false);
const favoriteHandler = () => {
@@ -17,6 +18,8 @@ export default function ProductCardStyleOne({ datas, hidden = false }) {
toast.warn("Remove to Favorite List");
}
};
return (
<div className="card-style-one flex flex-col justify-between w-full h-[387px] bg-white dark:bg-dark-white p-3 pb rounded-2xl">
<div className="content">
+75
View File
@@ -0,0 +1,75 @@
import React, { useState } from "react";
import { Link } from "react-router-dom";
import { toast } from "react-toastify";
export default function ProductCardStyleTwo({
className,
datas,
hidden = false,
bg
}) {
// debugger;
const [addFavorite, setValue] = useState(datas.whishlisted);
const [options, setOption] = useState(false);
const favoriteHandler = () => {
if (!addFavorite) {
setValue(true);
toast.success("Added to Favorite List");
} else {
setValue(false);
toast.warn("Remove to Favorite List");
}
};
return (
<div
className={`card-style-two w-full h-[336px] p-[20px] bg-white dark:bg-dark-white rounded-2xl section-shadow ${
className || ""
}`}
>
<div className="flex flex-col justify-between w-full h-full">
<div className="thumbnail-area w-full">
<div
className="w-full h-[236px] p-6 rounded-xl overflow-hidden"
style={{
// background: `url(${`https://blog.wrenchboard.com/wp-content/uploads/${datas.meta_value}`}) 0% 0% / cover no-repeat`,
background: `url(${`${bg}${datas.meta_value}`}) 0% 0% / cover no-repeat`,
}}
>
<div className="product-two-options flex justify-between mb-5 relative">
<div className="status">
{datas?.isActive && (
<span className="text-xs px-3 py-1.5 tracking-wide rounded-full bg-gold text-white">
Active
</span>
)}
</div>
</div>
{hidden && <div className="flex justify-center"></div>}
</div>
</div>
<div className="details-area">
{/* title */}
<a href={datas.guid} target="_blank" className="mb-2.5" rel="noreferrer">
<h1 className="font-bold text-xl tracking-wide line-clamp-1 text-dark-gray dark:text-white capitalize">
{datas.post_title || "dummy title..."}
</h1>
</a>
<div className="flex justify-between">
<div className="flex items-center space-x-2"></div>
<div className="my-1">
<a
href={datas.guid}
target="_blank"
className="px-4 py-2.5 text-white text-sm bg-pink rounded-full tracking-wide"
rel="noreferrer"
>
View
</a>
</div>
</div>
</div>
</div>
</div>
);
}
+4 -3
View File
@@ -1,8 +1,8 @@
import React, { useState, useEffect } from "react";
import Layout from "../Partials/Layout";
import FamilyManageTabs from "./Tabs/FamilyManageTabs";
import React, { useEffect, useState } from "react";
import { useLocation, useNavigate } from "react-router-dom";
import Layout from "../Partials/Layout";
import LoadingSpinner from "../Spinners/LoadingSpinner";
import FamilyManageTabs from "./FamilyManageTabs";
export default function FamilyManage() {
const [selectTab, setValue] = useState("today");
@@ -11,6 +11,7 @@ export default function FamilyManage() {
let location = useLocation();
let navigate = useNavigate();
let accountDetails = location?.state;
// tab handler
const filterHandler = (value) => {
setValue(value);
@@ -0,0 +1,233 @@
import React, {
Suspense,
useCallback,
useEffect,
useMemo,
useRef,
useState,
} from "react";
import { useReactToPrint } from "react-to-print";
import profile from "../../assets/images/profile-info-profile.png";
import usersService from "../../services/UsersService";
import LoadingSpinner from "../Spinners/LoadingSpinner";
import AssignTaskPopout from "./FamilyPopout/AssignTaskPopout";
import {
FamilyWaitlist,
FamilyAccount,
FamilyProfile,
FamilyTasks,
ProfileInfo,
} from "./Tabs";
export default function FamilyManageTabs({
className,
accountDetails,
listReload,
loader,
}) {
const [details, setDetails] = useState({
familyDetails: { loading: false, data: null },
familyTasks: { loading: false, data: null },
familyWaitList: { loading: false, data: null },
});
const [errMsg, setErrMsg] = useState("");
const [familyTaskPopout, setFamilyTaskPopout] = useState(false);
const familyPopUpHandler = () => {
setFamilyTaskPopout((prev) => !prev);
};
const [profileImg, setProfileImg] = useState(profile);
const profileImgInput = useRef(null);
const browseProfileImg = () => {
profileImgInput.current.click();
};
const profileImgChangeHandler = (e) => {
if (e.target.value !== "") {
const imgReader = new FileReader();
imgReader.onload = (event) => {
setProfileImg(event.target.result);
};
imgReader.readAsDataURL(e.target.files[0]);
}
};
const apiCall = useMemo(() => new usersService(), []);
const manageFamily = useCallback(async () => {
try {
setDetails({
familyDetails: { loading: true },
familyTasks: { loading: true },
familyWaitList: { loading: true },
});
const { family_uid } = accountDetails;
const reqData = { family_uid };
const [familyRes, tasksRes, familyWaitRes] = await Promise.all([
apiCall.ManageFamily(reqData),
apiCall.ManageTasks(reqData),
apiCall.ManageFamilyWaitlist(),
]);
const familyData = familyRes.data;
const tasksData = tasksRes.data;
const familyWaitData = familyWaitRes.data;
if (
familyData?.internal_return < 0 ||
tasksData?.internal_return < 0 ||
familyWaitData?.internal_return < 0
)
return;
setDetails({
familyDetails: { loading: false, data: familyData },
familyTasks: { loading: false, data: tasksData },
familyWaitList: { loading: false, data: familyWaitData },
});
} catch (error) {
setDetails({
familyDetails: { loading: false },
familyTasks: { loading: false },
familyWaitList: { loading: false },
});
setErrMsg("An error occurred");
throw new Error(error);
}
}, [apiCall, accountDetails]);
const accountRef = useRef();
const useHandlePrint = useReactToPrint({
content: () => accountRef.current,
});
const tabs = [
{ id: 1, name: "Tasks" },
{ id: 2, name: "Waiting" },
{ id: 3, name: "Account" },
{ id: 4, name: "Profile" },
];
const [tab, setTab] = useState(tabs[0].name);
const tabHandler = (value) => {
setTab(value);
};
const tabComponents = {
Tasks: (
<FamilyTasks
className={className}
loader={details.familyTasks.loading}
familyData={details.familyTasks.data}
accountDetails={accountDetails}
/>
),
Waiting: (
<FamilyWaitlist
familyData={details.familyWaitList.data}
loader={details.familyWaitList.loading}
/>
),
Account: (
<FamilyAccount
familyData={details.familyDetails.data}
myRef={accountRef}
loader={details.familyDetails.loading}
handlePrint={useHandlePrint}
/>
),
Profile: <FamilyProfile />,
};
const defaultTabComponent = (
<FamilyTasks
className={className}
loader={details.familyTasks.loading}
familyData={details.familyTasks.data}
accountDetails={accountDetails}
/>
);
const selectedTabComponent = tabComponents[tab] || defaultTabComponent;
useEffect(() => {
manageFamily();
}, [tab, manageFamily]);
return (
<div
className={`w-full bg-white dark:bg-dark-white overflow-y-auto rounded-2xl section-shadow min-h-[520px] max-h-[600px] ${
className || ""
}`}
>
<div className="relative w-full overflow-x-auto sm:rounded-lg">
<Suspense
fallback={
<div className="h-full min-h-[500px] w-full overflow-hidden flex justify-center items-center">
<LoadingSpinner size="16" color="sky-blue" />
</div>
}
>
<div className="w-full h-full text-sm text-left text-gray-500 dark:text-gray-400 relative grid grid-cols-4 min-h-[520px]">
<div className="border-r border-[#E3E4FE] dark:border-[#a7a9b533] p-6 h-full">
<ProfileInfo
profileImg={profileImg}
profileImgInput={profileImgInput}
profileImgChangeHandler={profileImgChangeHandler}
browseProfileImg={browseProfileImg}
accountDetails={accountDetails}
/>
</div>
<div className="col-span-3 justify-self-end h-full w-full">
<div className="flex flex-col w-full">
<div className="w-full pr-8 flex items-center gap-1">
<ul className="flex gap-2 items-center border-b border-b-[#FAFAF] w-full">
{tabs.map(({ name, id }) => (
<li
onClick={() => tabHandler(name)}
className={`p-4 flex hover:text-purple transition-all ease-in-out items-center cursor-pointer overflow-hidden text-xl relative top-1 ${
tab === name
? "text-purple border-r"
: "text-thin-light-gray"
}`}
key={id}
>
<h1>{name}</h1>
</li>
))}
</ul>
<button
type="button"
onClick={familyPopUpHandler}
className="p-1 my-1 w-[100px] flex justify-center items-center btn-gradient text-base rounded-full text-white"
>
Add task
</button>
</div>
<div className="flex-[0.9] lg:min-h-[450px] h-full">
<div className="h-full p-4 border border-[#dbd9d9] relative overflow-y-auto">
{selectedTabComponent}
</div>
</div>
</div>
</div>
</div>
</Suspense>
</div>
{familyTaskPopout && (
<AssignTaskPopout
action={familyPopUpHandler}
situation={familyTaskPopout}
familyDetails={details.familyDetails.data}
/>
)}
</div>
);
}
+95
View File
@@ -0,0 +1,95 @@
import React, { useCallback, useEffect, useMemo, useState } from "react";
import Layout from "../Partials/Layout";
import SearchCom from "../Helpers/SearchCom";
import FamilyMarketCard from "../Cards/FamilyMarketCard";
import usersService from "../../services/UsersService";
import SuggestTask from "../FamilyPopup/SuggestTask";
export default function FamilyMarket() {
const [popUp, setPopUp] = useState(false);
const [searchQuery, setSearchQuery] = useState("");
const [myFamilySampleTasks, setMyFamilySampleTasks] = useState({
loading: true,
status: false,
data: [],
});
const apiCall = useMemo(() => new usersService(), []);
const getMyFamilySampleTasks = useCallback(async () => {
try {
setMyFamilySampleTasks({ loading: true, status: false, data: [] });
const res = await apiCall.getFamilySampleTasks();
setMyFamilySampleTasks({
loading: false,
status: true,
data: res.data.result_list,
});
} catch (error) {
setMyFamilySampleTasks({ loading: false, status: false, data: [] });
console.log("Error getting tasks:", error);
}
}, [apiCall]);
useEffect(() => {
getMyFamilySampleTasks();
}, [getMyFamilySampleTasks]);
const handleSearch = (event) => {
setSearchQuery(event.target.value);
};
const filteredTasks =
myFamilySampleTasks.data?.filter((task) =>
task.title.toLowerCase().includes(searchQuery.toLowerCase())
) || [];
const popUpHandler = () => {
setPopUp((prev) => !prev);
};
return (
<Layout>
<div className="notification-page w-full mb-10">
<div className="notification-wrapper w-full">
{/* heading */}
<div className="sm:flex justify-between items-center mb-6">
<div className="mb-5 sm:mb-0">
<h1 className="text-26 font-bold inline-flex gap-3 text-dark-gray dark:text-white items-center">
<span>Suggest Task to the Parents</span>
</h1>
</div>
</div>
{/* Body */}
<div className="filter-section w-full items-center sm:flex justify-between mb-6">
{/* filter-search */}
<div className="sm:w-1/2 w-full sm:pr-20 pr-0 mb-5 sm:mb-0">
<SearchCom
placeholder="Search for tasks..."
handleSearch={handleSearch}
value={searchQuery}
/>
</div>
{/* filer-dropdown */}
<div className="flex-1 flex sm:justify-end">
<button
onClick={popUpHandler}
className="btn-gradient lg:flex hidden w-[153px] h-[46px] rounded-full text-white justify-center items-center"
>
Any Other Task
</button>
</div>
</div>
<div className="content-section w-full-width">
<div className="grid lg:grid-cols-3 sm:grid-cols-2 grid-cols-1 gap-[30px]">
{filteredTasks.map((task) => (
<FamilyMarketCard key={task.id} datas={task} />
))}
</div>
</div>
</div>
</div>
{popUp && <SuggestTask onClose={popUpHandler} situation={popUp} />}
</Layout>
);
}
@@ -0,0 +1,263 @@
import React, {useState, useEffect} from 'react'
import ModalCom from '../../Helpers/ModalCom'
import Detail from '../../jobPopout/popoutcomponent/Detail'
import usersService from '../../../services/UsersService'
import LoadingSpinner from '../../Spinners/LoadingSpinner'
import { PriceFormatter } from '../../Helpers/PriceFormatter'
function AssignTaskPopout({action, situation, familyDetails}) {
const apiCall = new usersService()
let [requestStatus, setRequestStatus] = useState({loading: false, status: false, message: ''}) // HOLDS RESPONSE FOR SENDING API REQUEST
let [familyTask, setFamilyTask] = useState({loading: true, data: []})
let [taskType, setTaskType] = useState('select') // SWITCHES BTW SELECT TASK AND NEW TASK
let [activeTask, setActiveTask] = useState({id: 0, data: {}}) // HOLDS SELECTED TASK
const switchTaskType = ({target:{value}}) => { // FUNCTION TO CHANGE SELECTED ACTIVE TASK
setTaskType(value)
}
const handleActiveTask = (id=0, data={}) => { // FUNCTION TO CHANGE SELECTED ACTIVE TASK
setActiveTask({id, data})
}
const assignFamilyTask = () => {
setRequestStatus({loading: true, status: false, message: ''})
let reqData = {}
if(taskType == 'select'){ // RUNS HERE IF TASK TYPE IS SELECT
if(!Object.keys(activeTask.data).length){
setRequestStatus({loading: false, status: false, message: 'No Task is seleted'})
return setTimeout(()=>{
setRequestStatus({loading: false, status: false, message: ''})
}, 3000)
}
reqData = { // API PAYLOADS
job_id: activeTask.data?.job_id,
job_uid: activeTask.data?.job_uid,
family_uid: familyDetails.uid,
job_description: activeTask.data?.description,
assign_mode: 110011,
}
apiCall.assignFamilyTask(reqData).then(res => {
if(res.status != 200 || res.data.internal_return < 0){
setRequestStatus({loading: false, status: false, message: 'failed to assign task'})
return setTimeout(()=>{
setRequestStatus({loading: false, status: false, message: ''})
}, 5000)
}
setRequestStatus({loading: false, status: true, message: 'action successful'})
setTimeout(()=>{
setRequestStatus({loading: false, status: false, message: ''})
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)
})
}
if(taskType == 'new'){ // RUNS HERE IF TASK TYPE IS NEW TASK
console.log('TESTING')
}
}
useEffect(()=>{
const reqData = {
limit: 30,
offset: 0,
job_type: 'FAMILY',
action: 13005
}
apiCall.getMyJobList(reqData).then(res => {
setFamilyTask({loading: false, data: res?.data?.result_list})
if(res?.data?.result_list?.length){
setActiveTask(prev => ({...prev, data:res?.data?.result_list[0]}))
}
}).catch(err => {
setFamilyTask({loading: false, data: []})
console.log('Error', err)
})
},[])
return (
<>
<ModalCom
action={action}
situation={situation}
>
<div className="w-full h-full lg:w-[700px] lg:h-auto bg-white dark:bg-dark-white lg:rounded-2xl">
<div className="w-full flex items-center justify-between lg:px-10 lg:py-8 px-[30px] py-[23px] border-b dark:border-[#5356fb29] border-light-purple">
<h1 className="text-26 font-bold text-dark-gray dark:text-white tracking-wide">
Assign task to {familyDetails?.firstname}
</h1>
<button
type="button"
className="text-[#374557] dark:text-red-500"
onClick={action}
>
<svg
width="36"
height="36"
viewBox="0 0 36 36"
fill="none"
className="fill-current"
xmlns="http://www.w3.org/2000/svg"
>
<path
d="M36 16.16C36 17.4399 36 18.7199 36 20.0001C35.7911 20.0709 35.8636 20.2554 35.8385 20.4001C34.5321 27.9453 30.246 32.9248 22.9603 35.2822C21.9006 35.6251 20.7753 35.7657 19.6802 35.9997C18.4003 35.9997 17.1204 35.9997 15.8401 35.9997C15.5896 35.7086 15.2189 35.7732 14.9034 35.7093C7.77231 34.2621 3.08728 30.0725 0.769671 23.187C0.435002 22.1926 0.445997 21.1199 0 20.1599C0 18.7198 0 17.2798 0 15.8398C0.291376 15.6195 0.214408 15.2656 0.270759 14.9808C1.71321 7.69774 6.02611 2.99691 13.0428 0.700951C14.0118 0.383805 15.0509 0.386897 15.9999 0C17.2265 0 18.4532 0 19.6799 0C19.7156 0.124041 19.8125 0.136067 19.9225 0.146719C27.3 0.868973 33.5322 6.21922 35.3801 13.427C35.6121 14.3313 35.7945 15.2484 36 16.16ZM33.011 18.0787C33.0433 9.77105 26.3423 3.00309 18.077 2.9945C9.78479 2.98626 3.00344 9.658 2.98523 17.8426C2.96667 26.1633 9.58859 32.9601 17.7602 33.0079C26.197 33.0577 32.9787 26.4186 33.011 18.0787Z"
fill=""
fillOpacity="0.6"
/>
<path
d="M15.9309 18.023C13.9329 16.037 12.007 14.1207 10.0787 12.2072C9.60071 11.733 9.26398 11.2162 9.51996 10.506C9.945 9.32677 11.1954 9.0811 12.1437 10.0174C13.9067 11.7585 15.6766 13.494 17.385 15.2879C17.9108 15.8401 18.1633 15.7487 18.6375 15.258C20.3586 13.4761 22.1199 11.7327 23.8822 9.99096C24.8175 9.06632 26.1095 9.33639 26.4967 10.517C26.7286 11.2241 26.3919 11.7413 25.9133 12.2178C24.1757 13.9472 22.4477 15.6855 20.7104 17.4148C20.5228 17.6018 20.2964 17.7495 20.0466 17.9485C22.0831 19.974 24.0372 21.8992 25.9689 23.8468C26.9262 24.8119 26.6489 26.1101 25.4336 26.4987C24.712 26.7292 24.2131 26.3441 23.7455 25.8757C21.9945 24.1227 20.2232 22.3892 18.5045 20.6049C18.0698 20.1534 17.8716 20.2269 17.4802 20.6282C15.732 22.4215 13.9493 24.1807 12.1777 25.951C11.7022 26.4262 11.193 26.7471 10.4738 26.4537C9.31345 25.9798 9.06881 24.8398 9.98589 23.8952C11.285 22.5576 12.6138 21.2484 13.9387 19.9355C14.5792 19.3005 15.2399 18.6852 15.9309 18.023Z"
fill="#"
fillOpacity="0.6"
/>
</svg>
</button>
</div>
{familyTask.loading ?
<div className='h-[100px] w-full flex justify-center items-center'>
<LoadingSpinner color='sky-blue' size='16' />
</div>
:
<>
<div className="job-action-modal-body w-full md:grid md:grid-cols-2">
<div className="p-4">
<div className="mb-2 w-full flex items-center gap-4">
<div className="flex items-center gap-2 text-sky-blue text-base">
<input type="radio" name='task-type' value='select' className="w-[20px] h-[20px] cursor-pointer" checked={taskType=='select'} onChange={switchTaskType}/>
<span>Select Task</span>
</div>
<div className="flex items-center gap-2 text-sky-blue text-base">
<input type="radio" name='task-type' value='new' className="w-[20px] h-[20px] cursor-pointer" checked={taskType=='new'} onChange={switchTaskType}/>
<span>New Task</span>
</div>
</div>
<div className='p-4 w-full h-[400px] overflow-y-auto bg-slate-100'>
{
taskType == 'select' ?
familyTask?.data?.length ?
familyTask?.data?.map((item, index)=>(
<div key={item.job_uid} className="mb-2 flex justify-start items-center gap-2 text-sky-blue text-base cursor-pointer" onClick={()=>handleActiveTask(item.job_uid, item)}>
<input
type="radio"
name='task-list'
checked={(activeTask.id == item.job_uid) || (activeTask.id==index)&& true}
onChange={()=>handleActiveTask(item.job_uid, item)}
className="w-[15px] h-[15px] cursor-pointer"
/>
<p className="w-full text-dark-gray tracking-wide">{item?.title}</p>
</div>
))
:
<p className="p-8 text-lg text-dark-gray dark:text-white tracking-wide text-center">No Task found!</p>
:
<p className="p-8 text-lg text-dark-gray dark:text-white tracking-wide text-center">SPACE FOR NEW TASK</p>
}
</div>
</div>
{familyTask?.data?.length > 0 ?
<div className="p-4">
<div className="w-full">
<p className="text-lg font-bold text-dark-gray dark:text-white tracking-wide border-b-2">{activeTask?.data?.title}</p>
<div className="my-3">
<Detail label="Description" value={activeTask?.data?.description} />
</div>
<div className='flex items-center'>
<div className="my-3 w-full flex items-center gap-1">
<label className='text-slate-900 dark:text-white tracking-wide font-semibold'>Price</label>
<p className='p-1 text-sm text-slate-900 dark:text-white'>{PriceFormatter(activeTask?.data?.price*0.01, activeTask?.data?.currency, activeTask?.data?.curreny_code)}</p>
</div>
<div className="my-3 w-full flex items-center gap-1">
<label className='text-slate-900 dark:text-white tracking-wide font-semibold'>Timeline</label>
<p className='p-1 text-sm text-slate-900 dark:text-white'>{`${activeTask?.data?.timeline_days} day(s)`}</p>
</div>
</div>
<div className="my-3 sm:flex items-center">
<Detail
label="Created"
value={`Dummy, no value found for created!`}
/>
</div>
<div className="my-3">
<label className="w-full text-slate-900 dark:text-white tracking-wide font-semibold">
Delivery Detail
</label>
<textarea
className={`p-1 w-full text-sm text-slate-900 outline-none border border-slate-300 rounded-md`}
rows="5"
style={{ resize: "none" }}
value={activeTask?.data?.job_detail}
readOnly
// onChange={handleInputChange}
/>
{/* <p>{}</p> */}
</div>
</div>
</div>
:
<></>
}
</div>
{/* BTN */}
<div className='p-2 border-t-2 flex justify-end items-center gap-3'>
{/* error or success display */}
{requestStatus.message != "" &&
(!requestStatus.status ? (
<div
className={`relative p-2 text-[#912741] bg-[#fcd9e2] border-[#fbc6d3] mb-4 rounded-[0.475rem] text-md font-light leading-[19.5px] text-[13px]`}
>
{requestStatus.message}
</div>
) : (
requestStatus.status && (
<div
className={`relative p-2 text-green-700 bg-slate-200 border-slate-800 mb-4 rounded-[0.475rem] text-md font-light leading-[19.5px] text-[13px]`}
>
{requestStatus.message}
</div>
)
))}
{/* End of error or success display */}
<button
disabled={requestStatus.loading}
onClick={action} type="button"
className="w-20 h-11 flex justify-center items-center border-gradient text-base rounded-full text-white cursor-pointer"
>
<span className='text-gradient'>Close</span>
</button>
<div className=''>
{requestStatus.loading ?
<LoadingSpinner color='sky-blue' size='8' />
:
<button
type="button"
disabled={requestStatus.loading}
onClick={assignFamilyTask}
className="px-1 w-20 h-11 flex justify-center items-center btn-gradient text-base rounded-full text-white cursor-pointer"
>
Assign
</button>
}
</div>
</div>
</>
}
</div>
</ModalCom>
</>
)
}
export default AssignTaskPopout
+1 -1
View File
@@ -135,7 +135,7 @@ export default function FamilyTable({ className, familyList, loader, popUpHandle
</button>
</div>
<div className="p-2 w-full md:w-1/2">
<img className='w-full' src={familyImage} alt="A Family" />
<img className='w-full' src={familyImage} alt="Add Family" />
</div>
</div>
)
@@ -0,0 +1,56 @@
import { forwardRef } from 'react'
import QRCode from 'react-qr-code';
const FamilyAccount = forwardRef(({ familyData, myRef, handlePrint }, ref) => {
return (
<div
className="w-full lg:min-h-[500px] h-full flex flex-col items-center justify-center"
ref={myRef}
>
<div className="update-table w-full lg:min-h-[450px] h-full p-8 bg-white dark:bg-dark-white overflow-hidden rounded-2xl section-shadow ">
<div className="flex items-center justify-around h-[380px]">
<div className="flex flex-col">
<h2 className="font-bold text-lg tracking-wide line-clamp-1 text-dark-gray dark:text-white capitalize">
Username:{" "}
<span className="ml-2 normal-case">
{familyData?.username ? familyData?.username : "please wait..."}
</span>
</h2>
<h2 className="font-bold text-lg tracking-wide line-clamp-1 text-dark-gray dark:text-white capitalize">
Pin:{" "}
<span className="ml-2 normal-case">
{familyData?.pin ? familyData?.pin : "please wait..."}
</span>
</h2>
</div>
<span className="text-5xl text-gray-400 opacity-20 font-bold">
or
</span>
<div className="max-w-[200px]">
<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>
<QRCode
size={256}
style={{ height: "auto", maxWidth: "100%", width: "100%" }}
value={`https://www.google.com`}
viewBox={`0 0 256 256`}
/>
</div>
</div>
<div className="h-[50px] w-full flex justify-center items-center">
<button
className="btn-shine w-[116px] h-[46px] text-white rounded-full text-base bg-pink flex justify-center items-center"
onClick={handlePrint}
>
Print
</button>
</div>
</div>
</div>
);
});
export default FamilyAccount
@@ -1,312 +0,0 @@
import React, {
Suspense,
forwardRef,
useCallback,
useEffect,
useMemo,
useRef,
useState,
} from "react";
import QRCode from "react-qr-code";
import { useReactToPrint } from "react-to-print";
import usersService from "../../../services/UsersService";
import LoadingSpinner from "../../Spinners/LoadingSpinner";
import profile from "../../../assets/images/profile-info-profile.png";
import FamilyTasks from "./FamilyTasks";
export default function FamilyManageTabs({
className,
accountDetails,
listReload,
loader,
}) {
const [details, setDetails] = useState({
familyDetails: {
loading: false,
data: null,
},
familyTasks: {
loading: false,
data: null,
},
});
const [errMsg, setErrMsg] = useState("");
// List of tabs
const tabs = [
{
id: 1,
name: "Tasks",
},
{
id: 2,
name: "Account",
},
{
id: 3,
name: "Profile",
},
];
const [tab, setTab] = useState(tabs[0].name);
const tabHandler = (value) => {
setTab(value);
};
// For profile uploads
const [profileImg, setProfileImg] = useState(profile);
// profile img
const profileImgInput = useRef(null);
const browseProfileImg = () => {
profileImgInput.current.click();
};
const profileImgChangHandler = (e) => {
if (e.target.value !== "") {
const imgReader = new FileReader();
imgReader.onload = (event) => {
setProfileImg(event.target.result);
};
imgReader.readAsDataURL(e.target.files[0]);
}
};
// Api call
const apiCall = useMemo(() => new usersService(), []);
// function for manage family
const manageFamily = useCallback(async () => {
try {
setDetails({
familyDetails: { loading: true },
familyTasks: { loading: true },
});
let { family_uid } = accountDetails;
let reqData = { family_uid };
// the family response
const familyRes = await apiCall.ManageFamily(reqData);
const familyData = familyRes.data;
// the tasks response
const tasksRes = await apiCall.ManageTasks(reqData);
const tasksData = tasksRes.data;
// checking the internal return
if (familyData?.internal_return < 0 || tasksData?.internal_return < 0)
return;
setDetails({
familyDetails: { loading: false, data: familyData },
familyTasks: { loading: false, data: tasksData },
});
} catch (error) {
setDetails({
familyDetails: { loading: false },
familyTasks: { loading: false },
});
setErrMsg("An error occurred");
throw new Error(error);
}
}, [apiCall, accountDetails]);
useEffect(() => {
manageFamily();
}, [tab, manageFamily]);
const accountRef = useRef();
// to handle printing
const useHandlePrint = useReactToPrint({
content: () => accountRef.current,
});
return (
<div
className={`update-table w-full bg-white dark:bg-dark-white overflow-y-auto rounded-2xl section-shadow min-h-[520px] max-h-[600px] ${
className || ""
}`}
>
<div className="relative w-full overflow-x-auto sm:rounded-lg">
<Suspense
fallback={
<div className="h-full min-h-[500px] w-full overflow-hidden flex justify-center items-center">
<LoadingSpinner size="16" color="sky-blue" />
</div>
}
>
<div className="w-full h-full text-sm text-left text-gray-500 dark:text-gray-400 relative grid grid-cols-4 min-h-[520px]">
<div className="border-r border-[#E3E4FE] dark:border-[#a7a9b533] p-6 h-full">
<ProfileInfo
profileImg={profileImg}
profileImgInput={profileImgInput}
profileImgChangHandler={profileImgChangHandler}
browseProfileImg={browseProfileImg}
accountDetails={accountDetails}
/>
</div>
<div className="col-span-3 justify-self-end h-full w-full">
<div className="flex flex-col w-full">
<ul className="flex-[0.1] flex gap-2 items-center border-b border-b-[#FAFAF] w-full">
{tabs.map(({ name, id }) => (
<li
onClick={() => tabHandler(name)}
className={`p-4 flex hover:text-purple transition-all ease-in-out items-center cursor-pointer overflow-hidden text-xl ${
tab === name
? "text-purple border-r"
: " text-thin-light-gray"
}`}
key={id}
>
<h1>{name}</h1>
</li>
))}
</ul>
<div className="flex-[0.9] lg:min-h-[450px] h-full">
{/* Your content here */}
{tabs.map(({ name, id }) => {
return (
<div
className={`${
tab === name ? "block" : "hidden"
} h-full p-4 border border-[#dbd9d9] relative overflow-y-auto`}
key={id}
>
{name === "Tasks" && (
<FamilyTasks
className={className}
loader={details.familyTasks.loading}
familyData={details.familyTasks.data}
/>
)}
{name === "Account" && (
<Account
familyData={details.familyDetails.data}
myRef={accountRef}
loader={details.familyDetails.loading}
handlePrint={useHandlePrint}
/>
)}
{name === "Profile" && <Profile />}
</div>
);
})}
</div>
</div>
</div>
</div>
</Suspense>
</div>
</div>
);
}
function ProfileInfo({
profileImg,
profileImgInput,
profileImgChangHandler,
browseProfileImg,
accountDetails,
}) {
return (
<div className="flex flex-col items-center gap-6">
<div className="flex justify-center">
<div className="w-full relative">
<img
src={profileImg}
alt=""
className="sm:w-[198px] sm:h-[198px] w-[120px] h-[120px] rounded-full overflow-hidden object-cover"
/>
<input
ref={profileImgInput}
onChange={(e) => profileImgChangHandler(e)}
type="file"
className="hidden"
/>
<div
onClick={browseProfileImg}
className="w-[32px] h-[32px] absolute bottom-7 sm:right-2 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"
>
<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>
<div className="flex flex-col justify-center gap-3 items-center">
<h1 className="font-bold text-xl tracking-wide line-clamp-1 text-dark-gray dark:text-white capitalize">
{accountDetails?.firstname}
</h1>
<h1 className="font-bold text-xl tracking-wide line-clamp-1 text-dark-gray dark:text-white capitalize">
{accountDetails?.lastname}
</h1>
<h1 className="font-bold text-xl tracking-wide line-clamp-1 text-dark-gray dark:text-white capitalize">
{accountDetails?.age}
</h1>
</div>
</div>
);
}
const Account = forwardRef(({ familyData, myRef, handlePrint }, ref) => {
return (
<div
className="w-full lg:min-h-[500px] h-full flex flex-col items-center justify-center"
ref={myRef}
>
<div className="update-table w-full lg:min-h-[450px] h-full p-8 bg-white dark:bg-dark-white overflow-hidden rounded-2xl section-shadow ">
<div className="flex items-center justify-around h-[380px]">
<div className="flex flex-col">
<h2 className="font-bold text-lg tracking-wide line-clamp-1 text-dark-gray dark:text-white capitalize">
Username:{" "}
<span className="ml-2 normal-case">
{familyData?.username ? familyData?.username : "please wait..."}
</span>
</h2>
<h2 className="font-bold text-lg tracking-wide line-clamp-1 text-dark-gray dark:text-white capitalize">
Pin:{" "}
<span className="ml-2 normal-case">
{familyData?.pin ? familyData?.pin : "please wait..."}
</span>
</h2>
</div>
<span className="text-5xl text-gray-400 opacity-20 font-bold">
or
</span>
<div className="max-w-[200px]">
<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>
<QRCode
size={256}
style={{ height: "auto", maxWidth: "100%", width: "100%" }}
value={`https://www.google.com`}
viewBox={`0 0 256 256`}
/>
</div>
</div>
<div className="h-[50px] w-full flex justify-center items-center">
<button
className="btn-shine w-[116px] h-[46px] text-white rounded-full text-base bg-pink flex justify-center items-center"
onClick={handlePrint}
>
Print
</button>
</div>
</div>
</div>
);
});
function Profile() {
return <>Profile</>;
}
@@ -0,0 +1,3 @@
export default function FamilyProfile() {
return <>Profile</>;
}
+37 -32
View File
@@ -5,16 +5,15 @@ import { handlePagingFunc } from "../../Pagination/HandlePagination";
import PaginatedList from "../../Pagination/PaginatedList";
import LoadingSpinner from "../../Spinners/LoadingSpinner";
import Icons from "../../Helpers/Icons";
import { PriceFormatter } from "../../Helpers/PriceFormatter";
import ModalCom from "../../Helpers/ModalCom";
import Detail from "../../jobPopout/popoutcomponent/Detail";
export default function FamilyTasks({ familyData, className, loader }) {
const filterCategories = ["All Categories", "Explore", "Featured"];
const [selectedCategory, setCategory] = useState(filterCategories[0]);
export default function FamilyTasks({ familyData, className, loader, accountDetails }) {
let navigate = useNavigate();
let { pathname } = useLocation();
let data = ["1", "2", "3", "4", "5", "6"]; // to be replaced later by result from API CALL
const [currentPage, setCurrentPage] = useState(0);
const indexOfFirstItem = Number(currentPage);
const indexOfLastItem =
@@ -24,10 +23,7 @@ export default function FamilyTasks({ familyData, className, loader }) {
indexOfLastItem
);
const handlePagination = (e) => {
handlePagingFunc(e, setCurrentPage);
};
const handlePagination = (e) => handlePagingFunc(e, setCurrentPage);
return (
<div
@@ -41,7 +37,6 @@ export default function FamilyTasks({ familyData, className, loader }) {
</div>
) : (
<>
{" "}
{familyData && familyData?.result_list && (
<div className="relative w-full overflow-x-auto sm:rounded-lg flex flex-col justify-between h-full">
<table className="w-full text-sm text-left text-gray-500 dark:text-gray-400">
@@ -52,8 +47,14 @@ export default function FamilyTasks({ familyData, className, loader }) {
familyData?.result_list &&
familyData.result_list.length > 0 &&
currentTask.map((value, index) => {
// find due date
const dueDate = value?.delivery_date.split(" ")[0]
// find due date
const dueDate = value?.delivery_date.split(" ")[0];
// the price
let thePrice = PriceFormatter(
value?.price * 0.01,
value?.currency_code,
value?.currency
);
return (
<tr
key={index}
@@ -73,37 +74,41 @@ export default function FamilyTasks({ familyData, className, loader }) {
{value.title}
</h1>
<div className="flex gap-4 items-center">
<span className="text-sm text-thin-light-gray">
Price:{" "}
<span className="text-purple">
{value.price * 0.01}
<span className="text-sm text-thin-light-gray flex flex-start gap-1">
Price:{" "}
<span className="text-purple">
{thePrice}
</span>
</span>
</span>
<span className="text-sm text-thin-light-gray">
Duration:{" "}
<span className="text-purple">
{" "}
{value.timeline_days} day(s)
<span className="text-sm text-thin-light-gray">
Duration:{" "}
<span className="text-purple">
{" "}
{value.timeline_days} day(s)
</span>
</span>
</span>
<span className="text-sm text-thin-light-gray">
Due Date:{" "}
<span className="text-purple">
{" "}
{dueDate}
<span className="text-sm text-thin-light-gray">
Due Date:{" "}
<span className="text-purple">
{" "}
{dueDate}
</span>
</span>
</span>
</div>
</div>
</div>
</td>
<td className="text-right py-4 px-2">
<button
type="button"
onClick={() => {
navigate("/manage-active-job", {
state: { ...value, pathname },
state: {
...value,
pathname,
accountDetails
}
});
}}
className="w-20 h-11 flex justify-center items-center btn-gradient text-base rounded-full text-white"
@@ -112,7 +117,7 @@ export default function FamilyTasks({ familyData, className, loader }) {
</button>
</td>
</tr>
)
);
})}
</>
}
@@ -0,0 +1,107 @@
import { useState } from "react";
import { useLocation, useNavigate } from "react-router-dom";
import { handlePagingFunc, PaginatedList } from "../../Pagination";
import LoadingSpinner from "../../Spinners/LoadingSpinner";
const FamilyWaitlist = ({ familyData, className, loader }) => {
const navigate = useNavigate();
const { pathname } = useLocation();
const [currentPage, setCurrentPage] = useState(0);
const itemsPerPage = Number(process.env.REACT_APP_ITEM_PER_PAGE);
const indexOfFirstItem = currentPage;
const indexOfLastItem = currentPage + itemsPerPage;
const currentTask = familyData?.result_list?.slice(
indexOfFirstItem,
indexOfLastItem
);
const handlePagination = (e) => handlePagingFunc(e, setCurrentPage);
return (
<div
className={`update-table w-full bg-white dark:bg-dark-white h-full lg:min-h-[450px] overflow-hidden rounded-2xl section-shadow ${
className || ""
}`}
>
{loader ? (
<div className="w-full h-full flex justify-center items-center lg:min-h-[470px]">
<LoadingSpinner size={16} color="sky-blue" />
</div>
) : (
<>
{familyData && familyData.result_list && (
<div className="relative w-full overflow-x-auto sm:rounded-lg flex flex-col justify-between h-full">
<table className="w-full text-sm text-left text-gray-500 dark:text-gray-400">
<tbody>
{currentTask.map((value) => {
const addedDate = value?.added.split(" ")[0];
const taskImg = require(`../../../assets/images/family/${
value?.banner || "default.jpg"
}`);
return (
<tr
className="bg-white dark:bg-dark-white border-b dark:border-[#5356fb29] hover:bg-gray-50"
key={value.uid}
>
<td className="py-4">
<div className="w-full flex justify-between items-center">
<div className="account-name flex space-x-4 items-center">
<div className="icon w-14 h-14 flex justify-center items-center">
<img
src={taskImg}
alt="task_img"
className="w-full object-cover"
/>
</div>
<div className="">
<p className="text-xl font-bold text-dark-gray dark:text-white mb-2 capitalize line-clamp-1">
{value.title}
</p>
<p className="text-sm text-thin-light-gray font-medium">
{value.description}
</p>
</div>
</div>
<div className="px-2">
<p className="text-sm font-bold text-dark-gray dark:text-white">
{addedDate}
</p>
<p className="text-sm text-dark-gray dark:text-white">
Status: {value.status_text}
</p>
</div>
</div>
</td>
<td className="text-right py-4 px-2">
<button
type="button"
className="w-20 h-11 flex justify-center items-center btn-gradient text-base rounded-full text-white"
>
View
</button>
</td>
</tr>
);
})}
</tbody>
</table>
<PaginatedList
onClick={handlePagination}
prev={currentPage === 0}
next={
currentPage + itemsPerPage >= familyData?.result_list.length
}
data={familyData?.result_list}
start={indexOfFirstItem}
stop={indexOfLastItem}
/>
</div>
)}
</>
)}
</div>
);
};
export default FamilyWaitlist;
@@ -0,0 +1,61 @@
import React from "react";
export default function ProfileInfo({
profileImg,
profileImgInput,
profileImgChangHandler,
browseProfileImg,
accountDetails,
}) {
return (
<div className="flex flex-col items-center gap-6">
<div className="flex justify-center">
<div className="w-full relative">
<img
src={profileImg}
alt=""
className="sm:w-[198px] sm:h-[198px] w-[120px] h-[120px] rounded-full overflow-hidden object-cover"
/>
<input
ref={profileImgInput}
onChange={(e) => profileImgChangHandler(e)}
type="file"
className="hidden"
/>
<div
onClick={browseProfileImg}
className="w-[32px] h-[32px] absolute bottom-7 sm:right-2 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"
>
<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>
<div className="flex flex-col justify-center gap-3 items-center">
<h1 className="font-bold text-xl tracking-wide line-clamp-1 text-dark-gray dark:text-white capitalize">
{accountDetails?.firstname}
</h1>
<h1 className="font-bold text-xl tracking-wide line-clamp-1 text-dark-gray dark:text-white capitalize">
{accountDetails?.lastname}
</h1>
<h1 className="font-bold text-xl tracking-wide line-clamp-1 text-dark-gray dark:text-white capitalize">
{accountDetails?.age}
</h1>
</div>
</div>
);
}
+13
View File
@@ -0,0 +1,13 @@
import FamilyAccount from "./FamilyAccount";
import FamilyProfile from "./FamilyProfile";
import FamilyTasks from "./FamilyTasks";
import FamilyWaitlist from "./FamilyWaitlist";
import ProfileInfo from "./ProfileInfo";
export {
FamilyAccount,
FamilyProfile,
FamilyTasks,
FamilyWaitlist,
ProfileInfo,
};
+177
View File
@@ -0,0 +1,177 @@
import React, { useMemo, useState } from "react";
import ModalCom from "../Helpers/ModalCom";
import { Form, Formik } from "formik";
import InputCom from "../Helpers/Inputs/InputCom";
import usersService from "../../services/UsersService";
const DEFAULT_IMAGE = require("../../assets/images/family/default.jpg");
const SuggestTask = ({ details, onClose, situation }) => {
const [submitTask, setSubmitTask] = useState({
loading: false,
msg: "",
state: "",
});
// default image
const selectedImage = details?.selectedImage || DEFAULT_IMAGE;
const initialValues = {
title: details?.title || "",
description: details?.description || "",
banner: details?.banner
};
const apiCall = new usersService();
const handleSubmit = async (values) => {
if(!values.title && !values.description) return;
try {
setSubmitTask({ loading: true });
const reqData = { ...values, };
const res = await apiCall.sendFamilySuggestedTasks(reqData);
if (res.internal_return < 0) {
setSubmitTask({ loading: false, msg: res.status, state: "bad" });
return;
}
setSubmitTask({ loading: false, msg: res.status, state: "success" });
setTimeout(() => {
onClose();
}, 2000);
} catch (error) {
setSubmitTask({ loading: false, msg: error, state: "bad" });
throw new Error("Error Occurred", error);
}
};
return (
<ModalCom action={onClose} situation={situation}>
<div className="logout-modal-wrapper lw-[90%] md:w-[768px] h-full lg:h-auto bg-white dark:bg-dark-white lg:rounded-2xl overflow-y-auto">
<div className="logout-modal-header w-full flex items-center justify-between lg:p-6 px-[30px] py-[23px] border-b dark:border-[#5356fb29] border-light-purple">
<h1 className="text-base md:text-lg font-bold text-dark-gray dark:text-white tracking-wide">
Suggest to Parent
</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>
<Formik initialValues={initialValues} onSubmit={handleSubmit}>
{(props) => {
return (
<Form>
<div className="p-5 w-full bg-white rounded-md flex justify-between">
<div className="p-4 w-full md:w-2/4 md:border-r-2">
<div
className="w-full h-[236px] p-6 bg-gray-400 rounded-xl overflow-hidden"
style={{
// background: `url(${selectedImage}) 0% 0% / cover no-repeat`,
background: `url(${selectedImage}) center / contain no-repeat`,
}}
></div>
</div>
{/* ACTION SECTION */}
<div className="p-4 w-full md:w-2/4 h-full">
{/* Title */}
<div className="field w-full mb-[15px]">
<InputCom
fieldClass="px-6"
label="Title"
labelClass="tracking-wide"
inputBg="bg-slate-100"
inputClass="disabled:cursor-default"
type="text"
name="title"
disable={details?.title}
value={props.values.title}
inputHandler={props.handleChange}
blurHandler={props.handleBlur}
error={
props.errors.title &&
props.touched.title &&
props.errors.title
}
/>
</div>
{/* Description */}
<div className="field w-full mb-[5px]">
<label
htmlFor="description"
className='className="input-label text-[#181c32] dark:text-white text-[13.975px] leading-[20.9625px] font-semibold flex items-center gap-1'
>
Description
{props.errors.description &&
props.touched.description && (
<span className="text-[12px] text-red-500">
{props.errors.description}
</span>
)}
</label>
<textarea
id="description"
rows="5"
className={`input-field px-3 pt-2 placeholder:text-base text-dark-gray dark:text-white w-full h-[130px] bg-slate-100 dark:bg-[#11131F] focus:ring-0 focus:outline-[#dce4e9] rounded-[10px]`}
style={{ resize: "none" }}
name="description"
value={props.values.description}
onChange={props.handleChange}
onBlur={props.handleBlur}
/>
</div>
</div>
</div>
<div className="w-full h-[70px] border-t border-light-purple dark:border-[#5356fb29] flex justify-end items-center">
<div className="flex items-center space-x-4 mr-9">
<button
type="button"
className=" border-gradient text-18 tracking-wide px-4 py-3 rounded-full"
onClick={onClose}
>
<span className="text-gradient"> Cancel</span>
</button>
<button
type="submit"
disabled={props.isSubmitting}
className="text-white primary-gradient text-18 tracking-wide px-4 py-3 rounded-full"
>
{submitTask.loading
? "Submitting Task"
: submitTask.state == "success"
? "Task Submitted"
: submitTask.state == "bad"
? "An Error Occurred"
: "Send to Parents"}
</button>
{/* )} */}
</div>
</div>
</Form>
);
}}
</Formik>
</div>
</ModalCom>
);
};
export default SuggestTask;
+2 -2
View File
@@ -10,7 +10,7 @@ export default function Accordion({ datas }) {
<>
<div className="accordion-item overflow-hidden relative z-[1]">
<div
className="accordion-title-bar flex items-center space-x-3 py-5 border-b border-light-purple dark:border-[#5356fb29] "
className="accordion-title-bar flex items-center space-x-3 py-5 border-b border-light-purple dark:border-[#5356fb29] cursor-pointer"
onClick={accordionHandler}
>
<div className="accordion-title-icon relative">
@@ -36,7 +36,7 @@ export default function Accordion({ datas }) {
<div className="w-[3px] h-auto bg-purple rounded-[28px]"></div>
<div className="flex-1">
<p className="text-base text-thin-light-gray tracking-wide">
{datas.content}
{datas.msg}
</p>
</div>
</div>
+56 -51
View File
@@ -1,59 +1,64 @@
/* eslint-disable no-underscore-dangle */
import React, { useEffect, useState } from "react";
import { useEffect, useState } from "react";
function CountDown({ lastDate = "" }) {
// const [showDate, setDate] = useState(0);
const [showHour, setHour] = useState(0);
const [showMinute, setMinute] = useState(0);
const [showSecound, setDateSecound] = useState(0);
// count Down
const provideDate = new Date(lastDate);
// format date
const year = provideDate.getFullYear();
const month = provideDate.getMonth();
// console.log(month);
const date = provideDate.getDate();
// console.log(date);
const hours = provideDate.getHours();
// console.log(hours);
const minutes = provideDate.getMinutes();
// console.log(minutes);
const seconds = provideDate.getSeconds();
// console.log(seconds);
// date calculation logic
const _seconds = 1000;
const _minutes = _seconds * 60;
const _hours = _minutes * 60;
const _date = _hours * 24;
// interval function
const startInterval = () => {
const timer = setInterval(() => {
const now = new Date();
const distance =
new Date(year, month, date, hours, minutes, seconds).getTime() -
now.getTime();
if (distance < 0) {
clearInterval(timer);
return;
}
// setDate(Math.floor(distance / _date));
setMinute(Math.floor((distance % _hours) / _minutes));
setHour(Math.floor((distance % _date) / _hours));
setDateSecound(Math.floor((distance % _minutes) / _seconds));
}, 1000);
};
// effect
useEffect(() => {
if (lastDate !== "") {
startInterval();
}
// State to store the countdown values
const [countdownValues, setCountdownValues] = useState({
showHour: 0,
showMinute: 0,
showSecond: 0,
});
useEffect(() => {
if (lastDate) {
// Interval function to update countdown values
const intervalId = setInterval(() => {
const now = new Date().getTime();
const targetDate = new Date(lastDate).getTime();
const distance = targetDate - now;
if (distance < 0) {
// If the countdown has reached zero or gone past the target date, clear the interval
clearInterval(intervalId);
setCountdownValues({
showHour: 0,
showMinute: 0,
showSecond: 0,
});
return;
}
// Calculate the countdown values (days, hours, minutes, seconds)
const days = Math.floor(distance / (1000 * 60 * 60 * 24));
const hours = Math.floor(
(distance % (1000 * 60 * 60 * 24)) / (1000 * 60 * 60)
);
const minutes = Math.floor((distance % (1000 * 60 * 60)) / (1000 * 60));
const seconds = Math.floor((distance % (1000 * 60)) / 1000);
// since we don't have a slot for days...
const totalHours = days * 24 + hours;
// Update the countdown values in the state
setCountdownValues({
showHour: totalHours,
showMinute: minutes,
showSecond: seconds,
});
}, 1000);
// Clean up the interval on component unmount or when the lastDate prop changes
return () => clearInterval(intervalId);
}
}, [lastDate]);
// Destructure the countdown values from the state
const { showHour, showMinute, showSecond } = countdownValues;
return (
<span>
{showHour} : {showMinute} : {showSecound}
{showHour < 10 ? "0" + showHour : showHour} :{" "}
{showMinute < 10 ? "0" + showMinute : showMinute} :{" "}
{showSecond < 10 ? "0" + showSecond : showSecond}
</span>
);
}
+10
View File
@@ -1,5 +1,9 @@
import React from "react";
import ATMCard from '../../assets/images/card.svg'
import VisaCard from '../../assets/images/visa.svg'
import MasterCard from '../../assets/images/master.svg'
export default function Icons({ name }) {
return (
<>
@@ -483,6 +487,12 @@ export default function Icons({ name }) {
c0.3,0,0.5-0.1,0.7-0.3l5.7-5.7c0,0,0,0,0,0C15.9,12.3,15.9,11.7,15.5,11.3z"
></path>
</svg>
) : name === "atm-card" ? (
<img className="w-[20px]" src={ATMCard} alt="card" />
) : name === "visa-card" ? (
<img className="w-[20px]" src={VisaCard} alt="card" />
) : name === "master-card" ? (
<img className="w-[20px]" src={MasterCard} alt="card" />
) : (
""
)}
@@ -1,6 +1,6 @@
import React, { useRef } from "react";
import Icons from "../../Icons";
import { Link } from "react-router-dom";
import Icons from "../../Icons";
export default function InputCom({
label,
@@ -21,7 +21,8 @@ export default function InputCom({
blurHandler,
spanTag,
inputBg,
direction
direction,
error,
}) {
const inputRef = useRef(null);
// Entry Validation
@@ -40,22 +41,29 @@ export default function InputCom({
// for Patterns
const inputPatterns = () => {
const inputConfig = inputConfigs[inputRef?.current?.name]?.pattern;
return inputConfig || ""
}
return inputConfig || "";
};
return (
<div className={`input-com ${parentClass}`}>
<div className={`flex items-center justify-between mb-2.5 ${labelClass}`}>
{label && (
<label
className="input-label text-[#181c32] dark:text-white text-[13.975px] leading-[20.9625px] font-semibold block"
className="input-label text-[#181c32] dark:text-white text-[13.975px] leading-[20.9625px] font-semibold flex items-center gap-1"
htmlFor={name}
>
{label}
{spanTag && (
{spanTag && spanTag == "*" ? (
<span className="text-red-700 text-sm tracking-wide">
{" "}
{spanTag}
</span>
) : (
<span className="text-green-700 text-sm tracking-wide">
{spanTag}
</span>
)}
{/* displays error is any */}
{error && <span className="text-[12px] text-red-500">{error}</span>}
</label>
)}
{forgotPassword && (
@@ -68,13 +76,13 @@ export default function InputCom({
)}
</div>
<div
className={`input-wrapper border border-[#f5f8fa] dark:border-[#5e6278] w-full rounded-[0.475rem] h-[42px] overflow-hidden relative font-medium leading-6 bg-clip-padding text-[#5e6278] dark:text-gray-100 bg-[#f5f8fa] dark:bg-[#5e6278] text-base ${inputClass}`}
className={`input-wrapper border w-full rounded-full h-[42px] overflow-hidden relative font-medium leading-6 bg-clip-padding text-[#5e6278] dark:text-gray-100 bg-[#f5f8fa] dark:bg-[#5e6278] text-base ${inputClass}`}
>
<input
placeholder={placeholder}
value={value}
onChange={inputHandler}
className={`input-field placeholder:text-base text-dark-gray w-full h-full ${
className={`input-field placeholder:text-base text-dark-gray w-full h-full tracking-wide ${
inputBg ? inputBg : "bg-[#FAFAFA]"
} dark:bg-[#11131F] focus:ring-0 focus:outline-none ${fieldClass}`}
type={type}
@@ -89,8 +97,10 @@ export default function InputCom({
dir={direction}
/>
{iconName && (
<div className="absolute right-6 bottom-[10px] z-10">
<Icons name={iconName} />
<div className="absolute right-6 bottom-[10px] z-10 flex gap-2">
{iconName.split(" ").map((item, index) => (
<Icons key={index} name={item} />
))}
</div>
)}
{passIcon && (
@@ -116,6 +126,9 @@ const inputConfigs = {
province: { minLength: 3, maxLength: 25, pattern: "[a-zA-Z]+" },
city: { minLength: 3, maxLength: 25, pattern: "[a-zA-Z]+" },
amount: { minLength: 1, maxLength: 9, pattern: "[0-9]+" },
description: { minLength: 5, maxLength: 299 },
title: { minLength: 5, maxLength: 149 },
job_detail: { minLength: 4, maxLength: 1440 }
};
/* Numbers Only: <input type="text" pattern="[0-9]*" /> strictly numbers
+51
View File
@@ -0,0 +1,51 @@
import React from "react";
// export const PriceFormatter = (price, currency, currencyName) => {
// const supportedCurrencies = ["USD", "EUR", "GBP"];
// const symbolFormatter = supportedCurrencies.includes(currency)
// ? currency
// : undefined;
// const formatter = new Intl.NumberFormat("en", {
// style: symbolFormatter,
// currencyDisplay: "symbol",
// minimumFractionDigits: 2,
// });
// const displayCurrencyName = symbolFormatter ? "" : currencyName;
// return `${formatter.format(price)} ${displayCurrencyName}`;
// };
export const PriceFormatter = (
price = "00",
currency = "",
currencyName = ""
) => {
// Convert the number to a string
let numStr = String(price);
// Split the string into integer and decimal parts
let parts = numStr.split(".");
let integerPart = parts[0] || "";
let decimalPart = parts[1] || "";
// Add thousands separators to the integer part
// let formattedInteger = integerPart.replace(/\B(?=(\d{3})+(?!\d))/g, ',');
let formattedInteger = integerPart;
// Truncate or pad the decimal part to two decimal points
let formattedDecimal = decimalPart.slice(0, 2).padEnd(2, "0");
// Combine the formatted integer and decimal parts
// let formattedNumber = formattedInteger + '.' + formattedDecimal;
// return formattedNumber;
return (
<span className="text-sm flex items-center">
<sup>{currency || currencyName || ""}</sup>
<span className="px-1 font-bold text-lg">{formattedInteger || ""}</span>
<sup>{formattedDecimal || ""}</sup>
</span>
);
};
+11 -3
View File
@@ -1,7 +1,13 @@
import React from "react";
import React, { useState } from "react";
import Icons from "./Icons";
export default function SearchCom({ className, inputClasses }) {
export default function SearchCom({
className,
inputClasses,
placeholder,
handleSearch,
value,
}) {
return (
<div
className={`w-full h-[48px] pl-8 flex rounded-full overflow-hidden bg-white dark:bg-dark-white ${
@@ -17,7 +23,9 @@ export default function SearchCom({ className, inputClasses }) {
inputClasses || ""
}`}
type="text"
placeholder="Search items, collections..."
onInput={handleSearch}
value={value}
placeholder={placeholder || "Search items, collections..."}
/>
</div>
</div>
+2 -2
View File
@@ -1,7 +1,7 @@
import React, { useState } from "react";
import useToggle from "../../../hooks/useToggle";
function SelectBox({ datas = [], className, action, contentBodyClasses }) {
function SelectBox({ datas = [], className, action, contentBodyClasses, position }) {
const [item, setItem] = useState(datas[0]);
// custom hook
const [toggle, setToggle] = useToggle(false);
@@ -49,7 +49,7 @@ function SelectBox({ datas = [], className, action, contentBodyClasses }) {
<div
style={{ boxShadow: "0px 4px 87px 0px #0000002B" }}
className={`drop-down-content w-[120px] bg-white dark:bg-dark-white rounded-[4px] p-3 absolute right-0 top-[100%] z-20 ${
className={`drop-down-content w-[120px] bg-white dark:bg-dark-white rounded-[4px] p-3 absolute ${position =='left' ? 'left-0' : 'right-0'} top-[100%] z-20 ${
toggle ? "active" : ""
} ${contentBodyClasses || ""}`}
>
+53 -27
View File
@@ -13,7 +13,9 @@ import LoadingSpinner from "../Spinners/LoadingSpinner";
export default function History() {
const apiCall = new usersService()
const apiCall = new usersService()
let [tab, setTab] = useState("purchases"); //STATE FOR SWITCHING BETWEEN TABS
let [paymentHistory, setPaymentHistory] = useState({ // FOR PAYMENT HISTORY
loading: true,
@@ -63,7 +65,7 @@ export default function History() {
<>
<Layout>
<div className="history-wrapper w-full mb-10">
<div className="main-wrapper w-full">
<div className="w-full">
<div className="sm:flex justify-between items-center mb-6">
<div className="mb-5 sm:mb-0">
<h1 className="text-26 font-bold text-dark-gray dark:text-white">
@@ -213,35 +215,59 @@ export default function History() {
{/*</div>*/}
{/*<MarketHistorySection />*/}
{/* <TopHxBox className="mb-11" /> */}
<div className='w-full lg:flex xl:space-x-8 lg:space-x-4 bottomMargin'>
{/* PURCHASE SECTION */}
<div className="lg:w-1/2 w-full mb-10 lg:mb-0">
<div className="wallet w-full md:p-8 p-4 h-full max-h-[700px] bg-white dark:bg-dark-white overflow-y-auto rounded-2xl shadow">
<h1 className="text-xl font-bold text-dark-gray dark:text-white tracking-wide">Purchases</h1>
{purchaseHistory.loading ?
<LoadingSpinner size='16' color='sky-blue' />
:
<PurchasesTable purchase={purchaseHistory} />
}
</div>
<div className='w-full p-4 md:p-8 bg-white dark:bg-dark-white rounded-2xl shadow bottomMargin'>
{/* switch button */}
<div className="my-1 flex items-center border-b border-slate-300">
<button
name="purchases"
onClick={(e) => setTab(e.target.name)}
className={`p-2 text-lg font-bold text-slate-600 dark:text-white border ${
tab == "purchases" ? "border-sky-blue" : "border-slate-300"
} tracking-wide transition duration-200`}
>
Purchases
</button>
<button
name="recent"
onClick={(e) => setTab(e.target.name)}
className={`p-2 text-lg font-bold text-slate-600 dark:text-white border ${
tab == "recent" ? "border-sky-blue" : "border-slate-300"
} tracking-wide transition duration-200`}
>
Recent Activity
</button>
</div>
{/* END OF PURCHASE SECTION */}
{/* RECENT ACTIVITY SECTION */}
<div className="lg:w-1/2 w-full mb-10 lg:mb-0">
<div className="wallet w-full md:p-8 p-4 h-full max-h-[700px] bg-white dark:bg-dark-white overflow-y-auto rounded-2xl shadow">
<h1 className="text-xl font-bold text-dark-gray dark:text-white tracking-wide">Recent Activity</h1>
{/* <p className='text-base text-slate-500 dark:text-white'>Activity Report</p> */}
{paymentHistory.loading ?
<LoadingSpinner size='16' color='sky-blue' />
:
<RecentActivityTable payment={paymentHistory} />
{/* END OF switch button */}
<div className="history-tables w-full">
{/* PURCHASE SECTION */}
{tab == 'purchases' &&
<div className="wallet w-full border-t">
<h1 className="p-2 text-xl font-bold text-dark-gray dark:text-white tracking-wide">Purchases</h1>
{purchaseHistory.loading ?
<LoadingSpinner size='16' color='sky-blue' />
:
<PurchasesTable purchase={purchaseHistory} />
}
</div>
}
</div>
{/* END OF PURCHASE SECTION */}
{/* RECENT ACTIVITY SECTION */}
{tab == 'recent' &&
<div className="wallet w-full border-t">
<h1 className="p-2 text-xl font-bold text-dark-gray dark:text-white tracking-wide">Recent Activity</h1>
{/* <p className='text-base text-slate-500 dark:text-white'>Activity Report</p> */}
{paymentHistory.loading ?
<LoadingSpinner size='16' color='sky-blue' />
:
<RecentActivityTable payment={paymentHistory} />
}
</div>
}
{/* END OF RECENT ACTIVITY SECTION */}
</div>
{/* END OF RECENT ACTIVITY SECTION */}
</div>
<HistoryTable />
{/*<HistoryTable />*/}
</div>
</div>
</Layout>
+5 -4
View File
@@ -1,13 +1,14 @@
import React, { useRef } from "react";
import ProductCardStyleOne from "../Cards/ProductCardStyleOne";
//import ProductCardStyleOne from "../Cards/ProductCardStyleOne";
import Icons from "../Helpers/Icons";
import SliderCom from "../Helpers/SliderCom";
import FamilyActiveJobsCard from "../Cards/FamilyActiveJobsCard";
export default function FamilyActiveLSlde({ className, trending }) {
const settings = {
arrows: false,
slidesToShow: 4,
slidesToScroll: 4,
slidesToShow: 3,
slidesToScroll: 3,
infinite: true,
responsive: [
{
@@ -94,7 +95,7 @@ export default function FamilyActiveLSlde({ className, trending }) {
{trending &&
trending.length > 0 &&
trending.map((item) => (
<ProductCardStyleOne key={item.id} datas={item} />
<FamilyActiveJobsCard key={item.id} datas={item} />
))}
</SliderCom>
</div>
+17 -18
View File
@@ -3,23 +3,22 @@ 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";
export default function FamilyDash({familyOffers}) {
console.log("PROPS IN FAMILY DASH->", familyOffers);
export default function FamilyDash(props) {
console.log("PROPS IN FAMILY DASH->",props);
const trending = datas.datas;
return (
<div>
<div className="home-page-wrapper">
<CommonHead
commonHeadData={props.commonHeadData}
/>
<FamilyActiveLSlde trending={trending} className="mb-10" />
<TopSellerTopBuyerSliderSection className="mb-10" />
</div>
</div>
);
}
const trending = datas.datas;
return (
<div>
<div className="home-page-wrapper">
{/* <CommonHead commonHeadData={props.commonHeadData} /> */}
<MyOffersFamilyTable familyOffers={familyOffers} className="mb-10" />
<FamilyActiveLSlde trending={trending} className="mb-10" />
{/*<TopSellerTopBuyerSliderSection className="mb-10" />*/}
<ParentWaiting className="mb-10" />
</div>
</div>
);
}
+3 -3
View File
@@ -56,15 +56,15 @@ export default function Hero({ className, bannerList, nextDueTask }) {
</div>
</div>
{/* countdown */}
{nextDueTask?.next_due && Object.keys(nextDueTask.next_due)?.length && (
{nextDueTask?.next_due && Object.keys(nextDueTask.next_due)?.length != 0 && (
<div className="w-full h-32 flex justify-evenly items-center sm:p-6 p-1 rounded-2xl border border-white-opacity">
<div className="flex flex-col justify-between">
<p className="text-base text-white tracking-wide">Current Task</p>
<p className="lg:text-2xl text-lg font-bold tracking-wide text-white">
{nextDueTask.next_due.item_code}
{(nextDueTask.next_due.item_code).substr(0,4)+'...'}
</p>
<p className="text-base text-white tracking-wide">
{nextDueTask.next_due.price} Naira
{nextDueTask.next_due.price * 0.01} Naira
</p>
</div>
<div className="w-[1px] h-full bg-white-opacity"></div>
+1 -2
View File
@@ -3,11 +3,10 @@ import slider2 from "../../assets/images/slider-2.jpg";
import HomeBannerOffersCard from "../Cards/HomeBannerOffersCard";
export default function HomeSliders(props) {
// console.log("BANNER LIST IN HomeSliders->", props.bannerList);
// debugger;
return (
<>
<div className="hero-slider relative 2xl:w-[600px] xl:w-[500px] lg:w-[420px] w-full mb-2 lg:mb-0 ">
<div className="hero-slider relative 2xl:w-[600px] xl:w-[400px] lg:w-[420px] w-full mb-2 lg:mb-0 ">
<div className="w-full">
<SliderCom settings={props.settings}>
{props.bannerList?.length <= 0 && (
+26 -5
View File
@@ -10,6 +10,7 @@ export default function Home(props) {
console.log("PROPS IN HOME->", props);
let [nextDueTask, setNextDueTask] = useState({});
const [MyOffersList, setMyOffersList] = useState([]);
const userApi = new usersService();
@@ -32,21 +33,41 @@ export default function Home(props) {
});
};
const getMyOffersList = async () => {
try {
const res = await userApi.getOffersList();
setMyOffersList(res.data?.result_list);
} catch (error) {
console.log("Error getting offers", error);
}
};
useEffect(() => {
getHomeDate();
const fetchData = async () => {
await Promise.all([getHomeDate(), getMyOffersList()]);
};
fetchData();
}, []);
return (
<Layout>
<div className="home-page-wrapper">
{userDetails && userDetails?.account_type == "FAMILY" && (
<FamilyDash commonHeadData={props.bannerList} />
)}
{userDetails && userDetails?.account_type == "FULL" && (
{userDetails && userDetails?.account_type == "FAMILY" ? (
<FamilyDash
account={userDetails}
commonHeadData={props.bannerList}
familyOffers={MyOffersList}
/>
) : userDetails && userDetails?.account_type == "FULL" ? (
<FullAccountDash
nextDueTask={nextDueTask}
bannerList={props.bannerList}
/>
) : (
<div>
You are not logged in or your account type is not supported.
</div>
)}
</div>
</Layout>
+81 -82
View File
@@ -1,106 +1,105 @@
import React, { useEffect, useState } from "react";
import React, { useEffect, useMemo, useState } from "react";
import DataIteration from "../Helpers/DataIteration";
import AvailableJobsCard from "../Cards/AvailableJobsCard";
import ListView from '../../assets/images/list-view.png'
import GridView from '../../assets/images/grid-view.svg'
import SelectBox from "../Helpers/SelectBox";
export default function MainSection({ className, marketPlaceProduct }) {
const [tab, setTab] = useState("all");
const [products, setProducts] = useState(marketPlaceProduct);
export default function MainSection({
className,
marketPlaceProduct,
categories,
}) {
// Creating All cart..
let marketCategories = useMemo(
() => ({ All: "All Categories", ...categories }),
[categories]
);
const [tab, setTab] = useState(Object.keys(marketCategories)[0]);
let [contentDisplay, setContentDisplay] = useState('grid') // STATE TO HOLD LIST VIEW STYLE
// Convert to array in order to map
const mappedArray = Object.entries(marketCategories).map(([key, value]) => {
return { key, value };
});
const [products, setProducts] = useState([]);
const tabHandler = (value) => {
setTab(value);
};
useEffect(() => {
if (tab === "artist") {
setProducts(marketPlaceProduct?.slice(0, 3));
} else if (tab === "market") {
setProducts(marketPlaceProduct?.slice(0, 6));
} else if (tab === "shop") {
setProducts(marketPlaceProduct?.slice(6, 9));
} else if (tab === "assets") {
setProducts(marketPlaceProduct?.slice(3, 6));
} else {
setProducts(marketPlaceProduct);
// Handles the category selection on mobile view
const handleSetCategory = (value) => {
for (let i in marketCategories) {
if (marketCategories[i] == value) {
setTab(i);
}
}
}, [tab, marketPlaceProduct]);
};
useEffect(() => {
if (tab === "All") {
setProducts(marketPlaceProduct);
} else {
const filteredProducts = marketPlaceProduct.filter((product) =>
product.category.includes(tab)
);
setProducts(filteredProducts);
}
}, [tab, marketPlaceProduct, categories, marketCategories]);
return (
<div className={`market-place-section w-full ${className || ""}`}>
<div className="market-place-wrapper w-full">
<div className="filter-navigate-area lg:flex justify-between mb-8 items-center">
<div className="tab-item lg:mb-0 mb-5">
<div className="md:flex md:space-x-8 space-x-2">
<span
onClick={() => tabHandler("all")}
className={`md:text-[18px] text-md text-dark-gray dark:text-white hover:text-pink border-b hover:border-pink font-medium cursor-pointer ${
tab === "all"
? "text-pink border-pink"
: " border-transparent"
}`}
>
All
</span>
<span
onClick={() => tabHandler("artist")}
className={`md:text-[18px] text-md text-dark-gray dark:text-white hover:text-pink border-b hover:border-pink font-medium cursor-pointer ${
tab === "artist"
? "text-pink border-pink"
: " border-transparent"
}`}
>
Featured Artist
</span>
<span
onClick={() => tabHandler("market")}
className={`md:text-[18px] text-md text-dark-gray dark:text-white hover:text-pink border-b hover:border-pink font-medium cursor-pointer ${
tab === "market"
? "text-pink border-pink"
: " border-transparent"
}`}
>
Open Market
</span>
<span
onClick={() => tabHandler("shop")}
className={`md:text-[18px] text-md text-dark-gray dark:text-white hover:text-pink border-b hover:border-pink font-medium cursor-pointer ${
tab === "shop"
? "text-pink border-pink"
: " border-transparent"
}`}
>
Partner Shops
</span>
<span
onClick={() => tabHandler("assets")}
className={`md:text-[18px] text-md text-dark-gray dark:text-white hover:text-pink border-b hover:border-pink font-medium cursor-pointer ${
tab === "assets"
? "text-pink border-pink"
: " border-transparent"
}`}
>
Game Assets
</span>
<div className="filter-navigate-area flex justify-between items-center mb-8">
<div className="tab-item w-full flex items-center">
<div className="hidden lg:flex md:space-x-8 space-x-2">
{mappedArray.map(({ key, value }) => (
<span
key={key}
onClick={() => tabHandler(key)}
className={`md:text-[18px] text-md text-dark-gray dark:text-white hover:text-pink border-b hover:border-pink font-medium cursor-pointer ${
tab === key
? "text-pink border-pink"
: " border-transparent"
}`}
>
{value}
</span>
))}
</div>
{/* market categories on screen smaller than large screen */}
<div className="w-[80%] lg:hidden">
<SelectBox
action={handleSetCategory}
datas={Object.values(marketCategories)}
className="Update-table-dropdown"
contentBodyClasses="w-auto min-w-max"
position='left'
/>
</div>
</div>
{/*<div className="search-item flex lg:flex-none justify-end">*/}
{/* <SearchCom*/}
{/* className="lg:bg-transparent"*/}
{/* inputClasses="lg:bg-transparent"*/}
{/* />*/}
{/*</div>*/}
{/* contentDisplay toggler */}
<div className="p-2 w-[35px] h-[35px] bg-white dark:bg-slate-200 rounded-lg">
<img
title={contentDisplay=='grid'? 'list view' : 'grid view'}
onClick={()=>setContentDisplay((prev)=>prev=='grid'? 'list' : 'grid')}
src={contentDisplay=='grid'? ListView : GridView}
className="w-full h-full cursor-pointer" alt="view"
/>
</div>
{/* end of contentDisplay toggler */}
</div>
<div className="filter-navigate-content w-full min-h-screen">
<div className="grid lg:grid-cols-3 sm:grid-cols-2 gap-[30px]">
<div className={contentDisplay == 'grid' ? 'grid lg:grid-cols-3 sm:grid-cols-2 gap-[30px]' : 'w-full'}>
<DataIteration
datas={products}
startLength={process.env.REACT_APP_ZERO_STATE}
endLength={products?.length}
>
{({ datas }) => (
<AvailableJobsCard key={datas.id} datas={datas} />
<AvailableJobsCard contentDisplay={contentDisplay} key={datas.id} datas={datas} />
)}
</DataIteration>
</div>
@@ -0,0 +1,12 @@
import React from 'react'
function Detail({label, value, bg,}) {
return (
<>
<label className='w-full md:w-1/4 text-slate-900 tracking-wide font-semibold'>{label}</label>
<p className={`p-1 w-full md:w-3/4 text-sm text-slate-900 ${bg ? bg : null}`}>{value}</p>
</>
)
}
export default Detail
+132 -101
View File
@@ -1,22 +1,98 @@
import React, { useCallback, useMemo, useState } from "react";
import { useCallback, useEffect, useMemo, useState } from "react";
import ModalCom from "../../Helpers/ModalCom";
import { toast } from "react-toastify";
import { Form, Formik } from "formik";
import usersService from "../../../services/UsersService";
import LoadingSpinner from "../../Spinners/LoadingSpinner";
import { PriceFormatter } from "../../Helpers/PriceFormatter";
const MarketPopUp = ({ details, onClose, situation, marketInt }) => {
const [marketMsg, setMarketMsg] = useState({
loading: false,
data: {},
state: undefined,
});
const [manageInt, setManageInt] = useState({
loading: false,
data: {},
state: undefined,
msg: "",
});
const MarketCalls = (details) => {
const [marketMsg, setMarketMsg] = useState({
loading: false,
data: {},
state: undefined,
});
const [manageInt, setManageInt] = useState({
loading: false,
data: {},
state: undefined,
msg: "",
});
const { offer_code } = details;
const reqData = { offer_code };
const MarketDetail = async () => {
try {
setMarketMsg({ loading: true });
if (!textValue) return;
reqData.yourmessage = textValue;
const marketMessage = await apiCall.MarketMessage(reqData);
const marketMessageRes = marketMessage?.data;
if (marketMessageRes?.internal_return < 0) {
toast.warn("Something wrong happened", {
autoClose: 2000,
hideProgressBar: true,
});
onClose();
return;
}
toast.success("Message sent", {
autoClose: 2500,
hideProgressBar: true,
});
setMarketMsg({ data: marketMessageRes, state: true });
setTimeout(() => onClose(), 2000);
} catch (error) {
throw new Error(error);
} finally {
setTimeout(() => {
setTextValue("");
setMarketMsg({ loading: false });
}, 2000);
}
};
const ManageInterest = async () => {
try {
setManageInt({ loading: true });
const manageInt = await apiCall.MarketInterest(reqData);
const manageIntRes = manageInt?.data;
if (manageIntRes?.internal_return < 0) {
setManageInt({
loading: false,
msg: manageIntRes?.status,
data: manageIntRes,
state: false,
});
} else {
setManageInt({
loading: false,
msg: manageIntRes?.status,
data: manageIntRes,
state: true,
});
}
setTimeout(() => setManageInt({ msg: "" }), 3000);
} catch (error) {
throw new Error(error);
}
};
// useEffect(() => {
// ManageInterest();
// }, []);
return { MarketDetail, ManageInterest, manageInt, marketMsg };
};
const [textValue, setTextValue] = useState("");
@@ -26,84 +102,26 @@ const MarketPopUp = ({ details, onClose, situation, marketInt }) => {
const apiCall = useMemo(() => new usersService(), []);
const marketCalls = useCallback(
async (e) => {
try {
const nameOfCall = e?.target?.name;
const { offer_code } = details;
const reqData = { offer_code };
let { manageInt, ManageInterest, MarketDetail, marketMsg } = MarketCalls(details);
if (nameOfCall === "market-message") {
setMarketMsg({ loading: true });
if (!textValue) return;
reqData.yourmessage = textValue;
const marketMessage = await apiCall.MarketMessage(reqData);
const marketMessageRes = marketMessage?.data;
if (marketMessageRes?.internal_return < 0) {
toast.warn("Something wrong happened", {
autoClose: 2000,
hideProgressBar: true,
});
onClose();
return;
}
toast.success("Message sent", {
autoClose: 2500,
hideProgressBar: true,
});
setMarketMsg({ data: marketMessageRes, state: true });
setTimeout(() => onClose(), 2000);
} else {
setManageInt({ loading: true });
const manageInt = await apiCall.MarketInterest(reqData);
const manageIntRes = manageInt?.data;
if (manageIntRes?.internal_return < 0) {
setManageInt({
loading: false,
msg: `Error - ${manageIntRes?.status}`,
data: manageIntRes,
state: false,
});
} else {
setManageInt({
loading: false,
msg: manageIntRes?.status,
data: manageIntRes,
state: true,
});
}
setTimeout(() => setManageInt({ msg: "" }), 3000);
return;
}
} catch (error) {
throw new Error(error);
} finally {
setTimeout(() => {
setTextValue("");
setMarketMsg({ loading: false });
}, 2000);
}
},
[apiCall, details, onClose, textValue]
let thePrice = PriceFormatter(
details?.price * 0.01,
details?.currency_code,
details?.currency
);
console.log('Checking my mangeInt',manageInt)
// let addedIntDate = marketInt?.added?.split(" ")[0];
let expireIntDate = marketInt?.expire?.split(" ")[0];
// let expireIntDate = marketInt?.expire?.split(" ")[0];
let cleanedText = details?.job_description
?.replace(/&lt;/g, "<")
.replace(/&gt;/g, ">")
.replace(/&quot;/g, '"')
.replace(/&amp;/g, "&");
return (
<ModalCom action={onClose} situation={situation} className="edit-popup">
<div className="logout-modal-wrapper md:w-[750px] md:h-[700px] h-full bg-white dark:bg-dark-white lg:rounded-2xl overflow-y-auto">
<div className="logout-modal-wrapper md:w-[750px] md:h-[660px] h-full bg-white dark:bg-dark-white lg:rounded-2xl overflow-y-auto">
<div className="logout-modal-header w-full flex items-center justify-between lg:p-6 px-[30px] py-[23px]">
<h1 className="text-26 font-bold text-dark-gray dark:text-white tracking-wide">
{details.offer_code}
@@ -112,8 +130,8 @@ const MarketPopUp = ({ details, onClose, situation, marketInt }) => {
</div>
<div className="md:flex bg-white rounded-lg">
<div className="p-4 w-full md:w-3/4 md:border-r-1">
<div className="min-h-[290px]">
<div className="p-4 w-full md:w-[75%] md:border-r-1">
<div className="min-h-[263px]">
<h2 className="font-semibold text-slate-900 dark:text-black tracking-wide">
{details?.title}
</h2>
@@ -128,17 +146,17 @@ const MarketPopUp = ({ details, onClose, situation, marketInt }) => {
name: "",
content: {
text: `Timeline: ${details.timeline_days} day(s) -- `,
bold: `Budget: ${details.price} naira`,
bold: `Budget: ${thePrice}`,
},
},
{
name: "Delivery Detail",
content: details.job_description,
content: cleanedText,
danger: true,
},
].map(({ name, content, danger }, idx) => (
<div className={`my-3 md:flex items-center`} key={idx}>
<label className="w-full md:w-1/4 text-slate-900 tracking-wide font-semibold">
<label className="w-full md:w-[19%] text-slate-900 tracking-wide font-semibold whitespace-pre-wrap">
{name}
</label>
<div
@@ -152,19 +170,23 @@ const MarketPopUp = ({ details, onClose, situation, marketInt }) => {
<p
className={``}
dangerouslySetInnerHTML={{
__html: danger && content?.replace(/"/g, ""),
__html: danger && content,
}}
/>
) : (
<p className={`w-full md:w-3/4 text-slate-900`}>
<p className={`w-full text-slate-900`}>
{name !== "Delivery Detail" ? (
<>
{typeof content !== "object" ? content : null}
{typeof content === "object" && (
<span className="flex items-center gap-2">
{content?.text}
<strong>{content?.bold}</strong>
</span>
<>
<hr className="mb-1" />
<span className="flex items-center gap-2">
{content?.text}
<strong>{thePrice}</strong>
</span>
<hr className="mt-1" />
</>
)}
</>
) : (
@@ -194,7 +216,7 @@ const MarketPopUp = ({ details, onClose, situation, marketInt }) => {
<button
className="self-end w-[150px] h-[52px] rounded-md text-base bg-yellow-500 text-white"
name="market-message"
onClick={marketCalls}
onClick={MarketDetail}
>
{marketMsg.loading ? (
<LoadingSpinner size={5} color="white" />
@@ -207,17 +229,17 @@ const MarketPopUp = ({ details, onClose, situation, marketInt }) => {
</div>
</div>
<div className="w-full md:w-1/4 h-full ">
<div className="w-[90%] mx-auto bg-[#f1f8ff] p-4 rounded-md md:min-h-[550px] flex flex-col justify-between">
<div className="w-full md:w-[23%] h-full ">
<div className="mx-auto bg-[#f1f8ff] p-4 rounded-md md:min-h-[498px] flex flex-col justify-between">
<div className="w-full flex flex-col justify-center py-4 gap-2">
<p className="w-full text-slate-900 tracking-wide my-1">
Interested in the task?
</p>
<hr />
<button
className="bg-[#57cd89] text-center text-lg font-semibold text-white py-2 px-4 rounded-md inline-flex flex-col items-center justify-center"
className="bg-[#57cd89] text-center text-lg font-semibold text-white py-2 px-4 rounded-md inline-flex sm:flex-col flex-row sm:gap-0 gap-1 items-center justify-center"
name="market-interest"
onClick={marketCalls}
onClick={ManageInterest}
>
{" "}
<span>Send</span>
@@ -248,9 +270,18 @@ const MarketPopUp = ({ details, onClose, situation, marketInt }) => {
Interest: <b className="ml-1">{details.interest_count}</b>
</p>
<hr />
<p className="my-1">Expire: {expireIntDate}</p>
<p className="my-1">
Expire: {details.expire}
</p>
</div>
</div>
<button
className="self-end w-[150px] mt-2 h-[52px] rounded-md text-base bg-transparent border border-red-500 text-red-500 mx-auto"
name="cancel"
onClick={onClose}
>
Cancel
</button>
</div>
{/* END OF ACTION SECTION */}
</div>
+9 -8
View File
@@ -1,22 +1,23 @@
import React from "react";
// import products from "../../data/marketplace_data.json";
//import CreateNft from "../Home/CreateNft";
import Layout from "../Partials/Layout";
import MainSection from "./MainSection";
import CommonHead from "../UserHeader/CommonHead";
import { useSelector } from "react-redux";
export default function MarketPlace({commonHeadData}) {
export default function MarketPlace({ commonHeadData }) {
let { jobLists } = useSelector((state) => state.jobLists);
const marketData = jobLists?.result_list;
const categories = jobLists?.categories;
// const marketProduct = products.data;
return (
<>
<Layout>
<CommonHead commonHeadData={commonHeadData} />
<MainSection marketPlaceProduct={marketData} className="mb-10" />
<CommonHead commonHeadData={commonHeadData} />
<MainSection
marketPlaceProduct={marketData}
categories={categories}
className="mb-10"
/>
</Layout>
</>
);
}
}
File diff suppressed because it is too large Load Diff
@@ -2,9 +2,9 @@ import React from 'react'
function CurrentJobAction() {
return (
<div className='job-action'>
<div className='job-action bg-white dark:bg-black'>
<p className="my-3 py-1 text-base active-owner">
<table className="w-full text-sm text-left text-gray-500 dark:text-gray-400">
<table className="w-full text-sm text-left text-gray-500">
<tbody>
<tr>
<td>
@@ -1,10 +1,16 @@
import React, { useState, useRef } from 'react'
import React, { useState } from 'react'
import { useNavigate } from 'react-router-dom'
import ModalCom from '../../Helpers/ModalCom'
import LoadingSpinner from '../../Spinners/LoadingSpinner'
import usersService from '../../../services/UsersService'
function CurrentTaskAction({jobDetails}) {
const apiCall = new usersService()
const navigate = useNavigate()
const [checked, setChecked] = useState(false)
const [reqStatus, setReqStatus] = useState({loading:false, status: false, message: ''})
@@ -20,22 +26,43 @@ function CurrentTaskAction({jobDetails}) {
// FUNCTION TO HANDLE WHEN USER CLICKS ON SEND FOR REVIEW AND ACCEPTANCE
const taskCompletedSubmit = () => {
setReqStatus({loading:true, status: false, message: ''})
if(!checked){
setReqStatus({loading:true, status: false, message: ''}) // Sets loading spinner active
let reqData = {
contract: jobDetails.contract,
contract_uid: jobDetails.contract_uid,
job_action: 'NOTIFY_COMPLETE',
}
if(!checked){ // checks that checkbox is selected
setReqStatus({loading:false, status: false, message: 'Please check the box above'})
return setTimeout(()=>{
setReqStatus({loading:false, status: false, message: ''})
}, 3000)
}
setTimeout(()=>{
setReqStatus({loading:false, status: false, message: ''})
}, 3000)
// API CALL TO MARK TASK AS COMPLETED BY WORKER
apiCall.workerJobAction(reqData).then((res)=>{
if(res.status != 200 || res.data.internal_return < 0){
setReqStatus({loading:false, status: false, message: 'unable to complete request. Try again'})
return
}
setReqStatus({loading:false, status: true, message: 'Task marked completed successfully'})
setTimeout(()=>{ // Sets popout to false and navigates user to /mytask after 3 seconds
popUpHandler()
navigate('/mytask', {replace: true})
}, 3000)
}).catch(err => {
setReqStatus({loading:false, status: false, message: 'Opps! Network error. Try again'})
}).finally(()=>{
setTimeout(()=>{
setReqStatus({loading:false, status: false, message: ''})
}, 3000)
})
}
return (
<div className='job-action'>
<div className='job-action bg-white dark:bg-black'>
<table className="w-full text-sm text-left text-gray-500 dark:text-gray-400 active-worker">
<table className="w-full text-sm text-left text-gray-500 active-worker">
<tbody>
<tr>
<td>
@@ -52,7 +79,7 @@ function CurrentTaskAction({jobDetails}) {
<tr>
<td>
<div className="flex justify-center items-center">
<button onClick={popUpHandler} type="button" className="w-120 h-11 flex justify-center items-center btn-gradient text-base rounded-full text-white">
<button onClick={popUpHandler} type="button" className="w-[150px] h-11 flex justify-center items-center btn-gradient text-base rounded-full text-white">
Send of Review
</button>
</div>
@@ -10,8 +10,8 @@ import ReviewJobAction from './ReviewJobAction'
import ReviewTaskAction from './ReviewTaskAction'
function IndexJobActions({details}) { // FUNCTION TO RENDER SPECIFIC JOB ACTION DEPENDING ON OWNER STATUS & STATUS DESCRIPTION
let owner = details.owner_status
let description = details.status_description
let owner = details?.owner_status
let description = details?.status_description
switch(owner) {
case 'OWNER':
return (()=>{
@@ -1,10 +1,109 @@
import React from 'react'
import React,{useState} from 'react'
import ModalCom from '../../Helpers/ModalCom'
import LoadingSpinner from '../../Spinners/LoadingSpinner'
import { useNavigate } from 'react-router-dom'
import usersService from '../../../services/UsersService'
function PastDueJobAction({jobDetails}) {
const apiCall = new usersService()
const navigate = useNavigate()
const [checked, setChecked] = useState(false)
const [extendedTime, setExtendedTime] = useState('') // VALUE OF NEW EXTENDED TIME
const [action, setAction] = useState('') // VALUE OF NEW EXTENDED TIME
const [reqStatus, setReqStatus] = useState({loading:false, status: false, message: ''})
let [popUp, setPopUp] = useState(false)
const popUpHandler = () => {
if(popUp){
setChecked(false)
}
setPopUp(prev => !prev)
}
// FUNCTION TO HANDLE WHEN OWNER CANCELS JOB
const cancelTask = () => {
setAction('cancel')
setReqStatus({loading:true, status: false, message: ''})
let reqData = { // API PAYLOADS
contract: jobDetails.contract,
contract_uid: jobDetails.contract_uid,
job_action: 'REQUEST_CANCEL',
}
if(!checked){ // CHECKS IF CHECKBOX IS SELECTED
setReqStatus({loading:false, status: false, message: 'Please check the box above'})
return setTimeout(()=>{
setReqStatus({loading:false, status: false, message: ''})
}, 3000)
}
// API CALL TO ACCEPT COMPLETION BY OWNER
apiCall.ownerJobAction(reqData).then((res)=>{
if(res.status != 200 || res.data.internal_return < 0){
setReqStatus({loading:false, status: false, message: 'unable to complete request. Try again'})
return
}
setReqStatus({loading:false, status: true, message: 'job cancelled successfully'})
setTimeout(()=>{ // Sets popout to false and navigates user to /my-pastdue-jobs after 3 seconds
popUpHandler()
navigate('/my-pastdue-jobs', {replace: true})
}, 3000)
}).catch(err => {
setReqStatus({loading:false, status: false, message: 'Opps! Network error. Try again'})
}).finally(()=>{
setTimeout(()=>{
setReqStatus({loading:false, status: false, message: ''})
}, 3000)
})
}
// FUNCTION TO HANDLE WHEN USER/OWNER CLICKS ON EXTEND TIMELINE FOR A JOB
const extendTime = () => {
setAction('extend')
setReqStatus({loading:true, status: false, message: ''}) // Sets loading spinner active
let reqData = {
contract: jobDetails.contract,
contract_uid: jobDetails.contract_uid,
job_action: 'EXTEND_TIMELINE',
extension: Number(extendedTime)
}
if(!extendedTime){ // checks that timeline duration is selected
setReqStatus({loading:false, status: false, message: 'Please select timeline duration'})
return setTimeout(()=>{
setReqStatus({loading:false, status: false, message: ''})
}, 3000)
}
// API CALL EXTEND TIMELINE BY OWNER
apiCall.ownerJobAction(reqData).then((res)=>{
if(res.status != 200 || res.data.internal_return < 0){
setReqStatus({loading:false, status: false, message: 'unable to complete request. Try again'})
return
}
setReqStatus({loading:false, status: true, message: 'Timeline extended successfully'})
setTimeout(()=>{ // Sets popout to false and navigates user to /my-pastdue-jobs after 3 seconds
popUpHandler()
navigate('/my-pastdue-jobs', {replace: true})
}, 3000)
}).catch(err => {
setReqStatus({loading:false, status: false, message: 'Opps! Network error. Try again'})
}).finally(()=>{
setTimeout(()=>{
setReqStatus({loading:false, status: false, message: ''})
}, 3000)
})
}
function PastDueJobAction() {
return (
<div className='job-action'>
<div className='job-action bg-white dark:bg-black'>
<table className="w-full text-sm text-left text-gray-500 dark:text-gray-400 owner-pastdue">
<table className="w-full text-sm text-left text-gray-500 owner-pastdue">
<tbody>
<tr>
<td>
@@ -20,7 +119,7 @@ function PastDueJobAction() {
<tr>
<td>
<div className="flex justify-center items-center">
<button type="button" className="w-120 h-11 flex justify-center items-center btn-gradient text-base rounded-full text-white">
<button type="button" onClick={popUpHandler} className="w-[150px] h-11 flex justify-center items-center btn-gradient text-base rounded-full text-white">
Cancel or Extend Timeline
</button>
</div>
@@ -28,6 +127,118 @@ function PastDueJobAction() {
</tr>
</tbody>
</table>
{popUp && (
<ModalCom action={popUpHandler} situation={popUp}>
<div className="logout-modal-wrapper lg:w-[460px] 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 dark:border-[#5356fb29] border-light-purple">
<h1 className="text-26 font-bold text-dark-gray dark:text-white tracking-wide">
Past Due Task
</h1>
<button
type="button"
className="text-[#374557] dark:text-red-500"
onClick={popUpHandler}
>
<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 px-10 py-8 gap-4">
<div className="w-full flex flex-col items-center">
<div className="mb-5 flex justify-center items-center gap-2">
<input
type='checkbox'
checked={checked}
onChange={()=>{setChecked(prev => !prev)}}
className='w-6 h-6 text-sky-blue bg-gray-100 focus:ring-sky-blue'
/>
<p className='font-bold text-base tracking-wide text-dark-gray dark:text-white'>I am ready to cancel this task</p>
</div>
<div className="mb-5 flex justify-center items-center">
{reqStatus.loading && action=='cancel'?
<LoadingSpinner color='sky-blue' size='10' />
:
<button disabled={reqStatus.loading} onClick={cancelTask} type="button" className="px-2 py-1 h-11 flex justify-center items-center border-gradient text-base rounded-full text-white">
<span className='text-gradient'>Cancel this task</span>
</button>
}
</div>
{/* EXTEND TIMELINE SECTION */}
<div className='w-full my-3 py-3 border-y flex flex-col items-center'>
<div className='mb-5 flex items-center gap-2'>
<p className='font-bold text-base tracking-wide text-dark-gray dark:text-white'>Extend the timeline by:</p>
<select
onChange={({target})=>{setExtendedTime(target.value)}}
className='text-base p-2 text-dark-gray dark:text-white rounded-md border border-slate-300 outline-0'
>
<option className='text-slate-500 text-lg' value=''>select</option>
<option className='text-slate-500 text-lg' value='2'>1 days</option>
<option className='text-slate-500 text-lg' value='3'>3 days</option>
<option className='text-slate-500 text-lg' value='5'>5 days</option>
<option className='text-slate-500 text-lg' value='7'>1 week</option>
</select>
</div>
{reqStatus.loading && action=='extend' ?
<LoadingSpinner color='sky-blue' size='10' />
:
<button disabled={reqStatus.loading} type="button" onClick={extendTime} className="px-2 py-1 h-11 flex justify-center items-center btn-gradient text-base rounded-full text-white">
Extend Timeline
</button>
}
</div>
{/* FOR SUCCESS/ERROR DISPLAY SECTION*/}
<div className="w-full">
{reqStatus.message != "" &&
(!reqStatus.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]`}
>
{reqStatus.message}
</div>
) : (
reqStatus.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]`}
>
{reqStatus.message}
</div>
)
))}
</div>
{/* END OF FOR SUCCESS/ERROR DISPLAY SECTION*/}
</div>
{/* 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>
</div>
</div>
</div>
</ModalCom>
)}
</div>
)
}
@@ -2,9 +2,9 @@ import React from 'react'
function PastDueTaskAction() {
return (
<div className='job-action'>
<div className='job-action bg-white dark:bg-black'>
<table className="w-full text-sm text-left text-gray-500 dark:text-gray-400 worker-pastdue">
<table className="w-full text-sm text-left text-gray-500 worker-pastdue">
<tbody>
<tr>
<td>
@@ -20,7 +20,7 @@ function PastDueTaskAction() {
<tr>
<td>
<div className="flex justify-center items-center">
<button type="button" className="w-120 h-11 flex justify-center items-center btn-gradient text-base rounded-full text-white">
<button type="button" className="w-[150px] h-11 flex justify-center items-center btn-gradient text-base rounded-full text-white">
Request Extension
</button>
</div>
@@ -1,10 +1,98 @@
import React from 'react'
import React,{useState} from 'react'
import ModalCom from '../../Helpers/ModalCom'
import LoadingSpinner from '../../Spinners/LoadingSpinner'
import { useNavigate } from 'react-router-dom'
import usersService from '../../../services/UsersService'
function ReviewJobAction() {
function ReviewJobAction({jobDetails}) {
const apiCall = new usersService()
const navigate = useNavigate()
const [checked, setChecked] = useState(false)
const [action, setAction] = useState('') // VALUE OF NEW EXTENDED TIME
const [reqStatus, setReqStatus] = useState({loading:false, status: false, message: ''})
let [popUp, setPopUp] = useState(false)
const popUpHandler = () => {
if(popUp){
setChecked(false)
}
setPopUp(prev => !prev)
}
// FUNCTION TO HANDLE WHEN OWNER ACCEPTS COMPLETION OF JOB
const acceptCompletion = () => {
setAction('accept')
setReqStatus({loading:true, status: false, message: ''})
let reqData = { // API PAYLOADS
contract: jobDetails.contract,
contract_uid: jobDetails.contract_uid,
job_action: 'ACCEPT_COMPLETE',
}
if(!checked){ // CHECKS IF CHECKBOX IS SELECTED
setReqStatus({loading:false, status: false, message: 'Please check the box above'})
return setTimeout(()=>{
setReqStatus({loading:false, status: false, message: ''})
}, 3000)
}
// API CALL TO ACCEPT COMPLETION BY OWNER
apiCall.ownerJobAction(reqData).then((res)=>{
if(res.status != 200 || res.data.internal_return < 0){
setReqStatus({loading:false, status: false, message: 'unable to complete request. Try again'})
return
}
setReqStatus({loading:false, status: true, message: 'job completion accepted successfully'})
setTimeout(()=>{ // Sets popout to false and navigates user to /my-review-jobs after 3 seconds
popUpHandler()
navigate('/my-review-jobs', {replace: true})
}, 3000)
}).catch(err => {
setReqStatus({loading:false, status: false, message: 'Opps! Network error. Try again'})
}).finally(()=>{
setTimeout(()=>{
setReqStatus({loading:false, status: false, message: ''})
}, 3000)
})
}
// FUNCTION TO HANDLE WHEN USER/OWNER CLICKS ON REJECT COMPLETION OF JOB
const rejectCompletion = () => {
setAction('reject')
setReqStatus({loading:true, status: false, message: ''}) // Sets loading spinner active
let reqData = { // API PAYLOADS
contract: jobDetails.contract,
contract_uid: jobDetails.contract_uid,
job_action: 'REJECT_COMPLETE',
}
// API CALL TO REJECT COMPLETION BY OWNER
apiCall.ownerJobAction(reqData).then((res)=>{
if(res.status != 200 || res.data.internal_return < 0){
setReqStatus({loading:false, status: false, message: 'unable to complete request. Try again'})
return
}
setReqStatus({loading:false, status: true, message: 'job completion rejected successfully'})
setTimeout(()=>{ // Sets popout to false and navigates user to /my-review-jobs after 3 seconds
popUpHandler()
navigate('/my-review-jobs', {replace: true})
}, 3000)
}).catch(err => {
setReqStatus({loading:false, status: false, message: 'Opps! Network error. Try again'})
}).finally(()=>{
setTimeout(()=>{
setReqStatus({loading:false, status: false, message: ''})
}, 3000)
})
}
return (
<div className='job-action'>
<p className="my-3 py-1 text-base">
<table className="w-full text-sm text-left text-gray-500 dark:text-gray-400 review-owner">
<div className='job-action bg-white dark:bg-black'>
<div className="my-3 py-1 text-base">
<table className="w-full text-sm text-left text-gray-500 review-owner">
<tbody>
<tr>
<td>
@@ -20,7 +108,7 @@ function ReviewJobAction() {
<tr>
<td>
<div className="flex justify-center items-center">
<button type="button" className="w-120 h-11 flex justify-center items-center btn-gradient text-base rounded-full text-white">
<button type="button" onClick={popUpHandler} className="w-[150px] h-11 flex justify-center items-center btn-gradient text-base rounded-full text-white">
Reject or Accept
</button>
</div>
@@ -28,7 +116,109 @@ function ReviewJobAction() {
</tr>
</tbody>
</table>
</p>
</div>
{popUp && (
<ModalCom action={popUpHandler} situation={popUp}>
<div className="logout-modal-wrapper lg:w-[460px] 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 dark:border-[#5356fb29] border-light-purple">
<h1 className="text-26 font-bold text-dark-gray dark:text-white tracking-wide">
Completion
</h1>
<button
type="button"
className="text-[#374557] dark:text-red-500"
onClick={popUpHandler}
>
<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 px-10 py-8 gap-4">
<div className="w-full flex flex-col items-center">
<div className="mb-5 flex justify-center items-center gap-2">
<input
type='checkbox'
checked={checked}
onChange={()=>{setChecked(prev => !prev)}}
className='w-6 h-6 text-sky-blue bg-gray-100 focus:ring-sky-blue'
/>
<p className='font-bold text-base tracking-wide text-dark-gray dark:text-white'>I am ready to accept completion</p>
</div>
<div className="mb-5 flex justify-center items-center">
{reqStatus.loading && action=='accept'?
<LoadingSpinner color='sky-blue' size='10' />
:
<button disabled={reqStatus.loading} onClick={acceptCompletion} type="button" className="px-2 py-1 h-11 flex justify-center items-center border-gradient text-base rounded-full text-white">
<span className='text-gradient'>Accept Completion</span>
</button>
}
</div>
{/* EXTEND TIMELINE SECTION */}
<div className='w-full my-3 py-3 border-y flex flex-col items-center'>
<div className='mb-5 flex items-center gap-2'>
<p className='font-bold text-base tracking-wide text-dark-gray dark:text-white'>I am not ready to accept completion of task</p>
</div>
{reqStatus.loading && action=='reject' ?
<LoadingSpinner color='sky-blue' size='10' />
:
<button disabled={reqStatus.loading} type="button" onClick={rejectCompletion} className="px-2 py-1 h-11 flex justify-center items-center btn-gradient text-base rounded-full text-white">
Reject Completion
</button>
}
</div>
{/* FOR SUCCESS/ERROR DISPLAY SECTION*/}
<div className="w-full">
{reqStatus.message != "" &&
(!reqStatus.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]`}
>
{reqStatus.message}
</div>
) : (
reqStatus.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]`}
>
{reqStatus.message}
</div>
)
))}
</div>
{/* END OF FOR SUCCESS/ERROR DISPLAY SECTION*/}
</div>
{/* 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>
</div>
</div>
</div>
</ModalCom>
)}
</div>
)
}
@@ -2,8 +2,8 @@ import React from 'react'
function ReviewTaskAction() {
return (
<div className='job-action'>
<p className="my-3 py-1 text-base">
<div className='job-action bg-white dark:bg-black'>
<p className="my-3 py-1 text-base text-dark-gray dark:text-white">
Waiting for the completion message from the client before you can approve. Worker True & Review Job
</p>
</div>
@@ -1,17 +1,13 @@
import React, { useState } from "react";
import { useLocation, useNavigate } from "react-router-dom";
import dataImage2 from "../../assets/images/data-table-user-2.png";
import { useNavigate, useLocation } from "react-router-dom";
import { handlePagingFunc } from "../Pagination/HandlePagination";
import PaginatedList from "../Pagination/PaginatedList";
import { PriceFormatter } from "../Helpers/PriceFormatter";
export default function MyActiveJobTable({ MyJobList, className }) {
const navigate = useNavigate();
let {pathname} = useLocation()
const filterCategories = ["All Categories", "Explore", "Featured"];
const [selectedCategory, setCategory] = useState(filterCategories[0]);
let { pathname } = useLocation();
const [currentPage, setCurrentPage] = useState(0);
const indexOfFirstItem = Number(currentPage);
@@ -44,82 +40,92 @@ export default function MyActiveJobTable({ MyJobList, className }) {
{
<>
{MyJobList &&
MyJobList?.result_list &&
MyJobList.result_list.length > 0 ?
currentActiveJobList.map((value, index) => (
<tr
key={index}
className="bg-white dark:bg-dark-white border-b dark:border-[#5356fb29] hover:bg-gray-50"
>
<td className=" py-4">
<div className="flex space-x-2 items-center w-full">
<div className="w-full h-[60px] rounded-full overflow-hidden flex justify-center items-center flex-[0.1] max-w-[60px]">
<img
src={dataImage2}
alt="data"
className="w-full h-full"
/>
MyJobList?.result_list &&
MyJobList.result_list.length > 0 ? (
currentActiveJobList.map((value, index) => {
let deliveryDate = value?.delivery_date?.split(" ")[0];
let thePrice = PriceFormatter(
value?.price * 0.01,
value?.currency_code,
value?.currency
);
return (
<tr
key={index}
className="bg-white dark:bg-dark-white border-b dark:border-[#5356fb29] hover:bg-gray-50"
>
<td className=" py-4">
<div className="flex space-x-2 items-center w-full">
<div className="w-full h-[60px] rounded-full overflow-hidden flex justify-center items-center flex-[0.1] max-w-[60px]">
<img
src={dataImage2}
alt="data"
className="w-full h-full"
/>
</div>
<div className="flex flex-col flex-[0.9]">
<h1 className="font-bold text-xl text-dark-gray dark:text-white">
{value.title}
</h1>
<div>{value.description}</div>
<span className="text-sm text-thin-light-gray flex flext-start gap-1">
Price:{" "}
<span className="text-purple">
{thePrice}
</span>
</span>
<div className="flex gap-4 items-center">
<span className="text-sm text-thin-light-gray">
Duration:{" "}
<span className="text-purple">
{" "}
{value.timeline_days} day(s)
</span>
</span>
<span className="text-sm text-thin-light-gray">
Due:{" "}
<span className="text-purple">
{" "}
{deliveryDate}
</span>
</span>
<span className="text-sm text-thin-light-gray">
Sent to:{" "}
<span className="text-purple">
{" "}
{value.job_to === null
? "public"
: value.job_to}
</span>
</span>
</div>
</div>
</div>
<div className="flex flex-col flex-[0.9]">
<h1 className="font-bold text-xl text-dark-gray dark:text-white">
{value.title}
</h1>
<div>{value.description}</div>
<span className="text-sm text-thin-light-gray">
Price:{" "}
<span className="text-purple">
{value.price * 0.01}
</span>
</span>
<span className="text-sm text-thin-light-gray">
Duration:{" "}
<span className="text-purple">
{" "}
{value.timeline_days} day(s)
</span>
</span>
<span className="text-sm text-thin-light-gray">
Expire:{" "}
<span className="text-purple">
{" "}
{value.expire}
</span>
</span>
<span className="text-sm text-thin-light-gray">
Send to:{" "}
<span className="text-purple">
{" "}
{value.job_to}
</span>
</span>
</div>
</div>
</td>
</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>
))
:
<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>
);
})
) : (
<tr className="font-bold text-xl text-dark-gray dark:text-white whitespace-nowrap">
<td className="p-2">
No Active Task!
</td>
<td className="p-2">No Active Task!</td>
</tr>
}
)}
</>
}
</tbody>
+28 -30
View File
@@ -5,38 +5,36 @@ import CommonHead from "../UserHeader/CommonHead";
import MyActiveJobTable from "./MyActiveJobTable";
export default function MyPastDueJobs(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"}`}
>
Pass Due Job(s)
Pass 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 -3
View File
@@ -10,11 +10,10 @@ export default function MyActiveJobs(props) {
setValue(value);
};
console.log("AMEYE LOC1", props.MyJobList);
return (
<Layout>
<CommonHead
commonHeadData={props.commonHeadData}
/>
<CommonHead commonHeadData={props.commonHeadData} />
<div className="notification-page w-full mb-10">
<div className="notification-wrapper w-full">
{/* heading */}
+68 -9
View File
@@ -1,6 +1,57 @@
import { useMemo, useState } from "react";
import usersService from "../../services/UsersService";
import { useDispatch } from "react-redux";
import { tableReload } from "../../store/TableReloads";
const CouponPopup = ({ popUpHandler, data }) => {
const [loader, setLoader] = useState(false);
const apiCall = useMemo(() => new usersService(), []);
const [statusMsg, setStatusMsg] = useState({
success: "",
error: "",
});
const dispatch = useDispatch();
const redeemCouponHandler = async () => {
setLoader(true);
try {
const { code, coupon_id } = data;
const reqData = { code_id: Number(coupon_id), code };
const res = await apiCall.getCouponRedeem(reqData);
if (res.data?.internal_return < 0)
setStatusMsg({ error: "An error occurred" });
else setStatusMsg({ success: res.data?.status_text });
dispatch(tableReload({ type: "COUPONTABLE" }));
setTimeout(() => {
popUpHandler();
setLoader(false);
}, 10000);
} catch (error) {
setLoader(false);
if (error?.status !== 200)
setStatusMsg({
error: {
status: true,
msg:
error?.statusText !== ""
? error?.statusText
: "An error occurred",
},
});
throw new Error(error);
} finally {
setStatusMsg({
success: "",
error: "",
});
}
};
return (
<div className="logout-modal-wrapper lg:w-[460px] 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 dark:border-[#5356fb29] border-light-purple">
@@ -39,22 +90,30 @@ const CouponPopup = ({ popUpHandler, data }) => {
Amount:
</label>
</div>
<div className=" flex-[0.7] max-w-[150px]">{`${data?.amount} Naira`}</div>
<div className=" flex-[0.7] max-w-[150px]">{data?.thePrice}</div>
</div>
<div className="signin-area w-full">
<div className="flex justify-end">
<button
// type="button"
className="text-white btn-gradient text-lg tracking-wide px-6 py-2 rounded-full"
type="button"
onClick={redeemCouponHandler}
className="text-white lg:w-[167px] btn-gradient flex justify-center items-center text-lg tracking-wide px-6 py-2 rounded-full"
>
{/* {loader ? (
{loader ? (
<div className="signup btn-loader"></div>
) : (
)} */}
<span className="font-semibold tracking-wider">Redeem Now!</span>
) : (
<span className="font-semibold tracking-wider">
Redeem Now!
</span>
)}
</button>
</div>
{statusMsg.success && (
<p className="text-sm text-green-500 italic">{statusMsg.success}</p>
)}
{statusMsg.error && (
<p className="text-sm text-red-500 italic">{statusMsg.error}</p>
)}
</div>
</form>
<div className="signin-area w-full px-5 py-4">
+11 -4
View File
@@ -4,6 +4,7 @@ import { handlePagingFunc } from "../Pagination/HandlePagination";
import ModalCom from "../Helpers/ModalCom";
import CouponPopup from "./CouponPopup";
import { useNavigate } from "react-router-dom";
import { PriceFormatter } from "../Helpers/PriceFormatter";
function CouponTable({ coupon }) {
const [currentPage, setCurrentPage] = useState(0);
@@ -38,12 +39,18 @@ function CouponTable({ coupon }) {
</thead>
{coupon.data.length ? (
<tbody>
{currentCoupon.map((item, index) => (
{currentCoupon.map((item, index) => {
let thePrice = PriceFormatter(
item?.amount * 0.01,
item?.currency_code,
item?.currency
);
return(
<tr key={index} className="text-slate-500">
<td className="p-2 cursor-default">
{item.added} <br /> {item.code}
</td>
<td className="p-2 text-center cursor-default">{`${item.amount} Naira`}</td>
<td className="p-2 text-center cursor-default">{thePrice}</td>
<td className="p-2 h-20 flex items-center justify-end">
<button
type="button"
@@ -51,7 +58,7 @@ function CouponTable({ coupon }) {
onClick={() => {
setCouponPopup((prev) => ({
state: !prev.state,
data: item,
data: {...item, thePrice},
}));
}}
>
@@ -59,7 +66,7 @@ function CouponTable({ coupon }) {
</button>
</td>
</tr>
))}
)})}
</tbody>
) : coupon.error ? (
<tbody>
+8 -6
View File
@@ -1,11 +1,13 @@
import React, { useEffect, useState } from "react";
import React, { useCallback, useEffect, useMemo, useState } from "react";
import Layout from "../Partials/Layout";
import LoadingSpinner from "../Spinners/LoadingSpinner";
import CouponTable from "./CouponTable";
import usersService from "../../services/UsersService";
import { useSelector } from "react-redux";
export default function MyCoupons() {
const apiCall = new usersService();
const apiCall = useMemo(() => new usersService(), []);
const {couponTable} = useSelector(state => state.tableReload)
let [couponHistory, setCouponHistory] = useState({
// FOR COUPON HISTORY
loading: true,
@@ -14,7 +16,7 @@ export default function MyCoupons() {
});
//FUNCTION TO GET COUPON HISTORY
const getCouponHistory = () => {
const getCouponHistory = useCallback(() => {
apiCall
.getCouponPending()
.then((res) => {
@@ -32,11 +34,11 @@ export default function MyCoupons() {
.catch((error) => {
setCouponHistory((prev) => ({ ...prev, loading: false, error: true }));
});
};
}, [apiCall]);
useEffect(() => {
getCouponHistory();
}, []);
}, [couponTable]);
return (
<>
@@ -59,7 +61,7 @@ export default function MyCoupons() {
<LoadingSpinner size="16" color="sky-blue" />
</div>
) : (
<CouponTable coupon={couponHistory} />
<CouponTable coupon={couponHistory} />
)}
</div>
</div>
+151 -118
View File
@@ -1,18 +1,37 @@
import React, { useCallback, useEffect, useMemo, useState } from "react";
import { useCallback, useEffect, useMemo, useState } from "react";
import dataImage2 from "../../assets/images/data-table-user-2.png";
import SelectBox from "../Helpers/SelectBox";
import DeleteJobPopout from "../jobPopout/DeleteJobPopout";
import JobListPopout from "../jobPopout/JobListPopout";
import LoadingSpinner from "../Spinners/LoadingSpinner";
import { useSelector } from "react-redux";
import usersService from "../../services/UsersService";
import { handlePagingFunc } from "../Pagination/HandlePagination";
import PaginatedList from "../Pagination/PaginatedList";
import EditJobPopOut from "../jobPopout/EditJobPopout";
import { PriceFormatter } from "../Helpers/PriceFormatter";
import EditIcon from '../../assets/images/icon-edit.svg'
import DeleteIcon from '../../assets/images/icon-delete.svg'
export default function MyJobTable({ MyJobList, reloadJobList, className }) {
// Getting the categories
const currentJobCart = MyJobList?.data?.categories;
// DropDown Box
const filterCategories = { All: "All Categories", ...currentJobCart };
const [selectedCategory, setCategory] = useState(
Object.keys(filterCategories)[0]
);
let [jobPopout, setJobPopout] = useState({ show: false, data: {} }); // STATE TO HOLD THE VALUE OF THE ALERT DETAILS AND DETERMINE WHEN TO SHOW
let [deleteJobPopout, setDeleteJobPopout] = useState({
show: false,
data: {},
}); // STATE TO HOLD THE VALUE OF THE ITEM DETAILS TO DELETE AND DETERMINE WHEN TO SHOW
const [editJob, setEditJob] = useState({ show: false, data: {} });
const [myCountry, setCountries] = useState("");
const {
userDetails: { country },
@@ -44,22 +63,25 @@ export default function MyJobTable({ MyJobList, reloadJobList, className }) {
getCountryList();
}, [getCountryList]);
const filterCategories = ["All Categories", "Explore", "Featured"];
const [selectedCategory, setCategory] = useState(filterCategories[0]);
let [jobPopout, setJobPopout] = useState({ show: false, data: {} }); // STATE TO HOLD THE VALUE OF THE ALERT DETAILS AND DETERMINE WHEN TO SHOW
let [deleteJobPopout, setDeleteJobPopout] = useState({
show: false,
data: {},
}); // STATE TO HOLD THE VALUE OF THE ITEM DETAILS TO DELETE AND DETERMINE WHEN TO SHOW
const [editJob, setEditJob] = useState({ show: false, data: {} });
// Handle Pagination
const [currentPage, setCurrentPage] = useState(0);
const indexOfFirstItem = Number(currentPage);
const indexOfLastItem =
Number(indexOfFirstItem) + Number(process.env.REACT_APP_ITEM_PER_PAGE);
const currentJobList = MyJobList?.data?.result_list?.slice(
// Handle Filter Job List
const filterJobList = () => {
if (selectedCategory === "All") return MyJobList?.data?.result_list;
else
return MyJobList?.data?.result_list?.filter((item) =>
item.category.includes(selectedCategory)
);
};
const currentJobList = filterJobList();
// Handling Filter Pagination
const filteredCurrentJobList = currentJobList?.slice(
indexOfFirstItem,
indexOfLastItem
);
@@ -68,6 +90,97 @@ export default function MyJobTable({ MyJobList, reloadJobList, className }) {
handlePagingFunc(e, setCurrentPage);
};
// Handles the category selection
const handleSetCategory = (value) => {
setCurrentPage(0);
for (let i in filterCategories) {
if (filterCategories[i] == value) {
setCategory(i);
}
}
};
const JobListItem = ({ value, index }) => {
let thePrice = PriceFormatter(
value?.price * 0.01,
value?.currency_code,
value?.currency
);
return (
<tr
key={index}
className="bg-white dark:bg-dark-white border-b dark:border-[#5356fb29] hover:bg-gray-50"
>
<td className="py-9">
<div className="sm:flex sm:space-x-2 sm:justify-between sm:items-center job-items">
<div className="flex space-x-2 items-center job-items w-full">
<div className="w-full h-[60px] rounded-full overflow-hidden flex justify-center items-center flex-[0.1] min-w-[60px] max-w-[60px]">
<img src={dataImage2} alt="data" className="w-full h-full" />
</div>
<div className="flex flex-col flex-[0.9]">
<h1 className="font-bold text-xl text-dark-gray dark:text-white">
{value.title}
</h1>
<div>{value.description}</div>
<span className="text-sm text-thin-light-gray flex items-start gap-1">
Price: <span className="text-purple">{thePrice}</span>
</span>
<span className="text-sm text-thin-light-gray">
Duration:{" "}
<span className="text-purple">
{" "}
{value.timeline_days} day(s)
</span>
</span>
</div>
</div>
<div className="h-[33px] w-[150px] flex flex-nowrap items-center self-end">
<button
type="button"
className="p-1 border-2 border-red-400 rounded-md"
onClick={() => {
setDeleteJobPopout({
show: true,
data: { thePrice, ...value },
});
}}
>
<img className="w-[21px] h-[21px]" src={DeleteIcon} alt='delete-icon' />
</button>
<div className="mx-[4px] h-full w-[1px] bg-black dark:bg-dark-gray"></div>
<button
type="button"
className="p-1 border-2 border-sky-blue rounded-md flex items-center"
onClick={() => {
setEditJob({
show: true,
data: { thePrice, ...value },
});
}}
>
<img className="w-[21px] h-[21px]" src={EditIcon} alt='edit-icon' />
<span className="text-sm text-sky-blue">Edit</span>
</button>
</div>
</div>
</td>
<td className="text-right py-9 px-2">
<button
type="button"
onClick={() => {
setJobPopout({ show: true, data: { thePrice, ...value } });
}}
className="w-20 h-11 flex justify-center items-center btn-gradient text-base rounded-full text-white"
>
Manage
</button>
</td>
</tr>
);
};
return (
<div
className={`update-table w-full p-8 bg-white dark:bg-dark-white overflow-hidden rounded-2xl section-shadow min-h-[520px] ${
@@ -77,12 +190,12 @@ export default function MyJobTable({ MyJobList, reloadJobList, className }) {
<div className="header w-full flex justify-between items-center mb-5">
<div className="flex space-x-2 items-center">
<h1 className="text-xl font-bold text-dark-gray dark:text-white tracking-wide">
All Jobs
{filterCategories[selectedCategory]} Jobs
</h1>
</div>
</div>
<SelectBox
action={setCategory}
datas={filterCategories}
action={handleSetCategory}
datas={Object.values(filterCategories)}
className="Update-table-dropdown"
contentBodyClasses="w-auto min-w-max"
/>
@@ -93,108 +206,27 @@ export default function MyJobTable({ MyJobList, reloadJobList, className }) {
<div className="relative w-full overflow-x-auto sm:rounded-lg flex flex-col justify-between h-full">
<table className="table-auto min-w-full text-sm text-left text-gray-500 dark:text-gray-400">
<tbody>
<tr className="text-base text-thin-light-gray border-b default-border-b dark:border-[#5356fb29] ottom ">
<td className="py-1">.</td>
<td className="py-1 text-right">.</td>
</tr>
{selectedCategory === "All Categories" ? (
<>
{MyJobList &&
MyJobList?.data?.result_list &&
MyJobList.data?.result_list.length > 0 ?
currentJobList.map((value, index) => (
<tr
key={index}
className="bg-white dark:bg-dark-white border-b dark:border-[#5356fb29] hover:bg-gray-50"
>
<td className="py-9">
<div className="sm:flex sm:space-x-2 sm:justify-between sm:items-center job-items">
<div className="flex space-x-2 items-center job-items w-full">
<div className="w-full h-[60px] rounded-full overflow-hidden flex justify-center items-center flex-[0.1] min-w-[60px] max-w-[60px]">
<img
src={dataImage2}
alt="data"
className="w-full h-full"
/>
</div>
<div className="flex flex-col flex-[0.9]">
<h1 className="font-bold text-xl text-dark-gray dark:text-white">
{value.title}
</h1>
<div>{value.description}</div>
<span className="text-sm text-thin-light-gray">
Price:{" "}
<span className="text-purple">
{value.price * 0.01}
</span>
</span>
<span className="text-sm text-thin-light-gray">
Duration:{" "}
<span className="text-purple">
{" "}
{value.timeline_days} day(s)
</span>
</span>
</div>
</div>
<div className="min-w-[110px] max-w-[110px] bg-yellow-300 mx-2 rounded-md flex flex-nowrap space-x-2 justify-center items-center self-end">
<button
type="button"
className="p-2 w-[60px] h-11"
onClick={() => {
setDeleteJobPopout({
show: true,
data: value,
});
}}
>
[Delete]
</button>
<span>|</span>
<button
type="button"
className="p-2 w-[40px] h-11"
onClick={() => {
setEditJob({
show: true,
data: value,
});
}}
>
Edit
</button>
</div>
</div>
</td>
<td className="text-right py-9 px-2">
<button
type="button"
onClick={() => {
setJobPopout({ show: true, data: value });
}}
className="w-20 h-11 flex justify-center items-center btn-gradient text-base rounded-full text-white"
>
Manage
</button>
</td>
</tr>
<>
{MyJobList &&
MyJobList?.data?.result_list &&
MyJobList.data?.result_list.length > 0 ? (
filteredCurrentJobList?.length ? (
filteredCurrentJobList.map((value, index) => (
<JobListItem index={index} key={index} value={value} />
))
:
) : (
<tr className="font-bold text-xl text-dark-gray dark:text-white whitespace-nowrap">
<td className="p-2">
No Jobs Avaliable!
No Jobs Available In This Category!
</td>
</tr>
}
</>
) : selectedCategory === "Explore" ? (
<></>
) : (
<></>
)}
)
) : (
<tr className="font-bold text-xl text-dark-gray dark:text-white whitespace-nowrap">
<td className="p-2">No Jobs Avaliable!</td>
</tr>
)}
</>
</tbody>
</table>
@@ -204,11 +236,11 @@ export default function MyJobTable({ MyJobList, reloadJobList, className }) {
prev={currentPage == 0 ? true : false}
next={
currentPage + Number(process.env.REACT_APP_ITEM_PER_PAGE) >=
MyJobList?.data?.result_list?.length
currentJobList?.length
? true
: false
}
data={MyJobList?.data?.result_list}
data={currentJobList}
start={indexOfFirstItem}
stop={indexOfLastItem}
/>
@@ -252,6 +284,7 @@ export default function MyJobTable({ MyJobList, reloadJobList, className }) {
}}
situation={editJob.show}
country={myCountry}
categories={currentJobCart}
/>
)}
</div>
+39 -27
View File
@@ -1,52 +1,64 @@
import React, { useState } from "react";
import { Link } from "react-router-dom";
import React, { useEffect, useState } from "react";
import { useLocation, useNavigate } from "react-router-dom";
import Layout from "../Partials/Layout";
import MyJobTable from "./MyJobTable";
import CommonHead from "../UserHeader/CommonHead";
import AddJobPage from "../../views/AddJobPage";
export default function MyJobs(props) {
const [selectTab, setValue] = useState("today");
const filterHandler = (value) => {
setValue(value);
let { state } = useLocation();
const navigate = useNavigate();
const [popUp, setPopUp] = useState(false);
const popUpHandler = () => {
setPopUp((prev) => !prev);
if (state?.popup) navigate("/", { replace: true })
else return
};
const categoryOptions = props.MyJobList?.data?.categories;
useEffect(() => {
if (!state?.popup) {
setPopUp(false);
} else {
setPopUp(true);
}
}, [state?.popup]);
return (
<Layout>
<CommonHead
commonHeadData={props.commonHeadData}
/>
<CommonHead commonHeadData={props.commonHeadData} />
<div className="notification-page w-full mb-10">
<div className="notification-wrapper w-full">
{/* heading */}
<div className="sm:flex justify-between items-center mb-6">
<div className="sm:flex items-center mb-6">
<div className="mb-5 sm:mb-0">
<h1 className="text-26 font-bold flex items-center space-x-1 text-dark-gray dark:text-white gap-2">
<span
className={`${selectTab === "today" ? "block" : "hidden"}`}
>
My Jobs
</span>
<span>My Jobs</span>
<Link
to="/add-job"
<button
className="text-white btn-gradient text-lg tracking-wide px-5 py-2 rounded-full"
onClick={popUpHandler}
>
Add Job
</Link>
</button>
</h1>
</div>
<div className="slider-btns flex space-x-4">
<div
onClick={() => filterHandler("today")}
className="relative"
></div>
</div>
</div>
<MyJobTable
MyJobList={props.MyJobList}
/>
<MyJobTable MyJobList={props.MyJobList} />
</div>
</div>
{/* Add Job List Popout */}
{popUp && (
<AddJobPage
action={popUpHandler}
situation={popUp}
categories={categoryOptions}
/>
)}
{/* End of Add Job List Popout */}
</Layout>
);
}

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