Compare commits

...

710 Commits

Author SHA1 Message Date
victorAnumudu a9cf3c9d22 added breadcrumb to some pages in full account type 2024-03-07 16:51:24 +01:00
ameye 01bf8a4c52 Merge branch 'login-page-width' of WrenchBoard/Users-Wrench into master 2024-03-07 12:50:47 +00:00
victorAnumudu 68ab094bf6 reduced login page width 2024-03-07 13:42:16 +01:00
ameye d938202f7a Merge branch 'home-banners-dashboard' of WrenchBoard/Users-Wrench into master 2024-03-07 12:25:12 +00:00
Ebube c9f66bdacb Completed layout for family activities 2024-03-07 10:01:43 +01:00
Ebube 5854ad194a Added layout for activities page 2024-03-07 09:23:14 +01:00
ameye 0f25a92b88 Merge branch 'home-banners-dashboard' of WrenchBoard/Users-Wrench into master 2024-03-06 22:01:34 +00:00
Ebube 9d7bcd91f4 . 2024-03-06 20:48:54 +01:00
Ebube 68115e79fa Added the following changes 2024-03-06 18:52:07 +01:00
ameye de379c2bbc Merge branch 'manage-job-padding' of WrenchBoard/Users-Wrench into master 2024-03-06 17:42:38 +00:00
victorAnumudu 2c11a0755d manage job page padding reduction 2024-03-06 18:21:58 +01:00
ameye f4ed892c5c Merge branch 'family-wallet-page' of WrenchBoard/Users-Wrench into master 2024-03-06 16:24:49 +00:00
ameye 30403f27c5 Merge branch 'home-banners-dashboard' of WrenchBoard/Users-Wrench into master 2024-03-06 16:24:44 +00:00
victorAnumudu cf2df7529d added family wallet page 2024-03-06 16:12:55 +01:00
Ebube b3695324b3 Removed next due task countdown and modified my page format 2024-03-06 15:21:55 +01:00
ameye cf8f32ed64 Merge branch 'past-due-task' of WrenchBoard/Users-Wrench into master 2024-03-06 11:00:07 +00:00
victorAnumudu bd59f26146 added past due task page 2024-03-06 00:00:27 +01:00
ameye 3a397aad86 Merge branch 'family-home-active-task' of WrenchBoard/Users-Wrench into master 2024-03-05 14:51:16 +00:00
victorAnumudu 0f548e216d removed active task display from family home page 2024-03-05 15:49:04 +01:00
ameye 50fd9711e0 Merge branch 'breadcrumb-addition' of WrenchBoard/Users-Wrench into master 2024-03-04 17:46:41 +00:00
victorAnumudu f4d9eb65c6 added breadcrumb component to other family account pages 2024-03-04 18:12:02 +01:00
ameye 774224ba6d Merge branch 'home-banners-dashboard' of WrenchBoard/Users-Wrench into master 2024-03-04 16:46:09 +00:00
Ebube 9eae733755 changed to square brackets and reduced font 2024-03-04 17:34:11 +01:00
ameye 0aee3b9d6f Merge branch 'home-banners-dashboard' of WrenchBoard/Users-Wrench into master 2024-03-04 15:29:11 +00:00
Ebube b676a2a4f3 username replacement and Active task window feedback 2024-03-04 15:16:41 +01:00
ameye 0baacb3057 Merge branch 'family-blog-population' of WrenchBoard/Users-Wrench into master 2024-03-04 14:14:58 +00:00
victorAnumudu 9737c02d45 added family blog api resources 2024-03-04 15:05:31 +01:00
ameye 1048e51ddf Merge branch 'family-commonheader' of WrenchBoard/Users-Wrench into master 2024-03-04 10:52:33 +00:00
victorAnumudu 6545c32326 removed common header in family single blog post page 2024-03-04 01:29:22 +01:00
ameye 6ea9078848 Merge branch 'family-file-resource' of WrenchBoard/Users-Wrench into master 2024-03-01 20:55:47 +00:00
victorAnumudu 735cc0b296 corrected text error 2024-03-01 21:36:28 +01:00
ameye 29e058828b Merge branch 'family-resource-pages' of WrenchBoard/Users-Wrench into master 2024-03-01 20:13:32 +00:00
victorAnumudu 5d5542c221 merged master to branch 2024-03-01 21:05:03 +01:00
ameye b59f92c89f Merge branch 'bug-fixing' of WrenchBoard/Users-Wrench into master 2024-03-01 19:58:07 +00:00
victorAnumudu e01bfa369b created pages for family resources 2024-03-01 18:49:23 +01:00
victorAnumudu 3d150f7a6b array length bug fixed 2024-03-01 15:18:20 +01:00
tokslaw bbd100d04e Merge branch 'resources-linking' of WrenchBoard/Users-Wrench into master 2024-03-01 00:35:21 +00:00
victorAnumudu fcb340007c linked family resources 2024-02-29 21:52:11 +01:00
ameye 95a10cf795 Merge branch 'home-banners-dashboard' of WrenchBoard/Users-Wrench into master 2024-02-29 19:48:34 +00:00
Ebube 5b5a083987 Added Home Img on Home btn 2024-02-29 20:06:18 +01:00
ameye 1564f29a4c Merge branch 'family-home-offerlist' of WrenchBoard/Users-Wrench into master 2024-02-29 17:35:02 +00:00
ameye 2b73191741 Merge branch 'parent-waiting-page' of WrenchBoard/Users-Wrench into master 2024-02-29 17:34:57 +00:00
victorAnumudu fee5c20fe2 removed offerlist from family home page 2024-02-29 17:09:47 +01:00
victorAnumudu 2d2b184291 parent waiting page top brought to top 2024-02-29 16:40:13 +01:00
ameye 5abd3665d1 Merge branch 'home-banners-dashboard' of WrenchBoard/Users-Wrench into master 2024-02-29 15:01:45 +00:00
Ebube a603a9eaff Merge branch 'master' of https://gitlab.chiefsoft.net/WrenchBoard/Users-Wrench into home-banners-dashboard 2024-02-29 15:54:35 +01:00
Ebube 5bd74b3ca4 Added new stuff 2024-02-29 15:54:11 +01:00
Ebube 60cc290004 reduced component 2024-02-29 13:05:44 +01:00
ameye 12f30ec1e8 Merge branch 'banner-pages' of WrenchBoard/Users-Wrench into master 2024-02-29 11:46:56 +00:00
victorAnumudu 2521fbc1d8 more pages created for family banner links 2024-02-29 12:38:35 +01:00
ameye 29812e9265 Merge branch 'family-banner-linking' of WrenchBoard/Users-Wrench into master 2024-02-29 09:35:47 +00:00
victorAnumudu 271180932c linked banner to page 2024-02-29 10:32:27 +01:00
ameye 0a2e291013 Merge branch 'home-banners-dashboard' of WrenchBoard/Users-Wrench into master 2024-02-28 18:00:00 +00:00
ameye 8be09a9f9f Merge branch 'parent-waiting' of WrenchBoard/Users-Wrench into master 2024-02-28 17:59:53 +00:00
Ebube 78aa4fe58b Added padding 2024-02-28 18:38:52 +01:00
victorAnumudu 154ea29504 added parent waiting page 2024-02-28 18:38:19 +01:00
ameye 4f670fa3ba Merge branch 'resources-banner-fix' of WrenchBoard/Users-Wrench into master 2024-02-28 16:49:51 +00:00
victorAnumudu 8d48435705 reduced family resources banner 2024-02-28 17:40:32 +01:00
ameye 76109dc458 Merge branch 'implemented-breadcrumb' of WrenchBoard/Users-Wrench into master 2024-02-28 16:00:09 +00:00
victorAnumudu 05a7b3ec12 added breadcrumb to my task page 2024-02-28 16:54:54 +01:00
ameye 0a3805ddf8 Merge branch 'family-banner-fix' of WrenchBoard/Users-Wrench into master 2024-02-28 15:20:57 +00:00
victorAnumudu 059fdaf294 family banner img height reduced 2024-02-28 15:48:59 +01:00
ameye 353c372284 Merge branch 'home-banners-dashboard' of WrenchBoard/Users-Wrench into master 2024-02-28 11:18:52 +00:00
ameye 26e5248a0f Merge branch 'family-button-reduction' of WrenchBoard/Users-Wrench into master 2024-02-28 11:18:47 +00:00
ameye ae6a677b25 Merge branch 'market-date-format' of WrenchBoard/Users-Wrench into master 2024-02-28 11:18:43 +00:00
Ebube cab5debfb7 Added icon toggler 2024-02-28 11:41:17 +01:00
Ebube c4c1bf6ca1 added dark mode feature to right sidebar 2024-02-28 11:20:29 +01:00
Ebube fc2a78dde7 changed the background 2024-02-28 11:05:58 +01:00
victorAnumudu 1249685c41 reduced family banner button 2024-02-28 10:26:13 +01:00
victorAnumudu 3c422d9036 formatted date style 2024-02-28 10:08:26 +01:00
Ebube 264511dfb2 adjusted home dash sizes 2024-02-28 08:30:43 +01:00
Ebube d8c5b3ff07 home_dash board sizing 2024-02-28 07:49:57 +01:00
Ebube 624f97df8d Merge branch 'master' of https://gitlab.chiefsoft.net/WrenchBoard/Users-Wrench into home-banners-dashboard 2024-02-28 07:48:31 +01:00
Ebube 4dfb442a80 responsiveness check 2024-02-28 07:47:23 +01:00
ameye 50f675e82b Merge branch 'home-banners-dashboard' of WrenchBoard/Users-Wrench into master 2024-02-28 04:52:03 +00:00
CHIEFSOFT\ameye a0584a26fe show new dash 2024-02-27 20:50:54 -05:00
Ebube 0d3025c4d0 Reduced fonts 2024-02-27 21:47:10 +01:00
ameye ed24fea513 Merge branch 'home-banners-dashboard' of WrenchBoard/Users-Wrench into master 2024-02-27 19:57:44 +00:00
ameye e6d83be4d9 Merge branch 'job-link-hidden' of WrenchBoard/Users-Wrench into master 2024-02-27 19:57:38 +00:00
Ebube 36e3154933 Merge branch 'master' of https://gitlab.chiefsoft.net/WrenchBoard/Users-Wrench into home-banners-dashboard 2024-02-27 19:36:19 +01:00
Ebube 5d59ff1b30 Reduced component height 2024-02-27 19:35:53 +01:00
victorAnumudu 33832fdd96 made quick job link hidden in family account 2024-02-27 18:57:16 +01:00
ameye 9f02395214 Merge branch 'home-banners-dashboard' of WrenchBoard/Users-Wrench into master 2024-02-27 17:38:03 +00:00
ameye 9cc88eebf6 Merge branch 'family-resources' of WrenchBoard/Users-Wrench into master 2024-02-27 17:37:45 +00:00
Ebube 2390670d8c added top banner 2024-02-27 18:35:49 +01:00
victorAnumudu d908b1e10e added family resources API 2024-02-27 18:10:54 +01:00
ameye d91a3bf9f0 Merge branch 'banner-match' of WrenchBoard/Users-Wrench into master 2024-02-27 12:39:53 +00:00
victorAnumudu 880c223834 matched the image height in family banner to full member banner 2024-02-27 13:27:32 +01:00
ameye e7bf07d09e Merge branch 'home-banners-dashboard' of WrenchBoard/Users-Wrench into master 2024-02-27 10:47:10 +00:00
ameye e1de701ea6 Merge branch 'family-header' of WrenchBoard/Users-Wrench into master 2024-02-27 10:47:03 +00:00
Ebube 3403210fe8 Populated the lower component 2024-02-27 11:36:53 +01:00
victorAnumudu 03a897829c changed family header bg color 2024-02-27 10:24:23 +01:00
victorAnumudu d884b2a43d added header on family home page 2024-02-27 10:15:13 +01:00
ameye 3b86c6cd0e Merge branch 'home-banners-dashboard' of WrenchBoard/Users-Wrench into master 2024-02-26 23:43:47 +00:00
Ebube 2b5de9c95f Added the first 3 banners 2024-02-27 00:39:51 +01:00
ameye 3a8a980660 Merge branch 'banner-api-caching' of WrenchBoard/Users-Wrench into master 2024-02-26 17:26:16 +00:00
victorAnumudu 06186b3c0f cache banner API using redux 2024-02-26 18:07:58 +01:00
ameye bddbe6ceb4 Merge branch 'home-banners-dashboard' of WrenchBoard/Users-Wrench into master 2024-02-26 15:46:14 +00:00
Ebube dd802ab22e increase size of dots 2024-02-26 16:38:57 +01:00
Ebube 8c363765ae Merge branch 'master' of https://gitlab.chiefsoft.net/WrenchBoard/Users-Wrench into home-banners-dashboard 2024-02-26 16:34:58 +01:00
Ebube f597c207fa Banner Area Layout 2024-02-26 16:34:08 +01:00
ameye dc07cff959 Merge branch 'asidebar-link' of WrenchBoard/Users-Wrench into master 2024-02-26 14:35:12 +00:00
victorAnumudu 38766c9cb7 changed Dashboard aside link name to Home 2024-02-26 15:30:30 +01:00
ameye fc04855d3f Merge branch 'banner-card' of WrenchBoard/Users-Wrench into master 2024-02-26 13:17:11 +00:00
victorAnumudu 56cb956482 adjusted banner card design 2024-02-26 14:07:15 +01:00
ameye 2f5abc8931 Merge branch 'home-banners-dashboard' of WrenchBoard/Users-Wrench into master 2024-02-26 10:39:34 +00:00
ameye 026dbb1a0a Merge branch 'family-banner-bug' of WrenchBoard/Users-Wrench into master 2024-02-26 10:39:23 +00:00
Ebube efd464ba8a Merge branch 'master' of https://gitlab.chiefsoft.net/WrenchBoard/Users-Wrench into home-banners-dashboard 2024-02-26 02:49:58 +01:00
Ebube 8c400c7ce1 fixed market bug, fixed key issue and added texts fork the cards 2024-02-26 02:49:35 +01:00
victorAnumudu d3d2b3cb5a changed to three cards banner 2024-02-26 02:40:25 +01:00
victorAnumudu 3054a5eb9a remove unused API from family login 2024-02-26 01:55:52 +01:00
Ebube 670f4bbf1a Added color to component and toggler to the env 2024-02-26 00:12:29 +01:00
ameye ae73f10c42 Merge branch 'family-banners' of WrenchBoard/Users-Wrench into master 2024-02-25 22:14:19 +00:00
victorAnumudu de477f32ed added banner to family dashboard banner list 2024-02-25 21:16:38 +01:00
ameye 2c6e36aea9 Merge branch 'home-banners-dashboard' of WrenchBoard/Users-Wrench into master 2024-02-23 20:12:12 +00:00
ameye dc3aa1470e Merge branch 'family_new_dashboard' of WrenchBoard/Users-Wrench into master 2024-02-23 20:12:07 +00:00
victorAnumudu 9029953432 added familybanners API 2024-02-23 17:29:18 +01:00
Ebube fa47de7292 Added empty component to the dashboard 2024-02-23 17:19:41 +01:00
victorAnumudu d0a2b804b9 configured family new dashboard to show only on dev 2024-02-23 16:27:17 +01:00
victorAnumudu aa48529dca configured family new dashboard to show only on dev 2024-02-23 16:15:46 +01:00
ameye ad2745dfce Merge branch 'section-addition' of WrenchBoard/Users-Wrench into master 2024-02-19 17:37:50 +00:00
victorAnumudu b6ab3a6ee7 added new section to family acct dashboard 2024-02-19 14:09:20 +01:00
ameye f968cc5a50 Merge branch 'home-banners-dashboard' of WrenchBoard/Users-Wrench into master 2024-02-17 16:41:08 +00:00
Ebube 1ddb2fd903 remove mypage component from family 2024-02-17 16:27:09 +01:00
ameye 70f6ac4e24 Merge branch 'failedtopup-alert' of WrenchBoard/Users-Wrench into master 2024-02-15 18:50:06 +00:00
victorAnumudu 3d30481852 displays failed popout box for failed top up transaction 2024-02-15 13:49:05 +01:00
ameye 63c0b07f61 Merge branch 'home-banners-dashboard' of WrenchBoard/Users-Wrench into master 2024-02-03 23:31:58 +00:00
ameye 60568c42e8 Merge branch 'number-fix' of WrenchBoard/Users-Wrench into master 2024-02-03 23:31:51 +00:00
Ebube 77ac52820d added the email and offed the autocomplete attr for the component 2024-02-04 00:29:09 +01:00
victorAnumudu 240e075305 assign offer message updated 2024-02-03 22:49:15 +01:00
victorAnumudu bea41d8181 group count fixed 2024-02-03 22:09:25 +01:00
ameye 6268d68b67 Merge branch 'home-banners-dashboard' of WrenchBoard/Users-Wrench into master 2024-02-03 20:31:19 +00:00
Ebube f96b16b373 Added the number identifier for job list 2024-02-03 21:18:49 +01:00
ameye 9fb6a4db86 Merge branch 'download-app-links' of WrenchBoard/Users-Wrench into master 2024-02-02 11:26:36 +00:00
victorAnumudu 1aa3c79666 added download app links to signup page 2024-02-02 09:42:51 +01:00
ameye ecc2360dc4 Merge branch 'app-download-link' of WrenchBoard/Users-Wrench into master 2024-02-01 20:49:27 +00:00
victorAnumudu ca9bb1c211 changed class to classname 2024-02-01 19:43:36 +01:00
victorAnumudu 2f756d189a added app download links on login page 2024-02-01 19:34:01 +01:00
ameye d7acea769c Merge branch 'home-banners-dashboard' of WrenchBoard/Users-Wrench into master 2024-01-30 17:58:31 +00:00
Ebube 7ff5e2b6e0 . 2024-01-30 17:10:39 +01:00
Ebube 2f90c4a6c2 Different dashboards for Home Banner 2024-01-30 17:10:30 +01:00
ameye 29e0345e1c Merge branch 'Jobs-Manager-Side' of WrenchBoard/Users-Wrench into master 2024-01-27 00:26:56 +00:00
Ebube 129cdc8cba Merge branch 'master' of https://gitlab.chiefsoft.net/WrenchBoard/Users-Wrench into Jobs-Manager-Side 2024-01-26 19:17:58 +01:00
Ebube 9d42ebebab added sidebar menu 2024-01-26 19:17:28 +01:00
ameye 6193f0b492 Merge branch 'Jobs-Manager-Side' of WrenchBoard/Users-Wrench into master 2024-01-26 18:03:56 +00:00
Ebube 1d86465812 Changed my pending jobs to offers and changed its routes 2024-01-26 17:44:24 +01:00
CHIEFSOFT\ameye 7a6f43ad20 Merge branch 'master' of https://gitlab.chiefsoft.net/WrenchBoard/Users-Wrench 2024-01-26 10:31:56 -05:00
CHIEFSOFT\ameye 37b94a68ca new layout 2024-01-26 10:31:45 -05:00
ameye a1b63c5d0c Merge branch 'Jobs-Manager-Side' of WrenchBoard/Users-Wrench into master 2024-01-26 14:49:22 +00:00
Ebube e5162a2aaf refactored some things 2024-01-26 01:46:56 +01:00
Ebube 6ee9c7e7d8 added .toFixed(2)} 2024-01-26 01:08:07 +01:00
Ebube 5928ddb3c1 added the popup component 2024-01-26 01:07:20 +01:00
ameye 06c1e339b1 Merge branch 'login-box-fix' of WrenchBoard/Users-Wrench into master 2024-01-25 22:18:49 +00:00
victorAnumudu 29cec58122 login box adjustment 2024-01-25 21:54:31 +01:00
ameye d258158a13 Merge branch 'bg-change' of WrenchBoard/Users-Wrench into master 2024-01-25 18:01:41 +00:00
victorAnumudu 1c47fa283a Merge branch 'master' into bg-change 2024-01-25 18:57:08 +01:00
victorAnumudu d1b07e6e66 added background based on country 2024-01-25 18:55:51 +01:00
ameye 648f228e45 Merge branch 'Jobs-Manager-Side' of WrenchBoard/Users-Wrench into master 2024-01-25 15:50:17 +00:00
Ebube 551a302ede added the amount checker 2024-01-25 16:39:13 +01:00
ameye 108c82b2f8 Merge branch 'family-type' of WrenchBoard/Users-Wrench into master 2024-01-25 12:44:16 +00:00
victorAnumudu f46713ef00 Merge branch 'master' into family-type 2024-01-25 08:17:59 +01:00
victorAnumudu 992993b710 family type dropdown added 2024-01-25 08:16:55 +01:00
ameye 617df4200e Merge branch 'Jobs-Manager-Side' of WrenchBoard/Users-Wrench into master 2024-01-24 21:57:28 +00:00
Ebube a47e398e87 Added background color to the group list component 2024-01-24 20:41:25 +01:00
ameye 281c4c7ab7 Merge branch 'relative-list' of WrenchBoard/Users-Wrench into master 2024-01-24 13:12:48 +00:00
victorAnumudu 3c901e5d0d fixed relative list 2024-01-24 13:43:12 +01:00
ameye 3bcbaae4c3 Merge branch 'add-relative' of WrenchBoard/Users-Wrench into master 2024-01-24 12:19:30 +00:00
ameye 17e972d603 Merge branch 'Jobs-Manager-Side' of WrenchBoard/Users-Wrench into master 2024-01-24 12:19:23 +00:00
victorAnumudu dbc821a804 added relative list 2024-01-24 10:12:06 +01:00
Ebube 0e4b1af1ce Jobs Manager Side task done 2024-01-24 01:11:49 +01:00
CHIEFSOFT\ameye ec88f304ab remove grp icon 2024-01-23 08:20:35 -05:00
ameye 8d795a29b4 Merge branch 'actions-variables' of WrenchBoard/Users-Wrench into master 2024-01-23 12:24:14 +00:00
ameye da12f5905c Merge branch 'link-icons' of WrenchBoard/Users-Wrench into master 2024-01-23 12:24:09 +00:00
victorAnumudu 99d4301588 added link icons 2024-01-23 10:32:29 +01:00
Ebube 8139fbb090 added third 2024-01-23 09:33:56 +01:00
Ebube 926837c656 added second batch 2024-01-23 09:00:30 +01:00
Ebube 14c894da6c first batch 2024-01-23 07:33:57 +01:00
CHIEFSOFT\ameye dbdc028ed4 api const 2024-01-22 11:49:49 -05:00
CHIEFSOFT\ameye c3a5a2fe34 left image 2024-01-18 11:31:35 -05:00
ameye eed2e62f62 Merge branch 'new-layout' of WrenchBoard/Users-Wrench into master 2024-01-18 14:34:53 +00:00
victorAnumudu 824f46fd9f dummy bg added 2024-01-17 21:36:05 +01:00
victorAnumudu ff5a73bec7 determines when to show new login layout from env variable 2024-01-17 20:45:34 +01:00
victorAnumudu ad37885445 determines when to show new login layout from env variable 2024-01-17 20:43:49 +01:00
victorAnumudu 0c3425a97a new layout pages duplicated 2024-01-17 20:28:43 +01:00
victorAnumudu 92ca50e02f added style className 2024-01-17 19:55:48 +01:00
victorAnumudu 4eed8abac1 added new layout auth layout page 2024-01-17 19:52:49 +01:00
victorAnumudu a6627555d9 added new layout for login page 2024-01-17 19:51:44 +01:00
victorAnumudu e127c79df2 added new layout for signup page 2024-01-17 19:50:46 +01:00
victorAnumudu 00278e32b6 added new layout for forget password page 2024-01-17 19:50:00 +01:00
victorAnumudu 255da58588 added new layout for verify link page 2024-01-17 19:49:17 +01:00
victorAnumudu 0464025eff added new layout for verify password page 2024-01-17 19:48:06 +01:00
victorAnumudu c300d0d7f4 added new layout for verify you page 2024-01-17 19:47:14 +01:00
ameye 47ae14bc7a Merge branch 'memberpage' of WrenchBoard/Users-Wrench into master 2024-01-16 04:23:21 +00:00
Ebube 185ca14750 Clean up and auto refresh 2024-01-13 19:06:35 +01:00
ameye 1ef92207fb Merge branch 'memberpage' of WrenchBoard/Users-Wrench into master 2024-01-10 16:54:06 +00:00
ameye 0b8cf50088 Merge branch 'alignment-fix' of WrenchBoard/Users-Wrench into master 2024-01-10 16:54:02 +00:00
Ebube 57be599bb5 added the pageload api 2024-01-10 17:41:52 +01:00
victorAnumudu 9ec512777e assign task text added 2024-01-09 18:31:05 +01:00
ameye 9dde87277c Merge branch 'new-tab-style' of WrenchBoard/Users-Wrench into master 2024-01-09 14:37:06 +00:00
victorAnumudu a176e8e6d5 assign job tabs style changed 2024-01-09 15:24:14 +01:00
ameye 8106f6a049 Merge branch 'memberpage' of WrenchBoard/Users-Wrench into master 2024-01-09 12:46:44 +00:00
Ebube aa26a63fcf Merge branch 'master' of https://gitlab.chiefsoft.net/WrenchBoard/Users-Wrench into memberpage 2024-01-09 13:31:41 +01:00
Ebube 3fbcfe5797 Done for now 2024-01-09 13:30:32 +01:00
Ebube 510545bf82 refactor: Your Page Code 2024-01-09 12:59:11 +01:00
ameye 7a149b87bd Merge branch 'memberpage' of WrenchBoard/Users-Wrench into master 2024-01-08 23:30:22 +00:00
ameye 391f6e0abd Merge branch 'modal-display-adjustment' of WrenchBoard/Users-Wrench into master 2024-01-08 23:30:14 +00:00
Ebube 057691b2f9 added more things 2024-01-09 00:21:29 +01:00
Ebube d085636843 Merge branch 'master' of https://gitlab.chiefsoft.net/WrenchBoard/Users-Wrench into memberpage 2024-01-08 23:37:48 +01:00
Ebube 0150caf4bb Added New Page 2024-01-08 23:37:13 +01:00
victorAnumudu dd580cadfd modal display box centered 2024-01-08 20:56:38 +01:00
ameye a59588d4a7 Merge branch 'assign-tabs' of WrenchBoard/Users-Wrench into master 2024-01-08 18:49:40 +00:00
victorAnumudu 393b4e7b37 change popout class style 2024-01-08 19:48:03 +01:00
victorAnumudu b2411285bc added assign tabs 2024-01-08 19:21:15 +01:00
ameye 091faec9bd Merge branch 'family-image' of WrenchBoard/Users-Wrench into master 2024-01-08 12:42:50 +00:00
victorAnumudu 15fc2e6c9b displays assign to group 2024-01-08 13:37:38 +01:00
ameye 3ef68adf7e Merge branch 'assign-to-group' of WrenchBoard/Users-Wrench into master 2024-01-08 09:33:17 +00:00
victorAnumudu 71f987cbff made group offer display when show offer to group is true 2024-01-08 09:47:48 +01:00
victorAnumudu e8c3dca731 assign task to group implementation 2024-01-07 19:45:32 +01:00
ameye 85d15ac2f1 Merge branch 'add-group-member' of WrenchBoard/Users-Wrench into master 2024-01-07 11:57:34 +00:00
ameye 236dfeae2c Merge branch 'add-group-member-form' of WrenchBoard/Users-Wrench into master 2024-01-07 11:57:29 +00:00
victorAnumudu 734acd9dd5 placeholder renamed 2024-01-07 08:01:11 +01:00
victorAnumudu 3f9534b968 placeholder renamed 2024-01-07 07:59:54 +01:00
victorAnumudu 13d69efbaf changed group member form style 2024-01-07 07:57:06 +01:00
victorAnumudu 151aa4ba0c made selected group active after adding a member 2024-01-05 20:00:55 +01:00
ameye 4985256c3a Merge branch 'group-page-bug-fixed' of WrenchBoard/Users-Wrench into master 2024-01-05 17:14:12 +00:00
victorAnumudu 5c552fe006 fixed group list member bug 2024-01-05 18:05:52 +01:00
ameye b14272f040 Merge branch 'job-groups-page' of WrenchBoard/Users-Wrench into master 2024-01-05 13:21:43 +00:00
victorAnumudu 5e4887c01c added pagination for member list table 2024-01-05 09:57:45 +01:00
victorAnumudu f6e55718bb add group api added 2024-01-04 22:45:32 +01:00
victorAnumudu 98ddc5aaae added dummy job groups page 2024-01-03 19:58:07 +01:00
ameye 53d8b0f411 Merge branch 'app-download-route' of WrenchBoard/Users-Wrench into master 2023-12-20 14:01:55 +00:00
victorAnumudu b3d33f87cf added app download route 2023-12-20 14:39:17 +01:00
ameye 5922064c42 Merge branch 'expire-session-clear' of WrenchBoard/Users-Wrench into master 2023-12-18 12:57:03 +00:00
victorAnumudu eae8dc43ec clears expire session value after 5secs 2023-12-18 13:40:36 +01:00
ameye 34e6322126 Merge branch 'signup-country' of WrenchBoard/Users-Wrench into master 2023-12-18 08:59:28 +00:00
victorAnumudu b3db82000b result_list array as list of countries 2023-12-18 09:53:42 +01:00
CHIEFSOFT\ameye fa565437f0 download links 2023-12-12 15:49:22 -05:00
ameye 59f407ba26 Merge branch 'edit_job' of WrenchBoard/Users-Wrench into master 2023-12-12 12:41:02 +00:00
ameye e7afc12334 Merge branch 'Server-path-added' of WrenchBoard/Users-Wrench into master 2023-12-12 12:40:54 +00:00
victorAnumudu 5f81c0b847 edit job categpry text color for dark mode added 2023-12-12 06:03:03 +01:00
ebube 0756747143 updated payload 2023-12-11 20:30:44 -08:00
ameye 99eb1591a2 Merge branch 'Server-path-added' of WrenchBoard/Users-Wrench into master 2023-12-11 19:46:01 +00:00
ameye 85b9aab229 Merge branch 'upload_filename' of WrenchBoard/Users-Wrench into master 2023-12-11 19:45:55 +00:00
ebube 67a0a34288 Fixed a typo 2023-12-10 10:19:56 -08:00
ebube 814e4c9693 Fixed the amount in naira add credit payload to flutterwave 2023-12-03 23:55:43 -08:00
victorAnumudu e74eb38caf defaulted filename to myfile 2023-11-29 19:56:28 +01:00
ameye 67db1a72bf Merge branch 'Server-path-added' of WrenchBoard/Users-Wrench into master 2023-11-29 14:32:03 +00:00
ebube 41e46d8030 Reverted updates 2023-11-29 06:30:36 -08:00
ebube 8707411dda added a linux checker 2023-11-28 21:02:17 -08:00
ameye 327a4a42ae Merge branch 'Server-path-added' of WrenchBoard/Users-Wrench into master 2023-11-29 01:33:58 +00:00
ebube 455f4001f7 Final Check 2023-11-28 09:18:54 -08:00
ebube 775bcd5005 Clean up...added feature to edit popup 2023-11-28 09:11:52 -08:00
ebube 492bda021f Select Option Bug Fixed 2023-11-28 07:41:31 -08:00
ebube eacfae19f0 Currency Bug Fix 2023-11-28 07:26:34 -08:00
ebube 376cf44a9c Currency option set to fixed and category option set 2023-11-28 06:41:32 -08:00
ameye 1aab0c2910 Merge branch 'upload_download_link' of WrenchBoard/Users-Wrench into master 2023-11-28 11:56:05 +00:00
victorAnumudu 1f5fb8c7c3 upload file bug fixed 2023-11-28 11:50:16 +01:00
victorAnumudu a303b24b53 upload file bug fixed 2023-11-28 11:33:59 +01:00
victorAnumudu 322927045c Merge master into upload_download_link 2023-11-28 03:18:45 +01:00
victorAnumudu 3cf1dc7166 added download link# 2023-11-28 03:17:23 +01:00
ameye 8ed331e909 Merge branch 'wrong-upload-placeholder' of WrenchBoard/Users-Wrench into master 2023-11-28 01:42:14 +00:00
ameye 2428923f73 Merge branch 'file_link_bug' of WrenchBoard/Users-Wrench into master 2023-11-28 01:41:53 +00:00
victorAnumudu 139f87c919 corrected wrong placeholders 2023-11-28 02:16:49 +01:00
victorAnumudu c24fbedf2c file link updated 2023-11-27 12:49:18 +01:00
ameye 1a817e723e Merge branch 'Server-path-added' of WrenchBoard/Users-Wrench into master 2023-11-27 11:31:03 +00:00
ebube c023911cd4 Fixed prev card issue 2023-11-27 03:19:39 -08:00
ameye 907dc298ab Merge branch 'file_link_update' of WrenchBoard/Users-Wrench into master 2023-11-27 10:32:02 +00:00
ameye 8095006386 Merge branch 'Server-path-added' of WrenchBoard/Users-Wrench into master 2023-11-27 10:31:57 +00:00
ebube d9d28791da Added and specified card_uid for prev and new card 2023-11-27 01:49:09 -08:00
ebube a98d967ba4 Fixed the credit issues 2023-11-27 01:12:24 -08:00
victorAnumudu 59a5e84ee2 uploaded link for file added 2023-11-27 07:00:58 +01:00
ameye 35ed1d48a3 Merge branch 'zero-bal-job-add' of WrenchBoard/Users-Wrench into master 2023-11-23 11:12:47 +00:00
victorAnumudu f9d385dfec made user to be able to add job even with zero wallet bal 2023-11-23 12:04:29 +01:00
ameye 033af03221 Merge branch 'Server-path-added' of WrenchBoard/Users-Wrench into master 2023-11-22 03:26:54 +00:00
ebube b3afb94030 Added family banner 2023-11-21 15:47:02 -08:00
ameye 0b1aa4e7d9 Merge branch 'Server-path-added' of WrenchBoard/Users-Wrench into master 2023-11-21 16:18:24 +00:00
ebube 18c4f31322 seen the issue 2023-11-21 08:02:11 -08:00
ebube 97e3344953 Added tabs as individual components 2023-11-21 07:32:36 -08:00
ebube 9565ca0d35 Fixed typo causing error displays in the console.log 2023-11-21 07:22:33 -08:00
ameye 51cc4edc1d Merge branch 'assign-task-message-bug' of WrenchBoard/Users-Wrench into master 2023-11-21 14:15:49 +00:00
victorAnumudu d12a1fd579 fixed error message display 2023-11-21 15:04:22 +01:00
ameye 21f1173e66 Merge branch 'Server-path-added' of WrenchBoard/Users-Wrench into master 2023-11-21 14:03:58 +00:00
ebube cf5ae81918 Added tabs for family settings 2023-11-21 05:58:57 -08:00
ameye 235bb7d667 Merge branch 'wallet-reload' of WrenchBoard/Users-Wrench into master 2023-11-21 11:25:59 +00:00
ameye 18a319a437 Merge branch 'Server-path-added' of WrenchBoard/Users-Wrench into master 2023-11-21 11:25:54 +00:00
victorAnumudu d3e2cc6744 added wallet reload when user accepts interest offer 2023-11-13 19:38:29 +01:00
victorAnumudu a08d8feb7d made wallet to reload when parent confirms task completion 2023-11-13 16:35:54 +01:00
Chief Bube 7cb5cc558b added box 2023-11-12 12:55:13 -08:00
ameye 36025ade56 Merge branch 'completed-job-history' of WrenchBoard/Users-Wrench into master 2023-11-12 11:01:25 +00:00
victorAnumudu b80c21358a tab bug fix 2023-11-11 22:11:02 +01:00
victorAnumudu f5ad68f7bb added completed job tab history 2023-11-11 22:06:35 +01:00
ameye 27cba1ac68 Merge branch 'Server-path-added' of WrenchBoard/Users-Wrench into master 2023-11-11 17:04:11 +00:00
Chief Bube dbb21d8a08 added a few to page 2023-11-11 08:13:26 -08:00
Chief Bube a4597599b7 added icon and page for family settings 2023-11-11 08:10:02 -08:00
ameye 894f986574 Merge branch 'holdings-removal' of WrenchBoard/Users-Wrench into master 2023-11-11 14:03:32 +00:00
victorAnumudu e42a358da6 removed holdings from childs wallet 2023-11-11 14:59:11 +01:00
ameye 23c2e840c2 Merge branch 'server-image-replacement' of WrenchBoard/Users-Wrench into master 2023-11-11 13:31:29 +00:00
victorAnumudu 7e7176ae5c server icon fix 2023-11-11 07:03:54 +01:00
victorAnumudu 306fa1300d replaced images to server images on mytask route 2023-11-11 06:26:00 +01:00
ameye 7b3178bd1a Merge branch 'add-family-btn-bug' of WrenchBoard/Users-Wrench into master 2023-11-10 17:50:11 +00:00
victorAnumudu 5e94ddee48 fixed add family btn 2023-11-09 07:09:24 +01:00
ameye 32171ae3ce Merge branch 'Server-path-added' of WrenchBoard/Users-Wrench into master 2023-11-08 19:47:53 +00:00
Chief Bube 2c99fedd8c removing session once user logs out or session expires 2023-11-08 10:58:42 -08:00
Chief Bube 7e926cc7bc added family image 2023-11-08 10:42:54 -08:00
ameye 587bcaa411 Merge branch 'family-wallet-copy' of WrenchBoard/Users-Wrench into master 2023-11-08 17:56:42 +00:00
ameye 756628ee1a Merge branch 'image-bug' of WrenchBoard/Users-Wrench into master 2023-11-08 17:56:37 +00:00
victorAnumudu 575710a807 image bug fixed 2023-11-08 18:26:50 +01:00
victorAnumudu c77d8e5693 merged from master branch 2023-11-08 18:18:06 +01:00
victorAnumudu 7e9d734e6f added copy wallet for family member 2023-11-08 18:15:21 +01:00
ameye 9ddb127bd3 Merge branch 'job-image-icon' of WrenchBoard/Users-Wrench into master 2023-11-08 16:24:00 +00:00
victorAnumudu a85e5fdb91 changed default icons to server icons 2023-11-08 09:41:06 +01:00
victorAnumudu 09ad8a94ca changed default icons to server icons 2023-11-08 09:39:28 +01:00
Chief Bube 0c1db6b4b5 fixed svg typos 2023-11-07 23:00:12 -08:00
ameye 6ddffdf2e6 Merge branch 'Server-path-added' of WrenchBoard/Users-Wrench into master 2023-11-08 04:29:31 +00:00
Chief Bube 5043540abb Added conditionals to the paths to avoid breakage and resolved some images in family account 2023-11-07 09:24:28 -08:00
ameye 5f39accdd6 Merge branch 'pend-interest' of WrenchBoard/Users-Wrench into master 2023-11-07 16:20:23 +00:00
victorAnumudu b5aeaf59a4 changed from offer_uid to job_uid 2023-11-07 17:13:50 +01:00
ameye f3e63d2ef6 Merge branch 'default-icon' of WrenchBoard/Users-Wrench into master 2023-11-07 13:52:19 +00:00
victorAnumudu 44a055e76b Merge master into default-icon 2023-11-07 14:22:49 +01:00
victorAnumudu 67c1b2aaa0 placed default job icons 2023-11-07 14:20:19 +01:00
ameye 313cfc1f28 Merge branch 'edit-job-bug-fix' of WrenchBoard/Users-Wrench into master 2023-11-07 12:40:34 +00:00
victorAnumudu 59a7f110be Fixed save job bug 2023-11-07 07:35:40 +01:00
ameye 52e8447f6a Merge branch 'Server-path-added' of WrenchBoard/Users-Wrench into master 2023-11-06 23:33:16 +00:00
Chief Bube c60e28928a changed to job_uid 2023-11-06 15:29:23 -08:00
ameye 58357fd501 Merge branch 'Server-path-added' of WrenchBoard/Users-Wrench into master 2023-11-06 21:15:03 +00:00
Chief Bube 5402980d6c Chnged path from profile to job 2023-11-06 11:15:11 -08:00
ameye 32c7b3d513 Merge branch 'Server-path-added' of WrenchBoard/Users-Wrench into master 2023-11-06 18:56:32 +00:00
Chief Bube a30b22170d Added images server path 2023-11-06 10:53:26 -08:00
ameye e3314b0460 Merge branch 'job-image-upload-fix' of WrenchBoard/Users-Wrench into master 2023-11-06 18:01:42 +00:00
victorAnumudu 86eb1f16bb fixed job image upload 2023-11-06 19:00:28 +01:00
ameye 5660d74e75 Merge branch 'job-image-upload' of WrenchBoard/Users-Wrench into master 2023-11-06 15:48:53 +00:00
victorAnumudu 612016784d added job image upload API 2023-11-06 16:34:41 +01:00
ameye 988aadfb7e Merge branch 'Server-path-added' of WrenchBoard/Users-Wrench into master 2023-11-06 14:28:14 +00:00
ameye 54851b5f77 Merge branch 'family-image-bug' of WrenchBoard/Users-Wrench into master 2023-11-06 14:27:50 +00:00
victorAnumudu 91f3b92138 Merge master into family-image-bug 2023-11-06 15:24:06 +01:00
victorAnumudu f48de9d65f upload bug fixed 2023-11-06 15:22:56 +01:00
Chief Bube c920c35a9c image now works 2023-11-06 06:15:12 -08:00
ameye 7bcaead120 Merge branch 'Server-path-added' of WrenchBoard/Users-Wrench into master 2023-11-06 14:04:08 +00:00
Chief Bube 828f56ed84 Added Image Path for server image 2023-11-06 05:30:30 -08:00
ameye 07118c10a8 Merge branch 'family-profile-image-upload' of WrenchBoard/Users-Wrench into master 2023-11-06 12:13:26 +00:00
victorAnumudu ca5923a7f8 Merge master into family-profile-image-upload 2023-11-06 12:43:20 +01:00
victorAnumudu 4f0d432176 family profile image upload API added 2023-11-06 12:42:10 +01:00
ameye dd3f9d99f3 Merge branch 'Family-Acc-layout-update' of WrenchBoard/Users-Wrench into master 2023-11-06 09:33:43 +00:00
Chief Bube 8807b671ad update market data 2023-11-06 01:25:44 -08:00
victorAnumudu 7020e6d4dc added family profile image upload 2023-11-06 04:10:03 +01:00
ameye d66093a2ad Merge branch 'edit-job-add-image' of WrenchBoard/Users-Wrench into master 2023-11-05 10:53:44 +00:00
victorAnumudu b79eb6b158 Merge master into edit-job-add-image 2023-11-04 17:37:19 +01:00
victorAnumudu a759daaf90 edit job upload image added 2023-11-04 17:35:18 +01:00
ameye 600aec62cf Merge branch 'Family-Acc-layout-update' of WrenchBoard/Users-Wrench into master 2023-11-04 15:54:50 +00:00
Chief Bube 1612bf11ce Added JobOfferCancel 2023-11-04 08:47:42 -07:00
ameye f4e261eb6c Merge branch 'Family-Acc-layout-update' of WrenchBoard/Users-Wrench into master 2023-11-04 08:13:03 +00:00
Chief Bube b622398b6a added assignJobTask function to userServices 2023-11-03 18:47:14 -07:00
ameye e60957e6be Merge branch 'dummy-lnd-page' of WrenchBoard/Users-Wrench into master 2023-11-02 21:35:01 +00:00
victorAnumudu 6a2082d732 added lnd page 2023-11-02 19:01:48 +01:00
ameye 452c6bf8a1 Merge branch 'session-bug' of WrenchBoard/Users-Wrench into master 2023-11-02 01:54:41 +00:00
victorAnumudu 9f89376aa9 Hid text when session expires 2023-11-01 22:11:34 +01:00
ameye 4e91e47978 Merge branch 'session-implement' of WrenchBoard/Users-Wrench into master 2023-11-01 20:09:08 +00:00
ameye fcaa485b17 Merge branch 'history-header-removal' of WrenchBoard/Users-Wrench into master 2023-11-01 20:08:55 +00:00
victorAnumudu e4526652d3 Merge master into session-implement 2023-11-01 20:27:35 +01:00
victorAnumudu 64056bb2a4 changed payment to purchase 2023-11-01 18:45:25 +01:00
victorAnumudu e49c4d66f8 removed table headers if no record is found 2023-11-01 18:39:24 +01:00
ameye 8fdb939b72 Merge branch 'Family-Acc-layout-update' of WrenchBoard/Users-Wrench into master 2023-11-01 17:06:51 +00:00
victorAnumudu 79325450f3 session logout implemented 2023-11-01 18:04:47 +01:00
Chief Bube 7849a027b4 changed fill 2023-11-01 10:04:00 -07:00
ameye 38432a6d50 Merge branch 'Family-Acc-layout-update' of WrenchBoard/Users-Wrench into master 2023-11-01 16:08:13 +00:00
Chief Bube 83a54ff3ef . 2023-11-01 07:37:00 -07:00
Chief Bube 5e0fdffa1e done 2023-11-01 07:34:23 -07:00
ameye 11d2cb3e3a Merge branch 'Added-uid-to-completesignup' of WrenchBoard/Users-Wrench into master 2023-11-01 14:00:31 +00:00
ameye 93ac55b44b Merge branch 'error-page-style' of WrenchBoard/Users-Wrench into master 2023-11-01 14:00:23 +00:00
Chief Bube 237ce13a6c . 2023-11-01 06:40:13 -07:00
Chief Bube 4253174494 . 2023-11-01 06:31:05 -07:00
victorAnumudu da0ed0364b changed the height of the page 2023-11-01 13:43:51 +01:00
ameye 466175c49d Merge branch 'fixing-height-in-intrest-popup' of WrenchBoard/Users-Wrench into master 2023-11-01 10:39:10 +00:00
Chief Bube ed148ce267 Fixed Family Profile Break 2023-10-31 21:42:15 -07:00
ameye 6de795c07b Merge branch 'error-page' of WrenchBoard/Users-Wrench into master 2023-10-31 21:29:49 +00:00
victorAnumudu 436498bef9 added background image to error page 2023-10-31 19:51:36 +01:00
victorAnumudu 086b1202a4 error 404 page adjusted 2023-10-31 16:59:02 +01:00
ameye a81ccdc4d7 Merge branch 'reward-tab-cleanup' of WrenchBoard/Users-Wrench into master 2023-10-31 12:57:55 +00:00
victorAnumudu 33abbbcd2b adjusted reward component contents 2023-10-31 13:53:02 +01:00
ameye 04844af733 Merge branch 'lastname-bug-fix' of WrenchBoard/Users-Wrench into master 2023-10-31 12:42:42 +00:00
victorAnumudu 46d919090d lastname bug fixed 2023-10-31 13:36:53 +01:00
ameye 8e67892f16 Merge branch 'rewards-tab' of WrenchBoard/Users-Wrench into master 2023-10-31 12:21:17 +00:00
victorAnumudu a67d2b7bb4 reward tab clean up 2023-10-31 13:16:33 +01:00
victorAnumudu 99c0c24489 Merge master into rewards-tab 2023-10-31 13:15:22 +01:00
victorAnumudu 1a1d59a8c7 rewards API implemented 2023-10-31 13:13:58 +01:00
ameye 31dcfcfd0b Merge branch 'fixing-height-in-intrest-popup' of WrenchBoard/Users-Wrench into master 2023-10-31 11:45:48 +00:00
Chief Bube e9aa58f2f5 Added tracking text color 2023-10-31 04:42:50 -07:00
victorAnumudu 55fe2bf6d2 Merge master into rewards-tab 2023-10-31 04:23:42 +01:00
victorAnumudu 738eb1589e rewards tab added 2023-10-31 04:22:50 +01:00
ameye 139e0c2827 Merge branch 'send-btn-disable' of WrenchBoard/Users-Wrench into master 2023-10-30 17:43:22 +00:00
victorAnumudu 30642bba14 disabled send btn on successful transfer 2023-10-30 16:29:45 +01:00
ameye f5921612b8 Merge branch 'balance-bug' of WrenchBoard/Users-Wrench into master 2023-10-30 14:43:49 +00:00
victorAnumudu 38afece043 Merge master into balance-bug 2023-10-30 15:40:34 +01:00
victorAnumudu d44447c6b3 returned balance to 2 decimal places 2023-10-30 15:39:38 +01:00
ameye d942c6641a Merge branch 'fixing-height-in-intrest-popup' of WrenchBoard/Users-Wrench into master 2023-10-30 14:12:06 +00:00
Chief Bube beed565ba0 Updated Transaction popup layout 2023-10-30 07:04:01 -07:00
ameye 329e27b83d Merge branch 'family-transfer-bug' of WrenchBoard/Users-Wrench into master 2023-10-28 22:42:25 +00:00
victorAnumudu 3145656d80 checked that required parameters are not empty before outputting success message 2023-10-28 23:39:11 +01:00
victorAnumudu 5098a45bd5 fixed family transfer bug 2023-10-28 23:06:53 +01:00
ameye 9e65a6e7d5 Merge branch 'family-transfer' of WrenchBoard/Users-Wrench into master 2023-10-28 21:33:04 +00:00
victorAnumudu 83ac1087f3 Merge master into family-transfer 2023-10-28 17:48:42 +01:00
victorAnumudu 55b2bccdf0 family transfer API added 2023-10-28 17:47:36 +01:00
ameye 0c51474dd2 Merge branch 'fixing-height-in-intrest-popup' of WrenchBoard/Users-Wrench into master 2023-10-28 12:38:17 +00:00
CHIEFSOFT\ameye 8c7ffb52a5 captcha text 2023-10-28 08:30:28 -04:00
Chief Bube 6c698bc399 fixed array list 2023-10-27 20:37:25 -07:00
ameye 540ad6f9ad Merge branch 'family-start-transfer' of WrenchBoard/Users-Wrench into master 2023-10-27 19:55:00 +00:00
victorAnumudu 11342c32c6 Family start transfer API added 2023-10-27 19:22:54 +01:00
ameye 356c11d029 Merge branch 'fixing-height-in-intrest-popup' of WrenchBoard/Users-Wrench into master 2023-10-26 20:17:15 +00:00
Chief Bube 934d95e822 Added Tracking to Family Profile 2023-10-26 10:25:00 -07:00
ameye 0aa49b49a9 Merge branch 'fixing-height-in-intrest-popup' of WrenchBoard/Users-Wrench into master 2023-10-26 08:22:30 +00:00
Chief Bube be52792271 fixed height issues 2023-10-25 13:51:39 -07:00
ameye c23e61e06b Merge branch 'family-add-fund-modal' of WrenchBoard/Users-Wrench into master 2023-10-25 20:20:19 +00:00
victorAnumudu 24b9a0e348 made amount to align right 2023-10-25 19:37:36 +01:00
victorAnumudu fcb154ba49 request message removed 2023-10-25 19:27:08 +01:00
victorAnumudu c0065ed569 Merge master into family-add-fund-modal 2023-10-25 19:23:06 +01:00
victorAnumudu 05a2ebec61 added family add fund modal 2023-10-25 19:19:55 +01:00
ameye 63f7deaa2b Merge branch 'job-increse-bug-fix' of WrenchBoard/Users-Wrench into master 2023-10-25 14:47:55 +00:00
Chief Bube d762ef42e8 Fixed positioning 2023-10-25 07:43:47 -07:00
ameye 3e34c81789 Merge branch 'job-increse-bug-fix' of WrenchBoard/Users-Wrench into master 2023-10-25 09:15:19 +00:00
Chief Bube a2340af314 tiny bug fix and checker added 2023-10-24 19:09:09 -07:00
Chief Bube 2a445fd2d4 tiny feature added 2023-10-24 18:55:37 -07:00
Chief Bube b5e52fb34e Fixed button position 2023-10-24 18:48:17 -07:00
ameye 87512bbc23 Merge branch 'family-balance' of WrenchBoard/Users-Wrench into master 2023-10-24 18:07:43 +00:00
victorAnumudu 1e59059394 Merge master into family-balance 2023-10-24 17:47:13 +01:00
victorAnumudu e59c83d216 made balance container take whole width 2023-10-24 17:45:10 +01:00
ameye 395569dab5 Merge branch 'job-increse-bug-fix' of WrenchBoard/Users-Wrench into master 2023-10-24 15:47:03 +00:00
Chief Bube 25a537c5bc changed duplicate 2023-10-24 08:25:52 -07:00
Chief Bube 5244a2875e Interest Popup Fix 2023-10-24 08:18:32 -07:00
victorAnumudu 54560b3fec Merge master into family-balance 2023-10-24 15:27:23 +01:00
victorAnumudu 078d26317f family balance added 2023-10-24 15:26:24 +01:00
ameye 8dbf638c7f Merge branch 'interest-popout-size' of WrenchBoard/Users-Wrench into master 2023-10-24 11:39:09 +00:00
CHIEFSOFT\ameye a68af7c3a4 cooment 2023-10-24 06:38:38 -04:00
CHIEFSOFT\ameye 905d48ceb7 package json lock 2023-10-24 06:34:30 -04:00
victorAnumudu 1e5dac0104 adjusted interest popout style 2023-10-24 11:22:51 +01:00
ameye 11e52a02f6 Merge branch 'family-wallet' of WrenchBoard/Users-Wrench into master 2023-10-24 08:51:02 +00:00
ameye 4e74198ae5 Merge branch 'job-increse-bug-fix' of WrenchBoard/Users-Wrench into master 2023-10-24 08:50:55 +00:00
ameye 5d673a630c Merge branch 'banner-fix' of WrenchBoard/Users-Wrench into master 2023-10-24 08:50:47 +00:00
victorAnumudu adcb33764b added family wallet icon 2023-10-24 07:26:27 +01:00
Chief Bube 305bff1167 Job count increase - bug fixed 2023-10-23 22:51:47 -07:00
victorAnumudu 1ebe9fe12b Fixed banner image 2023-10-23 20:37:10 +01:00
ameye 3fbaf6b78a Merge branch 'update-family-validation' of WrenchBoard/Users-Wrench into master 2023-10-23 16:37:19 +00:00
victorAnumudu 421a24c185 added validation to update family component 2023-10-23 17:23:43 +01:00
ameye 6386fbcda6 Merge branch 'family-update' of WrenchBoard/Users-Wrench into master 2023-10-23 11:34:35 +00:00
victorAnumudu 2e09d77597 bug fix 2023-10-23 12:27:54 +01:00
victorAnumudu 10dae9193c added family update component 2023-10-23 12:17:32 +01:00
ameye 3f487337f4 Merge branch 'interest-style-fix' of WrenchBoard/Users-Wrench into master 2023-10-22 18:08:02 +00:00
victorAnumudu 7eabee8855 updated interest popout btn and font style 2023-10-22 19:04:14 +01:00
ameye 360f0500a9 Merge branch 'btn-customization' of WrenchBoard/Users-Wrench into master 2023-10-22 12:16:34 +00:00
victorAnumudu 9b6c5b72ad changed cancel btn style 2023-10-22 05:52:47 +01:00
ameye afc5e25cfc Merge branch 'login-captcha' of WrenchBoard/Users-Wrench into master 2023-10-17 17:40:06 +00:00
victorAnumudu 58fe5eb82c captcha added in login page 2023-10-17 18:26:46 +01:00
CHIEFSOFT\ameye 2f3fdc0695 google captach settings 2023-10-17 07:01:05 -04:00
ameye 0a465fceb0 Merge branch 'btn-height-adjustment' of WrenchBoard/Users-Wrench into master 2023-10-16 20:26:23 +00:00
victorAnumudu 5e968db2f5 adjusted btn height 2023-10-16 21:08:06 +01:00
ameye 1916bc1a65 Merge branch 'btn-rename' of WrenchBoard/Users-Wrench into master 2023-10-16 18:41:55 +00:00
ameye 1103127cae Merge branch 'btn-customized' of WrenchBoard/Users-Wrench into master 2023-10-16 18:41:47 +00:00
ameye 751369071c Merge branch 'family-task-reload' of WrenchBoard/Users-Wrench into master 2023-10-16 18:41:39 +00:00
ameye 4cc8f27c14 Merge branch 'profile-upload-revert' of WrenchBoard/Users-Wrench into master 2023-10-16 18:41:07 +00:00
ameye aab455c2c3 Merge branch 'Changed-Naira-Wallet-Country-Display' of WrenchBoard/Users-Wrench into master 2023-10-16 13:52:03 +00:00
Ebube 21843c4bc7 Changed Naira Wallet Country Display 2023-10-16 13:43:48 +01:00
Ebube 8702728ffa Merge branch 'master' of https://gitlab.chiefsoft.net/WrenchBoard/Users-Wrench into Recent-Activities 2023-10-16 13:12:24 +01:00
victorAnumudu 79dcd0f2b5 btn style customized 2023-10-16 11:49:03 +01:00
ameye 47b25f3dfe Merge branch 'google-recaptcha' of WrenchBoard/Users-Wrench into master 2023-10-16 00:30:06 +00:00
victorAnumudu 89925a6af9 added google recaptcha 2023-10-16 01:18:48 +01:00
CHIEFSOFT\ameye 3c0f951b6e Merge branch 'master' of https://gitlab.chiefsoft.net/WrenchBoard/Users-Wrench 2023-10-15 19:32:29 -04:00
CHIEFSOFT\ameye ffbfb1da35 added "react-google-recaptcha": "^3.1.0" 2023-10-15 19:32:22 -04:00
victorAnumudu a8416a8433 made family page to reload after adding a task 2023-10-10 10:17:22 +01:00
victorAnumudu 70c9d1778c renamed edit job btn to save 2023-10-10 07:02:52 +01:00
Ebube 930559c96b Family Picture 2023-10-09 13:32:07 +01:00
victorAnumudu c4af2dfcc8 changed timeout duration 2023-10-09 11:29:11 +01:00
victorAnumudu b186549b8d added profile upload API 2023-10-09 11:22:38 +01:00
ameye ab191b6a72 Merge branch 'upload-profile' of WrenchBoard/Users-Wrench into master 2023-10-08 12:02:52 +00:00
victorAnumudu d130687cf9 made upload profile to show from env 2023-10-08 08:58:37 +01:00
ameye 5e9a442eac Merge branch 'online-name' of WrenchBoard/Users-Wrench into master 2023-10-07 18:38:23 +00:00
victorAnumudu 1093c25207 added online name 2023-10-07 18:28:30 +01:00
ameye 0623227807 Merge branch 'Recent-Activities' of WrenchBoard/Users-Wrench into master 2023-10-02 13:07:50 +00:00
Ebube 3a05790125 fixed table responsive issue 2023-10-02 12:26:18 +01:00
Ebube 85b0456011 Added price to review due task 2023-09-30 07:18:23 +01:00
ameye 6ddd16ee24 Merge branch 'Recent-Activities' of WrenchBoard/Users-Wrench into master 2023-09-27 23:54:04 +00:00
Ebube d034f9be45 Merge branch 'master' of https://gitlab.chiefsoft.net/WrenchBoard/Users-Wrench into Recent-Activities 2023-09-27 17:33:22 +01:00
Ebube 2f3940f99c fixed responsiveness issues for the layout 2023-09-27 17:31:31 +01:00
ameye 5e998e5e42 Merge branch 'btn-name' of WrenchBoard/Users-Wrench into master 2023-09-27 13:48:05 +00:00
victorAnumudu e113f40c5c btn name changed based on job owner status 2023-09-27 14:13:04 +01:00
Ebube 42a3df2d65 Fixed assign popup data display delay 2023-09-27 13:33:12 +01:00
ameye 3fb996887b Merge branch 'send-to-group' of WrenchBoard/Users-Wrench into master 2023-09-27 07:52:15 +00:00
victorAnumudu 6d2794dd2f displays send to group based on env variable 2023-09-27 06:57:51 +01:00
CHIEFSOFT\ameye 8e683f12a2 env update 2023-09-26 17:07:16 -04:00
ameye 1910ea2bb6 Merge branch 'home-active-task-display' of WrenchBoard/Users-Wrench into master 2023-09-26 20:36:17 +00:00
victorAnumudu 5f6471a16e made active task to display if no offer 2023-09-26 19:35:05 +01:00
ameye 98a4baddab Merge branch 'offer-notice' of WrenchBoard/Users-Wrench into master 2023-09-26 11:49:23 +00:00
ameye 67f8de325f Merge branch 'bug-logout-modal' of WrenchBoard/Users-Wrench into master 2023-09-26 11:49:17 +00:00
victorAnumudu 594d2a4224 displays offerlist if it exist on home page 2023-09-26 12:42:16 +01:00
victorAnumudu edd48e72ec made the close btn on logout modal to appear on light mode 2023-09-26 09:16:50 +01:00
ameye 47c9e1bb6e Merge branch 'delete-family-suggest' of WrenchBoard/Users-Wrench into master 2023-09-25 21:18:45 +00:00
victorAnumudu 2fe80ecbe3 adjusted width of delete btn 2023-09-25 16:58:11 +01:00
victorAnumudu 1640f25d9d delete and send reminder API added 2023-09-25 16:54:43 +01:00
ameye 28f3bbcad1 Merge branch 'Recent-Activities' of WrenchBoard/Users-Wrench into master 2023-09-25 09:56:25 +00:00
ameye c4eca264b1 Merge branch 'send-reminder-modified' of WrenchBoard/Users-Wrench into master 2023-09-25 09:56:15 +00:00
Ebube 89a9e77380 reduced the year length to 17 2023-09-25 07:39:08 +01:00
Ebube 349b524151 Added two select elements for year and month respectively. 2023-09-25 07:30:50 +01:00
victorAnumudu 084370b641 added the correct last time reminded 2023-09-25 05:00:24 +01:00
ameye c2a6cff3eb Merge branch 'send-reminder' of WrenchBoard/Users-Wrench into master 2023-09-24 10:21:07 +00:00
victorAnumudu d45e533d91 Merged master into send-reminder 2023-09-24 07:46:33 +01:00
victorAnumudu c89de2559a added send reminder popout 2023-09-24 07:45:05 +01:00
ameye a5a267927a Merge branch 'Recent-Activities' of WrenchBoard/Users-Wrench into master 2023-09-23 21:02:25 +00:00
ameye 557e0acba4 Merge branch 'add_job_detail' of WrenchBoard/Users-Wrench into master 2023-09-23 21:02:21 +00:00
Ebube 3e4dfadb41 .env 2023-09-22 15:09:18 +01:00
Ebube b480d49096 Merge branch 'master' of https://gitlab.chiefsoft.net/WrenchBoard/Users-Wrench into Recent-Activities 2023-09-22 15:05:07 +01:00
Ebube ece7bf41d3 Added Max 8 for family, added responsive breakpoints for wallet and layout 2023-09-22 15:03:57 +01:00
victorAnumudu 250dd0b171 Add job delivery details textarea tag size increased 2023-09-22 13:26:19 +01:00
victorAnumudu f2475ddd88 Add job delivery details expanded to accept 499 characters 2023-09-22 13:17:45 +01:00
ameye f302ca80d7 Merge branch 'recent-activities-height' of WrenchBoard/Users-Wrench into master 2023-09-19 07:55:39 +00:00
victorAnumudu 11b96e56da reduced the height of each activity and image removed 2023-09-19 06:03:12 +01:00
ameye daad9d18ec Merge branch 'card-adding-limit' of WrenchBoard/Users-Wrench into master 2023-09-18 22:06:47 +00:00
ameye 3c842f6606 Merge branch 'Recent-Activities' of WrenchBoard/Users-Wrench into master 2023-09-18 22:06:40 +00:00
Ebube 23ef007bb1 wallet card sizes and others 2023-09-18 22:45:51 +01:00
Ebube a9b9a17381 Merge branch 'master' of https://gitlab.chiefsoft.net/WrenchBoard/Users-Wrench into Recent-Activities 2023-09-18 22:42:29 +01:00
victorAnumudu d5e66618aa added limit to card and account adding 2023-09-18 20:46:17 +01:00
CHIEFSOFT\ameye 2e89f07ee2 env add 2023-09-18 12:17:54 -04:00
Ebube 1a51bcf0b5 Merge branch 'master' of https://gitlab.chiefsoft.net/WrenchBoard/Users-Wrench into Recent-Activities 2023-09-13 18:16:31 +01:00
Ebube f1ee5150a9 Fixed confirmation page and added comments 2023-09-13 18:16:04 +01:00
ameye 9db7f5c985 Merge branch 'signup-page-pathname-fix' of WrenchBoard/Users-Wrench into master 2023-09-13 06:21:36 +00:00
victorAnumudu d56363276b made the pathname to retain the country query param 2023-09-13 05:12:27 +01:00
ameye 5a9b49559b Merge branch 'signup-page-modification' of WrenchBoard/Users-Wrench into master 2023-09-12 19:14:16 +00:00
victorAnumudu fbc8228977 modified signup page to show select country list or defaults to country passed on search param 2023-09-12 20:05:56 +01:00
victorAnumudu a1140f7006 modified signup page to show select country list or defaults to country passed on search param 2023-09-12 20:05:22 +01:00
ameye f3561ff0fb Merge branch 'signup-country-param' of WrenchBoard/Users-Wrench into master 2023-09-12 15:58:35 +00:00
victorAnumudu 42b792ab9d made country auto filled if pathname contains country value 2023-09-12 15:24:30 +01:00
ameye 1e3a166172 Merge branch 'Recent-Activities' of WrenchBoard/Users-Wrench into master 2023-09-12 10:22:34 +00:00
ameye e1c6cb357e Merge branch 'wallet-fix' of WrenchBoard/Users-Wrench into master 2023-09-12 10:22:26 +00:00
Ebube 1aa64fba1f Merge branch 'master' of https://gitlab.chiefsoft.net/WrenchBoard/Users-Wrench into Recent-Activities 2023-09-12 09:51:01 +01:00
Ebube 4f0a6f67c3 Fixed Layout for confirm add and added comments 2023-09-12 09:50:36 +01:00
victorAnumudu 4f863f7b1d increase the wallet font and made it navigable 2023-09-12 06:28:01 +01:00
ameye b66f9d7ced Merge branch 'job-created-value' of WrenchBoard/Users-Wrench into master 2023-09-11 15:30:21 +00:00
ameye 493f209162 Merge branch 'Recent-Activities' of WrenchBoard/Users-Wrench into master 2023-09-11 15:30:17 +00:00
victorAnumudu 0aa109e280 added job created value to add job popout 2023-09-11 14:45:45 +01:00
Ebube b0a287c6a8 Merge branch 'master' of https://gitlab.chiefsoft.net/WrenchBoard/Users-Wrench into Recent-Activities 2023-09-11 14:29:34 +01:00
Ebube 8c00b157ad Modified the content layout and added formatted date 2023-09-11 14:29:11 +01:00
ameye 85bbd25bbb Merge branch 'add-job-dark-theme' of WrenchBoard/Users-Wrench into master 2023-09-11 11:51:23 +00:00
victorAnumudu 4d7e42fba8 dark theme for add jop popout fixed 2023-09-11 04:28:07 +01:00
ameye f91e8e9910 Merge branch 'Recent-Activities' of WrenchBoard/Users-Wrench into master 2023-09-10 06:00:27 +00:00
ameye 9e9b0b5557 Merge branch 'QR-value-update' of WrenchBoard/Users-Wrench into master 2023-09-10 06:00:17 +00:00
Ebube bfcfcdbf16 added variable for hiding social links 2023-09-10 06:25:36 +01:00
victorAnumudu 7e99e7ccab QR value updated 2023-09-10 05:19:19 +01:00
ameye fd9adb1c08 Merge branch 'QR-value' of WrenchBoard/Users-Wrench into master 2023-09-09 21:15:23 +00:00
victorAnumudu 7f2448efff family QR scan value changed 2023-09-09 21:29:24 +01:00
ameye 82ec224d66 Merge branch 'Recent-Activities' of WrenchBoard/Users-Wrench into master 2023-09-09 06:07:32 +00:00
Ebube 35ea200de5 change btn type to submit 2023-09-09 07:00:18 +01:00
ameye 7cdd387045 Merge branch 'resource_upload' of WrenchBoard/Users-Wrench into master 2023-09-08 19:54:33 +00:00
ameye 6b568ff56c Merge branch 'Recent-Activities' of WrenchBoard/Users-Wrench into master 2023-09-08 19:54:23 +00:00
ameye 8808a2f81c Merge branch 'twitter-logos-added' of WrenchBoard/Users-Wrench into master 2023-09-08 19:54:17 +00:00
Ebube 1991bab675 added active animations for job list popup 2023-09-07 16:53:16 +01:00
victorAnumudu f650097db2 implemented resource video and pdf upload 2023-09-07 12:21:05 +01:00
Ebube c427521ac3 dark mode styles added 2023-09-05 09:51:54 +01:00
victorAnumudu 2412059fd0 added new twitter logo in image folder 2023-09-05 06:36:43 +01:00
ameye 34dc467e8b Merge branch 'Recent-Activities' of WrenchBoard/Users-Wrench into master 2023-09-04 14:37:01 +00:00
Ebube 9fa16278ab Merge branch 'master' of https://gitlab.chiefsoft.net/WrenchBoard/Users-Wrench into Recent-Activities 2023-09-04 14:31:54 +01:00
Ebube cae02d1dc6 . 2023-09-04 14:31:28 +01:00
ameye abbd7b3993 Merge branch 'add-job-bug' of WrenchBoard/Users-Wrench into master 2023-09-04 12:44:22 +00:00
victorAnumudu 5523511524 Add Job Bug fixed 2023-09-04 10:49:04 +01:00
CHIEFSOFT\ameye 35db8ca7ec activities 2023-09-03 17:57:14 -04:00
CHIEFSOFT\ameye 51e0482a16 Hoe activities 2023-09-03 16:34:19 -04:00
ameye a615e25fcb Merge branch 'resources-tab-visibility' of WrenchBoard/Users-Wrench into master 2023-09-03 13:01:03 +00:00
victorAnumudu addede388e made resources tab visible if enabled 2023-09-03 06:26:19 +01:00
CHIEFSOFT\ameye 76474bb3b5 invite 2023-09-02 16:37:29 -04:00
tokslaw 6337c82374 Merge branch 'coupon-wallet-reload' of WrenchBoard/Users-Wrench into master 2023-08-29 16:57:03 +00:00
victorAnumudu a970411719 made wallet to reload on coupon redeem 2023-08-29 09:47:35 +01:00
ameye 10967acf9e Merge branch 'my-uploads-page' of WrenchBoard/Users-Wrench into master 2023-08-22 10:58:13 +00:00
Ebube fb6831d44f An Optimized version 2023-08-21 23:28:32 +01:00
Ebube 7dcae39320 BUG - Assign Job with less than amount 2023-08-21 22:41:21 +01:00
ameye a0ba60a2bc Merge branch 'flutter_key_removal' of WrenchBoard/Users-Wrench into master 2023-08-21 13:12:38 +00:00
victorAnumudu f377326b48 removed flutter api key from env variables 2023-08-21 11:38:51 +01:00
CHIEFSOFT\ameye feb8bd718d all status 2023-08-20 17:07:25 -04:00
ameye a2a2dbfc24 Merge branch 'my-uploads-page' of WrenchBoard/Users-Wrench into master 2023-08-18 10:46:24 +00:00
Ebube 1c0cb6c97f added the session parameter 2023-08-15 21:02:51 +01:00
ameye 41c9c60fd1 Merge branch 'terms-of-use-update' of WrenchBoard/Users-Wrench into master 2023-08-15 11:45:12 +00:00
victorAnumudu ba5646fe2c Merge master into terms-of-use-update 2023-08-13 07:59:40 +01:00
victorAnumudu 1f9ab45206 updated terms of use 2023-08-13 07:58:37 +01:00
tolik 0ffa1123d2 Hide Apple button 2023-08-13 10:56:42 +08:00
ameye 9420a201e4 Merge branch 'image-anchor-added' of WrenchBoard/Users-Wrench into master 2023-08-11 18:59:57 +00:00
ameye 1b7ea7321e Merge branch 'my-uploads-page' of WrenchBoard/Users-Wrench into master 2023-08-11 18:59:50 +00:00
victorAnumudu b3e273d712 file messages wrapped with anthor 2023-08-11 19:37:01 +01:00
Ebube 7fa74d6fd3 added img 2023-08-11 18:13:34 +01:00
CHIEFSOFT\ameye 02c195500a myfiles 2023-08-11 12:11:31 -04:00
ameye ca8e56c6dd Merge branch 'cancel-popout-removed' of WrenchBoard/Users-Wrench into master 2023-08-10 22:42:31 +00:00
victorAnumudu c265e9db33 popout removed 2023-08-10 22:06:30 +01:00
ameye 2861473ea3 Merge branch 'cancel-upload' of WrenchBoard/Users-Wrench into master 2023-08-10 19:41:46 +00:00
victorAnumudu a598aa3898 on cancel, takes user back to my item 2023-08-10 12:51:54 +01:00
ameye 9d190916a8 Merge branch 'wallet-styles' of WrenchBoard/Users-Wrench into master 2023-08-09 21:14:17 +00:00
Ebube eca9455f0f Merge branch 'master' of https://gitlab.chiefsoft.net/WrenchBoard/Users-Wrench into wallet-styles 2023-08-09 22:00:11 +01:00
Ebube a1bf0bad0f added uploaded files 2023-08-09 21:59:46 +01:00
ameye e236ad20f7 Merge branch 'upload-payload-fix' of WrenchBoard/Users-Wrench into master 2023-08-09 20:36:46 +00:00
victorAnumudu 330f47baa7 updated upload payloads 2023-08-09 21:23:51 +01:00
ameye 5088aa3ead Merge branch 'bug-fix' of WrenchBoard/Users-Wrench into master 2023-08-09 19:52:37 +00:00
victorAnumudu 07a443d082 file upload size validation initiated 2023-08-09 20:28:19 +01:00
victorAnumudu 59a370d763 enforces user upload supported file before proceeding to upload 2023-08-09 19:34:35 +01:00
victorAnumudu 68e709f34b list of supported uploads add 2023-08-09 18:19:34 +01:00
victorAnumudu b1bb730c7d made file name in upload not to have spaces 2023-08-09 17:01:38 +01:00
ameye 20a86f4802 Merge branch 'upload-action' of WrenchBoard/Users-Wrench into master 2023-08-09 14:28:05 +00:00
victorAnumudu 2d918517f8 upload action implemented 2023-08-09 12:52:46 +01:00
tolik a0c0cbdc98 Merge branch 'master' of ssh://gitlab.chiefsoft.net:10222/WrenchBoard/Users-Wrench 2023-08-09 10:39:47 +08:00
tolik ed309007e5 Implemented Facebook login 2023-08-09 10:39:37 +08:00
ameye 594c072806 Merge branch 'add-item-btn-moved' of WrenchBoard/Users-Wrench into master 2023-08-09 01:13:02 +00:00
victorAnumudu 4b9289cb8e bug fix 2023-08-08 22:15:09 +01:00
victorAnumudu 5b39e75119 Add item button moved down 2023-08-08 22:06:44 +01:00
CHIEFSOFT\ameye e61b2c8f43 my files page 2023-08-08 08:55:12 -04:00
CHIEFSOFT\ameye 4c6952c4b3 Page format 2023-08-08 08:23:48 -04:00
CHIEFSOFT\ameye 6a5cebed5b Upload new item 2023-08-08 08:16:32 -04:00
CHIEFSOFT\ameye 663e3e8bb7 fix text 2023-08-08 08:15:26 -04:00
CHIEFSOFT\ameye aaa5b9e8ff Resource Upload My Files 2023-08-08 06:38:03 -04:00
ameye 90ec033b34 Merge branch 'wallet-styles' of WrenchBoard/Users-Wrench into master 2023-08-07 16:22:27 +00:00
Ebube c3c359cfc8 Merge branch 'master' of https://gitlab.chiefsoft.net/WrenchBoard/Users-Wrench into wallet-styles 2023-08-07 16:27:26 +01:00
Ebube 68b4031752 added online_name 2023-08-07 16:26:25 +01:00
ameye 8a2559571f Merge branch 'wallet-styles' of WrenchBoard/Users-Wrench into master 2023-08-07 08:50:16 +00:00
ameye ccae5000ee Merge branch 'reset-pwd-validation' of WrenchBoard/Users-Wrench into master 2023-08-07 08:50:12 +00:00
Ebube 5f01cb14f9 updated payload 2023-08-07 08:10:51 +01:00
Ebube 9f62345a2e Merge branch 'master' of https://gitlab.chiefsoft.net/WrenchBoard/Users-Wrench into wallet-styles 2023-08-06 23:46:26 +01:00
Ebube b9096117bc updateprofile payload updated 2023-08-06 23:46:03 +01:00
victorAnumudu f70659901c valid pwd text added 2023-08-06 21:15:18 +01:00
tolik 451e4624b1 Merge branch 'master' of ssh://gitlab.chiefsoft.net:10222/WrenchBoard/Users-Wrench 2023-08-06 10:14:17 +08:00
tolik 1557ba1040 Fix crash 2023-08-06 10:13:15 +08:00
ameye 7133ad8de3 Merge branch 'reset-pwd-icon' of WrenchBoard/Users-Wrench into master 2023-08-05 16:33:25 +00:00
victorAnumudu 9fd8944987 pwd validation msg changed 2023-08-05 16:52:20 +01:00
victorAnumudu 874276dcba reset pwd icons added 2023-08-05 16:35:23 +01:00
ameye be1454f1b8 Merge branch 'reset-pwd-second-phase' of WrenchBoard/Users-Wrench into master 2023-08-05 14:16:16 +00:00
victorAnumudu 6a34ca6c18 reser pwd second phase implemented 2023-08-05 15:12:46 +01:00
ameye fc09771b8d Merge branch 'password-reset-first-stage' of WrenchBoard/Users-Wrench into master 2023-08-05 07:16:30 +00:00
victorAnumudu cab5ed0ece password reset first phase done 2023-08-05 05:41:46 +01:00
ameye 8d3dca96b4 Merge branch 'logo-sizing-and-alignment' of WrenchBoard/Users-Wrench into master 2023-08-04 14:26:23 +00:00
ameye e71a25630d Merge branch 'wallet-styles' of WrenchBoard/Users-Wrench into master 2023-08-04 14:26:14 +00:00
Ebube cf79a15837 size in dollar acc 2023-08-04 13:53:49 +01:00
victorAnumudu 042e8c2c17 site logo size reduced and aligned 2023-08-04 13:50:59 +01:00
Ebube 46d286e8f3 size again 2023-08-04 13:49:11 +01:00
Ebube c2bfcc81ea adjusted size 2023-08-04 13:46:43 +01:00
Ebube 654ed3741c added cancel btn style in withdraw 2023-08-04 13:41:53 +01:00
Ebube 6cccd1c372 changed font-color of naira withdraw cancel btn 2023-08-04 13:39:09 +01:00
Ebube 1e6be76449 Merge branch 'master' of https://gitlab.chiefsoft.net/WrenchBoard/Users-Wrench into wallet-styles 2023-08-04 13:29:39 +01:00
Ebube 476ca04f5f Added profile img 2023-08-04 13:29:14 +01:00
ameye ce41db474d Merge branch 'wallet-styles' of WrenchBoard/Users-Wrench into master 2023-08-04 02:32:07 +00:00
Ebube 7ace7e5b5d Added btn styles the of the credit and withdraw popup 2023-08-04 03:18:51 +01:00
ameye 51e829f7b4 Merge branch 'wallet-styles' of WrenchBoard/Users-Wrench into master 2023-08-01 19:53:51 +00:00
Ebube 173b2adc66 css fixes for uniform btn 2023-08-01 19:50:52 +01:00
ameye 31cb7a521d Merge branch 'pending-job-refresh' of WrenchBoard/Users-Wrench into master 2023-08-01 18:40:10 +00:00
ameye ad4f68b252 Merge branch 'tx_ref-change' of WrenchBoard/Users-Wrench into master 2023-08-01 18:39:53 +00:00
victorAnumudu 10abf35d4f refreshes pending Job when expiry date is extended 2023-08-01 18:42:18 +01:00
Ebube ce767086fd BUG - Flutter wave key 2023-08-01 18:36:47 +01:00
ameye 0f44616767 Merge branch 'request-message-changed' of WrenchBoard/Users-Wrench into master 2023-08-01 15:18:07 +00:00
victorAnumudu bce00b5c0e request message changed 2023-08-01 16:10:56 +01:00
ameye 312cd54f87 Merge branch 'pending-wrong-assign' of WrenchBoard/Users-Wrench into master 2023-08-01 14:47:44 +00:00
victorAnumudu a0c3437eae pending assign value changed 2023-08-01 15:42:28 +01:00
ameye 711226f913 Merge branch 'tx_ref-change' of WrenchBoard/Users-Wrench into master 2023-08-01 11:07:16 +00:00
Ebube f46c6232b0 updated error scenario for the modal header 2023-08-01 12:05:27 +01:00
Ebube 172f0ccbce cleanup 2 2023-08-01 11:46:33 +01:00
Ebube a2047cc2de clean up 2023-08-01 11:41:57 +01:00
Ebube 6ea52e6481 Merge branch 'master' of https://gitlab.chiefsoft.net/WrenchBoard/Users-Wrench into tx_ref-change 2023-08-01 11:38:26 +01:00
Ebube a7bbcfdc1b complete popup fixed 2023-08-01 11:37:56 +01:00
ameye ff28be3e70 Merge branch 'tx_ref-change' of WrenchBoard/Users-Wrench into master 2023-08-01 09:24:46 +00:00
Ebube e91b4a4424 Merge branch 'master' of https://gitlab.chiefsoft.net/WrenchBoard/Users-Wrench into tx_ref-change 2023-07-31 23:58:36 +01:00
Ebube 9960033b72 debounced success payment and and fixed credit refresh 2023-07-31 23:55:23 +01:00
ameye ef135f1a9b Merge branch 'tx_ref-change' of WrenchBoard/Users-Wrench into master 2023-07-31 17:56:15 +00:00
Ebube 1df6380c4a Correct Drop Down Style for naira withdrawal 2023-07-31 15:09:12 +01:00
Ebube fc8cf551e5 Merge branch 'master' of https://gitlab.chiefsoft.net/WrenchBoard/Users-Wrench into tx_ref-change 2023-07-31 14:47:15 +01:00
Ebube b1feb0438a still testing 2023-07-31 14:46:29 +01:00
ameye a89649f774 Merge branch 'amount-alignment' of WrenchBoard/Users-Wrench into master 2023-07-31 13:36:56 +00:00
victorAnumudu b96e8a3ed5 Merged master into amount-alignment 2023-07-31 14:23:57 +01:00
victorAnumudu f3226a6cfc amount alignment changed 2023-07-31 14:22:55 +01:00
ameye ff4c503100 Merge branch 'tx_ref-change' of WrenchBoard/Users-Wrench into master 2023-07-31 12:04:22 +00:00
Ebube f543a2d893 testing topupResult 2023-07-31 13:02:31 +01:00
ameye 41badd52be Merge branch 'list-alignment-fix' of WrenchBoard/Users-Wrench into master 2023-07-30 16:29:28 +00:00
victorAnumudu eeddd4e0a5 list alignment fixed 2023-07-30 06:10:44 +01:00
tokslaw ee4d136834 wrenchboard-logo-text + image added 2023-07-29 19:04:08 -04:00
tokslaw 283efa42b3 social comment added 2023-07-29 12:16:57 -04:00
tokslaw 5cbab4933c Merge branch 'master' of https://gitlab.chiefsoft.net/WrenchBoard/Users-Wrench 2023-07-29 12:13:14 -04:00
ameye 4de2181c18 Merge branch 'family-login-no-captcha' of WrenchBoard/Users-Wrench into master 2023-07-29 15:45:31 +00:00
ameye c8f0161a29 Merge branch 'tx_ref-change' of WrenchBoard/Users-Wrench into master 2023-07-29 15:45:26 +00:00
tokslaw 60222b6d88 text error correction 2023-07-29 11:11:23 -04:00
victorAnumudu 9ea3963239 family login captcha removed 2023-07-29 12:25:25 +01:00
Ebube a87592623b Merge branch 'master' of https://gitlab.chiefsoft.net/WrenchBoard/Users-Wrench into tx_ref-change 2023-07-29 07:01:22 +01:00
Ebube bfcf53f763 Not selected should appear not selected 2023-07-29 07:00:50 +01:00
CHIEFSOFT\ameye df4489c6f2 removed start top up 2023-07-29 00:09:59 -04:00
CHIEFSOFT\ameye 37185812b4 Result added 2023-07-28 16:55:45 -04:00
ameye eb3e78244d Merge branch 'tx_ref-change' of WrenchBoard/Users-Wrench into master 2023-07-28 13:25:00 +00:00
Ebube b302d7ba57 Merge branch 'master' of https://gitlab.chiefsoft.net/WrenchBoard/Users-Wrench into tx_ref-change 2023-07-28 14:22:18 +01:00
ameye d6c16169d9 Merge branch 'Family-Tabs-Size-Fix' of WrenchBoard/Users-Wrench into master 2023-07-28 13:18:31 +00:00
Ebube 57129da0bd changed tx_ref number 2023-07-28 13:58:10 +01:00
Ebube 9eaf7123d4 reverse 2023-07-28 13:56:23 +01:00
Ebube 096da29149 Changed tx_ref no 2023-07-28 13:51:48 +01:00
CHIEFSOFT\ameye c8331c51cf removed debugger 2023-07-28 08:20:04 -04:00
tokslaw 3b7618702b env text-spell error 2023-07-27 17:04:57 -04:00
Ebube 84968b4435 balance refresh bug for withdraw 2023-07-27 16:28:21 +01:00
Ebube 98f11a3d80 Merge branch 'master' of https://gitlab.chiefsoft.net/WrenchBoard/Users-Wrench into Family-Tabs-Size-Fix 2023-07-27 16:18:29 +01:00
CHIEFSOFT\ameye 98d734e869 Post data 2023-07-27 11:17:15 -04:00
Ebube 47004fec8c Merge branch 'master' of https://gitlab.chiefsoft.net/WrenchBoard/Users-Wrench into Family-Tabs-Size-Fix 2023-07-27 16:05:17 +01:00
CHIEFSOFT\ameye 48ce89489e console log 2023-07-27 11:04:01 -04:00
CHIEFSOFT\ameye 1ce05a3be3 add fund page 2023-07-27 11:01:22 -04:00
Ebube 28ab1116e9 Added Credit Limit & Pending Jobs Bug & removed reload for credit 2023-07-27 11:34:27 +01:00
ameye 994060d929 Merge branch 'Family-Tabs-Size-Fix' of WrenchBoard/Users-Wrench into master 2023-07-26 15:57:24 +00:00
Ebube a5c62564b7 Fixed tasks banner breaking 2023-07-26 16:54:31 +01:00
Ebube 22e61d2b41 Enforced send limit 2023-07-26 15:49:04 +01:00
ameye 6ab5eae0c0 Merge branch 'email-input-size' of WrenchBoard/Users-Wrench into master 2023-07-26 14:26:05 +00:00
victorAnumudu 3fbb47ee82 email input character size increased 2023-07-26 14:39:16 +01:00
ameye 3327b2b0df Merge branch 'Family-Tabs-Size-Fix' of WrenchBoard/Users-Wrench into master 2023-07-25 20:13:46 +00:00
Ebube ec204d0389 . 2023-07-25 20:50:56 +01:00
Ebube cb1259d2c8 Added loading height 2023-07-25 20:46:46 +01:00
Ebube 706cf141e7 Add Result Page 2023-07-25 20:26:33 +01:00
ameye 2229ebb9a0 Merge branch 'Family-Tabs-Size-Fix' of WrenchBoard/Users-Wrench into master 2023-07-25 11:17:32 +00:00
Ebube 05022c29b2 Remove the cause for the login error 2023-07-25 12:12:49 +01:00
ameye 9718cfc574 Merge branch 'job-listing-bug' of WrenchBoard/Users-Wrench into master 2023-07-25 10:39:59 +00:00
ameye aef082ac60 Merge branch 'Family-Tabs-Size-Fix' of WrenchBoard/Users-Wrench into master 2023-07-25 10:39:45 +00:00
Ebube 16afd4f90c Family Tab Sizes Fixed 2023-07-25 08:53:04 +01:00
255 changed files with 16848 additions and 5390 deletions
+42 -3
View File
@@ -24,7 +24,6 @@ REACT_APP_SESSION_EXPIRE_CHECKER=60000
REACT_APP_LOGIN_ERROR_TIMEOUT=7000
REACT_APP_SIGNUP_ERROR_TIMEOUT=7000
REACT_APP_FLUTTERWAVE_APIKEY=FLWPUBK_TEST-54c90141b028789d671067bd72f781a9-X
# Had to change the error time to 3sec cause it took too long
REACT_APP_RESET_START_ERROR_TIMEOUT=3000
@@ -46,7 +45,7 @@ REACT_APP_GOOGLE_REDIRECT_URL=http://localhost:9082/login/auth/
REACT_APP_FACEBOOK_CLIENT_ID2=390204307987009
REACT_APP_FACEBOOK_CLIENT_SECRET2=19f778e312f2ab96d147bacb612910c2
#developenet Account
#development Account Social
REACT_APP_FACEBOOK_CLIENT_ID=677857427521030
REACT_APP_FACEBOOK_CLIENT_SECRET=4801375f22072d8a75f64483fdd89829
@@ -64,12 +63,52 @@ REACT_APP_APPLE_REDIRECT_URL='http://localhost:9082/login/auth/apple'
# /* 'client_id' => */ 'com.wrenchboard.users.client',
# /* 'client_secret' => */ 'eyJ0eXAiOiJKV1QiLCJhbGciOiJFUzI1NiIsImtpZCI6Ilc1V1RXQzlEVEoifQ.eyJpc3MiOiJKUjM2M0ZFWThSIiwiaWF0IjoxNjU0MDgzODQxLCJleHAiOjE2NTkyNjc4NDEsImF1ZCI6Imh0dHBzOi8vYXBwbGVpZC5hcHBsZS5jb20iLCJzdWIiOiJjb20ud3JlbmNoYm9hcmQudXNlcnMuY2xpZW50In0.TIPMwjS2MgSysqEuw3yu1nrOcrH-6omzerDhx0CadjWn2yCO8wZhQiAlhIFs7F-WPektIJ6h-2BT62yGrILiTA',
# /* 'redirect_uri' => */ site_url('login/auth/apple')
#SOCIALS Links Display
REACT_APP_APPLE_SOCIAL_LOGIN=0
REACT_APP_LINKEDIN_SOCIAL_LOGIN=0
#File Handling
REACT_APP_MAX_FILE_SIZE=1000000
REACT_APP_TOTAL_NUM_FILE=4
#Auth Text(s)
REACT_APP_LOGOUT_TEXT="Sign Out"
#apigate.lotus.g1.wrenchboard.com:76.209.103.227
#apigate.orion.g1.wrenchboard.com:76.209.103.227
#Cards Handling
REACT_APP_MAX_CREDIT_CARDS=4
REACT_APP_MAX_CREDIT_BANK_ACCOUNT=4
#Family
REACT_APP_MAX_FAMILY_MEMBERS=8
REACT_APP_SHOW_OFFER_GROUP_JOB=0
#UPLOAD PROFILE PICTURE
REACT_APP_SHOW_UPLOAD_PROFILE_PICTURE=0
#GOOGLE RECAPTCHA SITEKEY
REACT_APP_GOOGLE_RECAPTCHA_SITEKEY=6Ld_qKooAAAAADNL1TPzRLmcBA8vlpvx__39Rj39
#6LeIxAcTAAAAAJcZVRqyHh71UMIEGNQ_MXjiZKhI
#FAMILY MEMBER MINIMUM AGE
REACT_APP_FAMILY_MINIMUM_AGE=4
REACT_APP_FAMILY_MAXIMUM_AGE=18
#CHANGE LOGIN LAYOUT
REACT_APP_NEW_LOGIN_LAYOUT=1
#APP DOWNLOAD LINKS
REACT_APP_ANDROID_APP='https://play.google.com/store/apps/details?id=com.wrenchboard.users'
REACT_APP_APPLE_APP='https://itunes.apple.com/us/app/wrenchboard/id1435718367?ls=1&mt=8'
# Displays the new family dashboard with boxes
REACT_APP_SHOW_NEW_FAMILY_DASH=1
# Displays the account dashboard
REACT_APP_SHOW_ACCOUNT_DASH=1
# Displays the slider banners
REACT_APP_SHOW_SLIDER_BANNERS=0
+38 -2
View File
@@ -24,7 +24,6 @@ REACT_APP_SESSION_EXPIRE_CHECKER=60000
REACT_APP_LOGIN_ERROR_TIMEOUT=7000
REACT_APP_SIGNUP_ERROR_TIMEOUT=7000
REACT_APP_FLUTTERWAVE_APIKEY=FLWPUBK_TEST-54c90141b028789d671067bd72f781a9-X
# Had to change the error time to 3sec cause it took too long
REACT_APP_RESET_START_ERROR_TIMEOUT=3000
@@ -43,4 +42,41 @@ REACT_APP_GOOGLE_REDIRECT_URL=http://localhost:9082/login/auth/
REACT_APP_MAX_FILE_SIZE=1000000
REACT_APP_TOTAL_NUM_FILE=4
REACT_APP_LOGOUT_TEXT="Sign Out"
REACT_APP_LOGOUT_TEXT="Sign Out"
REACT_APP_APPLE_SOCIAL_LOGIN=0
REACT_APP_LINKEDIN_SOCIAL_LOGIN=0
REACT_APP_MAX_CREDIT_CARDS=4
REACT_APP_MAX_CREDIT_BANK_ACCOUNT=4
REACT_APP_MAX_FAMILY_MEMBERS=8
REACT_APP_SHOW_OFFER_GROUP_JOB=0
#UPLOAD PROFILE PICTURE
REACT_APP_SHOW_UPLOAD_PROFILE_PICTURE=0
#GOOGLE RECAPTCHA SITEKEY
REACT_APP_GOOGLE_RECAPTCHA_SITEKEY=6Ld_qKooAAAAADNL1TPzRLmcBA8vlpvx__39Rj39
#6LeIxAcTAAAAAJcZVRqyHh71UMIEGNQ_MXjiZKhI
#FAMILY MEMBER MINIMUM AGE
REACT_APP_FAMILY_MINIMUM_AGE=4
REACT_APP_FAMILY_MAXIMUM_AGE=18
#CHANGE LOGIN LAYOUT
REACT_APP_NEW_LOGIN_LAYOUT=1
#APP DOWNLOAD LINKS
REACT_APP_ANDROID_APP='https://play.google.com/store/apps/details?id=com.wrenchboard.users'
REACT_APP_APPLE_APP='https://itunes.apple.com/us/app/wrenchboard/id1435718367?ls=1&mt=8'
# Displays the new family dashboard with boxes
REACT_APP_SHOW_NEW_FAMILY_DASH=1
# Displays the account dashboard
REACT_APP_SHOW_ACCOUNT_DASH=1
# Displays the slider banners
REACT_APP_SHOW_SLIDER_BANNERS=0
+37 -2
View File
@@ -24,7 +24,6 @@ REACT_APP_SESSION_EXPIRE_CHECKER=60000
REACT_APP_LOGIN_ERROR_TIMEOUT=7000
REACT_APP_SIGNUP_ERROR_TIMEOUT=7000
REACT_APP_FLUTTERWAVE_APIKEY=FLWPUBK_TEST-54c90141b028789d671067bd72f781a9-X
# Had to change the error time to 3sec cause it took too long
REACT_APP_RESET_START_ERROR_TIMEOUT=3000
@@ -50,4 +49,40 @@ DISABLE_ESLINT_PLUGIN=true
REACT_APP_MAX_FILE_SIZE=1000000
REACT_APP_TOTAL_NUM_FILE=4
REACT_APP_LOGOUT_TEXT="Sign Out"
REACT_APP_LOGOUT_TEXT="Sign Out"
REACT_APP_APPLE_SOCIAL_LOGIN=0
REACT_APP_LINKEDIN_SOCIAL_LOGIN=0
REACT_APP_MAX_CREDIT_CARDS=4
REACT_APP_MAX_CREDIT_BANK_ACCOUNT=4
REACT_APP_MAX_FAMILY_MEMBERS=8
REACT_APP_SHOW_OFFER_GROUP_JOB=0
#UPLOAD PROFILE PICTURE
REACT_APP_SHOW_UPLOAD_PROFILE_PICTURE=0
#GOOGLE RECAPTCHA SITEKEY
REACT_APP_GOOGLE_RECAPTCHA_SITEKEY=6Ld_qKooAAAAADNL1TPzRLmcBA8vlpvx__39Rj39
#6LeIxAcTAAAAAJcZVRqyHh71UMIEGNQ_MXjiZKhI
#FAMILY MEMBER MINIMUM AGE
REACT_APP_FAMILY_MINIMUM_AGE=4
REACT_APP_FAMILY_MAXIMUM_AGE=18
#CHANGE LOGIN LAYOUT
REACT_APP_NEW_LOGIN_LAYOUT=1
#APP DOWNLOAD LINKS
REACT_APP_ANDROID_APP='https://play.google.com/store/apps/details?id=com.wrenchboard.users'
REACT_APP_APPLE_APP='https://itunes.apple.com/us/app/wrenchboard/id1435718367?ls=1&mt=8'
# Displays the new family dashboard with boxes
REACT_APP_SHOW_NEW_FAMILY_DASH=1
# Displays the account dashboard
REACT_APP_SHOW_ACCOUNT_DASH=1
# Displays the slider banners
REACT_APP_SHOW_SLIDER_BANNERS=0
+2
View File
@@ -19,6 +19,8 @@
.env.test.local
.env.production.local
#package-lock.json
npm-debug.log*
yarn-debug.log*
yarn-error.log*
+74
View File
@@ -22,13 +22,17 @@
"flutterwave-react-v3": "^1.3.0",
"formik": "^2.2.9",
"react": "^18.2.0",
"react-apple-login": "^1.1.6",
"react-chartjs-2": "^4.1.0",
"react-countup": "^6.2.0",
"react-dom": "^18.2.0",
"react-google-recaptcha": "^3.1.0",
"react-qr-code": "^2.0.11",
"react-redux": "^8.0.5",
"react-router-dom": "^6.0.2",
"react-scripts": "5.0.1",
"react-slick": "^0.29.0",
"react-to-print": "^2.14.12",
"react-toastify": "^9.0.1",
"redux": "^4.2.0",
"slick-carousel": "^1.8.1",
@@ -14801,6 +14805,11 @@
"teleport": ">=0.2.0"
}
},
"node_modules/qr.js": {
"version": "0.0.0",
"resolved": "https://registry.npmjs.org/qr.js/-/qr.js-0.0.0.tgz",
"integrity": "sha512-c4iYnWb+k2E+vYpRimHqSu575b1/wKl4XFeJGpFmrJQz5I88v9aY2czh7s0w36srfCM1sXgC/xpoJz5dJfq+OQ=="
},
"node_modules/qs": {
"version": "6.11.0",
"resolved": "https://registry.npmjs.org/qs/-/qs-6.11.0.tgz",
@@ -14944,6 +14953,32 @@
"url": "https://opencollective.com/core-js"
}
},
"node_modules/react-apple-login": {
"version": "1.1.6",
"resolved": "https://registry.npmjs.org/react-apple-login/-/react-apple-login-1.1.6.tgz",
"integrity": "sha512-ySV6ax0aB+ksA7lKzhr4MvsgjwSH068VtdHJXS+7rL380IJnNQNl14SszR31k3UqB8q8C1H1oyjJFGq4MyO6tw==",
"engines": {
"node": ">=8",
"npm": ">=5"
},
"peerDependencies": {
"prop-types": "^15.5.4",
"react": "^15.0.0 || ^16.0.0 || ^17.0.0 || ^18.0.0",
"react-dom": "^15.0.0 || ^16.0.0 || ^17.0.0 || ^18.0.0"
}
},
"node_modules/react-async-script": {
"version": "1.2.0",
"resolved": "https://registry.npmjs.org/react-async-script/-/react-async-script-1.2.0.tgz",
"integrity": "sha512-bCpkbm9JiAuMGhkqoAiC0lLkb40DJ0HOEJIku+9JDjxX3Rcs+ztEOG13wbrOskt3n2DTrjshhaQ/iay+SnGg5Q==",
"dependencies": {
"hoist-non-react-statics": "^3.3.0",
"prop-types": "^15.5.0"
},
"peerDependencies": {
"react": ">=16.4.1"
}
},
"node_modules/react-chartjs-2": {
"version": "4.3.1",
"resolved": "https://registry.npmjs.org/react-chartjs-2/-/react-chartjs-2-4.3.1.tgz",
@@ -15044,11 +15079,41 @@
"resolved": "https://registry.npmjs.org/react-fast-compare/-/react-fast-compare-2.0.4.tgz",
"integrity": "sha512-suNP+J1VU1MWFKcyt7RtjiSWUjvidmQSlqu+eHslq+342xCbGTYmC0mEhPCOHxlW0CywylOC1u2DFAT+bv4dBw=="
},
"node_modules/react-google-recaptcha": {
"version": "3.1.0",
"resolved": "https://registry.npmjs.org/react-google-recaptcha/-/react-google-recaptcha-3.1.0.tgz",
"integrity": "sha512-cYW2/DWas8nEKZGD7SCu9BSuVz8iOcOLHChHyi7upUuVhkpkhYG/6N3KDiTQ3XAiZ2UAZkfvYKMfAHOzBOcGEg==",
"dependencies": {
"prop-types": "^15.5.0",
"react-async-script": "^1.2.0"
},
"peerDependencies": {
"react": ">=16.4.1"
}
},
"node_modules/react-is": {
"version": "16.13.1",
"resolved": "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz",
"integrity": "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ=="
},
"node_modules/react-qr-code": {
"version": "2.0.12",
"resolved": "https://registry.npmjs.org/react-qr-code/-/react-qr-code-2.0.12.tgz",
"integrity": "sha512-k+pzP5CKLEGBRwZsDPp98/CAJeXlsYRHM2iZn1Sd5Th/HnKhIZCSg27PXO58zk8z02RaEryg+60xa4vyywMJwg==",
"dependencies": {
"prop-types": "^15.8.1",
"qr.js": "0.0.0"
},
"peerDependencies": {
"react": "^16.x || ^17.x || ^18.x",
"react-native-svg": "*"
},
"peerDependenciesMeta": {
"react-native-svg": {
"optional": true
}
}
},
"node_modules/react-redux": {
"version": "8.0.5",
"resolved": "https://registry.npmjs.org/react-redux/-/react-redux-8.0.5.tgz",
@@ -15218,6 +15283,15 @@
"react-dom": "^0.14.0 || ^15.0.1 || ^16.0.0 || ^17.0.0 || ^18.0.0"
}
},
"node_modules/react-to-print": {
"version": "2.14.15",
"resolved": "https://registry.npmjs.org/react-to-print/-/react-to-print-2.14.15.tgz",
"integrity": "sha512-SKnwOzU2cJ8eaAkoJO7+gNhvfEDmm+Y34IdcHsjtHioUevUPhprqbVtvNJlZ2JkGJ8ExK2QNWM9pXECTDR5D8w==",
"peerDependencies": {
"react": "^15.0.0 || ^16.0.0 || ^17.0.0 || ^18.0.0",
"react-dom": "^15.0.0 || ^16.0.0 || ^17.0.0 || ^18.0.0"
}
},
"node_modules/react-toastify": {
"version": "9.1.1",
"resolved": "https://registry.npmjs.org/react-toastify/-/react-toastify-9.1.1.tgz",
+1
View File
@@ -21,6 +21,7 @@
"react-chartjs-2": "^4.1.0",
"react-countup": "^6.2.0",
"react-dom": "^18.2.0",
"react-google-recaptcha": "^3.1.0",
"react-qr-code": "^2.0.11",
"react-redux": "^8.0.5",
"react-router-dom": "^6.0.2",
Binary file not shown.

After

Width:  |  Height:  |  Size: 12 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 11 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 12 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 11 KiB

+3
View File
@@ -17,6 +17,9 @@
<link rel="apple-touch-icon" href="%PUBLIC_URL%/logo192.png" />
<link rel="manifest" href="%PUBLIC_URL%/manifest.json" />
<title>WrenchBoard</title>
<!-- FONT AWESOME -->
<link rel="stylesheet" href="https://use.fontawesome.com/releases/v6.5.1/css/all.css"/>
</head>
<body>
<noscript>You need to enable JavaScript to run this app.</noscript>
+1
View File
@@ -0,0 +1 @@
{"name":"","short_name":"","icons":[{"src":"/android-chrome-192x192.png","sizes":"192x192","type":"image/png"},{"src":"/android-chrome-512x512.png","sizes":"512x512","type":"image/png"}],"theme_color":"#ffffff","background_color":"#ffffff","display":"standalone"}
+13 -7
View File
@@ -1,15 +1,21 @@
import Toaster from "./components/Helpers/Toaster";
import { Navigate, useLocation } from "react-router-dom";
import Routers from "./Routers";
import Toaster from "./components/Helpers/Toaster";
import Default from "./components/Partials/Default";
function App() {
const { pathname } = useLocation();
return (
<Default>
<>
<Routers />
<Toaster />
</>
</Default>
<Default>
<>
{pathname.startsWith("/@") ? (
<Navigate to="/app" replace={true} />
) : (
<Routers />
)}
<Toaster />
</>
</Default>
);
}
+142 -61
View File
@@ -1,78 +1,123 @@
import { Route, Routes } from "react-router-dom";
import FourZeroFour from "./components/FourZeroFour";
import ScrollToTop from "./components/Helpers/ScrollToTop";
import MyCollection from "./components/MyCollection";
import StartJob from "./components/MyJobs/StartJob";
import Notification from "./components/Notification";
import AuthRoute from "./middleware/AuthRoute";
import AcitveBidsPage from "./views/AcitveBidsPage";
import AppDownloadPage from "./views/AppDownloadPage";
import AppleRedirectPage from "./views/AppleRedirectPage";
import AuthProfilePage from "./views/AuthProfilePage";
import AuthRedirect from "./views/AuthRedirect";
import BlogPage from "./views/BlogPage";
import CalendarPage from "./views/CalendarPage";
import CollectionItemPage from "./views/CollectionItemPage";
import FacebookRedirect from "./views/FacebookRedirect";
import FamilyAccPage from "./views/FamilyAccPage";
import FamilyManagePage from "./views/FamilyManagePage";
import FamilyMarketPage from "./views/FamilyMarketPage";
import FamilySettingsPage from "./views/FamilySettingsPage";
import ForgotPasswordPages from "./views/ForgotPasswordPages";
import ForgotPasswordPagesTwo from "./views/ForgotPasswordPagesTwo";
import HistoryPage from "./views/HistoryPage";
import HomePages from "./views/HomePages";
import JobGroupsPage from "./views/JobGroupsPage";
import LndPage from "./views/LndPage";
import LoginPage from "./views/LoginPage";
import LoginPageTwo from "./views/LoginPageTwo";
import ManageActiveJobs from "./views/ManageActiveJobs";
import ManageInterestOfferPage from "./views/ManageInterestOfferPage";
import MarketPlacePage from "./views/MarketPlacePage";
import MyActiveJobsPage from "./views/MyActiveJobsPage";
import MyCouponPage from "./views/MyCouponPage";
import MyJobsPage from "./views/MyJobsPage";
import MyOffersPage from "./views/MyOffersPage";
import MyPastDueJobsPage from "./views/MyPastDueJobsPage";
import MyReviewDueJobsPage from "./views/MyReviewDueJobsPage";
import MyTaskPage from "./views/MyTaskPage";
import MyWaitingJobsPage from "./views/MyWaitingJobsPage";
import MyWalletPage from "./views/MyWalletPage";
import OffersInterestPage from "./views/OffersInterestPage";
import ReferralPage from "./views/ReferralPage";
import RemindersPage from "./views/RemindersPage";
import ResourcePage from "./views/ResourcePage";
import SavedPage from "./views/SavedPage";
import SellPage from "./views/SellPage";
import SettingsPage from "./views/SettingsPage";
import ShopDetailsPage from "./views/ShopDetailsPage";
import SignupPage from "./views/SignupPage";
import SignupPageTwo from "./views/SignupPageTwo";
import TrackingPage from "./views/TrackingPage";
import UpdatePasswordPages from "./views/UpdatePasswordPages";
import UpdatePasswordPagesTwo from "./views/UpdatePasswordPagesTwo";
import UploadProductPage from "./views/UploadProductPage";
import UserProfilePage from "./views/UserProfilePage";
import VerifyYouPages from "./views/VerifyYouPages";
import VerifyPasswordPages from "./views/VerifyPasswordPages";
import RemindersPage from './views/RemindersPage';
import TrackingPage from "./views/TrackingPage";
import CalendarPage from "./views/CalendarPage";
import ResourcePage from "./views/ResourcePage";
import MyTaskPage from "./views/MyTaskPage";
import MyJobsPage from "./views/MyJobsPage";
import ReferralPage from "./views/ReferralPage";
import VerifyLinkPages from "./views/VerifyLinkPages";
import MyActiveJobsPage from "./views/MyActiveJobsPage";
import FamilyAccPage from "./views/FamilyAccPage";
import StartJob from "./components/MyJobs/StartJob";
import AddJobPage from "./views/AddJobPage";
import MyPendingJobsPage from "./views/MyPendingJobsPage";
import ManageActiveJobs from "./views/ManageActiveJobs";
import FamilyManagePage from "./views/FamilyManagePage";
import MyCouponPage from "./views/MyCouponPage";
import AuthRedirect from "./views/AuthRedirect";
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";
import FacebookRedirect from "./views/FacebookRedirect";
import AppleRedirectPage from "./views/AppleRedirectPage";
import VerifyLinkPagesTwo from "./views/VerifyLinkPagesTwo";
import VerifyPasswordPages from "./views/VerifyPasswordPages";
import VerifyPasswordPagesTwo from "./views/VerifyPasswordPagesTwo";
import VerifyYouPages from "./views/VerifyYouPages";
import VerifyYouPagesTwo from "./views/VerifyYouPagesTwo";
import YourPages from "./views/YourPage_";
import ParentWaitingPage from "./views/ParentWaitingPage";
import FamilyPendingOfferPage from "./views/FamilyPendingOfferPage";
import FamBlogPage from "./views/FamBlogPage"
import FamAIQuestionPage from "./views/FamAIQuestionPage"
import FamMyFilesPage from "./views/FamMyFilesPage"
import FamWorkInProgressPage from "./views/FamWorkInProgressPage";
import MyPastDueTasksPage from "./views/MyPastDueTasksPage";
import FamilyWalletPage from "./views/FamilyWalletPage";
import FamilyActivitiesPage from "./views/FamilyActivitiesPage";
export default function Routers() {
return (
<ScrollToTop>
<Routes>
{/* guest routes */}
<Route exact path="/login" element={<LoginPage />} />
<Route exact path="/signup" element={<SignupPage />} />
{process.env.REACT_APP_NEW_LOGIN_LAYOUT == 1 ? (
<>
<Route exact path="/login" element={<LoginPageTwo />} />
<Route exact path="/signup" element={<SignupPageTwo />} />
<Route
exact
path="/forgot-password"
element={<ForgotPasswordPagesTwo />}
/>
<Route
exact
path="/update-password"
element={<UpdatePasswordPagesTwo />}
/>
<Route path="/vemail" element={<VerifyLinkPagesTwo />} />
<Route path="/complereset" element={<VerifyPasswordPagesTwo />} />
<Route exact path="/outmessage" element={<VerifyYouPagesTwo />} />
<Route exact path="/eoffer" element={<LoginPageTwo />} />
<Route exact path="/invite" element={<LoginPageTwo />} />
</>
) : (
<>
<Route exact path="/login" element={<LoginPage />} />
<Route exact path="/signup" element={<SignupPage />} />
<Route
exact
path="/forgot-password"
element={<ForgotPasswordPages />}
/>
<Route
exact
path="/update-password"
element={<UpdatePasswordPages />}
/>
<Route path="/vemail" element={<VerifyLinkPages />} />
<Route path="/complereset" element={<VerifyPasswordPages />} />
<Route exact path="/outmessage" element={<VerifyYouPages />} />
<Route exact path="/eoffer" element={<LoginPage />} />
<Route exact path="/invite" element={<LoginPage />} />
</>
)}
<Route exact path="/login/auth" element={<AuthRedirect />} />
<Route exact path="/login/auth/flogin" element={<FacebookRedirect />} />
<Route exact path="/login/auth/apple" element={<AppleRedirectPage />} />
<Route
exact
path="/forgot-password"
element={<ForgotPasswordPages />}
/>
<Route
exact
path="/update-password"
element={<UpdatePasswordPages />}
/>
<Route path="/vemail" element={<VerifyLinkPages />} />
<Route path="/complereset" element={<VerifyPasswordPages />} />
<Route exact path="/outmessage" element={<VerifyYouPages />} />
<Route exact path="/lnd/*" element={<LndPage />} />
<Route exact path="/app" element={<AppDownloadPage />} />
{/* private route */}
<Route element={<AuthRoute />}>
@@ -81,35 +126,69 @@ export default function Routers() {
<Route exact path="/notification" element={<Notification />} />
<Route exact path="/market-place" element={<MarketPlacePage />} />
<Route exact path="/shop-details" element={<ShopDetailsPage />} />
<Route exact path="/my-wallet" element={<MyWalletPage />} />
<Route exact path="/my-collection" element={<MyCollection />} />*/}
<Route exact path="/my-collection" element={<MyCollection />} />*/}
<Route exact path="/reminders" element={<RemindersPage />} />
<Route exact path="/tracking" element={<TrackingPage />} />
<Route exact path="/calendar" element={<CalendarPage />} />
<Route exact path="/resources" element={<ResourcePage />} />
<Route exact path="/my-wallet/*" element={<MyWalletPage />} />
<Route exact path="/family-wallet" element={<FamilyWalletPage />} />
<Route exact path="/my-coupon" element={<MyCouponPage />} />
<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="/suggested" element={<ParentWaitingPage />} />
<Route exact path="/pending" element={<FamilyPendingOfferPage />} />
<Route exact path="/fam-blog" element={<FamBlogPage />} />
<Route exact path="/ai-question" element={<FamAIQuestionPage />} />
<Route exact path="/myfiles" element={<FamMyFilesPage />} />
<Route exact path="/ai-lab" element={<FamAIQuestionPage />} />
<Route exact path="/work-in-progress" element={<FamWorkInProgressPage />} />
<Route
exact
path="/familysettings"
element={<FamilySettingsPage />}
/>
<Route exact path="/pastdue" element={<MyPastDueTasksPage />} />
<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="/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 />} />
<Route exact path="/start-job" element={<StartJob />} />
<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 exact path="/my-active-jobs" element={<MyActiveJobsPage />} />
<Route
exact
path="/my-pastdue-jobs"
element={<MyPastDueJobsPage />}
/>
<Route exact path="/my-offers" element={<MyOffersPage />} />
<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="/acc-family/activities" element={<FamilyActivitiesPage />} />
<Route exact path="/manage-family" element={<FamilyManagePage />} />
<Route exact path="/start-job" element={<StartJob />} />
<Route exact path="/yourpage" element={<YourPages />} />
<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
exact
@@ -119,11 +198,13 @@ export default function Routers() {
<Route exact path="/sell" element={<SellPage />} />
<Route exact path="/saved" element={<SavedPage />} />
<Route exact path="/history" element={<HistoryPage />} />
<Route exact path="/upload-product" element={<UploadProductPage />} />
{/*<Route exact path="/upload-product" element={<UploadProductPage />} />*/}
<Route exact path="/my-uploads" element={<UploadProductPage />} />
<Route exact path="/profile" element={<AuthProfilePage />} />
<Route exact path="/user-profile" element={<UserProfilePage />} />
<Route exact path="/settings" element={<SettingsPage />} />
<Route exact path="/referral" element={<ReferralPage />} />
<Route exact path="/job-groups" element={<JobGroupsPage />} />
</Route>
<Route path="*" element={<FourZeroFour />} />
</Routes>
Binary file not shown.

After

Width:  |  Height:  |  Size: 71 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 12 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 11 KiB

@@ -1,4 +1,4 @@
<svg width="20" height="20" viewBox="0 0 20 20" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M17.5 0H2.5C1.12125 0 0 1.12125 0 2.5V17.5C0 18.8787 1.12125 20 2.5 20H17.5C18.8787 20 20 18.8787 20 17.5V2.5C20 1.12125 18.8787 0 17.5 0Z" fill="#1976D2"/>
<path d="M16.875 10H13.75V7.5C13.75 6.81 14.31 6.875 15 6.875H16.25V3.75H13.75C11.6788 3.75 10 5.42875 10 7.5V10H7.5V13.125H10V20H13.75V13.125H15.625L16.875 10Z" fill="#FAFAFA"/>
</svg>
<svg width="20" height="20" viewBox="0 0 20 20" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M17.5 0H2.5C1.12125 0 0 1.12125 0 2.5V17.5C0 18.8787 1.12125 20 2.5 20H17.5C18.8787 20 20 18.8787 20 17.5V2.5C20 1.12125 18.8787 0 17.5 0Z" fill="#1976D2"/>
<path d="M16.875 10H13.75V7.5C13.75 6.81 14.31 6.875 15 6.875H16.25V3.75H13.75C11.6788 3.75 10 5.42875 10 7.5V10H7.5V13.125H10V20H13.75V13.125H15.625L16.875 10Z" fill="#FAFAFA"/>
</svg>

Before

Width:  |  Height:  |  Size: 452 B

After

Width:  |  Height:  |  Size: 448 B

@@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 48 48" id="DownloadArrow"><g fill="#4687ba" class="color9a7baa svgShape"><path d="M30.179 3.525V1.857c0-1.551-1.784-2.415-3.011-1.449L24 2.907 20.824.402c-1.216-.959-3.002-.093-3.002 1.456v1.668a1 1 0 0 0 1 1h10.357c.552-.001 1-.449 1-1.001zM30.179 25.172v-2.524a1 1 0 0 0-1-1H18.822a1 1 0 0 0-1 1v2.524a1 1 0 0 0 1 1h10.357a1 1 0 0 0 1-1zM30.179 10.74V8.217a1 1 0 0 0-1-1H18.822a1 1 0 0 0-1 1v2.524a1 1 0 0 0 1 1h10.357c.552-.001 1-.448 1-1.001zM30.179 17.956v-2.524a1 1 0 0 0-1-1H18.822a1 1 0 0 0-1 1v2.524a1 1 0 0 0 1 1h10.357a1 1 0 0 0 1-1zM25.748 47.029l9.336-15.018c.852-1.371-.133-3.145-1.748-3.145H14.664c-1.614 0-2.6 1.774-1.748 3.145l9.336 15.018a2.058 2.058 0 0 0 3.496 0z" fill="#000000" class="color000000 svgShape"></path></g></svg>

After

Width:  |  Height:  |  Size: 799 B

+1
View File
@@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 32 32" id="error"><path fill="#ff1d25" d="M29.75,25.73,18.68,3.59a3,3,0,0,0-4-1.33,3.05,3.05,0,0,0-1.33,1.33L2.25,25.73a3,3,0,0,0,2.68,4.34H27.07a3,3,0,0,0,3-3A2.88,2.88,0,0,0,29.75,25.73ZM16,25.38a.94.94,0,1,1,.94-.94A.94.94,0,0,1,16,25.38Zm.94-4.69a.94.94,0,1,1-1.88,0V11.31a.94.94,0,1,1,1.88,0Z"></path></svg>

After

Width:  |  Height:  |  Size: 365 B

+1
View File
@@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" enable-background="new 0 0 32 28" viewBox="0 0 32 28" id="List"><circle cx="2" cy="2" r="2" fill="#767fad" class="color4e4e50 svgShape"></circle><path fill="#767fad" d="M8 0h24v4H8z" class="color4e4e50 svgShape"></path><circle cx="10" cy="10" r="2" fill="#767fad" class="color4e4e50 svgShape"></circle><path fill="#767fad" d="M16 8h16v4H16z" class="color4e4e50 svgShape"></path><circle cx="10" cy="26" r="2" fill="#767fad" class="color4e4e50 svgShape"></circle><path fill="#767fad" d="M16 24h16v4H16z" class="color4e4e50 svgShape"></path><circle cx="18" cy="18" r="2" fill="#767fad" class="color4e4e50 svgShape"></circle><path fill="#767fad" d="M24 16h8v4h-8z" class="color4e4e50 svgShape"></path></svg>

After

Width:  |  Height:  |  Size: 743 B

+1
View File
@@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" id="Group" x="0" y="0" version="1.1" viewBox="0 0 52 52" xml:space="preserve"><path d="M26.003 13.05c2.44 0 4.43-1.99 4.43-4.43a4.44 4.44 0 0 0-4.43-4.44c-2.45 0-4.44 1.99-4.44 4.44a4.44 4.44 0 0 0 4.44 4.43zM11.293 38.77c2.44 0 4.43-1.99 4.43-4.43a4.44 4.44 0 0 0-4.43-4.44c-2.45 0-4.43 1.99-4.43 4.44 0 2.44 1.98 4.43 4.43 4.43z" fill="#4687ba" class="color000000 svgShape"></path><path d="M49.493 41.93a10.091 10.091 0 0 0-3.643-3.739 6.418 6.418 0 0 1-5.138 2.58 6.392 6.392 0 0 1-4.371-1.737.975.975 0 0 0-.158-.258l-9.184-9.903V22.1h4.344c1.41 0 2.68-.73 3.4-1.94.72-1.23.73-2.7.04-3.95a10.09 10.09 0 0 0-3.643-3.739 6.418 6.418 0 0 1-5.138 2.579 6.43 6.43 0 0 1-5.144-2.58 10.085 10.085 0 0 0-3.645 3.74c-.69 1.25-.67 2.72.05 3.95a3.9 3.9 0 0 0 3.39 1.94h4.346v6.624c-.01.022-.016.044-.025.066l-9.22 9.941a.978.978 0 0 0-.22.42 6.378 6.378 0 0 1-4.242 1.62 6.417 6.417 0 0 1-5.14-2.584 10.086 10.086 0 0 0-3.65 3.743c-.69 1.25-.67 2.72.05 3.95a3.9 3.9 0 0 0 3.39 1.94h10.69c1.41 0 2.68-.73 3.4-1.94.72-1.23.73-2.7.04-3.95a10.03 10.03 0 0 0-2.134-2.612l8.01-8.636 8.055 8.685c-.815.73-1.53 1.58-2.08 2.563-.69 1.25-.67 2.72.05 3.95a3.9 3.9 0 0 0 3.39 1.94h10.69c1.41 0 2.68-.73 3.4-1.94.72-1.23.73-2.7.04-3.95z" fill="#4687ba" class="color000000 svgShape"></path><path d="M40.713 38.77c2.44 0 4.43-1.99 4.43-4.43a4.44 4.44 0 0 0-4.43-4.44c-2.45 0-4.44 1.99-4.44 4.44a4.44 4.44 0 0 0 4.44 4.43z" fill="#4687ba" class="color000000 svgShape"></path></svg>

After

Width:  |  Height:  |  Size: 1.5 KiB

+1
View File
@@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 64 64" id="List"><switch><g fill="#767fad" class="color000000 svgShape"><g fill="#4687ba" class="color0ac5ab svgShape"><path d="M20 0H8a8 8 0 00-8 8v12a8 8 0 008 8h12a8 8 0 008-8V8a8 8 0 00-8-8zM56 0H44a8 8 0 00-8 8v12a8 8 0 008 8h12a8 8 0 008-8V8a8 8 0 00-8-8zM20 36H8a8 8 0 00-8 8v12a8 8 0 008 8h12a8 8 0 008-8V44a8 8 0 00-8-8zM56 36H44a8 8 0 00-8 8v12a8 8 0 008 8h12a8 8 0 008-8V44a8 8 0 00-8-8z" fill="#767fad" class="color000000 svgShape"></path></g></g></switch></svg>

After

Width:  |  Height:  |  Size: 527 B

+1
View File
@@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 8 8" id="List"><path d="M0 0v3h3V0H0zm4 0v1h4V0H4zm0 2v1h3V2H4zM0 4v3h3V4H0zm4 0v1h4V4H4zm0 2v1h3V6H4z" fill="#b22b7d" class="color000000 svgShape"></path></svg>

After

Width:  |  Height:  |  Size: 214 B

+1
View File
@@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 32 32" id="List"><path d="M9 3a6 6 0 1 0 6 6A6 6 0 0 0 9 3zM9 13a4 4 0 1 1 4-4A4 4 0 0 1 9 13zM17 9H28a1 1 0 0 0 0-2H17a1 1 0 0 0 0 2zM17 13h6a1 1 0 0 0 0-2H17a1 1 0 0 0 0 2zM9 17a6 6 0 1 0 6 6A6 6 0 0 0 9 17zM9 27a4 4 0 1 1 4-4A4 4 0 0 1 9 27zM28 21H17a1 1 0 0 0 0 2H28a1 1 0 0 0 0-2zM23 25H17a1 1 0 0 0 0 2h6a1 1 0 0 0 0-2z" fill="#4687ba" class="color000000 svgShape"></path></svg>

After

Width:  |  Height:  |  Size: 437 B

+1
View File
@@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 68 50" id="Page"><path fill-rule="evenodd" d="M0 13V2a2 2 0 0 1 2-2h64a2 2 0 0 1 2 2v11H0Zm0 4v31a2 2 0 0 0 2 2h20V17H0Zm26 33h40a2 2 0 0 0 2-2V17H26v33Z" fill="#767fad" class="color000000 svgShape"></path></svg>

After

Width:  |  Height:  |  Size: 265 B

+1
View File
@@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" data-name="Layer 1" viewBox="0 0 64 64" id="delete"><path d="M44.41 19.59a2 2 0 0 0-2.83 0L32 29.18l-9.59-9.59a2 2 0 0 0-2.83 2.83L29.17 32l-9.59 9.59a2 2 0 1 0 2.83 2.83L32 34.83l9.59 9.59a2 2 0 0 0 2.83-2.83L34.83 32l9.59-9.59a2 2 0 0 0-.01-2.82Z" fill="#b22b7d" class="color000000 svgShape"></path><path d="M32 3a29 29 0 1 0 29 29A29 29 0 0 0 32 3Zm0 54a25 25 0 1 1 25-25 25 25 0 0 1-25 25Z" fill="#b22b7d" class="color000000 svgShape"></path></svg>

After

Width:  |  Height:  |  Size: 492 B

+1
View File
@@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" width="512" height="512" id="success"><path fill="#80af52" d="M256 26c127.03 0 230 102.97 230 230S383.03 486 256 486 26 383.03 26 256 128.97 26 256 26z"></path><path fill="#fff" d="M215.999 386a9.998 9.998 0 0 1-7.525-3.415l-70-80c-3.637-4.156-3.215-10.474.941-14.11s10.475-3.217 14.111.94l60.961 69.67 142.938-238.23c2.842-4.736 8.983-6.273 13.72-3.43 4.736 2.841 6.271 8.984 3.431 13.72l-150 250a9.998 9.998 0 0 1-8.577 4.855z"></path></svg>

After

Width:  |  Height:  |  Size: 483 B

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

After

Width:  |  Height:  |  Size: 1.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 235 KiB

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

After

Width:  |  Height:  |  Size: 1003 B

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

After

Width:  |  Height:  |  Size: 1.7 KiB

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

After

Width:  |  Height:  |  Size: 1.7 KiB

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

After

Width:  |  Height:  |  Size: 1.3 KiB

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

After

Width:  |  Height:  |  Size: 1.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 22 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 100 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 101 KiB

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

After

Width:  |  Height:  |  Size: 430 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 206 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 245 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 67 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 110 KiB

+102 -147
View File
@@ -1,98 +1,31 @@
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 {
validationSchema as VS,
useDispatch,
useSelector,
usersService,
initialValues as IV,
initialReqState,
useState,
tableReload,
Formik,
InputCom,
Field,
Form,
LoadingSpinner,
} from "./settings";
const validationSchema = Yup.object().shape({
country: Yup.string()
.min(1, "Minimum 3 characters")
.max(25, "Maximum 25 characters")
.required("Country is required"),
price: Yup.string()
.typeError("Invalid number")
.min(1, "Price must be greater than 0")
.test("no-e", "Invalid number", (value) => {
if (value && /\d+e/.test(value)) {
return false;
}
return true;
})
.required("Price is required"),
title: Yup.string()
.min(5, "Minimum 5 characters")
.max(149, "Maximum 149 characters")
.required("Title is required"),
description: Yup.string()
.min(5, "Minimum 5 characters")
.max(299, "Maximum 299 characters")
.required("Description is required"),
job_detail: Yup.string()
.min(3, "Minimum 3 characters")
.max(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"),
});
const validationSchema = VS;
function AddJob({ popUpHandler, categories }) {
const ApiCall = new usersService();
const { walletDetails } = useSelector((state) => state.walletDetails);
let dispatch = useDispatch();
let [currency, setCurrency] = useState({
loading: true,
status: false,
data: null,
}); // To Hold the array of currency getUserCurrency returns
const [requestStatus, setRequestStatus] = useState(initialReqState); // Holds state when submit button is pressed
let initialValues = {
// initial values for formik
country: "",
price: "",
title: "",
description: "",
job_detail: "",
timeline_days: "",
category: [],
};
let [requestStatus, setRequestStatus] = useState({
loading: false,
status: false,
message: "",
}); // Holds state when submit button is pressed
// FUNCTION TO GET Currency
const getUserCurrency = () => {
setCurrency((prev) => ({ ...prev, loading: true }));
ApiCall.getUserWallets()
.then((res) => {
if (res.data.internal_return < 0) {
setCurrency({ loading: false, status: true, data: [] });
return;
}
setCurrency({
loading: false,
status: true,
data: res.data.result_list,
});
})
.catch((err) => {
setCurrency({ loading: false, status: false, data: [] });
});
};
// FUNCTION TO HANDLE ADD JOB FORM
const handleAddJob = (values, helpers) => {
let reqData = {
const handleAddJob = async (values, helpers) => {
const reqData = {
country: values?.country,
price: Number(values.price) * 100,
title: values?.title,
@@ -103,19 +36,19 @@ function AddJob({ popUpHandler, categories }) {
};
setRequestStatus({ loading: true, status: false, message: "" });
ApiCall.jobManagerCreateJob(reqData)
.then((res) => {
if (res.data.internal_return < 1) {
setRequestStatus({
loading: false,
status: false,
message: "Could not complete your request at the moment",
});
setTimeout(() => {
popUpHandler();
}, 1500);
return;
}
try {
const res = await ApiCall.jobManagerCreateJob(reqData);
if (res.data.internal_return < 1) {
setRequestStatus({
loading: false,
status: false,
message: "Could not complete your request at the moment",
});
setTimeout(() => {
popUpHandler();
}, 1500);
} else {
setRequestStatus({
loading: false,
status: true,
@@ -125,29 +58,24 @@ function AddJob({ popUpHandler, categories }) {
dispatch(tableReload({ type: "JOBTABLE" }));
popUpHandler();
}, 1000);
})
.catch((err) => {
setRequestStatus({
loading: false,
status: false,
message: "Opps! something went wrong. Try Again",
});
})
.finally(() => {
setTimeout(() => {
setRequestStatus({ loading: false, status: false, message: "" });
}, 5000);
}
} catch (err) {
setRequestStatus({
loading: false,
status: false,
message: "Oops! Something went wrong. Try Again",
});
} finally {
setTimeout(() => {
setRequestStatus({ loading: false, status: false, message: "" });
}, 5000);
}
};
useEffect(() => {
getUserCurrency();
}, []);
return (
<div className="add-job p-5 w-full bg-white rounded-md flex flex-col justify-between">
<div className="add-job p-5 w-full bg-white dark:bg-dark-white dark:text-white rounded-md flex flex-col justify-between">
<Formik
initialValues={initialValues}
initialValues={IV}
validationSchema={validationSchema}
onSubmit={handleAddJob}
>
@@ -164,26 +92,30 @@ function AddJob({ popUpHandler, categories }) {
className="input-label text-[#181c32] dark:text-white text-[13.975px] leading-[20.9625px] font-semibold flex item-center gap-1"
>
Currency
{props.errors.country && props.touched.country && <span className="text-[12px] text-red-500">{props.errors.country}</span>}
{props.errors.country && props.touched.country && (
<span className="text-[12px] text-red-500">
{props.errors.country}
</span>
)}
</label>
<select
id="country"
name="country"
value={props.values.country}
className={`input-field p-2 mt-3 rounded-md placeholder:text-base text-dark-gray dark:text-white w-full h-10 bg-slate-100 dark:bg-[#11131F] focus:ring-0 focus:outline-none`}
className={`input-field p-2 mt-3 rounded-md placeholder:text-base text-dark-gray dark:text-white w-full h-10 bg-slate-100 dark:bg-[#11131F] focus:ring-0 focus:outline-none border`}
onChange={props.handleChange}
onBlur={props.handleBlur}
>
{currency.loading ? (
{walletDetails?.loading ? (
<option className="text-slate-500 text-lg" value="">
Loading...
</option>
) : currency.data.length ? (
) : walletDetails.data.length ? (
<>
<option className="text-slate-500 text-lg" value="">
Select a currency
</option>
{currency.data?.map((item, index) => (
{walletDetails.data?.map((item, index) => (
<option
key={index}
className="text-slate-500 text-lg"
@@ -207,14 +139,17 @@ function AddJob({ popUpHandler, categories }) {
fieldClass="px-6 text-right"
label="Price"
labelClass="tracking-wide"
inputBg="bg-slate-100"
type="number"
name="price"
placeholder="0"
value={props.values.price}
inputHandler={props.handleChange}
blurHandler={props.handleBlur}
error={props.errors.price && props.touched.price && props.errors.price}
error={
props.errors.price &&
props.touched.price &&
props.errors.price
}
/>
</div>
</div>
@@ -225,13 +160,16 @@ function AddJob({ popUpHandler, categories }) {
fieldClass="px-6"
label="Title"
labelClass="tracking-wide"
inputBg="bg-slate-100"
type="text"
name="title"
value={props.values.title}
inputHandler={props.handleChange}
blurHandler={props.handleBlur}
error={props.errors.title && props.touched.title && props.errors.title}
error={
props.errors.title &&
props.touched.title &&
props.errors.title
}
/>
</div>
@@ -241,13 +179,16 @@ function AddJob({ popUpHandler, categories }) {
fieldClass="px-6"
label="Description"
labelClass="tracking-wide"
inputBg="bg-slate-100"
type="text"
name="description"
value={props.values.description}
inputHandler={props.handleChange}
blurHandler={props.handleBlur}
error={props.errors.description && props.touched.description && props.errors.description}
error={
props.errors.description &&
props.touched.description &&
props.errors.description
}
/>
</div>
@@ -256,15 +197,20 @@ function AddJob({ popUpHandler, categories }) {
<div className="sm:w-[60%] w-full">
<label
htmlFor="Job Delivery Details"
className='className="input-label text-[#181c32] dark:text-white text-[13.975px] leading-[20.9625px] font-semibold flex items-center gap-1'
className="input-label text-[#181c32] dark:text-white text-[13.975px] leading-[20.9625px] font-semibold flex items-center gap-1"
>
Job Delivery Details
{props.errors.job_detail && props.touched.job_detail && <span className="text-[12px] text-red-500">{props.errors.job_detail}</span>}
{props.errors.job_detail &&
props.touched.job_detail && (
<span className="text-[12px] text-red-500">
{props.errors.job_detail}
</span>
)}
</label>
<textarea
id="Job Delivery Details"
rows="5"
className={`input-field px-3 py-2 placeholder:text-base text-dark-gray dark:text-white w-full h-[100px] bg-slate-100 dark:bg-[#11131F] focus:ring-0 focus:outline-[#dce4e9] rounded-[10px]`}
className={`input-field px-3 py-2 placeholder:text-base text-dark-gray dark:text-white w-full h-[100px] bg-slate-100 dark:bg-[#11131F] focus:ring-0 focus:outline-[#dce4e9] rounded-[10px] border`}
style={{ resize: "none" }}
name="job_detail"
value={props.values.job_detail}
@@ -286,19 +232,28 @@ function AddJob({ popUpHandler, categories }) {
role="group"
aria-labelledby="checked-group"
>
{Object?.entries(categories).map(([key, value]) => (
<label
key={key}
className="flex gap-1 w-full items-center"
>
<Field
type="checkbox"
name="category"
value={key}
/>
<span className="text-[13.975px]">{value}</span>
{categories ? (
<>
{Object?.entries(categories).map(([key, value]) => (
<label
key={key}
className="flex gap-1 w-full items-center"
>
<Field
type="checkbox"
name="category"
value={key}
/>
<span className="text-[13.975px]">{value}</span>
</label>
))}
</>
) : (
<label className="flex gap-1 w-full items-center">
<Field type="checkbox" name="category" />
<span className="text-[13.975px]">null</span>
</label>
))}
)}
<span className="h-5 text-sm italic text-[#cf3917]">
{props.errors.category &&
props.touched.category &&
@@ -324,7 +279,7 @@ function AddJob({ popUpHandler, categories }) {
<Field
component="select"
name="timeline_days"
className={`input-field p-2 mt-3 rounded-md placeholder:text-base text-dark-gray dark:text-white w-full h-10 bg-slate-100 dark:bg-[#11131F] focus:ring-0 focus:outline-none ${
className={`input-field p-2 mt-3 rounded-md placeholder:text-base text-dark-gray dark:text-white w-full h-10 bg-slate-100 dark:bg-[#11131F] focus:ring-0 focus:outline-none border ${
props.errors.timeline_days &&
props.touched.timeline_days
? "border-[#ff0a0a63] shadow-red-500 border-[0.5px] animate-shake"
@@ -335,6 +290,7 @@ function AddJob({ popUpHandler, categories }) {
<option value="">Select Duration</option>
{publicArray.map(({ name, duration }, idx) => (
<option
key={idx}
className="text-slate-500 text-lg"
value={duration}
>
@@ -383,13 +339,12 @@ function AddJob({ popUpHandler, categories }) {
</span>
</button>
{requestStatus.loading ? (
{requestStatus?.loading ? (
<LoadingSpinner size="8" color="sky-blue" />
) : (
<button
type="submit"
className="w-[152px] h-[46px] flex justify-center items-center btn-gradient text-base rounded-full text-white"
// className='w-20 h-11 flex justify-center items-center btn-gradient text-base rounded-full text-white'
>
Add Job
</button>
+100
View File
@@ -0,0 +1,100 @@
import { Field, Form, Formik } from "formik";
import { useEffect, useState } from "react";
import { useDispatch, useSelector } from "react-redux";
import * as Yup from "yup";
import usersService from "../../services/UsersService";
import { tableReload } from "../../store/TableReloads";
import InputCom from "../Helpers/Inputs/InputCom";
import LoadingSpinner from "../Spinners/LoadingSpinner";
// Initialize state for request values
const initialReqState = {
loading: false,
status: false,
message: "",
};
// For form initial values
const initialValues = {
// initial values for formik
country: "",
price: "",
title: "",
description: "",
job_detail: "",
timeline_days: "",
category: [],
};
// const getWalletDetail = (country) => { // A FUNCTION TO GET USER BALANCE BASED ON COUNTRY SELECTED
// const walletChecker = walletDetails?.data.find(
// (item) => item.country === country
// );
// return walletChecker ? walletChecker.amount : 0;
// };
// To get the validation schema
const validationSchema = Yup.object().shape({
country: Yup.string()
.min(1, "Minimum 3 characters")
.max(25, "Maximum 25 characters")
.required("Currency is required"),
price: Yup.string()
.typeError("Invalid number")
.min(1, "Price must be greater than 0")
.test("no-e", "Invalid number", (value) => {
if (value && /\d+e/.test(value)) {
return false;
}
return true;
})
.required("Price is required"),
title: Yup.string()
.min(5, "Minimum 5 characters")
.max(149, "Maximum 149 characters")
.required("Title is required"),
description: Yup.string()
.min(5, "Minimum 5 characters")
.max(299, "Maximum 299 characters")
.required("Description is required"),
job_detail: Yup.string()
.min(3, "Minimum 3 characters")
.max(499, "Maximum 499 characters")
.required("Details is required"),
timeline_days: Yup.number()
.typeError("you must specify a number")
.min(1, "Price must be greater than 0")
.required("Timeline is required"),
category: Yup.array().min(1, "Select category"),
});
const getWalletDetail = (countryParams, walletDetails) => {
// A FUNCTION TO GET USER BALANCE BASED ON COUNTRY SELECTED
const walletChecker = walletDetails?.data.find(
(item) => item.country === countryParams
);
return walletChecker
? {
description: walletChecker.description,
country: walletChecker.country,
}
: "";
};
export {
Field,
Form,
Formik,
useState,
useEffect,
useDispatch,
useSelector,
usersService,
InputCom,
LoadingSpinner,
initialReqState,
initialValues,
validationSchema,
getWalletDetail,
tableReload
};
@@ -0,0 +1,9 @@
import React from 'react'
export default function AppDownload() {
return (
<div>
<div className='h-screen flex justify-center items-center'>App Download Content Comes here</div>
</div>
)
}
+75
View File
@@ -0,0 +1,75 @@
import React, { useContext } from "react";
import { Link } from "react-router-dom";
import { localImgLoad } from "../../lib";
import DarkModeContext from "../Contexts/DarkModeContext";
export default function LoginLayout({ slogan, children }) {
const bgImg = localImgLoad("images/left-wrenchboard.jpg");
const bgImgNig = localImgLoad("images/wrench-home-back-nigeria.jpg");
const bgImgCom = localImgLoad("images/wrench-home-back-common.jpg");
const { countryMode } = useContext(DarkModeContext);
return (
<div
className={`h-screen overflow-y-auto bg-cover bg-center`}
style={{
backgroundImage: `url(${countryMode == "NG" ? bgImgNig : bgImgCom})`,
}}
>
<div className={`w-full grid grid-cols-1 lg:grid-cols-2`}>
{/* <div
className={`auth-bg hidden lg:block bg-blue-50 relative bg-cover bg-no-repeat border-0 after:content-[''] after:absolute after:inset-0`}
style={{backgroundImage: `url(${bgImg})`}}
>
</div> */}
<div className="p-5 sm:p-7 flex place-content-center lg:col-start-2">
<div className="py-5 w-full sm:w-11/12 max-w-[550px] shadow-md bg-slate-50 rounded-[0.475rem]">
<div className="w-full flex justify-center items-center">
{children && children}
</div>
<div className="w-full flex flex-col justify-center items-center px-10">
<div className="w-full flex justify-center items-center pt-5">
<div className="flex items-center">
<a
href="https://www.wrenchboard.com/about-us"
className="text-[#a1a5b7] text-[15px] px-2 font-medium hover:text-[#009ef7]"
target="_blank"
rel="noreferrer"
>
About
</a>
<a
href="https://www.wrenchboard.com/service"
className="text-[#a1a5b7] text-[15px] px-2 font-medium hover:text-[#009ef7]"
target="_blank"
rel="noreferrer"
>
Services
</a>
<a
href="https://www.wrenchboard.com/contact"
className="text-[#a1a5b7] text-[15px] px-2 font-medium hover:text-[#009ef7]"
target="_blank"
rel="noreferrer"
>
Contact Us
</a>
</div>
</div>
<p className="py-1 text-black text-[15px] px-2 font-medium flex items-center gap-1">
<span className="dark:text-white">
&copy; {new Date().getFullYear()} -
</span>
<Link to="/" className="text-[#009ef7] ml-1">
WrenchBoard
</Link>{" "}
</p>
</div>
</div>
</div>
</div>
</div>
);
}
@@ -1,8 +1,8 @@
import React, { useEffect } from 'react';
import { useDispatch } from "react-redux";
import { useLocation, useNavigate } from 'react-router-dom';
import usersService from '../../../services/UsersService';
import {updateUserDetails} from "../../../store/UserDetails";
import { useDispatch } from "react-redux";
import { updateUserDetails } from "../../../store/UserDetails";
import AuthLayout from "../AuthLayout";
function FbookRedirect() {
@@ -22,38 +22,50 @@ function FbookRedirect() {
console.log(codeResponse);
/*
POST /token HTTP/1.1
Host: oauth2.googleapis.com
Content-Type: application/x-www-form-urlencoded
https://developers.facebook.com/docs/facebook-login/guides/advanced/manual-flow/#exchangecode
Step 1. Get access token by code
code=4/P7q7W91a-oMsCeLvIaQm6bTrgtp7&
client_id=your_client_id&
client_secret=your_client_secret&
redirect_uri=https%3A//oauth2.example.com/code&
grant_type=authorization_code
GET https://graph.facebook.com/v17.0/oauth/access_token?
client_id={app-id}
&redirect_uri={redirect-uri}
&client_secret={app-secret}
&code={code-parameter}
https://developers.facebook.com/docs/facebook-login/guides/access-tokens/get-long-lived
Step 2. Get long-lived token by access token
curl -i -X GET "https://graph.facebook.com/{graph-api-version}/oauth/access_token?
grant_type=fb_exchange_token&
client_id={app-id}&
client_secret={app-secret}&
fb_exchange_token={your-access-token}"
*/
// process.env.REACT_APP_FACEBOOK_CLIENT_ID
// process.env.REACT_APP_FACEBOOK_CLIENT_SCOPE
var reqData = {
auth_type: "FACEBOOK",
code: codeResponse,
redirect_uri: process.env.REACT_APP_GOOGLE_REDIRECT_URL,
redirect_uri: process.env.REACT_APP_FACEBOOK_REDIRECT_URL,
};
// userApi
// .authStart(reqData)
// .then((res) => {
// if (res.status == 200 && res.data.internal_return >= 0 && res.data.member_id && res.data.uid && res.data.session) {
// localStorage.setItem("member_id", `${res.data.member_id}`);
// localStorage.setItem("uid", `${res.data.uid}`);
// localStorage.setItem("session_token", `${res.data.session}`);
// dispatch(updateUserDetails({...res.data}));
// navigate('/', {replace: true})
// return
// }
// navigate('/login', {state: {error: true}})
// })
// .catch((error) => {
// navigate('/login', {state: {error: true}})
// console.log(error);
// });
userApi
.authStart(reqData)
.then((res) => {
if (res.status == 200 && res.data.internal_return >= 0 && res.data.member_id && res.data.uid && res.data.session) {
localStorage.setItem("member_id", `${res.data.member_id}`);
localStorage.setItem("uid", `${res.data.uid}`);
localStorage.setItem("session_token", `${res.data.session}`);
dispatch(updateUserDetails({...res.data}));
navigate('/', {replace: true})
return
}
navigate('/login', {state: {error: true}})
})
.catch((error) => {
navigate('/login', {state: {error: true}})
console.log(error);
});
},[])
return (
<AuthLayout>
@@ -1,26 +1,26 @@
import React, { useEffect } from 'react';
import { useLocation, useNavigate } from 'react-router-dom';
import usersService from '../../../services/UsersService';
import {updateUserDetails} from "../../../store/UserDetails";
import React, { useEffect } from "react";
import { useDispatch } from "react-redux";
import { useLocation, useNavigate } from "react-router-dom";
import usersService from "../../../services/UsersService";
import { updateUserDetails } from "../../../store/UserDetails";
import AuthLayout from "../AuthLayout";
function Redirect() {
const location = useLocation();
const navigate = useNavigate();
const userApi = new usersService();
const dispatch = useDispatch()
const location = useLocation();
const navigate = useNavigate();
const userApi = new usersService();
const dispatch = useDispatch();
const queryParams = new URLSearchParams(location?.search);
const codeResponse = queryParams.get("code");
const queryParams = new URLSearchParams(location?.search);
const codeResponse = queryParams.get("code");
useEffect(()=>{
if(!codeResponse){
navigate('/login', {state: {error: true}})
return
}
console.log(codeResponse);
/*
useEffect(() => {
if (!codeResponse) {
navigate("/login", { state: { error: true } });
return;
}
console.log(codeResponse);
/*
POST /token HTTP/1.1
Host: oauth2.googleapis.com
Content-Type: application/x-www-form-urlencoded
@@ -31,34 +31,40 @@ function Redirect() {
redirect_uri=https%3A//oauth2.example.com/code&
grant_type=authorization_code
*/
var reqData = {
auth_type: "GOOGLE",
code: codeResponse,
redirect_uri: process.env.REACT_APP_GOOGLE_REDIRECT_URL,
};
userApi
.authStart(reqData)
.then((res) => {
if (res.status == 200 && res.data.internal_return >= 0 && res.data.member_id && res.data.uid && res.data.session) {
localStorage.setItem("member_id", `${res.data.member_id}`);
localStorage.setItem("uid", `${res.data.uid}`);
localStorage.setItem("session_token", `${res.data.session}`);
dispatch(updateUserDetails({...res.data}));
navigate('/', {replace: true})
return
}
navigate('/login', {state: {error: true}})
})
.catch((error) => {
navigate('/login', {state: {error: true}})
console.log(error);
});
},[])
var reqData = {
auth_type: "GOOGLE",
code: codeResponse,
redirect_uri: process.env.REACT_APP_GOOGLE_REDIRECT_URL,
};
userApi
.authStart(reqData)
.then((res) => {
if (
res.status == 200 &&
res.data.internal_return >= 0 &&
res.data.member_id &&
res.data.uid &&
res.data.session
) {
localStorage.setItem("member_id", `${res.data.member_id}`);
localStorage.setItem("uid", `${res.data.uid}`);
localStorage.setItem("session_token", `${res.data.session}`);
dispatch(updateUserDetails({ ...res.data }));
navigate("/", { replace: true });
return;
}
navigate("/login", { state: { error: true } });
})
.catch((error) => {
navigate("/login", { state: { error: true } });
console.log(error);
});
}, []);
return (
<AuthLayout>
<div className='min-h-[70vh]'>Redirecting ... </div>
</AuthLayout>
)
<div className="min-h-[70vh]">Redirecting ... </div>
</AuthLayout>
);
}
export default Redirect
export default Redirect;
@@ -0,0 +1,37 @@
import React from 'react'
import { useNavigate } from 'react-router-dom'
import localImgLoad from '../../lib/localImgLoad'
const ForgetPwdResponse = ({title, message, type}) => {
const navigate = useNavigate()
return (
<>
<div className="title-area flex flex-col justify-center items-center relative text-center mb-7">
<h1 className={`${type ? 'text-black' : 'text-red-500'}text-[#181c32] font-semibold dark:text-white mb-3 leading-[27.3px] text-[22.75px]`}>
{title}
</h1>
</div>
<div className="title-area w-[100px] h-[100px] mx-auto flex flex-col justify-center items-center relative text-center mb-7">
<img className='w-full h-full' src={`${type ? localImgLoad('images/icons/success.svg') : localImgLoad('images/icons/error.svg')}`} alt='alert-banner' />
</div>
<div className="title-area flex flex-col justify-center items-center relative text-center mb-7">
<p className={`${type ? 'text-sky-blue' : 'text-red-500'} font-semibold dark:text-white mb-3 leading-[27.3px] text-[18px]`}>
{message}
</p>
</div>
<div className="signin-area mb-3.5">
<div className="flex justify-center items-center gap-2">
<button
type="button"
onClick={() => navigate("/login")}
className={`h-[48px] rounded-full mb-6 text-[15px] font-semibold text-white flex justify-center bg-[#4687ba] hover:bg-[#009ef7] transition-all duration-300 items-center py-[0.8875rem] px-[1.81rem]`}
>
<span>Home</span>
</button>
</div>
</div>
</>
)
}
export default ForgetPwdResponse
+128 -99
View File
@@ -1,9 +1,13 @@
import React, { useState } from "react";
import { Link, useNavigate } from "react-router-dom";
import WrenchBoard from "../../../assets/images/wrenchboard.png";
import WrenchBoard from "../../../assets/images/wrenchboard-logo-text.png";
import usersService from "../../../services/UsersService";
import InputCom from "../../Helpers/Inputs/InputCom";
import AuthLayout from "../AuthLayout";
import EmailValidator from "../../../lib/EmailValidator";
import ForgetPwdResponse from "../ForgetPwdResponse";
import ReCAPTCHA from "react-google-recaptcha";
export default function ForgotPassword() {
const [checked, setValue] = useState(false);
@@ -11,7 +15,7 @@ export default function ForgotPassword() {
// email
const [email, setMail] = useState("");
const [msgError, setMsgError] = useState("");
const [msgSuccess, setMsgSuccess] = useState(false);
const [msgSuccess, setMsgSuccess] = useState(null);
const navigate = useNavigate();
const userApi = new usersService();
@@ -20,15 +24,38 @@ export default function ForgotPassword() {
setMail(e?.target.value);
};
const humanChecker = () => {
setValue(!checked);
};
// const humanChecker = () => {
// setValue(!checked);
// };
function humanChecker(value) {
// console.log("Captcha value:", value);
if(value){
setValue(true)
}else{
setValue(false)
}
}
const resetHandler = async () => {
if (email == "") {
setMsgError("An email is required");
} else if (!checked) {
return setTimeout(() => {
setMsgError(null);
}, process.env.REACT_APP_RESET_START_ERROR_TIMEOUT);
}
if (!checked) {
setMsgError("Check if you are human");
return setTimeout(() => {
setMsgError(null);
}, process.env.REACT_APP_RESET_START_ERROR_TIMEOUT);
}
if(!EmailValidator(email)){ // CHECKS IF EMAIL IS VALID
setMsgError("Invalid Email");
return setTimeout(() => {
setMsgError(null);
}, process.env.REACT_APP_RESET_START_ERROR_TIMEOUT);
}
if (email !== "" && checked) {
@@ -41,8 +68,11 @@ export default function ForgotPassword() {
setMail("");
setValue(false);
setResetLoading(false);
}else{
setMsgSuccess(false);
}
} catch (error) {
setMsgSuccess(false);
setResetLoading(false);
setMail("");
setMsgError("An error occurred");
@@ -53,9 +83,6 @@ export default function ForgotPassword() {
}, process.env.REACT_APP_RESET_START_ERROR_TIMEOUT);
}
}
setTimeout(() => {
setMsgError(null);
}, process.env.REACT_APP_RESET_START_ERROR_TIMEOUT);
};
return (
@@ -73,106 +100,108 @@ export default function ForgotPassword() {
</div>
<div className="content-wrapper login shadow-md w-full lg:max-w-[500px] mx-auto flex justify-center items-center xl:bg-white dark:bg-dark-white 2xl:w-[828px] rounded-[0.475rem] sm:p-7 p-5">
<div className="flex flex-col justify-center w-full h-full px-5">
<div className="title-area flex flex-col justify-center items-center relative text-center mb-7">
<h1 className="text-[#181c32] font-semibold dark:text-white mb-3 leading-[27.3px] text-[22.75px]">
Forget Password
</h1>
<span className="text-gray-400 font-medium text-[16.25px] leading-[24.375px]">
Enter your email to reset your password.
</span>
</div>
<div className="input-area">
<div className="input-item mb-10">
<InputCom
fieldClass="px-6"
placeholder="Your Username/Email"
label="Email"
name="email"
type="email"
value={email}
inputHandler={handleEmail}
iconName="message"
/>
{msgSuccess == null ?
<>
<div className="title-area flex flex-col justify-center items-center relative text-center mb-7">
<h1 className="text-[#181c32] font-semibold dark:text-white mb-3 leading-[27.3px] text-[22.75px]">
Forget Password
</h1>
<span className="text-gray-400 font-medium text-[16.25px] leading-[24.375px]">
Enter your email to reset your password.
</span>
</div>
{/* hCaptha clone for the time being */}
<div className="mb-10">
<div className="w-[303px] h-[78px] mx-auto overflow-hidden">
<div className="w-[300px] h-[74px] bg-white bottom-[1px] rounded border-gray-100 overflow-hidden cursor-pointer">
{/* Checkbox */}
<div className="h-full relative inline-block">
<div className="relative table top-0 h-full">
<div className="table-cell align-middle">
<div className="relative w-[30px] h-[30px] mx-[15px]">
<input
type="checkbox"
name="human-checkbox"
id="human-checkbox"
className="w-[28px] h-[28px] border-[1px] rounded border-gray-400 checked:bg-white"
checked={checked}
onChange={humanChecker}
/>
<div className="input-area">
<div className="input-item mb-10">
<InputCom
fieldClass="px-6"
placeholder="Your Username/Email"
label="Email"
name="email"
type="email"
value={email}
inputHandler={handleEmail}
iconName="message"
/>
</div>
{/* hCaptha clone for the time being */}
<div className="mb-10 flex justify-center w-full">
<ReCAPTCHA
sitekey={process.env.REACT_APP_GOOGLE_RECAPTCHA_SITEKEY}
onChange={humanChecker}
/>
</div>
{/* <div className="mb-10">
<div className="w-[303px] h-[78px] mx-auto overflow-hidden">
<div className="w-[300px] h-[74px] bg-white bottom-[1px] rounded border-gray-100 overflow-hidden cursor-pointer">
<div className="h-full relative inline-block">
<div className="relative table top-0 h-full">
<div className="table-cell align-middle">
<div className="relative w-[30px] h-[30px] mx-[15px]">
<input
type="checkbox"
name="human-checkbox"
id="human-checkbox"
className="w-[28px] h-[28px] border-[1px] rounded border-gray-400 checked:bg-white"
checked={checked}
onChange={humanChecker}
/>
</div>
</div>
</div>
</div>
</div>
<div className="h-full relative inline-block w-[170px]">
<label className="relative table top-0 h-full">
<label className="table-cell align-middle">
<label
className="text-800 text-sm"
htmlFor="human-checkbox"
>
I am human
<div className="h-full relative inline-block w-[170px]">
<label className="relative table top-0 h-full">
<label className="table-cell align-middle">
<label
className="text-800 text-sm"
htmlFor="human-checkbox"
>
I am human
</label>
</label>
</label>
</label>
</div>
<div className="h-full relative inline-block w-16"></div>
</div>
<div className="h-full relative inline-block w-16"></div>
</div>
</div> */}
{msgError && (
<div className="relative p-4 text-[#912741] bg-[#fcd9e2] border-[#fbc6d3] mb-4 rounded-[0.475rem] text-md font-light leading-[19.5px] text-[13px]">
{msgError}
</div>
)}
<div className="signin-area mb-3.5">
<div className="flex justify-center items-center gap-4">
<button
type="button"
onClick={() => navigate("/login")}
className={`h-[48px] rounded-full mb-6 text-[15px] font-semibold text-white hover:text-white flex justify-center bg-red-500 hover:bg-red-600 transition-all duration-300 items-center py-[0.8875rem] px-[1.8125rem] `}
>
Cancel
</button>
<button
type="button"
onClick={resetHandler}
className={`h-[48px] rounded-full mb-6 text-[15px] font-semibold text-white flex justify-center bg-[#4687ba] hover:bg-[#009ef7] transition-all duration-300 items-center py-[0.8875rem] px-[1.81rem]`}
>
{resetLoading ? (
<div className="signup btn-loader"></div>
) : (
<span>Send Code</span>
)}
</button>
</div>
</div>
</div>
{msgError && (
<div className="relative p-4 text-[#912741] bg-[#fcd9e2] border-[#fbc6d3] mb-4 rounded-[0.475rem] text-md font-light leading-[19.5px] text-[13px]">
{msgError}
</div>
)}
{msgSuccess && (
<div className="relative p-4 text-[#44228c] bg-[#e3d7fb] border-[#d5c4f9] mb-4 rounded-[0.475rem] text-md font-light leading-[19.5px] text-[13px]">
If we find your email, you will receive a link to reset your
password. Please use or{" "}
<Link
to="/contact"
className="text-[#4687ba] hover:text-[#009ef7]"
>
contact form
</Link>{" "}
if you did not get our message after few minutes.
</div>
)}
<div className="signin-area mb-3.5">
<div className="flex justify-center items-center gap-2">
<button
type="button"
onClick={resetHandler}
className={`rounded-[0.475rem] mb-6 text-[15px] font-semibold text-white flex justify-center bg-[#4687ba] hover:bg-[#009ef7] transition-all duration-300 items-center py-[0.8875rem] px-[1.81rem]`}
>
{resetLoading ? (
<div className="signup btn-loader"></div>
) : (
<span>Send Code</span>
)}
</button>
<button
type="button"
onClick={() => navigate("/login")}
className={`rounded-[0.475rem] mb-6 text-[15px] font-semibold text-[#009ef7] hover:text-white flex justify-center bg-[#f1faff] hover:bg-[#009ef7] transition-all duration-300 items-center py-[0.8875rem] px-[1.8125rem] `}
>
Cancel
</button>
</div>
</div>
</div>
</>
:
<ForgetPwdResponse
title={'Forget Password'}
message={msgSuccess? `Check your email for the link to continue password reset. Note the reset link will expire short time` : 'We are unable to continue with your request. Please try another username or contact us for help'}
type={msgSuccess}
/>
}
</div>
</div>
</div>
@@ -0,0 +1,211 @@
import React, { useState } from "react";
import { Link, useNavigate } from "react-router-dom";
import WrenchBoard from "../../../assets/images/wrenchboard-logo-text.png";
import usersService from "../../../services/UsersService";
import InputCom from "../../Helpers/Inputs/InputCom";
import AuthLayout from "../AuthLayout2";
import EmailValidator from "../../../lib/EmailValidator";
import ForgetPwdResponse from "../ForgetPwdResponse";
import ReCAPTCHA from "react-google-recaptcha";
export default function ForgotPassword() {
const [checked, setValue] = useState(false);
const [resetLoading, setResetLoading] = useState(false);
// email
const [email, setMail] = useState("");
const [msgError, setMsgError] = useState("");
const [msgSuccess, setMsgSuccess] = useState(null);
const navigate = useNavigate();
const userApi = new usersService();
const handleEmail = (e) => {
setMail(e?.target.value);
};
// const humanChecker = () => {
// setValue(!checked);
// };
function humanChecker(value) {
// console.log("Captcha value:", value);
if(value){
setValue(true)
}else{
setValue(false)
}
}
const resetHandler = async () => {
if (email == "") {
setMsgError("An email is required");
return setTimeout(() => {
setMsgError(null);
}, process.env.REACT_APP_RESET_START_ERROR_TIMEOUT);
}
if (!checked) {
setMsgError("Check if you are human");
return setTimeout(() => {
setMsgError(null);
}, process.env.REACT_APP_RESET_START_ERROR_TIMEOUT);
}
if(!EmailValidator(email)){ // CHECKS IF EMAIL IS VALID
setMsgError("Invalid Email");
return setTimeout(() => {
setMsgError(null);
}, process.env.REACT_APP_RESET_START_ERROR_TIMEOUT);
}
if (email !== "" && checked) {
const reqData = { email };
setResetLoading(true);
try {
const res = await userApi.StartResetPassword(reqData);
if (res.status === 200) {
setMsgSuccess(true);
setMail("");
setValue(false);
setResetLoading(false);
}else{
setMsgSuccess(false);
}
} catch (error) {
setMsgSuccess(false);
setResetLoading(false);
setMail("");
setMsgError("An error occurred");
throw new Error(error);
} finally {
setTimeout(() => {
setMsgError(null);
}, process.env.REACT_APP_RESET_START_ERROR_TIMEOUT);
}
}
};
return (
<>
<AuthLayout slogan="Welcome to WrenchBoard">
<div className="w-full">
<div className="mb-12">
<Link to="#">
<img
src={WrenchBoard}
alt="wrenchboard"
className="h-10 mx-auto"
/>
</Link>
</div>
<div className="flex place-content-center">
<div className="w-10/12">
{msgSuccess == null ?
<>
<div className="title-area flex flex-col justify-center items-center relative text-center mb-7">
<h1 className="text-[#181c32] font-semibold dark:text-white mb-3 leading-[27.3px] text-[22.75px]">
Forget Password
</h1>
<span className="text-gray-400 font-medium text-[16.25px] leading-[24.375px]">
Enter your email to reset your password.
</span>
</div>
<div className="input-area">
<div className="input-item mb-10">
<InputCom
fieldClass="px-6"
placeholder="Your Username/Email"
label="Email"
name="email"
type="email"
value={email}
inputHandler={handleEmail}
iconName="message"
/>
</div>
{/* hCaptha clone for the time being */}
<div className="mb-10 flex justify-center w-full">
<ReCAPTCHA
sitekey={process.env.REACT_APP_GOOGLE_RECAPTCHA_SITEKEY}
onChange={humanChecker}
/>
</div>
{/* <div className="mb-10">
<div className="w-[303px] h-[78px] mx-auto overflow-hidden">
<div className="w-[300px] h-[74px] bg-white bottom-[1px] rounded border-gray-100 overflow-hidden cursor-pointer">
<div className="h-full relative inline-block">
<div className="relative table top-0 h-full">
<div className="table-cell align-middle">
<div className="relative w-[30px] h-[30px] mx-[15px]">
<input
type="checkbox"
name="human-checkbox"
id="human-checkbox"
className="w-[28px] h-[28px] border-[1px] rounded border-gray-400 checked:bg-white"
checked={checked}
onChange={humanChecker}
/>
</div>
</div>
</div>
</div>
<div className="h-full relative inline-block w-[170px]">
<label className="relative table top-0 h-full">
<label className="table-cell align-middle">
<label
className="text-800 text-sm"
htmlFor="human-checkbox"
>
I am human
</label>
</label>
</label>
</div>
<div className="h-full relative inline-block w-16"></div>
</div>
</div>
</div> */}
{msgError && (
<div className="relative p-4 text-[#912741] bg-[#fcd9e2] border-[#fbc6d3] mb-4 rounded-[0.475rem] text-md font-light leading-[19.5px] text-[13px]">
{msgError}
</div>
)}
<div className="signin-area mb-3.5">
<div className="flex justify-center items-center gap-4">
<button
type="button"
onClick={() => navigate("/login")}
className={`h-[48px] rounded-full mb-6 text-[15px] font-semibold text-white hover:text-white flex justify-center bg-red-500 hover:bg-red-600 transition-all duration-300 items-center py-[0.8875rem] px-[1.8125rem] `}
>
Cancel
</button>
<button
type="button"
onClick={resetHandler}
className={`h-[48px] rounded-full mb-6 text-[15px] font-semibold text-white flex justify-center bg-[#4687ba] hover:bg-[#009ef7] transition-all duration-300 items-center py-[0.8875rem] px-[1.81rem]`}
>
{resetLoading ? (
<div className="signup btn-loader"></div>
) : (
<span>Send Code</span>
)}
</button>
</div>
</div>
</div>
</>
:
<ForgetPwdResponse
title={'Forget Password'}
message={msgSuccess? `Check your email for the link to continue password reset. Note the reset link will expire short time` : 'We are unable to continue with your request. Please try another username or contact us for help'}
type={msgSuccess}
/>
}
</div>
</div>
</div>
</AuthLayout>
</>
);
}
+149 -53
View File
@@ -2,9 +2,9 @@ import React, { useEffect, useLayoutEffect, useState } from "react";
import { Link, useLocation, useNavigate } from "react-router-dom";
import linkedInLogo from "../../../assets/images/Linkedin.png";
import appleLogo from "../../../assets/images/apple-black.svg";
import facebookLogo from "../../../assets/images/facebook-4.svg";
import facebookLogo from "../../../assets/images/facebook.svg";
import googleLogo from "../../../assets/images/google-logo.svg";
import WrenchBoard from "../../../assets/images/wrenchboard.png";
import WrenchBoard from "../../../assets/images/wrenchboard-logo-text.png";
import usersService from "../../../services/UsersService";
import InputCom from "../../Helpers/Inputs/InputCom";
import AuthLayout from "../AuthLayout";
@@ -14,10 +14,20 @@ import { useGoogleLogin } from "@react-oauth/google";
import { useDispatch } from "react-redux";
import { updateUserDetails } from "../../../store/UserDetails";
import ReCAPTCHA from "react-google-recaptcha";
export default function Login() {
// eslint-disable-next-line no-restricted-globals
const queryParams = new URLSearchParams(location?.search);
// const sessionExpired = queryParams.get("sessionExpired");
const dispatch = useDispatch();
const { state } = useLocation();
const [sessionExpired, setSessionExpired] = useState(queryParams.get("sessionExpired"))
const [validCaptcha, setValidCaptcha] = useState({ show: false, valid: "" }); // FOR CAPTCHA
let [loginType, setLoginType] = useState("");
const [loginLoading, setLoginLoading] = useState(false);
@@ -88,7 +98,6 @@ export default function Login() {
password: password,
sessionid: "STARTING",
login_mode: 1100,
action: 11025,
};
} else if (name == "family") {
// Post Data Info for family Login
@@ -97,7 +106,6 @@ export default function Login() {
pin: password,
sessionid: "20067A92714",
login_mode: 1105,
action: 11025,
};
} else {
setLoginLoading(false);
@@ -107,6 +115,15 @@ export default function Login() {
}, Number(process.env.REACT_APP_LOGIN_ERROR_TIMEOUT));
return;
}
if (name == "full" && !validCaptcha.valid && validCaptcha.show) {
// RUNS AND DISPLAYS CAPTCHA, IF ERROR OCCURED DURING LOGIN FOR FULL LOGIN ALONE
setMsgError("Please Verify Captcha");
setLoginLoading(false);
setTimeout(() => {
setMsgError("");
}, Number(process.env.REACT_APP_LOGIN_ERROR_TIMEOUT));
return;
}
userApi
.logInUser(postData)
.then((res) => {
@@ -120,11 +137,15 @@ export default function Login() {
// setMsgError("Wrong, email/password");
setLoginError(true);
setLoginLoading(false);
setValidCaptcha((prev) => ({ ...prev, show: true })); // DISPLAYS CAPTCHA IF ERROR
return;
}
localStorage.setItem("member_id", `${res.data.member_id}`);
localStorage.setItem("uid", `${res.data.uid}`);
localStorage.setItem("session_token", `${res.data.session}`);
if (name === "family") {
sessionStorage.setItem("family_uid", res.data?.family_uid);
}
// localStorage.setItem("session", `${res.data.session}`);
dispatch(updateUserDetails({ ...res.data }));
setTimeout(() => {
@@ -135,6 +156,7 @@ export default function Login() {
.catch((error) => {
setMsgError("Unable to login, try again");
setLoginLoading(false);
setValidCaptcha((prev) => ({ ...prev, show: true })); // DISPLAYS CAPTCHA IF ERROR
})
.finally(() => {
setTimeout(() => {
@@ -145,6 +167,15 @@ export default function Login() {
});
};
function captchaChecker(value) {
// FUNCTION TO VALIDATE CAPTCHA
if (value) {
setValidCaptcha({ show: true, valid: value });
} else {
setValidCaptcha({ show: true, valid: "" });
}
}
const googleLogin = useGoogleLogin({
flow: "auth-code",
ux_mode: "redirect",
@@ -204,6 +235,20 @@ export default function Login() {
setPassword("");
}, [loginType]);
// EFFECT TO CLEAR SESSION EXPIRY IF IT EXISTS AFTER SOME SECONDS
useEffect(()=>{
let timer;
if(sessionExpired == "true"){
timer = setTimeout(()=>{
setSessionExpired(false)
},5000)
}
return () => {
clearTimeout(timer)
}
}, [])
return (
<>
<AuthLayout slogan="Welcome to WrenchBoard">
@@ -219,20 +264,32 @@ export default function Login() {
</div>
<div className="content-wrapper login shadow-md w-full lg:max-w-[530px] mx-auto flex justify-center items-center xl:bg-white dark:bg-dark-white 2xl:w-[828px] rounded-[0.475rem] sm:p-7 p-5">
<div className="w-full">
<div className="title-area flex flex-col justify-center items-center relative text-center mb-7">
{/* <h1 className="text-[#181c32] font-semibold dark:text-white mb-3 leading-[27.3px] text-[22.75px]">
{/* HIDES THIS IF USER SESSION HAS EXPIRED */}
{sessionExpired != "true" && (
<div className="title-area flex flex-col justify-center items-center relative text-center mb-7">
{/* <h1 className="text-[#181c32] font-semibold dark:text-white mb-3 leading-[27.3px] text-[22.75px]">
Sign In to WrenchBoard
</h1> */}
<span className="text-gray-400 font-medium text-[16.25px] leading-[24.375px]">
New Here?{" "}
<Link
to="/signup"
className="font-semibold text-[#4687ba] hover:text-[#009ef7] transition"
>
Create an Account
</Link>
</span>
</div>
<span className="text-gray-400 font-medium text-[16.25px] leading-[24.375px]">
New Here?{" "}
<Link
to="/signup"
className="font-semibold text-[#4687ba] hover:text-[#009ef7] transition"
>
Create an Account
</Link>
</span>
</div>
)}
{/* SHOWS THIS IF USER SESSION HAS EXPIRED */}
{sessionExpired == "true" && (
<div className="w-full p-1 mb-7">
<p className="text-red-500 text-base text-center">
Your session expired and will need to login again
</p>
</div>
)}
{/* switch login component */}
<div className="ml-7 flex justify-start items-center gap-3">
@@ -285,7 +342,7 @@ export default function Login() {
<InputCom
labelClass="tracking-wider"
fieldClass="sm:px-6 px-2 tracking-[0.25em] text-2xl"
value={password.replace(/./g, "●")}
value={password}
inputHandler={handlePassword}
placeholder="● ● ● ● ● ●"
label="Password"
@@ -296,6 +353,19 @@ export default function Login() {
forgotPassword
/>
</div>
{/* hCaptha clone for the time being */}
{validCaptcha.show && (
<div className="mb-5 flex justify-center w-full">
<ReCAPTCHA
sitekey={
process.env.REACT_APP_GOOGLE_RECAPTCHA_SITEKEY
}
onChange={captchaChecker}
/>
</div>
)}
{loginError && (
<div className="relative p-4 text-[#912741] bg-[#fcd9e2] border-[#fbc6d3] mb-4 rounded-[0.475rem] text-md font-thin leading-[19.5px] text-[13px]">
Invalid username or password- Please{" "}
@@ -320,7 +390,7 @@ export default function Login() {
onClick={doLogin}
type="button"
disabled={loginLoading}
className={`btn-login rounded-[0.475rem] mb-6 text-xl text-white flex justify-center bg-[#4687ba] hover:bg-[#009ef7] transition-all duration-300 items-center text-[15px]`}
className={`btn-login rounded-full mb-6 text-xl text-white flex justify-center bg-[#4687ba] hover:bg-[#009ef7] transition-all duration-300 items-center text-[15px]`}
>
{loginLoading ? (
<div className="signup btn-loader"></div>
@@ -329,34 +399,51 @@ export default function Login() {
)}
</button>
</div>
<div className="sm:flex sm:justify-between sm:items-center sm:space-x-2">
<BrandBtn
link="#"
imgSrc={googleLogo}
brand="Google"
onClick={googleLogin}
/>
<BrandBtn
// link={`https://appleid.apple.com/auth/authorize?response_type=code&response_mode=form_post&client_id=${process.env.REACT_APP_APPLE_CLIENT_ID}&redirect_uri=https%3A%2F%2Fwork.wrenchboard.com%2Flogin%2Fauth%2Fapple&state=4b2c4456b7&scope=name+email`}
link={`https://appleid.apple.com/auth/authorize?response_type=code&response_mode=form_post&client_id=${process.env.REACT_APP_APPLE_CLIENT_ID}&redirect_uri=${process.env.REACT_APP_APPLE_REDIRECT_URL}&state=4b2c4456b7&scope=name+email`}
imgSrc={appleLogo}
brand="Apple"
isAnchor={true}
/>
</div>
<div className="sm:flex sm:justify-between sm:items-center sm:space-x-2">
<BrandBtn
link={`https://www.facebook.com/v14.0/dialog/oauth?client_id=${process.env.REACT_APP_FACEBOOK_CLIENT_ID}&redirect_uri=${process.env.REACT_APP_FACEBOOK_REDIRECT_URL}&scope=${process.env.REACT_APP_FACEBOOK_CLIENT_SCOPE}`}
imgSrc={facebookLogo}
brand="Facebook"
isAnchor={true}
/>
<BrandBtn
// link="https://www.linkedin.com/oauth/v2/authorization?response_type=code&client_id=YOUR_CLIENT_ID&redirect_uri=YOUR_REDIRECT_URI&scope=comma-separated-list-of-scopes&state=YOUR_STATE_VALUE"
imgSrc={linkedInLogo}
brand="LinkedIn"
isAnchor={true}
/>
<div className="sm:grid grid-cols-2 gap-1">
<div className="w-full">
<BrandBtn
link="#"
imgSrc={googleLogo}
brand="Google"
onClick={googleLogin}
/>
</div>
<div
className={`w-full ${
process.env.REACT_APP_APPLE_SOCIAL_LOGIN !== 0 &&
"hidden"
}`}
>
<BrandBtn
// link={`https://appleid.apple.com/auth/authorize?response_type=code&response_mode=form_post&client_id=${process.env.REACT_APP_APPLE_CLIENT_ID}&redirect_uri=https%3A%2F%2Fwork.wrenchboard.com%2Flogin%2Fauth%2Fapple&state=4b2c4456b7&scope=name+email`}
link={`https://appleid.apple.com/auth/authorize?response_type=code&response_mode=form_post&client_id=${process.env.REACT_APP_APPLE_CLIENT_ID}&redirect_uri=${process.env.REACT_APP_APPLE_REDIRECT_URL}&state=4b2c4456b7&scope=name+email`}
imgSrc={appleLogo}
brand="Apple"
isAnchor={true}
// style={{visibility: 'hidden'}}
/>
</div>
<div className="w-full">
<BrandBtn
link={`https://www.facebook.com/v14.0/dialog/oauth?client_id=${process.env.REACT_APP_FACEBOOK_CLIENT_ID}&redirect_uri=${process.env.REACT_APP_FACEBOOK_REDIRECT_URL}&scope=${process.env.REACT_APP_FACEBOOK_CLIENT_SCOPE}`}
imgSrc={facebookLogo}
brand="Facebook"
isAnchor={true}
/>
</div>
<div
className={`w-full ${
process.env.REACT_APP_LINKEDIN_SOCIAL_LOGIN !== 0 &&
"hidden"
}`}
>
<BrandBtn
// link="https://www.linkedin.com/oauth/v2/authorization?response_type=code&client_id=YOUR_CLIENT_ID&redirect_uri=YOUR_REDIRECT_URI&scope=comma-separated-list-of-scopes&state=YOUR_STATE_VALUE"
imgSrc={linkedInLogo}
brand="LinkedIn"
isAnchor={true}
/>
</div>
</div>
</div>
</div>
@@ -416,7 +503,7 @@ export default function Login() {
onClick={doLogin}
disabled={loginLoading}
type="button"
className={`btn-login rounded-[0.475rem] text-xl text-white flex justify-center bg-[#4687ba] hover:bg-[#009ef7] transition-all duration-300 items-center text-[15px]`}
className={`btn-login rounded-full text-xl text-white flex justify-center bg-[#4687ba] hover:bg-[#009ef7] transition-all duration-300 items-center text-[15px]`}
>
{loginLoading ? (
<div className="signup btn-loader"></div>
@@ -432,10 +519,12 @@ export default function Login() {
}
{/* END of login component */}
<div className="pt-5 text-[#181c32] text-center font-semibold text-[13.975px] leading-[20.9625px]">
This site is protected by hCaptcha and the our Privacy Policy
and Terms of Service apply.
</div>
{loginType == "full" && (
<div className="pt-5 text-[#181c32] text-center font-semibold text-[13.975px] leading-[20.9625px]">
This site is protected by a Captcha. Our Privacy Policy and
Terms of Service apply.
</div>
)}
</div>
</div>
</div>
@@ -444,7 +533,14 @@ export default function Login() {
);
}
const BrandBtn = ({ link, imgSrc, brand, onClick, isAnchor = false }) => {
const BrandBtn = ({
link,
imgSrc,
brand,
onClick,
isAnchor = false,
style = { visibility: "visible" },
}) => {
// const doGoogle = async () => {
// alert("start google");
// };
@@ -464,7 +560,7 @@ const BrandBtn = ({ link, imgSrc, brand, onClick, isAnchor = false }) => {
// alert("start facebook");
// };
return (
<div className="w-full sm:w-1/2 flex justify-center bottomMargin">
<div className="w-full flex justify-center bottomMargin" style={style}>
{isAnchor ? (
<a
href={link}
+629
View File
@@ -0,0 +1,629 @@
import React, { useEffect, useLayoutEffect, useState } from "react";
import { Link, useLocation, useNavigate } from "react-router-dom";
import linkedInLogo from "../../../assets/images/Linkedin.png";
import appleLogo from "../../../assets/images/apple-black.svg";
import facebookLogo from "../../../assets/images/facebook.svg";
import googleLogo from "../../../assets/images/google-logo.svg";
import WrenchBoard from "../../../assets/images/wrenchboard-logo-text.png";
import usersService from "../../../services/UsersService";
import InputCom from "../../Helpers/Inputs/InputCom";
import AuthLayout from "../AuthLayout2";
// import { GoogleOAuthProvider } from '@react-oauth/google';
import { useGoogleLogin } from "@react-oauth/google";
import { useDispatch } from "react-redux";
import { updateUserDetails } from "../../../store/UserDetails";
import ReCAPTCHA from "react-google-recaptcha";
export default function Login() {
// eslint-disable-next-line no-restricted-globals
const queryParams = new URLSearchParams(location?.search);
// const sessionExpired = queryParams.get("sessionExpired");
const dispatch = useDispatch();
const { state } = useLocation();
const [sessionExpired, setSessionExpired] = useState(
queryParams.get("sessionExpired")
);
const [validCaptcha, setValidCaptcha] = useState({ show: false, valid: "" }); // FOR CAPTCHA
let [loginType, setLoginType] = useState("");
const [loginLoading, setLoginLoading] = useState(false);
const [showPassword, setShowPassword] = useState(false);
//login error state
const [loginError, setLoginError] = useState(false);
// for the catch error
const [msgError, setMsgError] = useState("");
// To Show and Hide Password
const togglePasswordVisibility = () => {
setShowPassword(!showPassword);
};
//FUNCTION TO DETERMINE/CHANGE LOGIN COMPONENT
const handleLoginType = ({ target: { name } }) => {
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
const [email, setMail] = useState("");
const handleEmail = (e) => {
setMail(e.target.value);
};
// password
const [password, setPassword] = useState("");
const handlePassword = (e) => {
setPassword(e.target.value);
};
const navigate = useNavigate();
const userApi = new usersService();
// FUNCTION TO HANDLE USER LOGIN
const doLogin = ({ target: { name } }) => {
setMsgError("");
setLoginError(false);
setLoginLoading(true);
let postData; // Post Data for API
if (!email || !password) {
setLoginLoading(false);
setMsgError("Please fill all the fields");
setTimeout(() => {
setMsgError("");
}, Number(process.env.REACT_APP_LOGIN_ERROR_TIMEOUT));
return;
}
if (name == "full") {
//checks if email is a valid email address
let regEx = /^[^0-9][a-zA-Z0-9._%+-]+@[a-zA-Z]+(\.[a-zA-Z]+)+$/;
if (regEx.test(email) == false) {
setLoginLoading(false);
setMsgError("Invalid Email");
return setTimeout(() => {
setMsgError("");
}, 3000);
}
// Post Data Info for normal Login
postData = {
username: email,
password: password,
sessionid: "STARTING",
login_mode: 1100,
};
} else if (name == "family") {
// Post Data Info for family Login
postData = {
username: email,
pin: password,
sessionid: "20067A92714",
login_mode: 1105,
};
} else {
setLoginLoading(false);
setMsgError("Invalid Login Type. Consider refreshing the page");
setTimeout(() => {
setMsgError("");
}, Number(process.env.REACT_APP_LOGIN_ERROR_TIMEOUT));
return;
}
if (name == "full" && !validCaptcha.valid && validCaptcha.show) {
// RUNS AND DISPLAYS CAPTCHA, IF ERROR OCCURED DURING LOGIN FOR FULL LOGIN ALONE
setMsgError("Please Verify Captcha");
setLoginLoading(false);
setTimeout(() => {
setMsgError("");
}, Number(process.env.REACT_APP_LOGIN_ERROR_TIMEOUT));
return;
}
userApi
.logInUser(postData)
.then((res) => {
if (
res.status != 200 ||
res.data.internal_return < 0 ||
!res.data.member_id ||
!res.data.uid ||
!res.data.session
) {
// setMsgError("Wrong, email/password");
setLoginError(true);
setLoginLoading(false);
setValidCaptcha((prev) => ({ ...prev, show: true })); // DISPLAYS CAPTCHA IF ERROR
return;
}
localStorage.setItem("member_id", `${res.data.member_id}`);
localStorage.setItem("uid", `${res.data.uid}`);
localStorage.setItem("session_token", `${res.data.session}`);
if (name === "family") {
sessionStorage.setItem("family_uid", res.data?.family_uid);
}
// localStorage.setItem("session", `${res.data.session}`);
dispatch(updateUserDetails({ ...res.data }));
setTimeout(() => {
navigate("/", { replace: true });
setLoginLoading(false);
}, 2000);
})
.catch((error) => {
setMsgError("Unable to login, try again");
setLoginLoading(false);
setValidCaptcha((prev) => ({ ...prev, show: true })); // DISPLAYS CAPTCHA IF ERROR
})
.finally(() => {
setTimeout(() => {
setLoginError(false);
setMsgError("");
setLoginLoading(false);
}, Number(process.env.REACT_APP_LOGIN_ERROR_TIMEOUT));
});
};
function captchaChecker(value) {
// FUNCTION TO VALIDATE CAPTCHA
if (value) {
setValidCaptcha({ show: true, valid: value });
} else {
setValidCaptcha({ show: true, valid: "" });
}
}
const googleLogin = useGoogleLogin({
flow: "auth-code",
ux_mode: "redirect",
redirect_uri: process.env.REACT_APP_GOOGLE_REDIRECT_URL,
onSuccess: async (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);
if (state?.error) {
//check if the login path has an error state indicating any social handle login with error
setMsgError("Unexpected Error, Please try again soon.");
setTimeout(() => {
setMsgError("");
navigate("/login", { replace: true });
}, 4000);
}
}, []);
useEffect(() => {
setMail("");
setPassword("");
}, [loginType]);
// EFFECT TO CLEAR SESSION EXPIRY IF IT EXISTS AFTER SOME SECONDS
useEffect(() => {
let timer;
if (sessionExpired == "true") {
timer = setTimeout(() => {
setSessionExpired(false);
}, 5000);
}
return () => {
clearTimeout(timer);
};
}, []);
return (
<>
<AuthLayout slogan="Welcome to WrenchBoard">
<div className="w-full">
<div className="mb-5">
<Link to="#">
<img
src={WrenchBoard}
alt="wrenchboard"
className="h-10 mx-auto"
/>
</Link>
</div>
{/* <div className="content-wrapper login shadow-md w-10/12 mx-auto flex justify-center items-center xl:bg-white dark:bg-dark-white 2xl:w-[828px] rounded-[0.475rem] sm:p-7 p-5"> */}
<div className="flex place-content-center">
<div className="w-10/12">
{/* HIDES THIS IF USER SESSION HAS EXPIRED */}
{sessionExpired != "true" && (
<div className="title-area flex flex-col justify-center items-center relative text-center mb-7">
{/* <h1 className="text-[#181c32] font-semibold dark:text-white mb-3 leading-[27.3px] text-[22.75px]">
Sign In to WrenchBoard
</h1> */}
<span className="text-gray-400 font-medium text-[16.25px] leading-[24.375px]">
New Here?{" "}
<Link
to="/signup"
className="font-semibold text-[#4687ba] hover:text-[#009ef7] transition"
>
Create an Account
</Link>
</span>
</div>
)}
{/* SHOWS THIS IF USER SESSION HAS EXPIRED */}
{sessionExpired == "true" && (
<div className="w-full p-1 mb-7">
<p className="text-red-500 text-base text-center">
Your session expired and will need to login again
</p>
</div>
)}
{/* switch login component */}
<div className="ml-7 flex justify-start items-center gap-3">
<button
name="full"
className={`login-type-btn px-4 py-1 rounded-t-2xl ${
loginType == "full"
? "bg-[#4687ba] border-[2px] border-[#4687ba] text-white"
: "bg-white text-[#000] border-t-[2px]"
}`}
onClick={handleLoginType}
>
Sign in
</button>
<button
name="family"
className={`login-type-btn px-4 py-1 rounded-t-2xl ${
loginType == "family"
? "bg-[#4687ba] border-[2px] border-[#4687ba] text-white"
: "bg-white text-[#000] border-t-[2px]"
}`}
onClick={handleLoginType}
>
Family Account
</button>
</div>
{/* END of switch login component */}
{/* for login component */}
{
loginType == "full" ? (
//user login component
<div className="p-6 input-area login-area border-2 border-[#4687ba] rounded-2xl">
<div className="input-item mb-5">
<InputCom
labelClass="tracking-wider"
fieldClass="sm:px-6 px-2"
value={email}
inputHandler={handleEmail}
placeholder="Your Email"
label="Email"
name="email"
type="email"
iconName="message"
/>
</div>
<div className="input-item mb-5">
<InputCom
labelClass="tracking-wider"
fieldClass="sm:px-6 px-2 tracking-[0.25em] text-2xl"
value={password}
inputHandler={handlePassword}
placeholder="● ● ● ● ● ●"
label="Password"
name="password"
type={showPassword ? "text" : "password"}
onClick={togglePasswordVisibility}
passIcon={showPassword ? "password" : "password"}
forgotPassword
/>
</div>
{/* hCaptha clone for the time being */}
{validCaptcha.show && (
<div className="mb-5 flex justify-center w-full">
<ReCAPTCHA
sitekey={
process.env.REACT_APP_GOOGLE_RECAPTCHA_SITEKEY
}
onChange={captchaChecker}
/>
</div>
)}
{loginError && (
<div className="relative p-4 text-[#912741] bg-[#fcd9e2] border-[#fbc6d3] mb-4 rounded-[0.475rem] text-md font-thin leading-[19.5px] text-[13px]">
Invalid username or password- Please{" "}
<Link to="/#" className="text-[#009ef7]">
reset your password
</Link>{" "}
or{" "}
<Link to="/signup" className="text-[#009ef7]">
create a new account
</Link>
</div>
)}
{msgError && (
<div className="relative p-4 text-[#912741] bg-[#fcd9e2] border-[#fbc6d3] mb-4 rounded-[0.475rem] text-md font-light leading-[19.5px] text-[13px]">
{msgError}
</div>
)}
<div className="signin-area mb-3.5">
<div className="flex justify-center">
<button
name="full"
onClick={doLogin}
type="button"
disabled={loginLoading}
className={`btn-login rounded-full mb-6 text-xl text-white flex justify-center bg-[#4687ba] hover:bg-[#009ef7] transition-all duration-300 items-center text-[15px]`}
>
{loginLoading ? (
<div className="signup btn-loader"></div>
) : (
<>Continue</>
)}
</button>
</div>
<div className="sm:grid grid-cols-2 gap-1">
<div className="w-full">
<BrandBtn
link="#"
imgSrc={googleLogo}
brand="Google"
onClick={googleLogin}
/>
</div>
<div
className={`w-full ${
process.env.REACT_APP_APPLE_SOCIAL_LOGIN !== 0 &&
"hidden"
}`}
>
<BrandBtn
// link={`https://appleid.apple.com/auth/authorize?response_type=code&response_mode=form_post&client_id=${process.env.REACT_APP_APPLE_CLIENT_ID}&redirect_uri=https%3A%2F%2Fwork.wrenchboard.com%2Flogin%2Fauth%2Fapple&state=4b2c4456b7&scope=name+email`}
link={`https://appleid.apple.com/auth/authorize?response_type=code&response_mode=form_post&client_id=${process.env.REACT_APP_APPLE_CLIENT_ID}&redirect_uri=${process.env.REACT_APP_APPLE_REDIRECT_URL}&state=4b2c4456b7&scope=name+email`}
imgSrc={appleLogo}
brand="Apple"
isAnchor={true}
// style={{visibility: 'hidden'}}
/>
</div>
<div className="w-full">
<BrandBtn
link={`https://www.facebook.com/v14.0/dialog/oauth?client_id=${process.env.REACT_APP_FACEBOOK_CLIENT_ID}&redirect_uri=${process.env.REACT_APP_FACEBOOK_REDIRECT_URL}&scope=${process.env.REACT_APP_FACEBOOK_CLIENT_SCOPE}`}
imgSrc={facebookLogo}
brand="Facebook"
isAnchor={true}
/>
</div>
<div
className={`w-full ${
process.env.REACT_APP_LINKEDIN_SOCIAL_LOGIN !== 0 &&
"hidden"
}`}
>
<BrandBtn
// link="https://www.linkedin.com/oauth/v2/authorization?response_type=code&client_id=YOUR_CLIENT_ID&redirect_uri=YOUR_REDIRECT_URI&scope=comma-separated-list-of-scopes&state=YOUR_STATE_VALUE"
imgSrc={linkedInLogo}
brand="LinkedIn"
isAnchor={true}
/>
</div>
</div>
</div>
</div>
) : (
// END of user login compoenent
// family login compoenent
<div className="p-6 input-area login-area border-2 border-[#4687ba] rounded-2xl">
<div className="input-item mb-5">
<InputCom
labelClass="tracking-wider"
fieldClass="px-6"
value={email}
inputHandler={handleEmail}
placeholder="Account ID"
label="Username"
name="email"
type="email"
iconName="family-id"
/>
</div>
<div className="input-item mb-5">
<InputCom
labelClass="tracking-wider"
fieldClass="px-6"
value={password}
inputHandler={handlePassword}
placeholder="● ● ● ● ● ●"
label="Pin"
name="password"
type={showPassword ? "text" : "password"}
onClick={togglePasswordVisibility}
passIcon={showPassword ? "family-pin" : "family-pin"}
/>
</div>
{loginError && (
<div className="relative p-4 text-[#912741] bg-[#fcd9e2] border-[#fbc6d3] mb-4 rounded-[0.475rem] text-md font-thin leading-[19.5px] text-[13px]">
Invalid username or password{" "}
{/* <Link to="/#" className="text-[#009ef7]">
reset your password
</Link>{" "}
or{" "}
<Link to="/signup" className="text-[#009ef7]">
create a new account
</Link> */}
</div>
)}
{msgError && (
<div className="relative p-4 text-[#912741] bg-[#fcd9e2] border-[#fbc6d3] mb-4 rounded-[0.475rem] text-md font-light leading-[19.5px] text-[13px]">
{msgError}
</div>
)}
<div className="signin-area mb-1.5">
<div className="flex justify-center">
<button
name="family"
onClick={doLogin}
disabled={loginLoading}
type="button"
className={`btn-login rounded-full text-xl text-white flex justify-center bg-[#4687ba] hover:bg-[#009ef7] transition-all duration-300 items-center text-[15px]`}
>
{loginLoading ? (
<div className="signup btn-loader"></div>
) : (
<>Continue</>
)}
</button>
</div>
</div>
</div>
)
// END of family login compoenent
}
{/* END of login component */}
{/* APP DOWNLOAD STORE */}
<div className="w-full mt-4">
<div className="w-full flex justify-center items-center gap-4">
<div className="w-28 lg:w-32">
<a
className="px-1 py-1 lg:py-2 flex justify-center items-center gap-1 w-full rounded-md bg-black text-white hover:text-slate-500 hover:shadow-lg transition-all duration-300"
target="_blank"
rel="noreferrer"
href={process.env.REACT_APP_APPLE_APP}
>
<i className="fa-brands fa-apple text-3xl"></i>
<div className="flex flex-col">
<span className="text-[11px]">Available on the</span>
<span className="text-[12px] lg:text-base">
App Store
</span>
</div>
</a>
</div>
<div className="w-28 lg:w-32">
<a
className="px-1 py-1 lg:py-2 flex justify-center items-center gap-1 w-full rounded-md bg-black text-white hover:text-slate-500 hover:shadow-lg transition-all duration-300"
target="_blank"
rel="noreferrer"
href={process.env.REACT_APP_ANDROID_APP}
>
<i className="fa-brands fa-google-play text-2xl"></i>
<div className="flex flex-col">
<span className="text-[11px]">Available on the</span>
<span className="text-[12px] lg:text-base">
Google Play
</span>
</div>
</a>
</div>
</div>
</div>
{loginType == "full" && (
<>
<div className="pt-5 text-[#181c32] text-center font-semibold text-[13.975px] leading-[20.9625px]">
This site is protected by a Captcha. Our Privacy Policy and
Terms of Service apply.
</div>
</>
)}
</div>
</div>
</div>
</AuthLayout>
</>
);
}
const BrandBtn = ({
link,
imgSrc,
brand,
onClick,
isAnchor = false,
style = { visibility: "visible" },
}) => {
// const doGoogle = async () => {
// alert("start google");
// };
// onSuccess: (codeResponse) => setUser(codeResponse),
// const doGoogle = useGoogleLogin({
// onSuccess: (codeResponse) => console.log('Login onSuccess:', codeResponse),
// onError: (error) => console.log('Login Failed:', error)
// });
// const doApple = async () => {
// alert("start apple");
// };
// const doFacebook = async () => {
// alert("start facebook");
// };
return (
<div className="w-full flex justify-center bottomMargin" style={style}>
{isAnchor ? (
<a
href={link}
className="w-full border border-light-purple rounded-[0.475rem] h-[48px] flex justify-center bg-[#FAFAFA] hover:bg-[#eff2f5] hover:text-[#7e8299] transition duration-300 items-center font-medium cursor-pointer"
>
<img className="mr-3 h-6" src={imgSrc} alt="logo-icon(s)" />
<span className="text-lg text-thin-light-gray font-normal text-[15px]">
Continue with {brand}
</span>
</a>
) : (
<button
onClick={onClick}
// href="#dd"
className="w-full border border-light-purple rounded-[0.475rem] h-[48px] flex justify-center bg-[#FAFAFA] hover:bg-[#eff2f5] hover:text-[#7e8299] transition duration-300 items-center font-medium cursor-pointer"
>
<img className="mr-3 h-6" src={imgSrc} alt="logo-icon(s)" />
<span className="text-lg text-thin-light-gray font-normal text-[15px]">
Continue with {brand}
</span>
</button>
)}
</div>
);
};
+108 -26
View File
@@ -1,21 +1,27 @@
import React, { useCallback, useEffect, useState } from "react";
import { Link, useNavigate } from "react-router-dom";
import facebookLogo from "../../../assets/images/facebook-4.svg";
import WrenchBoard from "../../../assets/images/wrenchboard.png";
import { Link, useLocation, useNavigate } from "react-router-dom";
import WrenchBoard from "../../../assets/images/wrenchboard-logo-text.png";
import usersService from "../../../services/UsersService";
import InputCom from "../../Helpers/Inputs/InputCom";
import AuthLayout from "../AuthLayout";
export default function SignUp() {
// eslint-disable-next-line no-restricted-globals
const queryParams = new URLSearchParams(location?.search);
const country = queryParams.get("cnt")?.toUpperCase();
const {pathname} = useLocation()
const currentPath = country ? `${pathname}?cnt=${country.toLowerCase()}`:pathname // Determines the new pathname is country query params exist
const [signUpLoading, setSignUpLoading] = useState(false);
const [checked, setValue] = useState(false);
// for the catch error
const [msgError, setMsgError] = useState("");
const [showPassword, setShowPassword] = useState(false);
const [countries, setCountries] = useState([]);
const [countries, setCountries] = useState({loading:true, data:[]});
const [formData, setFormData] = useState({
country: "",
country: country? country : "",
first_name: "",
last_name: "",
email: "",
@@ -41,14 +47,22 @@ export default function SignUp() {
// Get Country Api
const getCountryList = useCallback(async () => {
const res = await userApi.getSignupCountryData();
try {
if (res.status === 200) {
const { signup_country } = await res.data;
setCountries(signup_country);
const res = await userApi.getSignupCountryData();
if (res.status === 200 && res.data.internal_return >= 0) {
const { result_list } = await res.data;
if(country){ // IF LINK/PATHNAME HAS CNT QUERY VALUE
let cnt = result_list.filter(item => item.code == country) // test to see country passed in query param exist from list of countries supplied by API
if(!cnt.length){ // IF CNT EMPTY, SET FORMDATA COUNTRY BACK TO EMPTY STRING: RE: THIS IS BCOS WE INITAIL SET COUNTRY VALUE IN FORMDATA, IF COUNTRY PARAM IS PRESENT IN LINK
setFormData(prev => ({...prev, country: ''}))
return setCountries({loading: false, data: result_list});
}
return setCountries({loading: false, data: cnt});
}
setCountries({loading: false, data:result_list});
} else if (res.data.result !== 100) {
setCountries("Nothing see here!");
setCountries({loading: false, data:[]});
}
} catch (error) {
throw new Error(error);
@@ -69,13 +83,17 @@ export default function SignUp() {
let regEx = /^[^0-9][a-zA-Z0-9._%+-]+@[a-zA-Z]+(\.[a-zA-Z]+)+$/;
if (regEx.test(email) == false) {
setMsgError("Invalid Email");
return setTimeout(()=>{setMsgError("");},3000)
return setTimeout(() => {
setMsgError("");
}, 3000);
}
//checks if terms and condition is checked
if (!checked) {
setMsgError("Terms and condition required");
return setTimeout(()=>{setMsgError("");},3000)
return setTimeout(() => {
setMsgError("");
}, 3000);
}
setSignUpLoading(true);
@@ -95,7 +113,9 @@ export default function SignUp() {
if (res.status === 200) {
const { data } = res;
if (data && data.acc === "DULPICATE") {
setMsgError("Unable to use this username. Please try another username.");
setMsgError(
"Unable to use this username. Please try another username."
);
setSignUpLoading(false);
}
if (data && data.status === "1") {
@@ -129,7 +149,7 @@ export default function SignUp() {
<AuthLayout slogan="Welcome to WrenchBoard">
<div className="w-full">
<div className="mb-5">
<Link to="#">
<Link to={currentPath}>
<img
src={WrenchBoard}
alt="wrenchboard"
@@ -167,6 +187,7 @@ export default function SignUp() {
name="country"
value={formData.country}
inputHandler={handleInputChange}
disable={country && countries?.data?.length <= 1 ? true : false}
/>
<div className="input-fl-name mb-5 sm:flex w-full sm:space-x-6 ">
<div className="input-item sm:w-1/2 w-full mb-5 sm:mb-0">
@@ -211,10 +232,8 @@ export default function SignUp() {
name="password"
type={showPassword ? "text" : "password"}
onClick={togglePasswordVisibility}
passIcon={
showPassword ? "show-password" : "hide-password"
}
value={formData.password.replace(/./g, "●")}
passIcon={showPassword ? "show-password" : "hide-password"}
value={formData.password}
inputHandler={handleInputChange}
/>
</div>
@@ -223,7 +242,48 @@ export default function SignUp() {
{msgError}
</div>
)}
<div className="forgot-password-area flex justify-between items-center mb-6">
<div className="remember-checkbox flex items-center space-x-2.5 group cursor-pointer">
<button
onClick={rememberMe}
type="button"
className={`w-6 h-6 border-[#4687ba] text-white flex justify-center items-center border rounded-[.45em] group-checked:text-white transition-all duration-200 group-checked:cursor-default ${
checked && "text-white bg-[#4687ba]"
}`}
>
{checked && (
<svg
xmlns="http://www.w3.org/2000/svg"
className="h-5 w-5"
viewBox="0 0 20 20"
fill="currentColor"
>
<path
fillRule="evenodd"
d="M16.707 5.293a1 1 0 010 1.414l-8 8a1 1 0 01-1.414 0l-4-4a1 1 0 011.414-1.414L8 12.586l7.293-7.293a1 1 0 011.414 0z"
clipRule="evenodd"
/>
</svg>
)}
</button>
<span
onClick={rememberMe}
className="cursor-default text-dark-gray dark:text-white text-[15px] group-checked:text-white transition-all duration-200 group-checked:cursor-default"
>
I agree with all
<Link
href="#"
className="text-base text-[#4687ba] hover:text-[#009ef7] mx-1 inline-block"
>
terms and condition
</Link>
</span>
</div>
</div>
{/* Forgot Password */}
{/* <div className="forgot-password-area flex justify-between items-center mb-6">
<div className="remember-checkbox flex items-center space-x-2.5">
<button
onClick={rememberMe}
@@ -258,13 +318,14 @@ export default function SignUp() {
</Link>
</span>
</div>
</div>
</div> */}
<div className="signin-area mb-1">
<div className="flex justify-center">
<button
disabled={countries.loading}
type="button"
onClick={handleSignUp}
className={`rounded-[0.475rem] mb-6 text-white flex justify-center bg-[#4687ba] hover:bg-[#009ef7] transition-all duration-300 items-center h-[42px] py-[0.8875rem] px-[1.81rem] text-[14.95px] btn-login`}
className={`rounded-full mb-6 text-white flex justify-center bg-[#4687ba] hover:bg-[#009ef7] transition-all duration-300 items-center h-[42px] py-[0.8875rem] px-[1.81rem] text-[14.95px] btn-login`}
>
{signUpLoading ? (
<div className="signup btn-loader"></div>
@@ -289,6 +350,7 @@ const SelectOption = ({
inputHandler,
value,
data, // passing the data from parent
disable
}) => {
return (
<div className="input-com mb-7">
@@ -302,19 +364,39 @@ const SelectOption = ({
</div>
<div>
<select
disabled={disable}
name={name}
id={name}
className="input-wrapper border border-[#f5f8fa] dark:border-[#5e6278] w-full rounded-full h-[42px] overflow-hidden relative font-medium leading-6 bg-clip-padding text-[#5e6278] dark:text-gray-100 bg-[#f5f8fa] dark:bg-[#5e6278] text-base focus-visible:border-transparent focus-visible:outline-0 focus-visible:ring-transparent "
onChange={inputHandler}
value={value}
>
<option value={""}>Select your Country</option>
{data?.length > 0 &&
data?.map((item, idx) => (
<option value={item[0]} key={idx}>
{item[1]}
{data?.data?.length > 1 ?
<>
<option value={""}>Select your Country</option>
{data?.data?.map((item) => (
<option value={item.code} key={item.uid}>
{item.country}
</option>
))}
</>
:
data?.data?.length == 1 ?
data?.data?.map((item) => (
<option value={item.code} key={item.uid}>
{item.country}
</option>
))
:
data?.data?.length < 1 && data.loading ?
<option value=''>
Loading...
</option>
:
<option value=''>
No Country Found!
</option>
}
</select>
</div>
</div>
+444
View File
@@ -0,0 +1,444 @@
import React, { useCallback, useEffect, useState } from "react";
import { Link, useLocation, useNavigate } from "react-router-dom";
import WrenchBoard from "../../../assets/images/wrenchboard-logo-text.png";
import usersService from "../../../services/UsersService";
import InputCom from "../../Helpers/Inputs/InputCom";
import AuthLayout from "../AuthLayout2";
export default function SignUp() {
// eslint-disable-next-line no-restricted-globals
const queryParams = new URLSearchParams(location?.search);
const country = queryParams.get("cnt")?.toUpperCase();
const { pathname } = useLocation();
const currentPath = country
? `${pathname}?cnt=${country.toLowerCase()}`
: pathname; // Determines the new pathname is country query params exist
const [signUpLoading, setSignUpLoading] = useState(false);
const [checked, setValue] = useState(false);
// for the catch error
const [msgError, setMsgError] = useState("");
const [showPassword, setShowPassword] = useState(false);
const [countries, setCountries] = useState({ loading: true, data: [] });
const [formData, setFormData] = useState({
country: country ? country : "",
first_name: "",
last_name: "",
email: "",
password: "",
});
const handleInputChange = (event) => {
const { name, value } = event?.target;
setFormData({ ...formData, [name]: value });
};
// To Show and Hide Password
const togglePasswordVisibility = () => {
setShowPassword(!showPassword);
};
const rememberMe = () => {
setValue(!checked);
};
const navigate = useNavigate();
const userApi = new usersService();
// Get Country Api
const getCountryList = useCallback(async () => {
try {
const res = await userApi.getSignupCountryData();
if (res.status === 200 && res.data.internal_return >= 0) {
const { result_list } = await res.data;
if (country) {
// IF LINK/PATHNAME HAS CNT QUERY VALUE
let cnt = result_list.filter((item) => item.code == country); // test to see country passed in query param exist from list of countries supplied by API
if (!cnt.length) {
// IF CNT EMPTY, SET FORMDATA COUNTRY BACK TO EMPTY STRING: RE: THIS IS BCOS WE INITAIL SET COUNTRY VALUE IN FORMDATA, IF COUNTRY PARAM IS PRESENT IN LINK
setFormData((prev) => ({ ...prev, country: "" }));
return setCountries({ loading: false, data: result_list });
}
return setCountries({ loading: false, data: cnt });
}
setCountries({ loading: false, data: result_list });
} else if (res.data.result !== 100) {
setCountries({ loading: false, data: [] });
}
} catch (error) {
throw new Error(error);
}
}, []);
const handleSignUp = async () => {
let { country, first_name, last_name, email, password } = formData;
try {
if (
email !== "" &&
password !== "" &&
first_name !== "" &&
last_name !== "" &&
country !== ""
) {
//checks if email is a valid email address
let regEx = /^[^0-9][a-zA-Z0-9._%+-]+@[a-zA-Z]+(\.[a-zA-Z]+)+$/;
if (regEx.test(email) == false) {
setMsgError("Invalid Email");
return setTimeout(() => {
setMsgError("");
}, 3000);
}
//checks if terms and condition is checked
if (!checked) {
setMsgError("Terms and condition required");
return setTimeout(() => {
setMsgError("");
}, 3000);
}
setSignUpLoading(true);
const reqData = {
country: country,
firstname: first_name,
lastname: last_name,
email: email,
username: email,
password: password,
terms: 1,
news: 1,
};
const res = await userApi.CreateUser(reqData);
if (res.status === 200) {
const { data } = res;
if (data && data.acc === "DULPICATE") {
setMsgError("Duplicate username. Please try another email.");
setSignUpLoading(false);
}
if (data && data.status === "1") {
setTimeout(() => {
navigate("/outmessage", { replace: true });
setSignUpLoading(false);
}, 2000);
}
if (data && data.status === "5") {
setMsgError("Something went wrong. Please try another email.");
setSignUpLoading(false);
}
} else {
setSignUpLoading(false);
setMsgError("An error occurred");
}
} else {
setMsgError("Please fill in fields");
}
} catch (error) {
throw new Error(error);
} finally {
setTimeout(() => {
setMsgError(null);
}, process.env.REACT_APP_SIGNUP_ERROR_TIMEOUT);
}
};
useEffect(() => {
getCountryList();
}, []);
return (
<>
<AuthLayout slogan="Welcome to WrenchBoard">
<div className="w-full">
<div className="mb-5">
<Link to={currentPath}>
<img
src={WrenchBoard}
alt="wrenchboard"
className="h-10 mx-auto"
/>
</Link>
</div>
<div className="flex place-content-center">
<div className="w-10/12">
<div className="title-area flex flex-col justify-center items-center relative text-center mb-7">
<h1 className="text-[#181c32] font-semibold dark:text-white mb-3 leading-[27.3px] text-[22.75px]">
Create Account
</h1>
<span className="text-gray-400 font-medium text-[16.25px] leading-[24.375px]">
Already have an account?{" "}
<Link
to="/login"
className="font-semibold text-[#4687ba] hover:text-[#009ef7] transition"
>
Sign in here
</Link>
</span>
</div>
<div className="w-full flex items-center gap-2">
<div className="border-b border-[#eff2f5] max-w-[50%] w-full"></div>
<span className="text-[#b5b5c3] font-medium text-[0.7rem] w-[2%]">
OR
</span>
<div className="border-b border-[#eff2f5] max-w-[50%] w-full"></div>
</div>
<div className="input-area">
<SelectOption
label="Country"
data={countries}
name="country"
value={formData.country}
inputHandler={handleInputChange}
disable={
country && countries?.data?.length <= 1 ? true : false
}
/>
<div className="input-fl-name mb-4 sm:flex w-full sm:space-x-6 ">
<div className="input-item sm:w-1/2 w-full mb-4 sm:mb-0">
<InputCom
fieldClass="px-6"
placeholder="Firstname"
label="First Name"
name="first_name"
type="text"
value={formData.first_name}
inputHandler={handleInputChange}
/>
</div>
<div className="input-item flex-1">
<InputCom
fieldClass="px-6"
placeholder="Lastname"
label="Last Name"
name="last_name"
type="text"
value={formData.last_name}
inputHandler={handleInputChange}
/>
</div>
</div>
<div className="input-item mb-4">
<InputCom
fieldClass="px-6"
placeholder="support@mermsemr.com"
label="Email"
name="email"
type="email"
value={formData.email}
inputHandler={handleInputChange}
/>
</div>
<div className="input-item mb-4">
<InputCom
fieldClass="sm:px-6 px-2 tracking-[0.25em] text-2xl"
placeholder="● ● ● ● ● ●"
label="Password"
name="password"
type={showPassword ? "text" : "password"}
onClick={togglePasswordVisibility}
passIcon={showPassword ? "show-password" : "hide-password"}
value={formData.password}
inputHandler={handleInputChange}
/>
</div>
{msgError && (
<div className="p-4 text-[#912741] bg-[#fcd9e2] border-[#fbc6d3] mb-4 rounded-[0.475rem] text-md font-light leading-[19.5px]">
{msgError}
</div>
)}
<div className="forgot-password-area flex justify-between items-center mb-6">
<div className="remember-checkbox flex items-center space-x-2.5 group cursor-pointer">
<button
onClick={rememberMe}
type="button"
className={`w-6 h-6 border-[#4687ba] text-white flex justify-center items-center border rounded-[.45em] group-checked:text-white transition-all duration-200 group-checked:cursor-default ${
checked && "text-white bg-[#4687ba]"
}`}
>
{checked && (
<svg
xmlns="http://www.w3.org/2000/svg"
className="h-5 w-5"
viewBox="0 0 20 20"
fill="currentColor"
>
<path
fillRule="evenodd"
d="M16.707 5.293a1 1 0 010 1.414l-8 8a1 1 0 01-1.414 0l-4-4a1 1 0 011.414-1.414L8 12.586l7.293-7.293a1 1 0 011.414 0z"
clipRule="evenodd"
/>
</svg>
)}
</button>
<span
onClick={rememberMe}
className="cursor-default text-dark-gray dark:text-white text-[15px] group-checked:text-white transition-all duration-200 group-checked:cursor-default"
>
I agree with all
<Link
href="#"
className="text-base text-[#4687ba] hover:text-[#009ef7] mx-1 inline-block"
>
terms and condition
</Link>
</span>
</div>
</div>
{/* Forgot Password */}
{/* <div className="forgot-password-area flex justify-between items-center mb-6">
<div className="remember-checkbox flex items-center space-x-2.5">
<button
onClick={rememberMe}
type="button"
className="w-6 h-6 bg-[#4687ba] text-white flex justify-center items-center border border-light-gray rounded-[.45em]"
>
{checked && (
<svg
xmlns="http://www.w3.org/2000/svg"
className="h-5 w-5"
viewBox="0 0 20 20"
fill="currentColor"
>
<path
fillRule="evenodd"
d="M16.707 5.293a1 1 0 010 1.414l-8 8a1 1 0 01-1.414 0l-4-4a1 1 0 011.414-1.414L8 12.586l7.293-7.293a1 1 0 011.414 0z"
clipRule="evenodd"
/>
</svg>
)}
</button>
<span
onClick={rememberMe}
className="cursor-default text-dark-gray dark:text-white text-[15px]"
>
I agree with all
<Link
href="#"
className="text-base text-[#4687ba] hover:text-[#009ef7] mx-1 inline-block"
>
terms and condition
</Link>
</span>
</div>
</div> */}
<div className="signin-area mb-1">
<div className="flex justify-center">
<button
disabled={countries.loading}
type="button"
onClick={handleSignUp}
className={`rounded-full text-white flex justify-center bg-[#4687ba] hover:bg-[#009ef7] transition-all duration-300 items-center h-[42px] py-[0.8875rem] px-[1.81rem] text-[14.95px] btn-login`}
>
{signUpLoading ? (
<div className="signup btn-loader"></div>
) : (
<span>Sign Up</span>
)}
</button>
</div>
</div>
{/* APP DOWNLOAD STORE */}
<div className="w-full mt-4">
<div className="w-full flex justify-center items-center gap-4">
<div className="w-28 lg:w-32">
<a
className="px-1 py-1 lg:py-2 flex justify-center items-center gap-1 w-full rounded-md bg-black text-white hover:text-slate-500 hover:shadow-lg transition-all duration-300"
target="_blank"
href={process.env.REACT_APP_APPLE_APP}
rel="noreferrer"
>
<i className="fa-brands fa-apple text-3xl"></i>
<div className="flex flex-col">
<span className="text-[11px]">Available on the</span>
<span className="text-[12px] lg:text-base">
App Store
</span>
</div>
</a>
</div>
<div className="w-28 lg:w-32">
<a
className="px-1 py-1 lg:py-2 flex justify-center items-center gap-1 w-full rounded-md bg-black text-white hover:text-slate-500 hover:shadow-lg transition-all duration-300"
target="_blank"
href={process.env.REACT_APP_ANDROID_APP}
rel="noreferrer"
>
<i className="fa-brands fa-google-play text-2xl"></i>
<div className="flex flex-col">
<span className="text-[11px]">Available on the</span>
<span className="text-[12px] lg:text-base">
Google Play
</span>
</div>
</a>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</AuthLayout>
</>
);
}
const SelectOption = ({
label,
name,
inputHandler,
value,
data, // passing the data from parent
disable,
}) => {
return (
<div className="input-com mb-7">
<div className="flex items-center justify-between">
<label
className="input-label text-[#181c32] dark:text-white text-[15px] font-semibold block mb-2.5"
htmlFor={name}
>
{label}
</label>
</div>
<div>
<select
disabled={disable}
name={name}
id={name}
className="input-wrapper border border-[#f5f8fa] dark:border-[#5e6278] w-full rounded-full h-[42px] overflow-hidden relative font-medium leading-6 bg-clip-padding text-[#5e6278] dark:text-gray-100 bg-[#f5f8fa] dark:bg-[#5e6278] text-base focus-visible:border-transparent focus-visible:outline-0 focus-visible:ring-transparent "
onChange={inputHandler}
value={value}
>
{data?.data?.length > 1 ? (
<>
<option value={""}>Select your Country</option>
{data?.data?.map((item) => (
<option value={item.code} key={item.uid}>
{item.country}
</option>
))}
</>
) : data?.data?.length == 1 ? (
data?.data?.map((item) => (
<option value={item.code} key={item.uid}>
{item.country}
</option>
))
) : data?.data?.length < 1 && data.loading ? (
<option value="">Loading...</option>
) : (
<option value="">No Country Found!</option>
)}
</select>
</div>
</div>
);
};
@@ -0,0 +1,81 @@
import React, { useState } from "react";
import titleShape from "../../../assets/images/shape/title-shape-two.svg";
import InputCom from "../../Helpers/Inputs/InputCom";
import AuthLayout from "../AuthLayout2";
import ThankYou from "../ThankYou";
export default function UpdatePassword() {
const [updated, setValue] = useState(false);
const [message, setMessage] = useState(false);
const updatePassword = () => {
setValue(!updated);
setTimeout(() => {
setMessage(!message);
}, 100);
};
return (
<>
<AuthLayout slogan="Welcome to myFit">
{updated === false ? (
<div className="flex place-content-center">
<div className="w-10/12">
<div className="title-area relative flex flex-col justify-center items-center mb-7">
<h1 className="sm:text-5xl text-4xl font-bold leading-[74px] text-dark-gray dark:text-white">
Update Password
</h1>
{/* w-[341px] absolute top-14 left-12 */}
<div className="shape sm:w-[341px] w-[270px] -mt-5 sm:-mt-1 ml-5">
<img src={titleShape} alt="shape" />
</div>
</div>
<div className="input-area">
<div className="input-item mb-5">
<InputCom
fieldClass="px-6"
placeholder="*********"
label="Old Password"
name="password"
type="password"
iconName="password"
/>
</div>
<div className="input-item mb-5">
<InputCom
fieldClass="px-6"
placeholder="*********"
label="New Password"
name="password"
type="password"
iconName="password"
/>
</div>
<div className="input-item mb-5">
<InputCom
fieldClass="px-6"
placeholder="*********"
label="Re-enter Password"
name="password"
type="password"
iconName="password"
/>
</div>
<div className="signin-area mb-3.5">
<button
onClick={updatePassword}
type="button"
className="w-full rounded-[50px] mb-5 h-[58px] text-xl text-white font-bold flex justify-center bg-purple items-center"
>
Continue
</button>
</div>
</div>
</div>
</div>
) : (
<ThankYou className={`thankyou-section ${message ? "active" : ""}`} />
)}
</AuthLayout>
</>
);
}
+60 -57
View File
@@ -1,6 +1,6 @@
import { useCallback, useEffect, useState } from "react";
import { Link, useLocation, useNavigate } from "react-router-dom";
import WrenchBoard from "../../../assets/images/wrenchboard.png";
import WrenchBoard from "../../../assets/images/wrenchboard-logo-text.png";
import debounce from "../../../hooks/debounce";
import usersService from "../../../services/UsersService";
import InputCom from "../../Helpers/Inputs/InputCom";
@@ -36,10 +36,7 @@ export default function VerifyLink() {
var postData = {
username: email,
password: password,
login_mode: 100,
sessionid: "STARTER-NOTREAL",
verify_link: token,
action: 11012,
};
const res = await userApi?.CompleteSignUp(postData);
@@ -54,6 +51,7 @@ export default function VerifyLink() {
localStorage.setItem("member_id", `${data?.member_id}`);
localStorage.setItem("session_token", `${data?.session}`);
localStorage.setItem("session", `${data?.session}`);
localStorage.setItem("uid", data?.uid);
navigate("/", { replace: true });
setLinkLoader(false);
@@ -81,19 +79,19 @@ export default function VerifyLink() {
};
// for verifying the incoming verification link and render the correct component
const verifyEmail = useCallback(async (code) => {
const verifyEmail = async (code) => {
try {
const verifyRes = await userApi.verifyEmail(code);
if (verifyRes.status === 200) {
let { data } = verifyRes;
console.log('TESTING VERIFY',data)
console.log("TESTING VERIFY", data);
if (
data &&
data.internal_return >= 0 &&
data.status == 0 &&
data.pending_id != '' &&
data.pending_uid != '' &&
data.username != '' &&
data.internal_return >= 0 &&
data.status == 0 &&
data.pending_id != "" &&
data.pending_uid != "" &&
data.username != "" &&
data.status_text === "Link Verified"
) {
setPageLoader(false);
@@ -107,12 +105,13 @@ export default function VerifyLink() {
setLinkSuccess(false);
throw new Error(error);
}
}, []);
};
// delay verify requests by 10000ms
const debouncedEmail = debounce(verifyEmail, 1000);
useEffect(() => {
setEmail("")
debouncedEmail(token);
}, []);
@@ -172,53 +171,57 @@ const SuccessfulComponent = ({
handleEmail,
msgErr,
loader,
}) => (
<div className="input-area">
{/* INPUT */}
<div className="mb-5">
<InputCom
fieldClass="px-6"
value={email}
inputHandler={handleEmail}
placeholder="support@mermsemr.com"
label="Email"
name="email"
type="email"
iconName="message"
/>
</div>
<div className="mb-5">
<InputCom
fieldClass="px-6"
value={password}
inputHandler={handlePassword}
placeholder="● ● ● ● ● ●"
label="Password"
name="password"
type="password"
iconName="password"
/>
</div>
{msgErr && (
<div className="relative p-4 text-[#912741] bg-[#fcd9e2] border-[#fbc6d3] mb-4 rounded-[0.475rem] text-md font-light leading-[19.5px] text-[13px]">
{msgErr}
}) => {
return (
<div className="input-area">
{/* INPUT */}
<div className="mb-5">
<InputCom
fieldClass="px-6"
value={email}
inputHandler={handleEmail}
placeholder="support@mermsemr.com"
label="Email"
name="email"
type="email"
iconName="message"
autoComplete="off"
/>
</div>
<div className="mb-5">
<InputCom
fieldClass="px-6"
value={password}
inputHandler={handlePassword}
placeholder="● ● ● ● ● ●"
label="Password"
name="password"
type="password"
iconName="password"
autoComplete="off"
/>
</div>
{msgErr && (
<div className="relative p-4 text-[#912741] bg-[#fcd9e2] border-[#fbc6d3] mb-4 rounded-[0.475rem] text-md font-light leading-[19.5px] text-[13px]">
{msgErr}
</div>
)}
<div className="signin-area mb-3.5">
<button
onClick={onSubmit}
type="button"
className={`btn-login rounded-[0.475rem] mb-6 text-xl text-white flex justify-center bg-[#4687ba] hover:bg-[#009ef7] transition-all duration-300 items-center text-[15px]`}
>
{loader ? (
<div className="signup btn-loader"></div>
) : (
<span>Continue</span>
)}
</button>
</div>
)}
<div className="signin-area mb-3.5">
<button
onClick={onSubmit}
type="button"
className={`btn-login rounded-[0.475rem] mb-6 text-xl text-white flex justify-center bg-[#4687ba] hover:bg-[#009ef7] transition-all duration-300 items-center text-[15px]`}
>
{loader ? (
<div className="signup btn-loader"></div>
) : (
<span>Continue</span>
)}
</button>
</div>
</div>
);
);
};
const ErrorComponent = ({ onClick }) => (
<div className="input-area">
@@ -0,0 +1,244 @@
import { useCallback, useEffect, useState } from "react";
import { Link, useLocation, useNavigate } from "react-router-dom";
import WrenchBoard from "../../../assets/images/wrenchboard-logo-text.png";
import debounce from "../../../hooks/debounce";
import usersService from "../../../services/UsersService";
import InputCom from "../../Helpers/Inputs/InputCom";
import AuthLayout from "../AuthLayout2";
export default function VerifyLink() {
const [email, setEmail] = useState("");
const [password, setPassword] = useState("");
const [msgError, setMsgError] = useState("");
const [linkLoader, setLinkLoader] = useState(false);
const [pageLoader, setPageLoader] = useState(true);
const [linkSuccess, setLinkSuccess] = useState(true);
const navigate = useNavigate();
const location = useLocation();
const queryParams = new URLSearchParams(location?.search);
const token = queryParams.get("vlnk");
const userApi = new usersService();
// email
const handleEmail = (e) => {
setEmail(e.target.value);
};
// password
const handlePassword = (e) => {
setPassword(e.target.value);
};
// if verification is okay. set a complete signup form
const completeSignup = async () => {
try {
if (email !== "" && password !== "") {
setLinkLoader(true);
var postData = {
username: email,
password: password,
verify_link: token,
};
const res = await userApi?.CompleteSignUp(postData);
if (res.status === 200) {
const { data } = res;
if (
data?.status > 0 &&
data?.internal_return == 100 &&
data?.session != ""
) {
localStorage.setItem("email", `${data?.email}`);
localStorage.setItem("member_id", `${data?.member_id}`);
localStorage.setItem("session_token", `${data?.session}`);
localStorage.setItem("session", `${data?.session}`);
localStorage.setItem("uid", data?.uid);
navigate("/", { replace: true });
setLinkLoader(false);
} else {
setLinkLoader(false);
setMsgError("Invalid Link or Password Combination");
}
} else {
setLinkLoader(false);
setLinkSuccess(false);
setMsgError("An error occurred");
}
} else {
setMsgError("Please fill in fields");
}
} catch (error) {
setLinkLoader(false);
setLinkSuccess(false);
throw new Error(error);
} finally {
setTimeout(() => {
setMsgError(null);
}, process.env.REACT_APP_SIGNUP_ERROR_TIMEOUT);
}
};
// for verifying the incoming verification link and render the correct component
const verifyEmail = useCallback(async (code) => {
try {
const verifyRes = await userApi.verifyEmail(code);
if (verifyRes.status === 200) {
let { data } = verifyRes;
console.log("TESTING VERIFY", data);
if (
data &&
data.internal_return >= 0 &&
data.status == 0 &&
data.pending_id != "" &&
data.pending_uid != "" &&
data.username != "" &&
data.status_text === "Link Verified"
) {
setEmail(data.username);
setPageLoader(false);
} else {
setPageLoader(false);
setLinkSuccess(false);
}
}
} catch (error) {
setPageLoader(false);
setLinkSuccess(false);
throw new Error(error);
}
}, []);
// delay verify requests by 10000ms
const debouncedEmail = debounce(verifyEmail, 1000);
useEffect(() => {
debouncedEmail(token);
}, []);
return (
<>
<AuthLayout slogan="Welcome to WrenchBoard">
{pageLoader ? (
<img src={WrenchBoard} alt="wrenchboard" className="h-10 mx-auto" />
) : (
<div className="w-full">
<div className="mb-12">
<Link to="#">
<img
src={WrenchBoard}
alt="wrenchboard"
className="h-10 mx-auto"
/>
</Link>
</div>
<div className="flex place-content-center">
<div className="w-10/12">
<div className="title-area flex flex-col justify-center items-center relative text-center mb-7">
<h1 className="text-[#181c32] font-semibold dark:text-white mb-3 leading-[27.3px] text-[22.75px]">
{linkSuccess
? "Sign In to WrenchBoard"
: "Invalid verification link"}
</h1>
</div>
{/* If the verification was a success */}
{linkSuccess ? (
<SuccessfulComponent
email={email}
password={password}
handleEmail={handleEmail}
handlePassword={handlePassword}
onSubmit={completeSignup}
msgErr={msgError}
loader={linkLoader}
/>
) : (
<ErrorComponent onClick={() => navigate("/login")} />
)}
</div>
</div>
</div>
)}
</AuthLayout>
</>
);
}
const SuccessfulComponent = ({
onSubmit,
password,
handlePassword,
email,
handleEmail,
msgErr,
loader,
}) => (
<div className="input-area">
{/* INPUT */}
<div className="mb-5">
<InputCom
fieldClass="px-6"
value={email}
inputHandler={handleEmail}
placeholder="support@mermsemr.com"
label="Email"
name="email"
type="email"
iconName="message"
autoComplete="off"
/>
</div>
<div className="mb-5">
<InputCom
fieldClass="px-6"
value={password}
inputHandler={handlePassword}
placeholder="● ● ● ● ● ●"
label="Password"
name="password"
type="password"
iconName="password"
autoComplete="off"
/>
</div>
{msgErr && (
<div className="relative p-4 text-[#912741] bg-[#fcd9e2] border-[#fbc6d3] mb-4 rounded-[0.475rem] text-md font-light leading-[19.5px] text-[13px]">
{msgErr}
</div>
)}
<div className="signin-area mb-3.5">
<button
onClick={onSubmit}
type="button"
className={`btn-login rounded-[0.475rem] mb-6 text-xl text-white flex justify-center bg-[#4687ba] hover:bg-[#009ef7] transition-all duration-300 items-center text-[15px]`}
>
{loader ? (
<div className="signup btn-loader"></div>
) : (
<span>Continue</span>
)}
</button>
</div>
</div>
);
const ErrorComponent = ({ onClick }) => (
<div className="input-area">
<div className="my-5">
<p className="text-[14px] leading-[19px] text-center text-[#181c32]">
This error occurs because you have already verified this link or the
link has expired. Try login or reset password. If none worked, try to
create the account from the start.
</p>
</div>
<div className="signin-area flex justify-center mb-3.5">
<button
onClick={onClick}
type="button"
className={`rounded-[0.475rem] mb-6 text-[15px] font-semibold text-[#009ef7] hover:text-white flex justify-center bg-[#f1faff] hover:bg-[#009ef7] transition-all duration-300 items-center py-[0.8875rem] px-[1.81rem]`}
>
<span>Return Home</span>
</button>
</div>
</div>
);
+150 -95
View File
@@ -1,16 +1,24 @@
import { useState } from "react";
import { useEffect, useState } from "react";
import { Link, useLocation, useNavigate } from "react-router-dom";
import WrenchBoard from "../../../assets/images/wrenchboard.png";
import WrenchBoard from "../../../assets/images/wrenchboard-logo-text.png";
import usersService from "../../../services/UsersService";
import InputCom from "../../Helpers/Inputs/InputCom";
import AuthLayout from "../AuthLayout";
import ForgetPwdResponse from "../ForgetPwdResponse";
import PasswordValidator from "../../../lib/PasswordValidator";
import LoadingSpinner from "../../Spinners/LoadingSpinner";
const VerifyPassword = () => {
const [password, setPassword] = useState("");
const [confirmPassword, setConfirmPassword] = useState("");
const [requestStatus, setRequestStatus] = useState({
loading: true,
status: false,
data: [],
});
const [msgError, setMsgError] = useState("");
const [linkLoader, setLinkLoader] = useState(false);
const [linkSuccess, setLinkSuccess] = useState(true);
const [linkSuccess, setLinkSuccess] = useState(null);
const [showPassword, setShowPassword] = useState(false);
const navigate = useNavigate();
const location = useLocation();
@@ -23,11 +31,6 @@ const VerifyPassword = () => {
setShowPassword(!showPassword);
};
// little checker for the validity of the token
if (token?.length != 64) {
setLinkSuccess(false);
}
// Password
const handlePassword = (e) => {
let { name, value } = e?.target;
@@ -36,45 +39,63 @@ const VerifyPassword = () => {
};
const completeReset = async () => {
if (!password || !confirmPassword) {
// CHECKS IF PASSWORD IS EMPTY
setMsgError("Please fill in fields");
return setTimeout(() => {
setMsgError(null);
}, process.env.REACT_APP_RESET_START_ERROR_TIMEOUT);
}
if (password != confirmPassword) {
// CHECKS IF PASSWORD EQUALS CONFIRM PASSWORD
setMsgError("Passwords does not match");
return setTimeout(() => {
setMsgError(null);
}, process.env.REACT_APP_RESET_START_ERROR_TIMEOUT);
}
if (password.length < 6) {
// CHECKS IF PASSWORD LENGTH IS UPTO 6 CHARACTERS
setMsgError("Password must be upto six characters");
return setTimeout(() => {
setMsgError(null);
}, process.env.REACT_APP_RESET_START_ERROR_TIMEOUT);
}
if (!PasswordValidator(password)) {
// CHECKS IF PASSWORD IS VALID
setMsgError(
"Password must contain alphanumeric, uppercase and special character: eg: Password1@"
);
return setTimeout(() => {
setMsgError(null);
}, process.env.REACT_APP_RESET_START_ERROR_TIMEOUT);
}
try {
if (password !== "" && confirmPassword !== "") {
if (password === confirmPassword) {
setLinkLoader(true);
var reqData = {
sessionid: "DUMMY-CANNOT_BE_EMPTY",
reset_link: token,
newpass: password,
step: 300,
action: 730,
};
setLinkLoader(true);
var reqData = {
reset_link: token,
newpass: password,
m_uid: requestStatus.data?.m_uid || "",
reset_uid: requestStatus.data?.reset_uid || "",
step: 300,
};
const res = await userApi?.CompleteResetPassword(reqData);
const res = await userApi?.CompleteResetPassword(reqData);
if (res.status === 200) {
const { data } = res;
if (data?.status > 0 && data?.email) {
setTimeout(() => {
navigate("/login", { replace: true });
setLinkLoader(false);
}, 2000);
} else if (data && data?.status == "Invalid Request") {
setLinkLoader(false);
setLinkSuccess(false);
} else {
setLinkLoader(false);
setMsgError("An error occurred");
}
} else {
setLinkLoader(false);
setLinkSuccess(false);
}
if (res.status == 200) {
const { data } = res;
if (data?.internal_return >= 0) {
// setTimeout(() => {
// navigate("/login", { replace: true });
// setLinkLoader(false);
// }, 2000);
setLinkSuccess(true);
} else {
setLinkLoader(false);
setMsgError("Passwords does not match");
setMsgError("An error occurred");
setLinkSuccess(false);
}
} else {
setMsgError("Please fill in fields");
setLinkLoader(false);
setLinkSuccess(false);
}
} catch (error) {
setLinkLoader(false);
@@ -87,6 +108,33 @@ const VerifyPassword = () => {
}
};
const verifyResetPwd = () => {
// FUNCTION TO VERIFY RESET PASSWORD LINK
setRequestStatus({ loading: true, status: false, data: [] });
var reqData = {
reset_link: token,
step: 200,
};
userApi
.CompleteResetPassword(reqData)
.then((res) => {
if (res.status != 200 || res.data.internal_return < 0) {
return setRequestStatus({ loading: false, status: false, data: [] });
}
setRequestStatus({ loading: false, status: true, data: res.data });
})
.catch((error) => {
setRequestStatus({ loading: false, status: false, data: [] });
});
};
useEffect(() => {
// little checker for the validity of the token
if (token == null || token?.length != 64) {
return setRequestStatus({ loading: false, status: false, data: [] });
}
verifyResetPwd();
}, []);
return (
<>
<AuthLayout slogan="Welcome to WrenchBoard">
@@ -101,39 +149,68 @@ const VerifyPassword = () => {
</Link>
</div>
<div className="content-wrapper login shadow-md w-full lg:max-w-[500px] mx-auto flex justify-center items-center dark:bg-dark-white 2xl:w-[828px] rounded-[0.475rem] sm:p-7 p-5">
<div className="w-full">
<div className="title-area flex flex-col justify-center items-center relative text-center mb-7">
<h1 className="text-[#181c32] font-semibold dark:text-white mb-3 leading-[27.3px] text-[22.75px]">
{linkSuccess ? "Password Reset" : "Invalid verification link"}
</h1>
{linkSuccess && (
<span className="text-gray-400 font-medium text-[16.25px] leading-[24.375px]">
Enter a new password to reset
</span>
)}
{linkSuccess && (
<span className="text-gray-400 font-medium text-[16.25px] leading-[24.375px]">
We'll send an email to confirm reset
</span>
{requestStatus.loading ? (
<LoadingSpinner color="sky-blue" size="16" height="h-300px" />
) : !requestStatus.loading && requestStatus.status ? (
<div className="w-full">
{linkSuccess == null ? (
<>
<div className="title-area flex flex-col justify-center items-center relative text-center mb-7">
<h1 className="text-[#181c32] font-semibold dark:text-white mb-3 leading-[27.3px] text-[22.75px]">
Password Reset
</h1>
<span className="text-gray-400 font-medium text-[16.25px] leading-[24.375px]">
Enter a new password to reset
</span>
<span className="text-gray-400 font-medium text-[16.25px] leading-[24.375px]">
We'll send an email to confirm reset
</span>
</div>
<div>
<p className="text-red-500 font-semibold mb-3 leading-[27.3px] text-[13px]">
Must include a special, numeric, uppercase and lowercase
character
</p>
</div>
<SuccessfulComponent
password={password}
confirmPassword={confirmPassword}
handlePassword={handlePassword}
onSubmit={completeReset}
msgErr={msgError}
loader={linkLoader}
showPassword={showPassword}
onClick={togglePasswordVisibility}
navigateHandler={() => navigate("/login")}
/>
</>
) : (
<ForgetPwdResponse
title={
linkSuccess
? "Password Reset Complete"
: "Password Reset Error"
}
message={
linkSuccess
? "Password Reset Complete. You can login now with your new credentials"
: "Password Reset Error. Please get in touch with support for further support"
}
type={linkSuccess}
/>
)}
</div>
{/* If the verification was a success */}
{linkSuccess ? (
<SuccessfulComponent
password={password}
confirmPassword={confirmPassword}
handlePassword={handlePassword}
onSubmit={completeReset}
msgErr={msgError}
loader={linkLoader}
showPassword={showPassword}
onClick={togglePasswordVisibility}
navigateHandler={() => navigate("/login")}
) : (
<div className="title-area flex flex-col justify-center items-center relative text-center mb-7">
<ForgetPwdResponse
title={"Forget Password"}
message={
"We are unable to continue to reset process. This error is usually due to expired links. Please start all over or contact us"
}
type={requestStatus.status}
/>
) : (
<ErrorComponent onClick={() => navigate("/login")} />
)}
</div>
</div>
)}
</div>
</div>
</AuthLayout>
@@ -159,7 +236,7 @@ const SuccessfulComponent = ({
<div className="mb-5">
<InputCom
fieldClass="sm:px-6 px-2 tracking-[0.25em] text-2xl"
value={password?.replace(/./g, "●")}
value={password}
inputHandler={handlePassword}
placeholder="● ● ● ● ● ●"
label="Password"
@@ -172,7 +249,7 @@ const SuccessfulComponent = ({
<div className="mb-5">
<InputCom
fieldClass="sm:px-6 px-2 tracking-[0.25em] text-2xl"
value={confirmPassword?.replace(/./g, "●")}
value={confirmPassword}
inputHandler={handlePassword}
placeholder="● ● ● ● ● ●"
label="Confirm Password"
@@ -209,25 +286,3 @@ const SuccessfulComponent = ({
</div>
</div>
);
const ErrorComponent = ({ onClick }) => (
<div className="input-area">
<div className="my-5">
<p className="text-[14px] leading-[19px] text-center text-[#181c32]">
This error occurs because you have already used this link or the link
has broken/expired. Start with the reset process again. If it doesn't
work, try to create the account from the start.
</p>
</div>
<div className="signin-area flex justify-center mb-3.5">
<button
onClick={onClick}
type="button"
className={`rounded-[0.475rem] mb-6 text-[15px] font-semibold text-[#009ef7] hover:text-white flex justify-center bg-[#f1faff] hover:bg-[#009ef7] transition-all duration-300 items-center py-[0.8875rem] px-[1.81rem]`}
>
<span>Return Home</span>
</button>
</div>
</div>
);
@@ -0,0 +1,288 @@
import { useEffect, useState } from "react";
import { Link, useLocation, useNavigate } from "react-router-dom";
import WrenchBoard from "../../../assets/images/wrenchboard-logo-text.png";
import usersService from "../../../services/UsersService";
import InputCom from "../../Helpers/Inputs/InputCom";
import AuthLayout from "../AuthLayout2";
import ForgetPwdResponse from "../ForgetPwdResponse";
import PasswordValidator from "../../../lib/PasswordValidator";
import LoadingSpinner from "../../Spinners/LoadingSpinner";
const VerifyPassword = () => {
const [password, setPassword] = useState("");
const [confirmPassword, setConfirmPassword] = useState("");
const [requestStatus, setRequestStatus] = useState({
loading: true,
status: false,
data: [],
});
const [msgError, setMsgError] = useState("");
const [linkLoader, setLinkLoader] = useState(false);
const [linkSuccess, setLinkSuccess] = useState(null);
const [showPassword, setShowPassword] = useState(false);
const navigate = useNavigate();
const location = useLocation();
const queryParams = new URLSearchParams(location?.search);
const token = queryParams.get("passlink");
const userApi = new usersService();
// To Show and Hide Password
const togglePasswordVisibility = () => {
setShowPassword(!showPassword);
};
// Password
const handlePassword = (e) => {
let { name, value } = e?.target;
if (name == "password") setPassword(value);
if (name == "confirm_password") setConfirmPassword(value);
};
const completeReset = async () => {
if (!password || !confirmPassword) {
// CHECKS IF PASSWORD IS EMPTY
setMsgError("Please fill in fields");
return setTimeout(() => {
setMsgError(null);
}, process.env.REACT_APP_RESET_START_ERROR_TIMEOUT);
}
if (password != confirmPassword) {
// CHECKS IF PASSWORD EQUALS CONFIRM PASSWORD
setMsgError("Passwords does not match");
return setTimeout(() => {
setMsgError(null);
}, process.env.REACT_APP_RESET_START_ERROR_TIMEOUT);
}
if (password.length < 6) {
// CHECKS IF PASSWORD LENGTH IS UPTO 6 CHARACTERS
setMsgError("Password must be upto six characters");
return setTimeout(() => {
setMsgError(null);
}, process.env.REACT_APP_RESET_START_ERROR_TIMEOUT);
}
if (!PasswordValidator(password)) {
// CHECKS IF PASSWORD IS VALID
setMsgError(
"Password must contain alphanumeric, uppercase and special character: eg: Password1@"
);
return setTimeout(() => {
setMsgError(null);
}, process.env.REACT_APP_RESET_START_ERROR_TIMEOUT);
}
try {
setLinkLoader(true);
var reqData = {
reset_link: token,
newpass: password,
m_uid: requestStatus.data?.m_uid || "",
reset_uid: requestStatus.data?.reset_uid || "",
step: 300,
};
const res = await userApi?.CompleteResetPassword(reqData);
if (res.status == 200) {
const { data } = res;
if (data?.internal_return >= 0) {
// setTimeout(() => {
// navigate("/login", { replace: true });
// setLinkLoader(false);
// }, 2000);
setLinkSuccess(true);
} else {
setLinkLoader(false);
setMsgError("An error occurred");
setLinkSuccess(false);
}
} else {
setLinkLoader(false);
setLinkSuccess(false);
}
} catch (error) {
setLinkLoader(false);
setLinkSuccess(false);
throw new Error(error);
} finally {
setTimeout(() => {
setMsgError(null);
}, process.env.REACT_APP_SIGNUP_ERROR_TIMEOUT);
}
};
const verifyResetPwd = () => {
// FUNCTION TO VERIFY RESET PASSWORD LINK
setRequestStatus({ loading: true, status: false, data: [] });
var reqData = {
reset_link: token,
step: 200,
};
userApi
.CompleteResetPassword(reqData)
.then((res) => {
if (res.status != 200 || res.data.internal_return < 0) {
return setRequestStatus({ loading: false, status: false, data: [] });
}
setRequestStatus({ loading: false, status: true, data: res.data });
})
.catch((error) => {
setRequestStatus({ loading: false, status: false, data: [] });
});
};
useEffect(() => {
// little checker for the validity of the token
if (token == null || token?.length != 64) {
return setRequestStatus({ loading: false, status: false, data: [] });
}
verifyResetPwd();
}, []);
return (
<>
<AuthLayout slogan="Welcome to WrenchBoard">
<div className="w-full">
<div className="mb-12">
<Link to="#">
<img
src={WrenchBoard}
alt="wrenchboard"
className="h-10 mx-auto"
/>
</Link>
</div>
<div className="flex place-content-center">
{requestStatus.loading ? (
<LoadingSpinner color="sky-blue" size="16" height="h-300px" />
) : !requestStatus.loading && requestStatus.status ? (
<div className="w-10/12">
{linkSuccess == null ? (
<>
<div className="title-area flex flex-col justify-center items-center relative text-center mb-7">
<h1 className="text-[#181c32] font-semibold dark:text-white mb-3 leading-[27.3px] text-[22.75px]">
Password Reset
</h1>
<span className="text-gray-400 font-medium text-[16.25px] leading-[24.375px]">
Enter a new password to reset
</span>
<span className="text-gray-400 font-medium text-[16.25px] leading-[24.375px]">
We'll send an email to confirm reset
</span>
</div>
<div>
<p className="text-red-500 font-semibold mb-3 leading-[27.3px] text-[13px]">
Must include a special, numeric, uppercase and lowercase
character
</p>
</div>
<SuccessfulComponent
password={password}
confirmPassword={confirmPassword}
handlePassword={handlePassword}
onSubmit={completeReset}
msgErr={msgError}
loader={linkLoader}
showPassword={showPassword}
onClick={togglePasswordVisibility}
navigateHandler={() => navigate("/login")}
/>
</>
) : (
<ForgetPwdResponse
title={
linkSuccess
? "Password Reset Complete"
: "Password Reset Error"
}
message={
linkSuccess
? "Password Reset Complete. You can login now with your new credentials"
: "Password Reset Error. Please get in touch with support for further support"
}
type={linkSuccess}
/>
)}
</div>
) : (
<div className="w-11/12 sm:max-w-[500px] title-area flex flex-col justify-center items-center relative text-center mb-7">
<ForgetPwdResponse
title={"Forget Password"}
message={
"We are unable to continue to reset process. This error is usually due to expired links. Please start all over or contact us"
}
type={requestStatus.status}
/>
</div>
)}
</div>
</div>
</AuthLayout>
</>
);
};
export default VerifyPassword;
const SuccessfulComponent = ({
onSubmit,
navigateHandler,
showPassword,
onClick,
password,
confirmPassword,
handlePassword,
msgErr,
loader,
}) => (
<div className="input-area">
{/* INPUT */}
<div className="mb-5">
<InputCom
fieldClass="sm:px-6 px-2 tracking-[0.25em] text-2xl"
value={password}
inputHandler={handlePassword}
placeholder="● ● ● ● ● ●"
label="Password"
name="password"
type={showPassword ? "text" : "password"}
onClick={onClick}
passIcon={showPassword ? "show-password" : "hide-password"}
/>
</div>
<div className="mb-5">
<InputCom
fieldClass="sm:px-6 px-2 tracking-[0.25em] text-2xl"
value={confirmPassword}
inputHandler={handlePassword}
placeholder="● ● ● ● ● ●"
label="Confirm Password"
name="confirm_password"
type="password"
/>
</div>
{msgErr && (
<div className="relative p-4 text-[#912741] bg-[#fcd9e2] border-[#fbc6d3] mb-4 rounded-[0.475rem] text-md font-light leading-[19.5px] text-[13px]">
{msgErr}
</div>
)}
<div className="signin-area mb-3.5">
<button
onClick={onSubmit}
type="button"
className={`btn-login rounded-[0.475rem] mb-6 text-xl text-white flex justify-center bg-[#4687ba] hover:bg-[#009ef7] transition-all duration-300 items-center text-[15px]`}
>
{loader ? (
<div className="signup btn-loader"></div>
) : (
<span>Continue</span>
)}
</button>
</div>
<div className="signin-area mb-3.5">
<button
onClick={navigateHandler}
type="button"
className={`rounded-[0.475rem] mb-6 text-[15px] font-semibold text-[#009ef7] hover:text-white flex justify-center bg-[#f1faff] hover:bg-[#009ef7] transition-all duration-300 items-center py-[0.8875rem] px-[1.81rem] btn-login`}
>
<span>Return Home</span>
</button>
</div>
</div>
);
+1 -1
View File
@@ -1,6 +1,6 @@
import { useNavigate, Link } from "react-router-dom";
import AuthLayout from "../AuthLayout";
import WrenchBoard from "../../../assets/images/wrenchboard.png";
import WrenchBoard from "../../../assets/images/wrenchboard-logo-text.png";
export default function VerifyYou() {
const navigate = useNavigate();
@@ -0,0 +1,64 @@
import { useNavigate, Link } from "react-router-dom";
import AuthLayout from "../AuthLayout2";
import WrenchBoard from "../../../assets/images/wrenchboard-logo-text.png";
export default function VerifyYou() {
const navigate = useNavigate();
return (
<>
<AuthLayout slogan="Welcome to WrenchBoard">
<div className="w-full">
<div className="mb-12">
<Link to="#">
<img
src={WrenchBoard}
alt="wrenchboard"
className="h-10 mx-auto"
/>
</Link>
</div>
<div className="flex place-content-center">
<div className="w-10/12">
<div className="title-area flex flex-col justify-center items-center relative text-center mb-7">
<h1 className="text-[#181c32] font-semibold dark:text-white mb-3 leading-[27.3px] text-[22.75px]">
Let's verify your email now
</h1>
<span className="text-gray-400 font-medium text-[16.25px] leading-[24.375px]">
Check your email.
</span>
</div>
<div className="input-area">
<div className="mb-5">
<p className="text-[14px] leading-[19px] text-center text-[#181c32]">
<b>Verify Email.</b> Help us secure your WrenchBoard account
by verifying your email registration address. Verification
will let you access all of WrenchBoard's features.
</p>
</div>
<div className="mb-5">
<p className="text-[14px] leading-[19px] text-center text-[#181c32]">
If you do not receive the confirmation message within a few
minutes of signing up, please check your Junk E-mail folder
just in case the confirmation email got delivered there
instead of your inbox. If so, select the confirmation
message and click Not Junk, which will allow future messages
to get through.
</p>
</div>
</div>
<div className="signin-area flex justify-center mb-3.5">
<button
onClick={() => navigate("/")}
type="button"
className={`rounded-[0.475rem] mb-6 text-[15px] font-semibold text-[#009ef7] hover:text-white flex justify-center bg-[#f1faff] hover:bg-[#009ef7] transition-all duration-300 items-center py-[0.8875rem] px-[1.81rem]`}
>
<span>Return Home</span>
</button>
</div>
</div>
</div>
</div>
</AuthLayout>
</>
);
}
+37 -15
View File
@@ -1,12 +1,18 @@
import React, { useEffect, useState } from "react";
import { Link, useNavigate } from "react-router-dom";
import Layout from "../Partials/Layout";
import CommonHead from "../UserHeader/CommonHead";
import { useNavigate } from "react-router-dom";
import usersService from "../../services/UsersService";
import Layout from "../Partials/Layout";
import LoadingSpinner from "../Spinners/LoadingSpinner";
import CommonHead from "../UserHeader/CommonHead";
import { useSelector } from "react-redux";
import CustomBreadcrumb from "../Breadcrumb/CustomBreadcrumb";
export default function BlogItem(props) {
const {
userDetails: { account_type },
} = useSelector((state) => state?.userDetails); // CHECKS IF USER Details account type
const apiCall = new usersService()
const navigate = useNavigate()
@@ -16,6 +22,7 @@ export default function BlogItem(props) {
const filterHandler = (value) => {
setValue(value);
};
// eslint-disable-next-line no-restricted-globals
const queryParams = new URLSearchParams(location?.search);
const blog_id = queryParams.get("blog_id");
@@ -32,11 +39,26 @@ export default function BlogItem(props) {
},[blog_id])
return (
<Layout>
<CommonHead
commonHeadData={props.commonHeadData}
/>
{account_type == 'FULL' &&
<CommonHead
commonHeadData={props.commonHeadData}
/>
}
{ account_type == 'FAMILY' &&
<div className="mb-5">
<CustomBreadcrumb
title = {blogdata?.data?.blogdata?.length > 0 ? blogdata?.data?.blogdata[0]?.post_title : 'Blog'}
breadcrumb={
[
{ link: "/", title: "Home" },
{ link: "/fam-blog", title: "Blogs", active: true},
]
}
/>
</div>
}
<div className="notification-page w-full mb-10">
<div className="mb-5">
{/* <div className="mb-5">
<h1 className="text-26 font-bold text-dark-gray dark:text-white">
<span
className={`${selectTab === "today" ? "block" : "hidden"}`}
@@ -44,16 +66,16 @@ export default function BlogItem(props) {
{blogdata.data?.blogdata?.[0]?.post_title}
</span>
</h1>
</div>
</div> */}
<div className="notification-wrapper w-full bg-white p-8 rounded-2xl">
{blogdata.loading ?
{blogdata?.loading ?
<LoadingSpinner size='8' color='sky-blue' height='h-[100px]' />
:
blogdata?.data?.blogdata && blogdata.data?.blogdata.length ?
<div className="w-full">
{/* heading */}
<div className="sm:flex justify-between items-center mb-6">
{/* <div className="mb-5">
{/* <div className="sm:flex justify-between items-center mb-6">
<div className="mb-5">
<h1 className="text-26 font-bold text-dark-gray dark:text-white">
<span
className={`${selectTab === "today" ? "block" : "hidden"}`}
@@ -61,10 +83,10 @@ export default function BlogItem(props) {
{blogdata.data?.blogdata?.[0]?.post_title}
</span>
</h1>
</div> */}
{/* <div className="slider-btns flex space-x-4">
</div> */}
</div>
</div>
<div className="slider-btns flex space-x-4">
</div>
</div> */}
<div dangerouslySetInnerHTML={{__html: blogdata.data?.blogdata?.[0]?.post_content}}>
</div>
</div>
@@ -0,0 +1,22 @@
import React from 'react'
import { Link } from 'react-router-dom'
export default function CustomBreadcrumb({title, breadcrumb}) {
return (
<div className="">
<h3 className="text-26 font-bold text-dark-gray dark:text-white">{title}</h3>
<nav aria-label="breadcrumb" className='text-base text-dark-gray dark:text-white flex items-center'>
{breadcrumb.map((value, index) => (
<p key={index}>
<Link className={`${value.active ? 'opacity-60' : ''}`} to={value.link}>{value.title}</Link>
{index != breadcrumb.length - 1 ?
<span className='px-1'>/</span>
:
null
}
</p>
))}
</nav>
</div>
)
}
+23 -57
View File
@@ -1,15 +1,13 @@
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 { useState } from "react";
import { PriceFormatter } from "../Helpers/PriceFormatter";
import dataImage2 from "../../assets/images/data-table-user-2.png";
import MarketPopUp from "../MarketPlace/PopUp/MarketPopUp";
export default function AvailableJobsCard({
className,
datas,
hidden = false,
contentDisplay,
image_server,
}) {
//debugger;
const [marketPopUp, setMarketPopUp] = useState({ show: false, data: {} });
@@ -21,10 +19,17 @@ export default function AvailableJobsCard({
datas?.currency
);
useEffect(() => {
const imagePath = require(`../../assets/images/${datas.thumbnil}`); // Replace with your directory path for local images
setImageUrl(imagePath);
}, []);
// useEffect(() => {
// const imagePath = require(`../../assets/images/${datas.thumbnil}`); // Replace with your directory path for local images
// setImageUrl(imagePath);
// }, []);
const image = localStorage.getItem("session_token")
? `${image_server}${localStorage.getItem("session_token")}/job/${
datas.job_uid
}`
: "";
return (
<>
{contentDisplay == "grid" ? (
@@ -50,7 +55,7 @@ export default function AvailableJobsCard({
Added
</p>
<p className="text-base text-dark-gray dark:text-white">
{datas.offer_added}
{new Date(datas.offer_added).toLocaleDateString()}
</p>
</div>
</div>
@@ -61,7 +66,7 @@ export default function AvailableJobsCard({
Expires
</p>
<p className="text-base text-dark-gray dark:text-white text-right">
{datas.expire}
{new Date(datas.expire).toLocaleDateString()}
</p>
</div>
</div>
@@ -70,34 +75,16 @@ export default function AvailableJobsCard({
<div
className="w-full h-[236px] p-6 rounded-xl overflow-hidden bg-center bg-cover bg-no-repeat"
style={{
backgroundImage: `url('${imageUrl}')`,
backgroundImage: `url('${image}')`,
}}
>
<div className="flex justify-center">{datas.description}</div>
<div className="flex justify-center bg-slate-100 p-2 rounded-md">
{datas.description}
</div>
</div>
</div>
<div className="details-area">
<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 className=" review flex space-x-2">*/}
{/* <button*/}
{/* onClick={favoriteHandler}*/}
{/* type="button"*/}
{/* className={`w-7 h-7 bg-white rounded-full flex justify-center items-center ${*/}
{/* addFavorite ? "text-red-500" : "text-thin-light-gray"*/}
{/* }`}*/}
{/* >*/}
{/* <Icons name="star" />*/}
{/* </button>*/}
{/*</div>*/}
</div>
<div className="product-two-options flex justify-between mb-5 relative"></div>
<div className="flex justify-between">
<div className="flex items-center space-x-2">
@@ -130,10 +117,10 @@ export default function AvailableJobsCard({
</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="card-style-two w-full px-4 py-[0.4rem] my-2 flex items-center gap-4 bg-white dark:bg-dark-white rounded-2xl section-shadow">
<div className="flex gap-5 items-center w-full">
<div className="w-full h-[60px] rounded-full overflow-hidden flex justify-center items-center flex-[0.1] min-w-[60px] max-w-[60px]">
<img src={dataImage2} alt="data" className="w-full h-full" />
<img src={image} alt="data" className="w-full h-full" />
</div>
<div className="flex flex-col flex-[0.9]">
<h1
@@ -156,27 +143,6 @@ export default function AvailableJobsCard({
</p>
</div>
{/* <div className="card-two-info flex gap-2 items-center">
<div className="owned-by flex space-x-2 items-center">
<div>
<p className="text-thin-light-gray text-sm leading-3">Added</p>
<p className="text-base text-dark-gray dark:text-white">
{datas.offer_added}
</p>
</div>
</div>
<div className="w-[1px] bg-light-purple dark:bg-dark-light-purple h-7"></div>
<div className="created-by flex space-x-2 items-center flex-row-reverse">
<div>
<p className="text-thin-light-gray text-sm leading-3 text-right">
Expires
</p>
<p className="text-base text-dark-gray dark:text-white text-right">
{datas.expire}
</p>
</div>
</div>
</div> */}
<div className="block sm:flex flex-wrap gap-4">
<p className="text-sm text-thin-light-gray flex flext-start gap-1">
Price: <span className="text-purple">{thePrice}</span>
@@ -5,7 +5,7 @@ import localImgLoad from "../../lib/localImgLoad";
import CountDown from "../Helpers/CountDown";
import Icons from "../Helpers/Icons";
export default function FamilyActiveJobsCard({ datas, hidden = false }) {
export default function FamilyActiveJobsCard({ datas, hidden = false, image_server }) {
let { pathname } = useLocation();
@@ -22,7 +22,8 @@ export default function FamilyActiveJobsCard({ datas, hidden = false }) {
}
};
//debugger;
const bannerName = datas.banner == null ?'default.jpg':datas.banner;
// const bannerName = datas.banner == null ?'default.jpg':datas.banner;
let image = `${image_server}${localStorage.getItem('session_token')}/job/${datas.origin_job_uid}`
return (
<div className="card-style-one flex flex-col justify-between w-full h-[387px] bg-white dark:bg-dark-white p-3 pb rounded-2xl">
<div className="content">
@@ -31,10 +32,13 @@ export default function FamilyActiveJobsCard({ datas, hidden = false }) {
{/* thumbnail image */}
<div
className="thumbnail w-full h-full rounded-xl overflow-hidden px-4 pt-4"
// style={{
// background: `url(${localImgLoad(
// `images/taskbanners/${bannerName}`
// )}) center / contain no-repeat`,
// }}
style={{
background: `url(${localImgLoad(
`images/taskbanners/${bannerName}`
)}) center / contain no-repeat`,
background: `url(${image}) center / contain no-repeat`,
}}
>
{/* <div className="product-options flex justify-between relative">*/}
@@ -8,7 +8,7 @@ export default function HomeBannerOffersCard(props) {
useEffect(() => {
let { banner, banner_location } = props?.itemData;
if (banner_location === "LOCAL") {
const imagePath = require(`../../assets/images/${banner}`); // Replace with your directory path for local images
const imagePath = require(`../../assets/images/${banner}`);
setImageUrl(imagePath);
} else if (banner_location === "URL") setImageUrl(banner);
else return null;
+11 -25
View File
@@ -2,13 +2,20 @@ import localImgLoad from "../../lib/localImgLoad";
import CountDown from "../Helpers/CountDown";
import { PriceFormatter } from "../Helpers/PriceFormatter";
export default function OfferCard({ datas, hidden = false, setOfferPopout }) {
export default function OfferCard({
datas,
hidden = false,
setOfferPopout,
image_server,
}) {
let thePrice = PriceFormatter(
datas?.price * 0.01,
datas?.currency_code,
datas?.currency
);
let image = `${image_server}${localStorage.getItem("session_token")}/job/${datas.job_uid}`
return (
<div className="card-style-one flex flex-col justify-between w-full h-[387px] bg-white dark:bg-dark-white p-3 pb rounded-2xl">
<div className="content">
@@ -18,9 +25,7 @@ export default function OfferCard({ datas, hidden = false, setOfferPopout }) {
<div
className="thumbnail w-full h-full rounded-xl overflow-hidden px-4 pt-4"
style={{
background: `url(${localImgLoad(
`images/taskbanners/${datas.banner}`
)}) center / contain no-repeat`,
background: `url(${image}) center / contain no-repeat`,
}}
>
{hidden && <div className="flex justify-center"></div>}
@@ -31,26 +36,7 @@ export default function OfferCard({ datas, hidden = false, setOfferPopout }) {
<h1 className="text-xl font-bold text-dark-gray dark:text-white mb-2 capitalize line-clamp-1">
{datas.title}
</h1>
{/* countdown */}
{/* <div className="w-full h-[54px] flex justify-evenly items-center p-2 rounded-lg border border-[#E3E4FE] dark:border-[#a7a9b533] ">
<div className="flex flex-col justify-between">
<p className="text-sm text-thin-light-gray dark:text-white tracking-wide">
Task Code
</p>
<p className="text-base font-bold tracking-wide text-dark-gray dark:text-white">
{datas.contract}
</p>
</div>
<div className="w-[1px] h-full bg-[#E3E4FE] dark:bg-[#a7a9b533] "></div>
<div className="flex flex-col justify-between">
<p className="text-sm text-thin-light-gray dark:text-white tracking-wide">
Remaining Time
</p>
<p className="text-base font-bold tracking-wide text-dark-gray dark:text-white">
<CountDown lastDate={datas.expire} />
</p>
</div>
</div> */}
<div className="w-full p-2 rounded-lg border border-[#E3E4FE] dark:border-[#a7a9b533] ">
<div className="grid grid-cols-2 gap-2">
<div className="flex flex-col justify-between items-center border-r-2">
@@ -96,7 +82,7 @@ export default function OfferCard({ datas, hidden = false, setOfferPopout }) {
<button
type="button"
onClick={() =>
setOfferPopout({ show: true, data: { ...datas, thePrice } })
setOfferPopout({ show: true, data: { ...datas, thePrice, image } })
}
className="btn-shine w-[98px] h-[33px] text-white rounded-full text-sm bg-pink flex justify-center items-center"
>
@@ -0,0 +1,204 @@
import React from "react";
import { Link } from "react-router-dom";
const AccountDashboard = ({ className, bannerList }) => {
// getting the upper three banners for the home layout
const getUpperBanner = bannerList?.filter((value, idx) => idx <= 2);
const getLowerBanner = bannerList?.filter((value, idx) => idx > 2);
let getImage = ({ banner_location, banner }) => {
if (banner_location == "LOCAL") {
return require(`../../assets/images/${banner}`);
} else {
return banner;
}
};
console.log(getLowerBanner);
return (
<div
className={`w-full min-h-[450px] flex flex-col justify-between items-center gap-4 rounded-2xl overflow-hidden ${
className || ""
}`}
>
<div className="w-full grid xxs:grid-cols-1 sm:grid-cols-2 lg:grid-cols-3 items-center justify-center gap-2 md:gap-4">
{getUpperBanner?.map((props, idx) => {
let image = getImage(props);
let { short_title, short_description, short_button_text, link_path } =
props;
return (
<TopBanner
btn={short_button_text}
image={image}
title={short_title}
desc={short_description}
link_path={link_path}
key={idx}
/>
);
})}
</div>
<div className="w-full grid-cols-1 md:grid-cols-2 2xl::grid-cols-3 grid items-center justify-center gap-2 md:gap-4">
{getLowerBanner?.map((props, idx) => {
let image = getImage(props);
let { short_title, short_description, short_button_text, link_path } =
props;
return (
<LowerBanner
btn={short_button_text}
image={image}
title={short_title}
desc={short_description}
link_path={link_path}
key={idx}
/>
);
})}
</div>
</div>
);
};
export default AccountDashboard;
const TopBanner = ({ image, title = "", desc = "", btn, link_path, key }) => {
return (
<div className="flex flex-col shadow-md rounded-xl dark:border-[#5356fb29]" key={key}>
<Link to={link_path} className="h-[12rem] rounded-t-xl">
<img
src={image}
alt="banner-img"
loading="lazy"
className="w-full h-full rounded-t-xl object-cover"
/>
</Link>
<div className="h-[7rem] rounded-b-xl bg-white dark:bg-dark-white">
<div className="border-b border-slate-300 px-2 py-1 h-[5.4rem] flex flex-col gap-2 dark:text-white">
<Link to={link_path} className="font-bold text-lg">
{title}
</Link>
<Link to={link_path} className="text-sm">
{desc}
</Link>
</div>
<div className="flex justify-between w-full px-2 items-center pt-[0.2rem]">
<Link to={link_path} className="text-slate-300 font-semibold text-sm">
{btn}
</Link>
<button className="flex items-center justify-center gap-2">
<div className="w-[4px] h-[4px] bg-slate-400 rounded-full"></div>
<div className="w-[4px] h-[4px] bg-slate-400 rounded-full"></div>
<div className="w-[4px] h-[4px] bg-slate-400 rounded-full"></div>
</button>
</div>
</div>
</div>
);
};
const LowerBanner = ({ image, title = "", desc = "", btn, link_path, key }) => {
return (
<div
key={key}
className="flex flex-col bg-white shadow-md h-full rounded-xl dark:border-[#5356fb29] dark:bg-dark-white"
>
<div className="w-full flex justify-between border-b border-slate-300 p-2">
<div className="h-[130px] flex justify-between items-center">
<div className="px-2 flex flex-col gap-2 dark:text-white">
<Link to={link_path} className="text-lg font-bold">
{title}
</Link>
<p to={link_path} className="text-sm">
{desc}
</p>
</div>
</div>
<Link to={link_path} className="w-[150px] h-[100px]">
<img
src={image}
alt="banner-img"
loading="lazy"
className="w-full h-full rounded-xl"
/>
</Link>
</div>
<div className="flex justify-between w-full px-2 items-center">
<Link to={link_path} className="text-slate-300 font-semibold text-sm">
{btn}
</Link>
<button className="flex items-center justify-center gap-2">
<div className="w-[4px] h-[4px] bg-slate-400 rounded-full"></div>
<div className="w-[4px] h-[4px] bg-slate-400 rounded-full"></div>
<div className="w-[4px] h-[4px] bg-slate-400 rounded-full"></div>
</button>
</div>
</div>
);
};
const BannerSection = ({ banners, variant }) => {
const getImage = ({ banner_location, banner }) => {
return banner_location === "LOCAL"
? require(`../../assets/images/${banner}`)
: banner;
};
return (
<>
{banners?.map((props, idx) => {
const { short_title, short_description, short_button_text, link_path } =
props;
const image = getImage(props);
return (
<div
key={idx}
className={`flex flex-col shadow-md rounded-xl ${
variant === "top" ? "" : "bg-white"
}`}
>
<div className={`${variant === "top" ? "rounded-t-xl" : ""}`}>
<Link to={link_path}>
<img
src={image}
alt="banner-img"
className="w-full h-full rounded-t-xl object-cover"
/>
</Link>
</div>
<div
className={`${
variant === "top"
? "rounded-b-xl bg-white"
: "border-b border-slate-300"
} h-[7rem]`}
>
<div className="border-b border-slate-300 px-2 py-1 h-[5.4rem] flex flex-col gap-2">
<Link to={link_path} className="font-bold text-lg">
{short_title}
</Link>
<Link to={link_path} className="text-sm">
{short_description}
</Link>
</div>
<div className="flex justify-between w-full px-2 items-center">
<Link to={link_path} className="text-slate-300 font-semibold">
{short_button_text}
</Link>
<button className="flex items-center justify-center gap-2">
<div className="w-[4px] h-[4px] bg-slate-400 rounded-full"></div>
<div className="w-[4px] h-[4px] bg-slate-400 rounded-full"></div>
<div className="w-[4px] h-[4px] bg-slate-400 rounded-full"></div>
</button>
</div>
</div>
</div>
);
})}
</>
);
};
@@ -0,0 +1,49 @@
import React from "react";
// import HomeSliders from "./HomeSliders";
import { useSelector } from "react-redux";
export default function FamilyParentDashboard({
className,
bannerList,
nextDueTask,
}) {
const { userDetails } = useSelector((state) => state?.userDetails);
let loginDate = userDetails?.last_login.split(" ")[0];
let { firstname, lastname, email } = userDetails;
let userEmail = email.split("@")[0];
return (
<div
className={`w-full md:h-[47px] xxs:h-[74px] flex px-[1.05rem] py-[0.35rem] flex-wrap justify-between items-center gap-2 rounded-2xl overflow-hidden bg-sky-blue ${
className || ""
}`}
style={{
// background: `url(${heroBg})`,
backgroundRepeat: "no-repeat",
backgroundSize: "cover",
}}
>
<div className="h-full flex flex-wrap w-full justify-between mb-5 lg:mb-0">
{/* heading */}
<div className="flex gap-8">
<h1 className="text-base font-medium text-white tracking-wide">
Welcome
</h1>
{/* user */}
<div className="flex flex-col gap-1">
<p className="text-base tracking-wide font-bold antise text-white leading-[1]">
{`${firstname} ${lastname}`}
</p>
<p className="text-sm tracking-wide text-white leading-[1]">
@{userEmail}
</p>
</div>
</div>
<span className="text-base font-thin tracking-wide text-white flex items-end">
Last Login : {loginDate}
</span>
</div>
</div>
);
}
@@ -0,0 +1,46 @@
import React from "react";
// import HomeSliders from "./HomeSliders";
import { useSelector } from "react-redux";
export default function HomeDashboard({ className, bannerList, nextDueTask }) {
const { userDetails } = useSelector((state) => state?.userDetails);
let loginDate = userDetails?.last_login.split(" ")[0];
let { firstname, lastname, email } = userDetails;
let userEmail = email.split("@")[0];
return (
<div
className={`w-full md:h-[47px] xxs:h-[74px] flex px-[1.05rem] py-[0.35rem] flex-wrap justify-between items-center gap-2 rounded-2xl overflow-hidden bg-sky-blue ${
className || ""
}`}
style={{
// background: `url(${heroBg})`,
backgroundRepeat: "no-repeat",
backgroundSize: "cover",
}}
>
<div className="h-full flex flex-wrap w-full justify-between mb-5 lg:mb-0">
{/* heading */}
<div className="flex gap-8">
<h1 className="text-base font-medium text-white tracking-wide">
Welcome
</h1>
{/* user */}
<div className="flex flex-col gap-1">
<p className="text-base tracking-wide font-bold antise text-white leading-[1]">
{`${firstname} ${lastname}`}
</p>
<p className="text-sm tracking-wide text-white leading-[1]">
@{userEmail}
</p>
</div>
</div>
<span className="text-base font-thin tracking-wide text-white flex items-end">
Last Login : {loginDate}
</span>
</div>
</div>
);
}
@@ -0,0 +1,50 @@
import React from "react";
// import HomeSliders from "./HomeSliders";
import { useSelector } from "react-redux";
export default function JobOwnerDashboard({
className,
bannerList,
nextDueTask,
}) {
const { userDetails } = useSelector((state) => state?.userDetails);
let loginDate = userDetails?.last_login.split(" ")[0];
let { firstname, lastname, email } = userDetails;
let userEmail = email.split("@")[0];
return (
<div
className={`w-full md:h-[47px] xxs:h-[74px] flex px-[1.05rem] py-[0.35rem] flex-wrap justify-between items-center gap-2 rounded-2xl overflow-hidden bg-sky-blue ${
className || ""
}`}
style={{
// background: `url(${heroBg})`,
backgroundRepeat: "no-repeat",
backgroundSize: "cover",
}}
>
<div className="h-full flex flex-wrap w-full justify-between mb-5 lg:mb-0">
{/* heading */}
<div className="flex gap-8">
<h1 className="text-base font-medium text-white tracking-wide">
Welcome
</h1>
{/* user */}
<div className="flex flex-col gap-1">
<p className="text-base tracking-wide font-bold antise text-white leading-[1]">
{`${firstname} ${lastname}`}
</p>
<p className="text-sm tracking-wide text-white leading-[1]">
@{userEmail}
</p>
</div>
</div>
<span className="text-base font-thin tracking-wide text-white flex items-end">
Last Login : {loginDate}
</span>
</div>
</div>
);
}
+110
View File
@@ -0,0 +1,110 @@
import React from "react";
import { Link } from "react-router-dom";
import heroUser from "../../assets/images/hero-user.png";
import CountDown from "../Helpers/CountDown";
// import HomeSliders from "./HomeSliders";
import { useSelector } from "react-redux";
import HomeSliders from "../Home/HomeSliders";
export default function WorkerDashboard({
className,
bannerList,
nextDueTask,
}) {
const settings = {
autoplay: true,
dots: true,
arrows: false,
infinite: true,
swipe: true,
};
const sildeData = null;
const { userDetails } = useSelector((state) => state?.userDetails);
let loginDate = userDetails?.last_login.split(" ")[0];
let { firstname, lastname, email, profile_pic } = userDetails;
let userEmail = email.split("@")[0];
return (
<div
className={`w-full min-h-[400px] md:grid grid-cols-2 lg:p-8 p-4 justify-between items-center gap-2 rounded-2xl overflow-hidden bg-blue-900 ${
className || ""
}`}
style={{
// background: `url(${heroBg})`,
backgroundRepeat: "no-repeat",
backgroundSize: "cover",
}}
>
<div className="h-full flex flex-col justify-between mb-5 lg:mb-0">
{/* heading */}
<div>
<h1 className="lg:text-2xl text-xl font-medium text-white tracking-wide">
Welcome
</h1>
<span className="text-[18px] font-thin tracking-wide text-white">
Last Login : {loginDate}
</span>
</div>
{/* user */}
<div className="flex items-center space-x-3">
<div className="w-14 h-14 flex justify-center items-center rounded-full overflow-hidden">
<img src={profile_pic != "" ? profile_pic : heroUser} alt="" />
</div>
<div>
<p className="text-xl tracking-wide font-bold antise text-white">
{`${firstname} ${lastname}`}
</p>
<p className="text-sm tracking-wide text-white">@{userEmail}</p>
</div>
</div>
{/* countdown */}
{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 back-dark1 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.substr(0, 4) + "..."}
</p>
<p className="text-base text-white tracking-wide">
{nextDueTask.next_due.price * 0.01} Naira
</p>
</div>
<div className="w-[1px] h-full bg-white-opacity"></div>
<div className="flex flex-col justify-between">
<p className="text-base text-white tracking-wide">
Next due in
</p>
<p className="lg:text-2xl text-lg font-bold tracking-wide text-white">
{/* <CountDown lastDate="2023-04-26 4:00:00" /> */}
<CountDown lastDate={nextDueTask.next_due.due_date} />
</p>
<div className="text-base text-white tracking-wide flex gap-[23px]">
<span>Hrs</span>
<span>Min</span>
<span>Sec</span>
</div>
</div>
</div>
)}
{/* action */}
<div className="flex lg:space-x-3 space-x-1 items-center">
<Link to="/mytask" className="text-white text-base">
<span className=" border-b dark:border-[#5356fb29] border-white">
{" "}
View All Task(s)
</span>
</Link>
</div>
</div>
<HomeSliders
settings={settings}
sideData={sildeData}
bannerList={bannerList}
/>
</div>
);
}
@@ -0,0 +1,49 @@
import React from "react";
// import HomeSliders from "./HomeSliders";
import { useSelector } from "react-redux";
export default function WorkerDashboard({
className,
bannerList,
nextDueTask,
}) {
const { userDetails } = useSelector((state) => state?.userDetails);
let loginDate = userDetails?.last_login.split(" ")[0];
let { firstname, lastname, email } = userDetails;
let userEmail = email.split("@")[0];
return (
<div
className={`w-full md:h-[47px] xxs:h-[74px] flex px-[1.05rem] py-[0.35rem] flex-wrap justify-between items-center gap-2 rounded-2xl overflow-hidden bg-sky-blue ${
className || ""
}`}
style={{
// background: `url(${heroBg})`,
backgroundRepeat: "no-repeat",
backgroundSize: "cover",
}}
>
<div className="h-full flex flex-wrap w-full justify-between mb-5 lg:mb-0">
{/* heading */}
<div className="flex gap-8">
<h1 className="text-base font-medium text-white tracking-wide">
Welcome
</h1>
{/* user */}
<div className="flex flex-col gap-1">
<p className="text-base tracking-wide font-bold antise text-white leading-[1]">
{`${firstname} ${lastname}`}
</p>
<p className="text-sm tracking-wide text-white leading-[1]">
@{userEmail}
</p>
</div>
</div>
<span className="text-base font-thin tracking-wide text-white flex items-end">
Last Login : {loginDate}
</span>
</div>
</div>
);
}
+13
View File
@@ -0,0 +1,13 @@
import FamilyParentDashboard from "./FamilyParentDashboard";
import HomeDashboard from "./HomeDashboard";
import JobOwnerDashboard from "./JobOwnerDashboard";
import WorkerDashboard from "./WorkerDashboard";
import AccountDashboard from "./AccountDashboard";
export {
FamilyParentDashboard,
HomeDashboard,
JobOwnerDashboard,
WorkerDashboard,
AccountDashboard
};
@@ -0,0 +1,78 @@
import React, { Suspense, useMemo, useState } from "react";
import { Link } from "react-router-dom";
import usersService from "../../services/UsersService";
import Layout from "../Partials/Layout";
import LoadingSpinner from "../Spinners/LoadingSpinner";
import FamilyTableNew from "./FamilyTableNew";
export default function FamilyActivities() {
const [familyList, setFamilyList] = useState({});
const [loader, setLoader] = useState(false);
const apiCall = useMemo(() => new usersService(), []);
return (
<Layout>
{/*<CommonHead />*/}
<div className="notification-page w-full mb-10">
<div className="notification-wrapper w-full">
{/* heading */}
<div className="sm:flex justify-between items-center mb-6">
<div className="mb-5 sm:mb-0">
<h1 className="text-26 font-bold inline-flex gap-3 text-dark-gray dark:text-white items-center">
<span className={``}>Tasks & Chores</span>
</h1>
</div>
<Link className="flex items-center gap-2" to="/acc-family">
<svg
xmlns="http://www.w3.org/2000/svg"
fill="none"
viewBox="0 0 24 24"
strokeWidth={1.5}
stroke="currentColor"
className="w-4 h-4"
>
<path
strokeLinecap="round"
strokeLinejoin="round"
d="M10.5 19.5 3 12m0 0 7.5-7.5M3 12h18"
/>
</svg>
<span>Family</span>
</Link>
</div>
<Suspense fallback={<LoadingSpinner color="sky-blue" size="16" />}>
<FamilyTableNew
familyList={familyList?.result_list}
loader={loader}
imageServer={familyList?.session_image_server}
/>
</Suspense>
</div>
</div>
</Layout>
);
}
const CloseIcon = () => (
<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>
);
+235 -94
View File
@@ -1,19 +1,19 @@
import React, {
Suspense,
lazy,
useCallback,
useEffect,
useMemo,
useRef,
useState,
useTransition,
} from "react";
import { useReactToPrint } from "react-to-print";
import profile from "../../assets/images/profile-info-profile.png";
import profile from "../../assets/images/icons/family.svg";
import localImgLoad from "../../lib/localImgLoad";
import usersService from "../../services/UsersService";
import LoadingSpinner from "../Spinners/LoadingSpinner";
import AssignTaskPopout from "./FamilyPopout/AssignTaskPopout";
import localImgLoad from "../../lib/localImgLoad";
import FamilyWallet from "./Tabs/FamilyWallet";
import { apiConst } from "../../lib/apiConst";
// Lazy Imports for components
const FamilyWaitlist = lazy(() => import("./Tabs/FamilyWaitlist"));
@@ -29,113 +29,150 @@ export default function FamilyManageTabs({
listReload,
loader,
}) {
// Initial state for family details
const initialDetailState = {
loading: false,
data: null,
};
// console.log('accountDetails',accountDetails)
// State for family details, tasks, waitlist, and pending
const [details, setDetails] = useState({
familyDetails: { loading: false, data: null },
familyTasks: { loading: false, data: null },
familyWaitList: { loading: false, data: null },
familyPending: { loading: false, data: null },
familyDetails: { ...initialDetailState },
familyTasks: { ...initialDetailState },
familyWaitList: { ...initialDetailState },
familyPending: { ...initialDetailState },
});
// Function to reset family details, tasks, waitlist, and pending
const resetDetails = () => {
setDetails({
familyDetails: { ...initialDetailState },
familyTasks: { ...initialDetailState },
familyWaitList: { ...initialDetailState },
familyPending: { ...initialDetailState },
});
};
const [updatePage, setUpdatePage] = useState(false) // State to determine when to update the page
// State for family task data
const [familyTask, setFamilyTask] = useState({ loading: false, data: [] });
// State for active task
const [activeTask, setActiveTask] = useState({ id: 0, data: {} });
// State for error messages
const [errMsg, setErrMsg] = useState("");
// State for family task popout
const [familyTaskPopout, setFamilyTaskPopout] = useState(false);
const [profileImg, setProfileImg] = useState(profile);
let [uploadStatus, setUploadStatus] = useState({loading: false, status: false, message:''}) // HOLDS STATE FOR UPLOAD PROFILE PICTURE STATUS
// State for profile image
const [profileImg, setProfileImg] = useState(accountDetails.image ? accountDetails.image : profile);
// Ref for profile image input
const profileImgInput = useRef(null);
const [isPending, startTransition] = useTransition();
// Create an instance of the usersService class
const apiCall = useMemo(() => new usersService(), []);
// Function to handle toggling the family task popout
const familyPopUpHandler = () => {
setFamilyTaskPopout((prev) => !prev);
};
// Function to trigger a click on the hidden profile image input
const browseProfileImg = () => {
profileImgInput.current.click();
};
/**
* Handles the change event of the profile image input field.
* Checks if the selected file exceeds the maximum file size limit and displays an alert if it does.
* If the file is within the size limit, it reads the file using the FileReader API and sets the profile image state with the result.
*/
const profileImgChangeHandler = (e) => {
setUploadStatus({loading: false, status: false, message:''})
let acceptedFormat = ["jpeg", "jpg", "png", "bmp", "gif"] // ARRAY OF SUPPORTED FORMATS
let uploadedFile = e.target.files[0] //UPLOADED FILE
const fileFormat = uploadedFile?.type?.split("/")[1]?.toLowerCase();
if(!acceptedFormat.includes(fileFormat)){ //CHECKING FOR CORRECT UPLOAD FORMAT
const msg = `Please select ${acceptedFormat.slice(0, -1).join(', ')} or ${acceptedFormat.slice(-1)}`;
setUploadStatus({loading: false, status: false, message:msg})
return setTimeout(()=>{
profileImgInput.current.value = '' // clear the input
setUploadStatus({loading: false, status: false, message:''})
},5000)
}
if(uploadedFile.size > 5*1048576){ // CHECKING FOR CORRECT FILE SIZE
setUploadStatus({loading: false, status: false, message:'File must not exceed 5MB'})
return setTimeout(()=>{
profileImgInput.current.value = '' // clear the input
setUploadStatus({loading: false, status: false, message:''})
},5000)
}
if (e.target.value !== "") {
const imgReader = new FileReader();
imgReader.onload = (event) => {
setProfileImg(event.target.result);
let base64Img = imgReader.result.split(",")[1];
let reqData = { // PAYLOAD FOR API CALL
family_uid: accountDetails?.family_uid,
file_name: uploadedFile?.name,
file_size: uploadedFile?.size,
file_type: uploadedFile?.type?.split("/")[0]?.toLowerCase(),
file_data: base64Img,
msg_type: 'FILE',
action: apiConst.WRENCHBOARD_PICTURE_FAMMEMBER
}
setUploadStatus({loading: true, status: false, message:'Loading...'})
apiCall.sendFiles(reqData).then(res=>{
if(res.status != 200 || res.data.internal_return < 0){
return setUploadStatus({loading: false, status: false, message: 'Something went wrong, try again'})
}
setUploadStatus({loading: false, status: true, message: 'Uploaded successfully'})
setProfileImg(event.target.result);
}).catch(error=>{
setUploadStatus({loading: false, status: false, message: 'Network error, try again'})
}).finally(()=>{
setTimeout(()=>{
setUploadStatus({loading: false, status: false, message: ''})
},5000)
})
};
imgReader.readAsDataURL(e.target.files[0]);
}
};
const manageFamily = useCallback(async () => {
try {
setDetails((prevDetails) => ({
...prevDetails,
familyDetails: { loading: true },
familyTasks: { loading: true },
familyWaitList: { loading: true },
familyPending: { loading: true },
}));
const { family_uid } = accountDetails;
const reqData = { family_uid };
const [familyRes, tasksRes, familyWaitRes, familyPending] =
await Promise.all([
apiCall.ManageFamily(reqData),
apiCall.ManageTasks(reqData),
apiCall.ManageFamilyWaitlist(),
apiCall.ManageFamilyPending(),
]);
const familyData = familyRes.data;
const tasksData = tasksRes.data;
const familyWaitData = familyWaitRes.data;
const familyPendingData = familyPending.data;
if (
familyData?.internal_return < 0 ||
tasksData?.internal_return < 0 ||
familyWaitData?.internal_return < 0 ||
familyPendingData?.internal_return < 0
) {
return;
}
startTransition(() => {
setDetails({
familyDetails: { loading: false, data: familyData },
familyTasks: { loading: false, data: tasksData },
familyWaitList: { loading: false, data: familyWaitData },
familyPending: { loading: false, data: familyPendingData },
});
});
} catch (error) {
setDetails((prevDetails) => ({
...prevDetails,
familyDetails: { loading: false },
familyTasks: { loading: false },
familyWaitList: { loading: false },
familyPending: { loading: false },
}));
setErrMsg("An error occurred");
throw new Error(error);
}
}, [apiCall, accountDetails]);
// Ref for the account section
const accountRef = useRef();
// React-to-Print hook for handling printing
const useHandlePrint = useReactToPrint({
content: () => accountRef.current,
});
// Array of tab names
const tabs = [
{ id: 1, name: "Tasks" },
{ id: 2, name: "Waiting" },
{ id: 3, name: "Pending" },
];
// State for the currently selected tab
const [tab, setTab] = useState(tabs[0].name);
// Function to handle tab changes
const tabHandler = (value) => {
startTransition(() => {
setTab(value);
});
setTab(value);
};
// Object that maps tab names to their corresponding components
const tabComponents = {
Tasks: (
<FamilyTasks
@@ -156,7 +193,7 @@ export default function FamilyManageTabs({
<FamilyPending
familyData={details.familyPending.data}
accountDetails={accountDetails}
loader={details.familyWaitList.loading}
loader={details.familyPending.loading}
/>
),
Account: (
@@ -167,9 +204,11 @@ export default function FamilyManageTabs({
handlePrint={useHandlePrint}
/>
),
Profile: <FamilyProfile />,
Profile: <FamilyProfile familyData={details.familyDetails.data} />,
wallet: <FamilyWallet familyData={details.familyDetails.data} />,
};
// Default tab component
const defaultTabComponent = (
<FamilyTasks
className={className}
@@ -179,33 +218,117 @@ export default function FamilyManageTabs({
/>
);
// Selected tab component based on the current 'tab'
const selectedTabComponent = tabComponents[tab] || defaultTabComponent;
// Effect to manage family details and related data
useEffect(() => {
let __manageFamily = true;
if (__manageFamily) {
manageFamily();
}
return () => {
__manageFamily = false;
const manageFamily = async () => {
try {
resetDetails();
setDetails({
familyDetails: { loading: true },
familyTasks: { loading: true },
familyWaitList: { loading: true },
familyPending: { loading: true },
});
const { family_uid } = accountDetails;
const reqData = { family_uid };
const [familyRes, tasksRes, familyWaitRes, familyPending] =
await Promise.all([
apiCall.ManageFamily(reqData),
apiCall.ManageTasks(reqData),
apiCall.ManageFamilyWaitlist(),
apiCall.ManageFamilyPending(),
]);
const familyData = familyRes.data;
const tasksData = tasksRes.data;
const familyWaitData = familyWaitRes.data;
const familyPendingData = familyPending.data;
// Function to check for errors in data
const checkDataError = (data) => data?.internal_return < 0;
if (
checkDataError(familyData) ||
checkDataError(tasksData) ||
checkDataError(familyWaitData) ||
checkDataError(familyPendingData)
) {
return;
}
setDetails({
familyDetails: { loading: false, data: familyData },
familyTasks: { loading: false, data: tasksData },
familyWaitList: { loading: false, data: familyWaitData },
familyPending: { loading: false, data: familyPendingData },
});
} catch (error) {
resetDetails();
setErrMsg("An error occurred");
throw new Error(error);
}
};
}, [tab, manageFamily]);
// Invoke the manageFamily function when the component mounts
manageFamily();
}, [updatePage]);
// Effect to manage family tasks
useEffect(() => {
let checkFamilyTask = true;
const reqData = {
limit: 30,
offset: 0,
job_type: "FAMILY",
action: apiConst.WRENCHBOARD_PICTURE_FAMMEMBER,
};
if (checkFamilyTask) {
setFamilyTask({ loading: true });
apiCall
.getMyJobList(reqData)
.then((res) => {
setFamilyTask({ loading: false, data: res?.data?.result_list });
if (res?.data?.result_list?.length) {
setActiveTask((prev) => ({
...prev,
data: res?.data?.result_list[0],
}));
}
})
.catch((err) => {
setFamilyTask({ loading: false, data: [] });
console.log("Error", err);
});
}
// Cleanup function to prevent memory leaks
return () => {
checkFamilyTask = false;
};
}, []);
return (
<div
className={`w-full bg-white dark:bg-dark-white overflow-y-auto rounded-2xl section-shadow min-h-[520px] max-h-[600px] ${
className={`w-full bg-white dark:bg-dark-white overflow-y-auto rounded-2xl section-shadow h-full ${
className || ""
}`}
>
<div className="relative w-full overflow-x-auto sm:rounded-lg">
<div className="relative w-full sm:rounded-lg overflow-x-auto">
<Suspense
fallback={
<div className="h-full min-h-[500px] w-full overflow-hidden flex justify-center items-center">
<div className="h-full min-h-[609px] w-full overflow-hidden flex justify-center items-center">
<LoadingSpinner size="16" color="sky-blue" />
</div>
}
>
<div className="w-full h-full text-sm text-left text-gray-500 dark:text-gray-400 relative grid grid-cols-4 min-h-[520px]">
<div className="w-full h-full text-sm text-left text-gray-500 dark:text-gray-400 relative grid grid-cols-4 min-h-[575px]">
<div className="border-r border-[#E3E4FE] dark:border-[#a7a9b533] p-6 h-full flex flex-col justify-between">
<ProfileInfo
profileImg={profileImg}
@@ -213,18 +336,19 @@ export default function FamilyManageTabs({
profileImgChangeHandler={profileImgChangeHandler}
browseProfileImg={browseProfileImg}
accountDetails={accountDetails}
uploadStatus={uploadStatus}
/>
<div className="mt-4 flex justify-start items-center gap-2">
<div className="mt-4 flex flex-col justify-center items-center gap-2 lg:flex-row lg:justify-center lg:items-center xl:flex-col xl:justify-center xl:items-center 2xl:flex-row 2xl:justify-center 2xl:items-center 2xl:gap-[2px]">
<button
onClick={() => tabHandler("Account")}
className="family-icon p-2 border-2 border-sky-blue rounded-2xl flex flex-col justify-between items-center max-w-[65px] w-full"
>
<img
src={localImgLoad("images/icons/account.svg")}
className="max-w-[30px] w-full"
className="max-w-[30px]"
alt="Settings-Icon"
/>
<p className="text-lg text-sky-blue">Acc.</p>
<p className="text-[16px] text-sky-blue">Acc.</p>
</button>
<button
onClick={() => tabHandler("Profile")}
@@ -232,17 +356,28 @@ export default function FamilyManageTabs({
>
<img
src={localImgLoad("images/icons/profile.svg")}
className="max-w-[30px] w-full"
className="max-w-[30px]"
alt="Settings-Icon"
/>
<p className="text-lg text-sky-blue">Profile</p>
<p className="text-[16px] text-sky-blue">Profile</p>
</button>
<button
onClick={() => tabHandler("wallet")}
className="family-icon p-2 border-2 border-sky-blue rounded-2xl flex flex-col justify-between items-center max-w-[65px] w-full"
>
<img
src={localImgLoad("images/icons/wallet.svg")}
className="max-w-[30px]"
alt="Settings-Icon"
/>
<p className="text-[16px] text-sky-blue">Wallet</p>
</button>
</div>
</div>
<div className="col-span-3 justify-self-end h-full w-full">
<div className="col-span-3 h-full w-full">
<div className="flex flex-col w-full">
<div className="w-full pr-8 flex items-center gap-1">
<ul className="flex gap-2 items-center border-b border-b-[#FAFAF] w-full">
<div className="w-full pr-8 flex items-center gap-1 border-b border-b-[#FAFAF]">
<ul className="flex gap-2 items-center w-full">
{tabs.map(({ name, id }) => (
<li
onClick={() => tabHandler(name)}
@@ -265,8 +400,9 @@ export default function FamilyManageTabs({
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">
<div className="flex-[0.9] h-full">
<div className="h-full relative overflow-y-auto">
<Suspense
fallback={<LoadingSpinner size="16" color="sky-blue" />}
>
@@ -284,7 +420,12 @@ export default function FamilyManageTabs({
<AssignTaskPopout
action={familyPopUpHandler}
situation={familyTaskPopout}
familyDetails={details.familyDetails.data}
familyTask={familyTask}
setFamilyTask={setFamilyTask}
setActiveTask={setActiveTask}
activeTask={activeTask}
familyDetailsData={details.familyDetails.data}
setUpdatePage={setUpdatePage}
/>
)}
</div>
+13 -1
View File
@@ -4,6 +4,7 @@ import SearchCom from "../Helpers/SearchCom";
import FamilyMarketCard from "../Cards/FamilyMarketCard";
import usersService from "../../services/UsersService";
import SuggestTask from "../FamilyPopup/SuggestTask";
import CustomBreadcrumb from "../Breadcrumb/CustomBreadcrumb";
export default function FamilyMarket() {
const [popUp, setPopUp] = useState(false);
@@ -53,12 +54,23 @@ export default function FamilyMarket() {
<div className="notification-page w-full mb-10">
<div className="notification-wrapper w-full">
{/* heading */}
<div className="sm:flex justify-between items-center mb-6">
{/* <div className="sm:flex justify-between items-center mb-6">
<div className="mb-5 sm:mb-0">
<h1 className="text-26 font-bold inline-flex gap-3 text-dark-gray dark:text-white items-center">
<span>Suggest Task to the Parents</span>
</h1>
</div>
</div> */}
<div className="mb-5">
<CustomBreadcrumb
title = {'Suggest Task to the Parents'}
breadcrumb={
[
{ link: "/", title: "Home" },
{ link: "/familymarket", title: "Family Market", active: true},
]
}
/>
</div>
{/* Body */}
<div className="filter-section w-full items-center sm:flex justify-between mb-6">
@@ -0,0 +1,58 @@
import React, { useEffect, useState } from 'react'
import Layout from '../Partials/Layout'
import MyOffersFamilyTable from '../MyTasks/MyOffersFamilyTable'
import LoadingSpinner from '../Spinners/LoadingSpinner';
import usersService from '../../services/UsersService';
import CustomBreadcrumb from '../Breadcrumb/CustomBreadcrumb';
export default function FamilyPendingOffer() {
const userApi = new usersService();
const [myOffersList, setMyOffersList] = useState({loading: true, data: []});
const getMyOffersList = async () => {
try {
const res = await userApi.getOffersList();
setMyOffersList({loading:false, data:res.data});
console.log('SAME', res.data)
} catch (error) {
setMyOffersList({loading:false, data:[]});
console.log("Error getting offers", error);
}
};
useEffect(()=>{
getMyOffersList()
},[])
return (
<Layout>
<div className="mb-5">
<CustomBreadcrumb
title = {'Ready to Start'}
breadcrumb={
[
{ link: "/", title: "Home" },
{ link: "/pending", title: "Pending", active: true},
]
}
/>
</div>
{myOffersList.loading ?
<div className='w-full flex justify-center items-center rounded-2xl bg-white'>
<LoadingSpinner size='10' color='sky-blue' height='h-[20rem]' />
</div>
:
myOffersList?.data?.result_list && myOffersList?.data?.result_list.length > 0 ?
<MyOffersFamilyTable
familyOffers={myOffersList?.data?.result_list}
image_server={myOffersList?.data?.session_image_server}
className="mb-10"
/>
:
<div className='w-full h-[30rem] bg-white dark:bg-dark-white flex justify-center items-center rounded-2xl'>
<p className='text-black dark:text-white'>No Record Found!</p>
</div>
}
</Layout>
)
}
@@ -1,441 +1,424 @@
import React, { useState, useEffect, useTransition } from "react";
import ModalCom from "../../Helpers/ModalCom";
import Detail from "../../jobPopout/popoutcomponent/Detail";
import React, { useState } from "react";
import usersService from "../../../services/UsersService";
import LoadingSpinner from "../../Spinners/LoadingSpinner";
import ModalCom from "../../Helpers/ModalCom";
import { PriceFormatter } from "../../Helpers/PriceFormatter";
import LoadingSpinner from "../../Spinners/LoadingSpinner";
import Detail from "../../jobPopout/popoutcomponent/Detail";
import { NewTasks } from "./forms";
import { tableReload } from "../../../store/TableReloads";
import { useDispatch } from "react-redux";
const AssignTaskPopout = React.memo(({ action, details, situation, familyDetails }) => {
const apiCall = new usersService();
const AssignTaskPopout = React.memo(
({
action,
details,
situation,
familyDetailsData,
familyTask,
activeTask,
setActiveTask,
setUpdatePage
}) => {
const apiCall = new usersService();
let [requestStatus, setRequestStatus] = useState({
loading: false,
status: false,
message: "",
}); // HOLDS RESPONSE FOR SENDING API REQUEST
const dispatch = useDispatch()
let [familyTask, setFamilyTask] = useState({ loading: false, data: [] });
let [requestStatus, setRequestStatus] = useState({
loading: false,
status: false,
message: "",
}); // HOLDS RESPONSE FOR SENDING API REQUEST
let [taskType, setTaskType] = useState(details ? "new" : "select"); // SWITCHES BTW SELECT TASK AND NEW TASK
let [taskType, setTaskType] = useState(details ? "new" : "select"); // SWITCHES BTW SELECT TASK AND NEW TASK
let [activeTask, setActiveTask] = useState({ id: 0, data: {} }); // HOLDS SELECTED TASK
const switchTaskType = ({ target: { value } }) => {
// FUNCTION TO CHANGE SELECTED ACTIVE TASK
setTaskType(value);
};
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 handleActiveTask = (id = 0, data = {}) => {
// FUNCTION TO CHANGE SELECTED ACTIVE TASK
setActiveTask({ id, data });
};
// New Task
const [formState, setFormState] = useState({
// Initialize form state with desired fields
banner: details?.banner || "default.jpg",
country: details?.country || "",
price: details?.price || "",
title: details?.title || "",
description: details?.description || "",
job_detail: details?.job_detail || "",
timeline_days: details?.timeline_days || "",
category: details?.category || "",
});
// New Task
const [formState, setFormState] = useState({
// Initialize form state with desired fields
banner: details?.banner || "default.jpg",
country: details?.country || "",
price: details?.price || "",
title: details?.title || "",
description: details?.description || "",
job_detail: details?.job_detail || "",
timeline_days: details?.timeline_days || "",
category: details?.category || "",
});
const assignFamilyTask = () => {
setRequestStatus({ loading: true, status: false, message: "" });
let reqData = {};
if (taskType == "select") {
// RUNS HERE IF TASK TYPE IS SELECT
if (!Object.keys(activeTask.data).length) {
setRequestStatus({
loading: false,
status: false,
message: "No Task is seleted",
});
return setTimeout(() => {
setRequestStatus({ loading: false, status: false, message: "" });
}, 3000);
}
reqData = {
// API PAYLOADS
job_id: activeTask.data?.job_id,
job_uid: activeTask.data?.job_uid,
family_uid: familyDetails?.uid || details?.family_uid,
job_description: activeTask.data?.description,
assign_mode: 110011,
};
}
if (taskType === "new") {
const {
banner,
category,
country,
description,
job_detail,
price,
timeline_days,
title,
} = formState;
const requiredFields = {
banner,
// category,
country,
description,
job_detail,
price,
timeline_days,
title,
};
for (let field in requiredFields) {
if (requiredFields[field] == "") {
// let currencyErrMsg = field == "country" && "currency"
const assignFamilyTask = () => {
setRequestStatus({ loading: true, status: false, message: "" });
let reqData = {};
if (taskType == "select") {
// RUNS HERE IF TASK TYPE IS SELECT
if (!Object.keys(activeTask.data).length) {
setRequestStatus({
loading: false,
status: false,
message: `${field} is empty`,
message: "No Task is seleted",
});
return setTimeout(() => {
setRequestStatus({ loading: false, status: false, message: "" });
}, 3000);
}
reqData = {
// API PAYLOADS
job_id: activeTask.data?.job_id,
job_uid: activeTask.data?.job_uid,
family_uid: familyDetailsData?.uid || details?.family_uid,
job_description: activeTask.data?.description,
assign_mode: 110011,
};
}
reqData = {
banner,
category,
country,
description,
job_detail,
price: price * 100,
timeline_days,
title,
assign_mode: 110055,
family_uid: details?.family_uid || familyDetails?.uid,
};
}
if (taskType === "new") {
const {
banner,
category,
country,
description,
job_detail,
price,
timeline_days,
title,
} = formState;
apiCall
.assignFamilyTask(reqData)
.then((res) => {
if (res.status != 200 || res.data.internal_return < 0) {
const requiredFields = {
banner,
// category,
currency:country,
description,
'job detail':job_detail,
price,
timeline:timeline_days,
title,
};
for (let field in requiredFields) {
if (requiredFields[field] == "") {
// let currencyErrMsg = field == "country" && "currency"
setRequestStatus({
loading: false,
status: false,
message: `${field[0].toUpperCase()+field.slice(1).toLowerCase()} is empty`,
});
return setTimeout(() => {
setRequestStatus({ loading: false, status: false, message: "" });
}, 3000);
}
}
reqData = {
banner,
category,
country,
description,
job_detail,
price: price * 100,
timeline_days,
title,
assign_mode: 110055,
family_uid: details?.family_uid || familyDetailsData?.uid,
};
}
apiCall
.assignFamilyTask(reqData)
.then((res) => {
if (res.status != 200 || res.data.internal_return < 0) {
setRequestStatus({
loading: false,
status: false,
message: "failed to assign task",
});
return setTimeout(() => {
setRequestStatus({ loading: false, status: false, message: "" });
}, 5000);
}
setRequestStatus({
loading: false,
status: true,
message: "action successful",
});
setUpdatePage(prev => !prev) // Updates family task page by calling the useeffect hook
dispatch(tableReload({ type: "WALLETTABLE" })); // RELOADS USER WALLET
setTimeout(() => {
setRequestStatus({ loading: false, status: false, message: "" });
action(); // FUNCTION THAT CLOSES THE MODAL BOX
}, 5000);
})
.catch((err) => {
setRequestStatus({
loading: false,
status: false,
message: "failed to assign task",
message: "An Error occured, try again",
});
return setTimeout(() => {
setTimeout(() => {
setRequestStatus({ loading: false, status: false, message: "" });
}, 5000);
}
setRequestStatus({
loading: false,
status: true,
message: "action successful",
});
setTimeout(() => {
setRequestStatus({ loading: false, status: false, message: "" });
action(); // FUNCTION THAT CLOSES THE MODAL BOX
}, 5000);
})
.catch((err) => {
setRequestStatus({
loading: false,
status: false,
message: "An Error occured, try again",
});
setTimeout(() => {
setRequestStatus({ loading: false, status: false, message: "" });
}, 5000);
});
};
useEffect(() => {
let checkFamilyTask = true;
const reqData = {
limit: 30,
offset: 0,
job_type: "FAMILY",
action: 13005,
};
apiCall
.getMyJobList(reqData)
.then((res) => {
if (checkFamilyTask) {
setFamilyTask({ loading: false, data: res?.data?.result_list });
if (res?.data?.result_list?.length) {
setActiveTask((prev) => ({
...prev,
data: res?.data?.result_list[0],
}));
}
}
})
.catch((err) => {
setFamilyTask({ loading: false, data: [] });
console.log("Error", err);
});
return () => {
checkFamilyTask = false;
};
}, []);
return (
<>
<ModalCom
action={action}
situation={situation}
className="assign-task-popup"
>
<div className="w-full h-full lg:w-[700px] lg:h-auto bg-white dark:bg-dark-white lg:rounded-2xl">
<div className="w-full flex items-center justify-between lg:px-10 lg:py-8 px-[30px] py-[23px] border-b dark:border-[#5356fb29] border-light-purple">
<h1 className="text-26 font-bold text-dark-gray dark:text-white tracking-wide">
Assign task to {familyDetails?.firstname || details.firstName}
</h1>
<button
type="button"
className="text-[#374557] dark:text-red-500"
onClick={action}
>
<svg
width="36"
height="36"
viewBox="0 0 36 36"
fill="none"
className="fill-current"
xmlns="http://www.w3.org/2000/svg"
return (
<>
<ModalCom
action={action}
situation={situation}
>
<div className="w-11/12 lg:w-[700px] bg-white dark:bg-dark-white lg:rounded-2xl overflow-y-auto">
<div className="w-full flex items-center justify-between lg:px-10 lg:py-8 px-[30px] py-[23px] border-b dark:border-[#5356fb29] border-light-purple">
<h1 className="text-26 font-bold text-dark-gray dark:text-white tracking-wide">
Assign task to{" "}
{familyDetailsData?.firstname || details?.firstName}
</h1>
<button
type="button"
className="text-[#374557] dark:text-red-500"
onClick={action}
>
<path
d="M36 16.16C36 17.4399 36 18.7199 36 20.0001C35.7911 20.0709 35.8636 20.2554 35.8385 20.4001C34.5321 27.9453 30.246 32.9248 22.9603 35.2822C21.9006 35.6251 20.7753 35.7657 19.6802 35.9997C18.4003 35.9997 17.1204 35.9997 15.8401 35.9997C15.5896 35.7086 15.2189 35.7732 14.9034 35.7093C7.77231 34.2621 3.08728 30.0725 0.769671 23.187C0.435002 22.1926 0.445997 21.1199 0 20.1599C0 18.7198 0 17.2798 0 15.8398C0.291376 15.6195 0.214408 15.2656 0.270759 14.9808C1.71321 7.69774 6.02611 2.99691 13.0428 0.700951C14.0118 0.383805 15.0509 0.386897 15.9999 0C17.2265 0 18.4532 0 19.6799 0C19.7156 0.124041 19.8125 0.136067 19.9225 0.146719C27.3 0.868973 33.5322 6.21922 35.3801 13.427C35.6121 14.3313 35.7945 15.2484 36 16.16ZM33.011 18.0787C33.0433 9.77105 26.3423 3.00309 18.077 2.9945C9.78479 2.98626 3.00344 9.658 2.98523 17.8426C2.96667 26.1633 9.58859 32.9601 17.7602 33.0079C26.197 33.0577 32.9787 26.4186 33.011 18.0787Z"
fill=""
fillOpacity="0.6"
/>
<path
d="M15.9309 18.023C13.9329 16.037 12.007 14.1207 10.0787 12.2072C9.60071 11.733 9.26398 11.2162 9.51996 10.506C9.945 9.32677 11.1954 9.0811 12.1437 10.0174C13.9067 11.7585 15.6766 13.494 17.385 15.2879C17.9108 15.8401 18.1633 15.7487 18.6375 15.258C20.3586 13.4761 22.1199 11.7327 23.8822 9.99096C24.8175 9.06632 26.1095 9.33639 26.4967 10.517C26.7286 11.2241 26.3919 11.7413 25.9133 12.2178C24.1757 13.9472 22.4477 15.6855 20.7104 17.4148C20.5228 17.6018 20.2964 17.7495 20.0466 17.9485C22.0831 19.974 24.0372 21.8992 25.9689 23.8468C26.9262 24.8119 26.6489 26.1101 25.4336 26.4987C24.712 26.7292 24.2131 26.3441 23.7455 25.8757C21.9945 24.1227 20.2232 22.3892 18.5045 20.6049C18.0698 20.1534 17.8716 20.2269 17.4802 20.6282C15.732 22.4215 13.9493 24.1807 12.1777 25.951C11.7022 26.4262 11.193 26.7471 10.4738 26.4537C9.31345 25.9798 9.06881 24.8398 9.98589 23.8952C11.285 22.5576 12.6138 21.2484 13.9387 19.9355C14.5792 19.3005 15.2399 18.6852 15.9309 18.023Z"
fill="#"
fillOpacity="0.6"
/>
</svg>
</button>
</div>
{familyTask.loading ? (
<div className="h-[100px] w-full flex justify-center items-center">
<LoadingSpinner color="sky-blue" size="16" />
<svg
width="36"
height="36"
viewBox="0 0 36 36"
fill="none"
className="fill-current"
xmlns="http://www.w3.org/2000/svg"
>
<path
d="M36 16.16C36 17.4399 36 18.7199 36 20.0001C35.7911 20.0709 35.8636 20.2554 35.8385 20.4001C34.5321 27.9453 30.246 32.9248 22.9603 35.2822C21.9006 35.6251 20.7753 35.7657 19.6802 35.9997C18.4003 35.9997 17.1204 35.9997 15.8401 35.9997C15.5896 35.7086 15.2189 35.7732 14.9034 35.7093C7.77231 34.2621 3.08728 30.0725 0.769671 23.187C0.435002 22.1926 0.445997 21.1199 0 20.1599C0 18.7198 0 17.2798 0 15.8398C0.291376 15.6195 0.214408 15.2656 0.270759 14.9808C1.71321 7.69774 6.02611 2.99691 13.0428 0.700951C14.0118 0.383805 15.0509 0.386897 15.9999 0C17.2265 0 18.4532 0 19.6799 0C19.7156 0.124041 19.8125 0.136067 19.9225 0.146719C27.3 0.868973 33.5322 6.21922 35.3801 13.427C35.6121 14.3313 35.7945 15.2484 36 16.16ZM33.011 18.0787C33.0433 9.77105 26.3423 3.00309 18.077 2.9945C9.78479 2.98626 3.00344 9.658 2.98523 17.8426C2.96667 26.1633 9.58859 32.9601 17.7602 33.0079C26.197 33.0577 32.9787 26.4186 33.011 18.0787Z"
fill=""
fillOpacity="0.6"
/>
<path
d="M15.9309 18.023C13.9329 16.037 12.007 14.1207 10.0787 12.2072C9.60071 11.733 9.26398 11.2162 9.51996 10.506C9.945 9.32677 11.1954 9.0811 12.1437 10.0174C13.9067 11.7585 15.6766 13.494 17.385 15.2879C17.9108 15.8401 18.1633 15.7487 18.6375 15.258C20.3586 13.4761 22.1199 11.7327 23.8822 9.99096C24.8175 9.06632 26.1095 9.33639 26.4967 10.517C26.7286 11.2241 26.3919 11.7413 25.9133 12.2178C24.1757 13.9472 22.4477 15.6855 20.7104 17.4148C20.5228 17.6018 20.2964 17.7495 20.0466 17.9485C22.0831 19.974 24.0372 21.8992 25.9689 23.8468C26.9262 24.8119 26.6489 26.1101 25.4336 26.4987C24.712 26.7292 24.2131 26.3441 23.7455 25.8757C21.9945 24.1227 20.2232 22.3892 18.5045 20.6049C18.0698 20.1534 17.8716 20.2269 17.4802 20.6282C15.732 22.4215 13.9493 24.1807 12.1777 25.951C11.7022 26.4262 11.193 26.7471 10.4738 26.4537C9.31345 25.9798 9.06881 24.8398 9.98589 23.8952C11.285 22.5576 12.6138 21.2484 13.9387 19.9355C14.5792 19.3005 15.2399 18.6852 15.9309 18.023Z"
fill="#"
fillOpacity="0.6"
/>
</svg>
</button>
</div>
) : (
<>
<div
className={`job-action-modal-body w-full md:grid ${
taskType !== "new" ? "md:grid-cols-2" : "md:grid-cols-1"
}`}
>
<div className="p-4">
<div className="mb-2 w-full flex items-center gap-4">
<div className="flex items-center gap-2 text-sky-blue text-base">
<input
type="radio"
name="task-type"
value="select"
className="w-[20px] h-[20px] cursor-pointer"
checked={taskType == "select"}
onChange={switchTaskType}
/>
<span>Select Task</span>
{familyTask?.loading ? (
<div className="h-[100px] w-full flex justify-center items-center">
<LoadingSpinner color="sky-blue" size="16" />
</div>
) : (
<>
<div
className={`job-action-modal-body w-full md:grid ${
taskType !== "new" ? "md:grid-cols-2" : "md:grid-cols-1"
}`}
>
<div className="p-4">
<div className="mb-2 w-full flex items-center gap-4">
<div className="flex items-center gap-2 text-sky-blue text-base">
<input
type="radio"
name="task-type"
value="select"
className="w-[20px] h-[20px] cursor-pointer"
checked={taskType == "select"}
onChange={switchTaskType}
/>
<span>Select Task</span>
</div>
<div className="flex items-center gap-2 text-sky-blue text-base">
<input
type="radio"
name="task-type"
value="new"
className="w-[20px] h-[20px] cursor-pointer"
checked={taskType == "new"}
onChange={switchTaskType}
/>
<span>New Task</span>
</div>
</div>
<div className="flex items-center gap-2 text-sky-blue text-base">
<input
type="radio"
name="task-type"
value="new"
className="w-[20px] h-[20px] cursor-pointer"
checked={taskType == "new"}
onChange={switchTaskType}
/>
<span>New Task</span>
</div>
</div>
{/* Task Type === select */}
{taskType == "select" && (
<div className="p-4 w-full h-[400px] overflow-y-auto bg-slate-100">
{familyTask?.data?.length ? (
familyTask?.data?.map((item, index) => (
<div
key={item.job_uid}
className="mb-2 flex justify-start items-center gap-2 text-sky-blue text-base cursor-pointer"
onClick={() => handleActiveTask(item.job_uid, item)}
>
<input
type="radio"
name="task-list"
checked={
activeTask.id == item.job_uid ||
(activeTask.id == index && true)
}
onChange={() =>
{/* Task Type === select */}
{taskType == "select" && (
<div className="p-4 w-full h-[400px] overflow-y-auto bg-slate-100 rounded-md dark:bg-[#11131f] dark:text-white">
{familyTask?.data?.length ? (
familyTask?.data?.map((item, index) => (
<div
key={item.job_uid}
className="mb-2 flex justify-start items-center gap-2 text-sky-blue text-base cursor-pointer"
onClick={() =>
handleActiveTask(item.job_uid, item)
}
className="w-[15px] h-[15px] cursor-pointer"
/>
<p className="w-full text-dark-gray tracking-wide">
{item?.title}
>
<input
type="radio"
name="task-list"
checked={
activeTask.id == item.job_uid ||
(activeTask.id == index && true)
}
onChange={() =>
handleActiveTask(item.job_uid, item)
}
className="w-[15px] h-[15px] cursor-pointer"
/>
<p className="w-full text-dark-gray dark:text-white tracking-wide">
{item?.title}
</p>
</div>
))
) : (
<p className="p-8 text-lg text-dark-gray dark:text-white tracking-wide text-center cursor-default">
No Task found!
</p>
)}
</div>
)}
{taskType == "new" && (
<div className="p-4 w-full h-[400px]">
<NewTasks
formState={formState}
setFormState={setFormState}
/>
</div>
)}
</div>
{/*Right Hand Side for details && Task Type === select */}
{taskType == "select" && (
<>
{familyTask?.data?.length > 0 ? (
<div className="p-4">
<div className="w-full">
<p className="text-lg font-bold text-dark-gray dark:text-white tracking-wide border-b-2">
{activeTask?.data?.title}
</p>
<div className="my-3">
<Detail
label="Description"
value={activeTask?.data?.description}
/>
</div>
<div className="flex items-center">
<div className="my-3 w-full flex items-center gap-1">
<label className="text-slate-900 dark:text-white tracking-wide font-semibold">
Price
</label>
<p className="p-1 text-sm text-slate-900 dark:text-white">
{PriceFormatter(
activeTask?.data?.price * 0.01,
activeTask?.data?.currency,
activeTask?.data?.curreny_code
)}
</p>
</div>
<div className="my-3 w-full flex items-center gap-1">
<label className="text-slate-900 dark:text-white tracking-wide font-semibold">
Timeline
</label>
<p className="p-1 text-sm text-slate-900 dark:text-white">{`${activeTask?.data?.timeline_days} day(s)`}</p>
</div>
</div>
<div className="my-3 sm:flex items-center">
<Detail
label="Created"
value={`Dummy, no value found for created!`}
/>
</div>
<div className="my-3">
<label className="w-full text-slate-900 dark:text-white tracking-wide font-semibold">
Delivery Detail
</label>
<textarea
className={`p-1 w-full text-sm text-slate-900 dark:text-white bg-transparent outline-none border border-slate-300 rounded-md`}
rows="5"
style={{ resize: "none" }}
value={activeTask?.data?.job_detail}
readOnly
// onChange={handleInputChange}
/>
</div>
</div>
))
</div>
) : (
<p className="p-8 text-lg text-dark-gray dark:text-white tracking-wide text-center cursor-default">
No Task found!
</p>
<></>
)}
</div>
)}
{taskType == "new" && (
<div className="p-4 w-full h-[400px]">
<NewTasks
formState={formState}
setFormState={setFormState}
/>
</div>
</>
)}
</div>
{/*Right Hand Side for details && Task Type === select */}
{taskType == "select" && (
<>
{familyTask?.data?.length > 0 ? (
<div className="p-4">
<div className="w-full">
<p className="text-lg font-bold text-dark-gray dark:text-white tracking-wide border-b-2">
{activeTask?.data?.title}
</p>
<div className="my-3">
<Detail
label="Description"
value={activeTask?.data?.description}
/>
</div>
<div className="flex items-center">
<div className="my-3 w-full flex items-center gap-1">
<label className="text-slate-900 dark:text-white tracking-wide font-semibold">
Price
</label>
<p className="p-1 text-sm text-slate-900 dark:text-white">
{PriceFormatter(
activeTask?.data?.price * 0.01,
activeTask?.data?.currency,
activeTask?.data?.curreny_code
)}
</p>
</div>
<div className="my-3 w-full flex items-center gap-1">
<label className="text-slate-900 dark:text-white tracking-wide font-semibold">
Timeline
</label>
<p className="p-1 text-sm text-slate-900 dark:text-white">{`${activeTask?.data?.timeline_days} day(s)`}</p>
</div>
</div>
<div className="my-3 sm:flex items-center">
<Detail
label="Created"
value={`Dummy, no value found for created!`}
/>
</div>
<div className="my-3">
<label className="w-full text-slate-900 dark:text-white tracking-wide font-semibold">
Delivery Detail
</label>
<textarea
className={`p-1 w-full text-sm text-slate-900 outline-none border border-slate-300 rounded-md`}
rows="5"
style={{ resize: "none" }}
value={activeTask?.data?.job_detail}
readOnly
// onChange={handleInputChange}
/>
</div>
</div>
</div>
) : (
<></>
)}
</>
)}
</div>
{/* BTN */}
<div className="py-2 px-4 border-t-2 flex justify-between items-center">
{/* error or success display */}
<div className="w-auto h-auto flex items-center">
{requestStatus.message != "" &&
(!requestStatus.status ? (
<div
className={`relative p-2 text-[#912741] bg-[#fcd9e2] border-[#fbc6d3] rounded-[0.475rem] text-md font-light leading-[19.5px] text-[13px] self-start`}
>
{requestStatus.message}
</div>
) : (
requestStatus.status && (
{/* BTN */}
<div className="py-2 px-4 border-t-2 flex justify-between items-center">
{/* error or success display */}
<div className="w-auto h-auto flex items-center">
{requestStatus.message != "" &&
(!requestStatus.status ? (
<div
className={`relative p-2 text-green-700 bg-slate-200 border-slate-800 mb-4 rounded-[0.475rem] text-md font-light leading-[19.5px] text-[13px]`}
className={`relative p-2 text-[#912741] bg-[#fcd9e2] border-[#fbc6d3] rounded-[0.475rem] text-md font-light leading-[19.5px] text-[13px] self-start`}
>
{requestStatus.message}
</div>
)
))}
</div>
) : (
requestStatus.status && (
<div
className={`relative p-2 text-green-700 bg-slate-200 border-slate-800 mb-4 rounded-[0.475rem] text-md font-light leading-[19.5px] text-[13px]`}
>
{requestStatus.message}
</div>
)
))}
</div>
{/* End of error or success display */}
<div className="w-auto h-auto flex items-center gap-3">
<button
disabled={requestStatus.loading}
onClick={action}
type="button"
className="w-20 h-11 flex justify-center items-center border-gradient text-base rounded-full text-white cursor-pointer"
>
<span className="text-gradient">Close</span>
</button>
<div className="">
{requestStatus.loading ? (
<LoadingSpinner color="sky-blue" size="8" />
) : taskType == "select" ? (
<button
type="button"
disabled={requestStatus.loading}
onClick={assignFamilyTask}
className="px-1 w-20 h-11 flex justify-center items-center btn-gradient text-base rounded-full text-white cursor-pointer"
>
Assign
</button>
) : (
<button
type="button"
disabled={requestStatus.loading}
onClick={assignFamilyTask}
className="px-1 w-40 h-11 flex justify-center items-center btn-gradient text-base rounded-full text-white cursor-pointer"
>
{`Assign to ${
familyDetails?.firstname || details?.firstName
}`}
</button>
)}
{/* End of error or success display */}
<div className="w-auto h-auto flex items-center gap-3">
<button
disabled={requestStatus.loading}
onClick={action}
type="button"
className="w-20 h-11 flex justify-center items-center border-gradient text-base rounded-full text-white cursor-pointer"
>
<span className="text-gradient">Close</span>
</button>
<div className="">
{requestStatus.loading ? (
<LoadingSpinner color="sky-blue" size="8" />
) : taskType == "select" ? (
<button
type="button"
disabled={requestStatus.loading}
onClick={assignFamilyTask}
className="px-1 w-20 h-11 flex justify-center items-center btn-gradient text-base rounded-full text-white cursor-pointer"
>
Assign
</button>
) : (
<button
type="button"
disabled={requestStatus.loading}
onClick={assignFamilyTask}
className="px-1 w-40 h-11 flex justify-center items-center btn-gradient text-base rounded-full text-white cursor-pointer"
>
{`Assign to ${
familyDetailsData?.firstname || details?.firstName
}`}
</button>
)}
</div>
</div>
</div>
</div>
</>
)}
</div>
</ModalCom>
</>
);
})
</>
)}
</div>
</ModalCom>
</>
);
}
);
export default AssignTaskPopout;
export default AssignTaskPopout;
@@ -69,7 +69,7 @@ export default function NewTasks({ formState, setFormState }) {
onChange={handleInputChange}
// onBlur={props.handleBlur}
>
{currency.loading ? (
{currency?.loading ? (
<option className="text-slate-500 text-[13.975px]" value="">
Loading...
</option>
@@ -0,0 +1,9 @@
import React from 'react'
const AddFamily = () => {
return (
<div>Add Family</div>
)
}
export default AddFamily
@@ -0,0 +1,154 @@
import React, { useMemo, useRef, useState } from "react";
import usersService from "../../../../services/UsersService";
import { apiConst } from "../../../../lib/apiConst";
const FamilyBanner = ({ imageServer }) => {
const uploadedImage = `${imageServer}${localStorage.getItem(
"session_token"
)}/familybanner/${localStorage.getItem("uid")}`;
const familyBannerRef = useRef(null);
const jobApi = useMemo(() => new usersService(), []);
const [familyBannerImage, setFamilyBannerImage] = useState(uploadedImage);
const [uploadStatus, setUploadStatus] = useState({
loading: false,
status: false,
message: "",
});
/**
* Handles the change event of the family image input field.
* Checks if the selected file exceeds the maximum file size limit and displays an alert if it does.
* If the file is within the size limit, it reads the file using the FileReader API and sets the profile image state with the result.
*/
const familyBannerHandler = (e) => {
setUploadStatus({ loading: false, status: false, message: "" });
let acceptedFormat = ["jpeg", "jpg", "png", "bmp"]; // ARRAY OF SUPPORTED FORMATS
let uploadedFile = e.target.files[0]; //UPLOADED FILE
const fileFormat = uploadedFile?.type?.split("/")[1]?.toLowerCase();
if (!acceptedFormat.includes(fileFormat)) {
//CHECKING FOR CORRECT UPLOAD FORMAT
const msg = `Please select ${acceptedFormat
.slice(0, -1)
.join(", ")} or ${acceptedFormat.slice(-1)}`;
setUploadStatus({ loading: false, status: false, message: msg });
return setTimeout(() => {
// profileImgInput.current.value = '' // clear the input
setUploadStatus({ loading: false, status: false, message: "" });
}, 5000);
}
if (uploadedFile.size > 5 * 1048576) {
// CHECKING FOR CORRECT FILE SIZE
setUploadStatus({
loading: false,
status: false,
message: "File must not exceed 5MB",
});
return setTimeout(() => {
// profileImgInput.current.value = '' // clear the input
setUploadStatus({ loading: false, status: false, message: "" });
}, 5000);
}
if (e.target.value !== "") {
const imgReader = new FileReader();
imgReader.onload = (event) => {
let base64Img = imgReader.result.split(",")[1];
let reqData = {
// PAYLOAD FOR API CALL
job_uid: localStorage.getItem("uid"),
file_name: uploadedFile?.name.slice(0, 19),
file_size: uploadedFile?.size,
file_type: uploadedFile?.type?.split("/")[0]?.toLowerCase(),
file_data: base64Img,
msg_type: "FILE",
action: apiConst.WRENCHBOARD_PICTURE_JOB,
};
setUploadStatus({
loading: true,
status: false,
message: "Loading...",
});
jobApi
.sendFiles(reqData)
.then((res) => {
if (res.status != 200 || res.data.internal_return < 0) {
return setUploadStatus({
loading: false,
status: false,
message: "Something went wrong, try again",
});
}
setUploadStatus({
loading: false,
status: true,
message: "Uploaded successfully",
});
setFamilyBannerImage(event.target.result);
})
.catch((error) => {
setUploadStatus({
loading: false,
status: false,
message: "Network error, try again",
});
})
.finally(() => {
setTimeout(() => {
setUploadStatus({ loading: false, status: false, message: "" });
}, 5000);
});
};
imgReader.readAsDataURL(e.target.files[0]);
}
};
return (
<div className="flex flex-col gap-4">
<div className="w-full lg:h-[400px] rounded-2xl">
<img
src={familyBannerImage}
alt="familyBanner"
className="w-full h-full object-cover rounded-2xl"
/>
</div>
<div className="flex justify-between w-full">
<div>
{uploadStatus.message && !uploadStatus.loading && (
<p
className={`text-center ${
uploadStatus.status ? "text-green-500" : "text-red-500"
}`}
>
{uploadStatus.message}
</p>
)}
{uploadStatus.loading && (
<p className="text-center">{uploadStatus.message}</p>
)}
</div>
<div>
<input
ref={familyBannerRef}
className="hidden"
type="file"
accept="image/*"
onChange={familyBannerHandler}
/>
<button
className="btn-gradient w-[153px] h-[46px] rounded-full text-white lg:flex hidden justify-center items-center"
onClick={() => familyBannerRef.current.click()}
>
Change Banner
</button>
</div>
</div>
</div>
);
};
export default FamilyBanner;
@@ -0,0 +1,275 @@
import React, { useState } from 'react'
import ModalCom from '../../../Helpers/ModalCom';
import LoadingSpinner from '../../../Spinners/LoadingSpinner';
import InputCom from '../../../Helpers/Inputs/InputCom/index';
import { Formik, Form } from 'formik';
import * as Yup from "yup";
import usersService from '../../../../services/UsersService';
const validationSchema = Yup.object().shape({
email: Yup.string()
.email("Wrong email format")
.matches(
/^[^0-9][a-zA-Z0-9._%+-]+@[a-zA-Z]+(\.[a-zA-Z]+)+$/,
"Invalid email format"
)
.min(3, "Minimum 3 characters")
.max(50, "Maximum 50 characters")
.required("Email is required"),
firstname: Yup.string()
.min(3, "Minimum 3 characters")
.max(25, "Maximum 25 characters")
.required("Firstname is required"),
lastname: Yup.string()
.min(3, "Minimum 3 characters")
.max(25, "Maximum 25 characters")
.required("Lastname is required"),
family_type: Yup.string()
.min(3, "Minimum 3 characters")
.max(25, "Maximum 25 characters")
.required("Family Type is required"),
});
const initialValues = {
firstname: '',
lastname: '',
family_type: '',
email: ''
};
export default function InviteRelative({action, situation, setReloadRelList, relativeList}) {
const api = new usersService()
let [requestStatus, setRequestStatus] = useState({
loading: false,
status: false,
message: "",
}); // STATE FOR KNOWING WHEN A REQUEST IS MADE TO THE SERVER
const handleInvite = (values) => {
setRequestStatus({loading: true, status: false, message: ""})
api.inviteFamilyRelative(values).then(response => {
let {status, data} = response
if(data?.internal_return < 0){
setRequestStatus({loading: false, status: false, message: "Unable to complete"})
return setTimeout(()=>{
setRequestStatus({ loading: false, status: false, message: ""})
},3000)
}
setRequestStatus({loading: false, status: true, message: "Relative Added"})
//RELOAD RELATIVE LSIT TABLE
setReloadRelList(prev => !prev)
setTimeout(()=>{
action()
},3000)
}).catch(error => {
setRequestStatus({loading: false, status: false, message: "Something went wrong, try again!"})
setTimeout(()=>{
setRequestStatus({ loading: false, status: false, message: ""})
},3000)
})
}
return (
<ModalCom
action={action}
situation={situation}
>
<div className="lg:w-[600px] w-11/12 lg:overflow-hidden lg:rounded-2xl bg-white dark:bg-dark-white dark:text-white">
<div className="logout-modal-header w-full flex items-center justify-between lg:px-10 lg:py-8 px-[30px] py-[23px] border-b border-light-purple dark:border-[#5356fb29] ">
<h1 className="text-26 font-bold text-dark-gray dark:text-white tracking-wide">
Invite Parent/Relative
</h1>
<button
type="button"
className="text-[#374557] dark:text-red-500"
onClick={action}
>
<svg
width="36"
height="36"
viewBox="0 0 36 36"
fill="none"
className="fill-current"
xmlns="http://www.w3.org/2000/svg"
>
<path
d="M36 16.16C36 17.4399 36 18.7199 36 20.0001C35.7911 20.0709 35.8636 20.2554 35.8385 20.4001C34.5321 27.9453 30.246 32.9248 22.9603 35.2822C21.9006 35.6251 20.7753 35.7657 19.6802 35.9997C18.4003 35.9997 17.1204 35.9997 15.8401 35.9997C15.5896 35.7086 15.2189 35.7732 14.9034 35.7093C7.77231 34.2621 3.08728 30.0725 0.769671 23.187C0.435002 22.1926 0.445997 21.1199 0 20.1599C0 18.7198 0 17.2798 0 15.8398C0.291376 15.6195 0.214408 15.2656 0.270759 14.9808C1.71321 7.69774 6.02611 2.99691 13.0428 0.700951C14.0118 0.383805 15.0509 0.386897 15.9999 0C17.2265 0 18.4532 0 19.6799 0C19.7156 0.124041 19.8125 0.136067 19.9225 0.146719C27.3 0.868973 33.5322 6.21922 35.3801 13.427C35.6121 14.3313 35.7945 15.2484 36 16.16ZM33.011 18.0787C33.0433 9.77105 26.3423 3.00309 18.077 2.9945C9.78479 2.98626 3.00344 9.658 2.98523 17.8426C2.96667 26.1633 9.58859 32.9601 17.7602 33.0079C26.197 33.0577 32.9787 26.4186 33.011 18.0787Z"
fill=""
fillOpacity="0.6"
/>
<path
d="M15.9309 18.023C13.9329 16.037 12.007 14.1207 10.0787 12.2072C9.60071 11.733 9.26398 11.2162 9.51996 10.506C9.945 9.32677 11.1954 9.0811 12.1437 10.0174C13.9067 11.7585 15.6766 13.494 17.385 15.2879C17.9108 15.8401 18.1633 15.7487 18.6375 15.258C20.3586 13.4761 22.1199 11.7327 23.8822 9.99096C24.8175 9.06632 26.1095 9.33639 26.4967 10.517C26.7286 11.2241 26.3919 11.7413 25.9133 12.2178C24.1757 13.9472 22.4477 15.6855 20.7104 17.4148C20.5228 17.6018 20.2964 17.7495 20.0466 17.9485C22.0831 19.974 24.0372 21.8992 25.9689 23.8468C26.9262 24.8119 26.6489 26.1101 25.4336 26.4987C24.712 26.7292 24.2131 26.3441 23.7455 25.8757C21.9945 24.1227 20.2232 22.3892 18.5045 20.6049C18.0698 20.1534 17.8716 20.2269 17.4802 20.6282C15.732 22.4215 13.9493 24.1807 12.1777 25.951C11.7022 26.4262 11.193 26.7471 10.4738 26.4537C9.31345 25.9798 9.06881 24.8398 9.98589 23.8952C11.285 22.5576 12.6138 21.2484 13.9387 19.9355C14.5792 19.3005 15.2399 18.6852 15.9309 18.023Z"
fill="#"
fillOpacity="0.6"
/>
</svg>
</button>
</div>
<div className="logout-modal-body w-full flex flex-col items-center px-10 py-8">
<div className="personal-info-tab w-full flex flex-col justify-between">
<Formik
initialValues={initialValues}
validationSchema={validationSchema}
onSubmit={handleInvite}
>
{(props) => {
return (
<Form>
<div className="w-full flex flex-col-reverse sm:flex-row">
<div className="fields w-full">
{/* inputs starts here */}
{/* Email */}
<div className="field w-full mb-6">
<InputCom
fieldClass="px-6"
label="Email"
type="text"
name="email"
placeholder="Email"
value={props.values.email}
inputHandler={props.handleChange}
blurHandler={props.handleBlur}
error={(props.errors.email && props.touched.email) ? props.errors.email : '' }
/>
</div>
{/* first name and last name */}
<div className="md:flex md:space-x-7 mb-6">
<div className="field w-full mb-6 md:mb-0">
<InputCom
fieldClass="px-6"
label="First Name"
type="text"
name="firstname"
placeholder="First Name"
value={props.values.firstname}
inputHandler={props.handleChange}
blurHandler={props.handleBlur}
error={(props.errors.firstname && props.touched.firstname) ? props.errors.firstname : '' }
/>
</div>
<div className="field w-full">
<InputCom
fieldClass="px-6"
label="Last Name"
type="text"
name="lastname"
placeholder="Last Name"
value={props.values.lastname}
inputHandler={props.handleChange}
blurHandler={props.handleBlur}
error={(props.errors.lastname && props.touched.lastname) ? props.errors.lastname : '' }
/>
</div>
</div>
{/* Type */}
<div className="md:flex md:space-x-7 items-end mb-6">
{/* <div className="field w-full mb-6 md:mb-0">
<InputCom
fieldClass="px-6"
label="Type"
type="text"
name="family_type"
placeholder="Family Type"
value={props.values.family_type}
inputHandler={props.handleChange}
blurHandler={props.handleBlur}
error={(props.errors.family_type && props.touched.family_type) ? props.errors.family_type : '' }
/>
</div> */}
<div className="field w-full mb-6 xl:mb-0">
<label
htmlFor="family_type"
className="input-label text-[#181c32] dark:text-white text-[13.975px] leading-[20.9625px] font-semibold flex item-center gap-1"
>
Family Type
{props.errors.family_type && props.touched.family_type && (
<span className="text-[12px] text-red-500">
{props.errors.family_type}
</span>
)}
</label>
<select
id="family_type"
name="family_type"
value={props.values.family_type}
className={`input-field p-2 mt-3 rounded-full placeholder:text-base text-dark-gray dark:text-white w-full h-10 bg-slate-100 dark:bg-[#11131F] focus:ring-0 focus:outline-none border`}
onChange={props.handleChange}
onBlur={props.handleBlur}
>
{relativeList?.loading ? (
<option className="text-slate-500 text-lg" value="">
Loading...
</option>
) : relativeList?.family_types?.length ? (
<>
<option className="text-slate-500 text-lg" value="">
Select Family Type
</option>
{relativeList?.family_types?.map((item, index) => (
<option
key={index}
className="text-slate-500 text-lg"
value={item?.ty}
>
{item?.ty}
</option>
))}
</>
) : (
<option className="text-slate-500 text-lg" value="">
No Options Found! Try Again
</option>
)}
</select>
</div>
<div className="field w-full flex justify-end">
<div className="flex">
{requestStatus.loading ? (
<LoadingSpinner size="8" color="sky-blue" />
) : (
<button
type="submit"
className={`text-white btn-gradient text-lg tracking-wide px-5 py-2 rounded-full ${(requestStatus.status || requestStatus.loading) && 'opacity-50'}`}
disabled={requestStatus.status || requestStatus.loading}
>
Send Message
</button>
)}
</div>
</div>
</div>
</div>
</div>
</Form>
);
}}
</Formik>
</div>
{/* ERROR DISPLAY AND SUBMIT BUTTON */}
{requestStatus.message != "" &&
(!requestStatus.status ? (
<div
className={`w-full relative p-4 my-4 text-[#912741] bg-[#fcd9e2] border-[#fbc6d3] mb-4 rounded-[0.475rem] text-md font-light leading-[19.5px] text-[13px]`}
>
{requestStatus.message}
</div>
) : (
requestStatus.status && (
<div
className={`relative p-4 my-4 text-green-700 bg-slate-200 border-slate-800 mb-4 rounded-[0.475rem] text-md font-light leading-[19.5px] text-[13px]`}
>
{requestStatus.message}
</div>
)
))}
{/* End of error or success display */}
</div>
</div>
</ModalCom>
)
}
@@ -0,0 +1,73 @@
import React, { useState } from 'react'
import { handlePagingFunc } from '../../../Pagination/HandlePagination';
import PaginatedList from '../../../Pagination/PaginatedList';
export default function RelativeTable({relativeList}) {
// Handle Pagination
const [currentPage, setCurrentPage] = useState(0);
const indexOfFirstItem = Number(currentPage);
const indexOfLastItem =Number(indexOfFirstItem) + Number(process.env.REACT_APP_ITEM_PER_PAGE);
const currentRelativeList = relativeList?.slice(indexOfFirstItem, indexOfLastItem);
const handlePagination = (e) => {
handlePagingFunc(e, setCurrentPage);
};
return (
<div className={`w-full overflow-hidden rounded-2xl`}>
<div className="relative w-full overflow-x-auto sm:rounded-lg flex flex-col justify-between min-h-[400px]">
<table className="w-full text-sm text-left text-gray-500 dark:text-gray-400">
<tbody>
<>
{relativeList && relativeList?.length > 0 ? (
currentRelativeList.map((value, index) => (
<tr key={value.uid || index} className="border-b dark:border-[#5356fb29] hover:bg-gray-50">
<td className='p-2'>
<div className="flex flex-col">
<h1 className="font-bold text-xl text-dark-gray dark:text-white">
{value.firstname && value.firstname} {value.lastname && value.lastname}
</h1>
<span className="text-sm text-thin-light-gray">
{value.email && value.email}
</span>
</div>
</td>
<td className='p-2'>
{/* <span>Family Type</span> */}
<span>{value.family_type && value.family_type.toUpperCase()}</span>
</td>
<td className='p-2 text-right'>
{value.status && value.status}
</td>
</tr>
))
) : (
<tr className="font-bold text-xl text-dark-gray dark:text-white whitespace-nowrap">
<td className="p-2">No Members Found</td>
</tr>
)}
</>
</tbody>
</table>
{/* PAGINATION BUTTON */}
<PaginatedList
onClick={handlePagination}
prev={currentPage == 0 ? true : false}
next={
currentPage + Number(process.env.REACT_APP_ITEM_PER_PAGE) >=
relativeList?.length
? true
: false
}
data={relativeList}
start={indexOfFirstItem}
stop={indexOfLastItem}
/>
{/* END OF PAGINATION BUTTON */}
</div>
</div>
);
};
@@ -0,0 +1,69 @@
import React, { useEffect, useState } from 'react'
import RelativeTable from './RelativeTable'
import InviteRelative from './InviteRelative'
import usersService from '../../../../services/UsersService'
import LoadingSpinner from '../../../Spinners/LoadingSpinner'
const Relatives = () => {
const api = new usersService()
const [reloadRelList, setReloadRelList] = useState(false)
const [invitePopout, setInvitePopout] = useState(false)
const [relativeList, setRelativeList] = useState({loading: true, result_list:[], family_types:[]})
const showInviteMemberPopout = () => {
setInvitePopout(true)
}
const getRelativeList = () => {
setRelativeList(prev => ({...prev, loading: true}))
api.getFamilyRelativeList().then(response => {
let {status, data} = response
if(status != 200 || !data){
setRelativeList({loading:false, result_list:[], family_types:[]})
return
}
setRelativeList({loading:false, result_list:data?.result_list, family_types:data?.family_types})
}).catch(error => {
setRelativeList({loading:false, result_list:[], family_types:[]})
})
}
useEffect(()=>{
getRelativeList()
},[reloadRelList])
return (
<>
<div className=''>
<div className='py-4'>
<button onClick={showInviteMemberPopout} className='text-white btn-gradient text-lg tracking-wide px-5 py-2 rounded-full'>Invite</button>
<div className='my-4 border-b-2'></div>
</div>
<div className='min-h-[500px]'>
{relativeList.loading ?
<LoadingSpinner size='8' height='h-full' />
:
<RelativeTable relativeList={relativeList.result_list} />
}
</div>
</div>
{/* INVITE RELATIVE POPOUT */}
{invitePopout &&
<InviteRelative
action={()=>setInvitePopout(false)}
situation={invitePopout}
setReloadRelList={setReloadRelList}
relativeList={relativeList}
/>
}
{/* END OF INVITE RELATIVE POPOUT */}
</>
)
}
export default Relatives
@@ -0,0 +1,5 @@
import AddFamily from "./AddFamily";
import FamilyBanner from "./FamilyBanner";
import Relatives from "./Relatives";
export {AddFamily, FamilyBanner, Relatives}
@@ -0,0 +1,107 @@
import React, { useEffect, useState } from "react";
import { Link, useLocation } from "react-router-dom";
import Icons from "../../Helpers/Icons";
import Layout from "../../Partials/Layout";
import { AddFamily, FamilyBanner, Relatives } from "./Tabs";
const FamilySettings = () => {
let {state} = useLocation()
const tabs = [
{
id: 1,
name: "add_family",
title: "Add Family",
iconName: "new-family",
},
{
id: 2,
name: "relatives",
title: "Relatives",
iconName: "people-hover",
},
{
id: 3,
name: "family_banner",
title: "Family Banner",
iconName: "people-hover",
},
];
const [tab, setTab] = useState(() => {
// Retrieve the active tab from local storage or use the default tab
return localStorage.getItem("activeTab") || tabs[0].name;
});
const tabHandler = (value) => {
setTab(value);
};
// Update local storage when the tab changes
useEffect(() => {
localStorage.setItem("activeTab", tab);
}, [tab]);
const tabComponents = {
add_family: <AddFamily />,
relatives: <Relatives />,
family_banner: <FamilyBanner imageServer={state.imageServer} />,
};
const defaultTabComponent = Array(tabComponents)[0].add_family;
const selectedComponent = tabComponents[tab] || defaultTabComponent;
return (
<Layout>
<div className="notification-page w-full mb-10">
<div className="notification-wrapper w-full">
{/* heading */}
<div className="sm:flex justify-between items-center mb-6">
<div className="mb-5 sm:mb-0">
<h1 className="text-26 font-bold inline-flex gap-3 text-dark-gray dark:text-white items-center">
<span className={``}>Family Settings</span>
</h1>
</div>
<Link to="/acc-family" className="flex gap-2 items-center text-dark-gray dark:text-white">
<svg className="w-5 h-5 rtl:rotate-180" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" strokeWidth="1.5" stroke="currentColor">
<path strokeLinecap="round" strokeLinejoin="round" d="M6.75 15.75L3 12m0 0l3.75-3.75M3 12h18" />
</svg>
Family
</Link>
</div>
<div className="w-full bg-white dark:bg-dark-white overflow-y-auto rounded-2xl section-shadow h-full ">
<div className="update-table w-full h-full p-4 bg-white dark:bg-dark-white overflow-y-auto rounded-2xl section-shadow min-h-[520px] lg:flex lg:px-10 px-4 justify-between">
<div className="content-tab-items lg:w-[230px] w-full mr-2">
<ul className="overflow-hidden mb-10 lg:mb-0 py-8">
{tabs.map(({ name, id, title, iconName }) => (
<li
onClick={() => tabHandler(name)}
key={id}
className={`flex lg:space-x-4 space-x-2 hover:text-purple transition-all duration-300 ease-in-out items-center cursor-pointer lg:mb-11 mb-2 mr-6 lg:mr-0 float-left lg:float-none overflow-hidden ${
tab === name ? "text-purple" : " text-thin-light-gray"
}`}
>
<div>
<Icons name={iconName} />
</div>
<div>
<p className="text-18 tracking-wide">{title}</p>
</div>
</li>
))}
</ul>
</div>
<div className="w-[1px] bg-[#E3E4FE] dark:bg-[#a7a9b533] mr-10"></div>
<div className="flex-1">
<div className="tab-item">{selectedComponent}</div>
</div>
</div>
</div>
</div>
</div>
</Layout>
);
};
export default FamilySettings;
+207 -124
View File
@@ -1,29 +1,179 @@
import React, { useState } from "react";
import dataImage1 from "../../assets/images/data-table-user-1.png";
import LoadingSpinner from "../Spinners/LoadingSpinner";
import { useNavigate } from "react-router-dom";
import { handlePagingFunc } from "../Pagination/HandlePagination";
import PaginatedList from "../Pagination/PaginatedList";
import LoadingSpinner from "../Spinners/LoadingSpinner";
import familyImage from '../../assets/images/no-family-side.png'
import familyImage from "../../assets/images/no-family-side.png";
import { formatDateString } from "../../lib";
import localImgLoad from "../../lib/localImgLoad";
export default function FamilyTable({ className, familyList, loader, popUpHandler }) {
const navigate = useNavigate();
/**
* Renders a list of family members that can be managed.
* It has its current maximum members at 8 and it comes with pagination and loading spinner functionality.
* @returns {JSX.Element} - The rendered component.
*/
export default function FamilyTable({
className,
familyList,
loader,
popUpHandler,
imageServer,
}) {
const navigate = useNavigate();
const [currentPage, setCurrentPage] = useState(0);
const itemsPerPage = Number(process.env.REACT_APP_ITEM_PER_PAGE);
const indexOfFirstItem = Number(currentPage);
const indexOfLastItem =
Number(indexOfFirstItem) + Number(process.env.REACT_APP_ITEM_PER_PAGE);
const currentFamilyList = familyList?.slice(indexOfFirstItem, indexOfLastItem);
const currentFamilyList = familyList?.slice(
indexOfFirstItem,
indexOfLastItem
);
const handleManageClick = (familyMember) => {
navigate("/manage-family", { state: familyMember });
};
const handlePagination = (e) => {
handlePagingFunc(e, setCurrentPage);
};
/**
* Renders a table row for a family member.
* @returns {JSX.Element} - The table row component.
*/
const FamilyRow = ({
firstname,
lastname,
age,
added,
last_login,
task_count,
family_uid,
banner,
enable_traking,
profile_picture,
imageServer,
username,
}) => {
// Check for valid dates
const addedDate = added ? added.split(" ")[0] : "N/A";
const loginDate = last_login ? formatDateString(last_login) : "N/A";
const key = `family-${family_uid}`; // Assign a unique key
const image = localStorage.getItem("session_token")
? `${imageServer}${localStorage.getItem(
"session_token"
)}/family/${family_uid}`
: "";
const trackingStatus =
enable_traking === "0"
? "Stopped"
: enable_traking === "100"
? "Active"
: "";
return (
<tr
className="bg-white dark:bg-dark-white border-b dark:border-[#5356fb29] hover:bg-gray-50"
key={key}
>
<td className="py-4">
<div className="flex space-x-2 items-center w-full">
<div className="w-[60px] h-[60px] rounded-full overflow-hidden flex justify-center items-center">
<img
// src={profile_picture}
src={
image ||
profile_picture ||
localImgLoad(`images/icons/${banner}`)
}
alt={`Avatar of ${firstname} ${lastname}`}
className="w-full h-full"
/>
</div>
<div className="flex flex-col flex-[0.9]">
<h1 className="font-bold text-lg text-dark-gray dark:text-white whitespace-nowrap">
{`${firstname} ${lastname}`}{" "}
<span className="ml-1 text-sm">{`[${username}]`}</span>
</h1>
<span className="text-sm text-thin-light-gray">
Added: <span className="text-purple ml-1">{addedDate}</span>
</span>
<span className="text-sm text-thin-light-gray">
Last Login:{" "}
<span className="text-purple ml-1">{loginDate}</span>
</span>
</div>
</div>
</td>
<td className="text-center py-4 px-2">
<div className="flex space-x-1 items-center justify-center">
<span
className={`text-base font-medium whitespace-nowrap ${
enable_traking === "0"
? "text-[#FF0000] dark:text-[#FF6666]"
: enable_traking === "100"
? "text-[#00A000] dark:text-[#00FF00]"
: "text-dark-gray dark:text-white"
}`}
>
{trackingStatus}
</span>
</div>
</td>
<td className="text-center py-4 px-2">
<div className="flex space-x-1 items-center justify-center">
<span className="text-base text-dark-gray dark:text-white font-medium whitespace-nowrap">
{task_count}
</span>
</div>
</td>
<td className="text-right py-4 px-2 flex items-center justify-center">
<button
onClick={() =>
handleManageClick({
firstname,
lastname,
age,
added,
last_login,
task_count,
family_uid,
banner,
image,
})
}
type="button"
className="w-12 h-11 flex justify-center items-center btn-gradient text-base rounded-full text-white"
>
<svg
xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 11 20"
id="Arrow"
className="w-[0.7rem]"
>
<path
fillRule="evenodd"
d="M.366 19.708c.405.39 1.06.39 1.464 0l8.563-8.264a1.95 1.95 0 0 0 0-2.827L1.768.292A1.063 1.063 0 0 0 .314.282a.976.976 0 0 0-.011 1.425l7.894 7.617a.975.975 0 0 1 0 1.414L.366 18.295a.974.974 0 0 0 0 1.413"
// fill=""
className="color000000 svgShape fill-[#fff]"
></path>
</svg>
</button>
</td>
</tr>
);
};
return (
<div
className={`update-table w-full h-full p-8 bg-white dark:bg-dark-white overflow-y-auto rounded-2xl section-shadow min-h-[520px] ${
className={`update-table w-full h-full p-4 bg-white dark:bg-dark-white overflow-y-auto rounded-2xl section-shadow min-h-[520px] flex flex-col justify-between ${
className || ""
}`}
>
@@ -32,125 +182,58 @@ export default function FamilyTable({ className, familyList, loader, popUpHandle
<div className="h-full min-h-[500px] w-full overflow-hidden flex justify-center items-center">
<LoadingSpinner size="16" color="sky-blue" />
</div>
)
:
familyList?.length > 0 ?
(
<table className="w-full text-sm text-left text-gray-500 dark:text-gray-400 relative">
<thead className="sticky top-0">
<tr className="text-base text-thin-light-gray whitespace-nowrap border-b dark:border-[#5356fb29] default-border-bottom ">
<th className="py-4">Name</th>
<th className="py-4 text-center">Last Login</th>
<th className="py-4 text-center">No of Tasks</th>
<th className="py-4 text-right"></th>
</tr>
</thead>
<tbody className="h-full">
{currentFamilyList?.map((props, idx) => {
let {
firstname,
lastname,
age,
added,
last_login,
task_count,
family_uid,
banner
} = props;
let addedDate = added?.split(" ")[0];
let LoginDate = last_login?.split(" ")[0];
return (
<tr
className="bg-white dark:bg-dark-white border-b dark:border-[#5356fb29] hover:bg-gray-50"
key={family_uid}
>
<td className=" py-4">
<div className="flex space-x-2 items-center w-full">
<div className="w-[60px] h-[60px] rounded-full overflow-hidden flex justify-center items-center flex-[0.1]">
<img
// src={dataImage1}
src={localImgLoad(`images/icons/${banner}`)}
alt="data"
className="w-full h-full"
/>
</div>
<div className="flex flex-col flex-[0.9]">
<h1 className="font-bold text-xl text-dark-gray dark:text-white whitespace-nowrap">
{`${firstname} ${lastname} (${age})`}
</h1>
<span className="text-sm text-thin-light-gray">
Added:{" "}
<span className="text-purple ml-1">
{addedDate}
</span>
</span>
</div>
</div>
</td>
<td className="text-center py-4 px-2">
<div className="flex space-x-1 items-center justify-center">
<span className="text-base text-dark-gray dark:text-white font-medium whitespace-nowrap">
{LoginDate}
</span>
</div>
</td>
<td className="text-center py-4 px-2">
<div className="flex space-x-1 items-center justify-center">
<span className="text-base text-dark-gray dark:text-white font-medium whitespace-nowrap">
{task_count}
</span>
</div>
</td>
<td className="text-right py-4 px-2 flex items-center justify-center">
<button
onClick={() => {
navigate("/manage-family", {
state: { ...props },
});
}}
type="button"
className="w-20 h-11 flex justify-center items-center btn-gradient text-base rounded-full text-white"
>
Manage
</button>
</td>
</tr>
);
})
}
</tbody>
</table>
)
:
(
<div className="font-bold text-center text-xl md:text-2xl lg:text-4xl text-dark-gray md:flex items-center justify-between">
<div className="p-2 w-full md:w-1/2">
<p className="mb-4 p-3 md:p-16">Add your family, assign tasks, and get the whole team engaged.</p>
<button
onClick={popUpHandler}
type="button"
className="text-white btn-gradient text-lg tracking-wide px-5 py-2 rounded-full"
>
Add Family
</button>
</div>
<div className="p-2 w-full md:w-1/2">
<img className='w-full' src={familyImage} alt="Add Family" />
</div>
</div>
)
}
) : (
<>
{familyList?.length > 0 ? (
<table className="w-full text-sm text-left text-gray-500 dark:text-gray-400 relative">
<thead className="sticky top-0">
<tr className="text-base text-thin-light-gray whitespace-nowrap border-b dark:border-[#5356fb29] default-border-bottom ">
<th className="py-4">Name</th>
<th className="py-4 text-center">Tracking</th>
<th className="py-4 text-center">No of Tasks</th>
<th className="py-4 text-right"></th>
</tr>
</thead>
<tbody className="h-full">
{currentFamilyList?.map((familyMember, index) => {
return (
<FamilyRow
key={index}
{...familyMember}
imageServer={imageServer}
/>
);
})}
</tbody>
</table>
) : (
<div className="font-bold text-center text-xl md:text-2xl lg:text-4xl text-dark-gray md:flex items-center justify-between">
<div className="p-2 w-full md:w-1/2">
<p className="mb-4 p-3 md:p-16">
Add your family, assign tasks, and get the whole team
engaged.
</p>
<button
onClick={popUpHandler}
type="button"
className="text-white btn-gradient text-lg tracking-wide px-5 py-2 rounded-full"
>
Add Family
</button>
</div>
<div className="p-2 w-full md:w-1/2">
<img className="w-full" src={familyImage} alt="Add Family" />
</div>
</div>
)}
</>
)}
</div>
{/* PAGINATION BUTTON */}
<PaginatedList
{/* PAGINATION BUTTON */}
<PaginatedList
onClick={handlePagination}
prev={currentPage == 0 ? true : false}
next={
currentPage + Number(process.env.REACT_APP_ITEM_PER_PAGE) >=
familyList?.length
? true
: false
}
prev={currentPage == 0}
next={currentPage + itemsPerPage >= familyList?.length}
data={familyList}
start={indexOfFirstItem}
stop={indexOfLastItem}
+314
View File
@@ -0,0 +1,314 @@
import React, {
Suspense,
lazy,
useEffect,
useMemo,
useRef,
useState,
} from "react";
import { useReactToPrint } from "react-to-print";
import localImgLoad from "../../lib/localImgLoad";
import usersService from "../../services/UsersService";
import LoadingSpinner from "../Spinners/LoadingSpinner";
import AssignTaskPopout from "./FamilyPopout/AssignTaskPopout";
import FamilyWallet from "./Tabs/FamilyWallet";
import { apiConst } from "../../lib/apiConst";
// Lazy Imports for components
const FamilyWaitlist = lazy(() => import("./Tabs/FamilyWaitlist"));
const FamilyAccount = lazy(() => import("./Tabs/FamilyAccount"));
const FamilyProfile = lazy(() => import("./Tabs/FamilyProfile"));
const FamilyTasks = lazy(() => import("./Tabs/FamilyTasks"));
const FamilyPending = lazy(() => import("./Tabs/FamilyPending"));
export default function FamilyTableNew({
className,
accountDetails,
listReload,
loader,
}) {
// Initial state for family details
const initialDetailState = {
loading: false,
data: null,
};
// console.log('accountDetails',accountDetails)
// State for family details, tasks, waitlist, and pending
const [details, setDetails] = useState({
familyDetails: { ...initialDetailState },
familyTasks: { ...initialDetailState },
familyWaitList: { ...initialDetailState },
familyPending: { ...initialDetailState },
});
// Function to reset family details, tasks, waitlist, and pending
const resetDetails = () => {
setDetails({
familyDetails: { ...initialDetailState },
familyTasks: { ...initialDetailState },
familyWaitList: { ...initialDetailState },
familyPending: { ...initialDetailState },
});
};
const [updatePage, setUpdatePage] = useState(false) // State to determine when to update the page
// State for family task data
const [familyTask, setFamilyTask] = useState({ loading: false, data: [] });
// State for active task
const [activeTask, setActiveTask] = useState({ id: 0, data: {} });
// State for error messages
const [errMsg, setErrMsg] = useState("");
// State for family task popout
const [familyTaskPopout, setFamilyTaskPopout] = useState(false);
// Create an instance of the usersService class
const apiCall = useMemo(() => new usersService(), []);
// Function to handle toggling the family task popout
const familyPopUpHandler = () => {
setFamilyTaskPopout((prev) => !prev);
};
// Ref for the account section
const accountRef = useRef();
// React-to-Print hook for handling printing
const useHandlePrint = useReactToPrint({
content: () => accountRef.current,
});
// Array of tab names
const tabs = [
{ id: 1, name: "Tasks" },
{ id: 2, name: "Waiting" },
{ id: 3, name: "Pending" },
];
// State for the currently selected tab
const [tab, setTab] = useState(tabs[0].name);
// Function to handle tab changes
const tabHandler = (value) => {
setTab(value);
};
// Object that maps tab names to their corresponding components
const tabComponents = {
Tasks: (
<FamilyTasks
className={className}
loader={details.familyTasks.loading}
familyData={details.familyTasks.data}
accountDetails={accountDetails}
/>
),
Waiting: (
<FamilyWaitlist
familyData={details.familyWaitList.data}
accountDetails={accountDetails}
loader={details.familyWaitList.loading}
/>
),
Pending: (
<FamilyPending
familyData={details.familyPending.data}
accountDetails={accountDetails}
loader={details.familyPending.loading}
/>
),
Account: (
<FamilyAccount
familyData={details.familyDetails.data}
myRef={accountRef}
loader={details.familyDetails.loading}
handlePrint={useHandlePrint}
/>
),
Profile: <FamilyProfile familyData={details.familyDetails.data} />,
wallet: <FamilyWallet familyData={details.familyDetails.data} />,
};
// Default tab component
const defaultTabComponent = (
<FamilyTasks
className={className}
loader={details.familyTasks.loading}
familyData={details.familyTasks.data}
accountDetails={accountDetails}
/>
);
// Selected tab component based on the current 'tab'
const selectedTabComponent = tabComponents[tab] || defaultTabComponent;
// Effect to manage family details and related data
useEffect(() => {
const manageFamily = async () => {
try {
resetDetails();
setDetails({
familyDetails: { loading: true },
familyTasks: { loading: true },
familyWaitList: { loading: true },
familyPending: { loading: true },
});
const { family_uid } = accountDetails;
const reqData = { family_uid };
const [familyRes, tasksRes, familyWaitRes, familyPending] =
await Promise.all([
apiCall.ManageFamily(reqData),
apiCall.ManageTasks(reqData),
apiCall.ManageFamilyWaitlist(),
apiCall.ManageFamilyPending(),
]);
const familyData = familyRes.data;
const tasksData = tasksRes.data;
const familyWaitData = familyWaitRes.data;
const familyPendingData = familyPending.data;
// Function to check for errors in data
const checkDataError = (data) => data?.internal_return < 0;
if (
checkDataError(familyData) ||
checkDataError(tasksData) ||
checkDataError(familyWaitData) ||
checkDataError(familyPendingData)
) {
return;
}
setDetails({
familyDetails: { loading: false, data: familyData },
familyTasks: { loading: false, data: tasksData },
familyWaitList: { loading: false, data: familyWaitData },
familyPending: { loading: false, data: familyPendingData },
});
} catch (error) {
resetDetails();
setErrMsg("An error occurred");
throw new Error(error);
}
};
// Invoke the manageFamily function when the component mounts
manageFamily();
}, [updatePage]);
// Effect to manage family tasks
useEffect(() => {
let checkFamilyTask = true;
const reqData = {
limit: 30,
offset: 0,
job_type: "FAMILY",
action: apiConst.WRENCHBOARD_PICTURE_FAMMEMBER,
};
if (checkFamilyTask) {
setFamilyTask({ loading: true });
apiCall
.getMyJobList(reqData)
.then((res) => {
setFamilyTask({ loading: false, data: res?.data?.result_list });
if (res?.data?.result_list?.length) {
setActiveTask((prev) => ({
...prev,
data: res?.data?.result_list[0],
}));
}
})
.catch((err) => {
setFamilyTask({ loading: false, data: [] });
console.log("Error", err);
});
}
// Cleanup function to prevent memory leaks
return () => {
checkFamilyTask = false;
};
}, []);
return (
<div
className={`w-full bg-white dark:bg-dark-white overflow-y-auto rounded-2xl section-shadow h-full ${
className || ""
}`}
>
<div className="relative w-full sm:rounded-lg overflow-x-auto">
<Suspense
fallback={
<div className="h-full min-h-[609px] w-full overflow-hidden flex justify-center items-center">
<LoadingSpinner size="16" color="sky-blue" />
</div>
}
>
<div className="w-full h-full text-sm text-left text-gray-500 dark:text-gray-400 relative grid grid-cols-3 min-h-[575px]">
<div className="col-span-3 h-full w-full">
<div className="flex flex-col w-full">
<div className="w-full pr-8 flex items-center gap-1 border-b border-b-[#FAFAF]">
<ul className="flex gap-2 items-center w-full">
{tabs.map(({ name, id }) => (
<li
onClick={() => tabHandler(name)}
className={`p-4 flex hover:text-purple transition-all ease-in-out items-center cursor-pointer overflow-hidden text-xl relative top-[2px] ${
tab === name
? "text-purple border-r"
: "text-thin-light-gray"
}`}
key={id}
>
<h1>{name}</h1>
</li>
))}
</ul>
<button
type="button"
onClick={familyPopUpHandler}
className="p-1 my-1 w-[100px] flex justify-center items-center btn-gradient text-base rounded-full text-white"
>
Add task
</button>
</div>
<div className="flex-[0.9] h-full">
<div className="h-full relative overflow-y-auto">
<Suspense
fallback={<LoadingSpinner size="16" color="sky-blue" />}
>
{selectedTabComponent}
</Suspense>
</div>
</div>
</div>
</div>
</div>
</Suspense>
</div>
{familyTaskPopout && (
<AssignTaskPopout
action={familyPopUpHandler}
situation={familyTaskPopout}
familyTask={familyTask}
setFamilyTask={setFamilyTask}
setActiveTask={setActiveTask}
activeTask={activeTask}
familyDetailsData={details.familyDetails.data}
setUpdatePage={setUpdatePage}
/>
)}
</div>
);
}
@@ -0,0 +1,12 @@
import React from 'react'
import Layout from '../Partials/Layout'
import ParentWaiting from '../MyOffers/ParentWaiting'
export default function ParentWaitingLayout() {
return (
<Layout>
<ParentWaiting />
</Layout>
)
}
@@ -1,10 +1,13 @@
import { forwardRef } from "react";
import QRCode from "react-qr-code";
import { useSelector } from "react-redux";
import LoadingSpinner from "../../Spinners/LoadingSpinner";
const FamilyAccount = forwardRef(({ familyData, myRef, handlePrint }, ref) => {
const FamilyAccount = forwardRef(({ familyData, myRef, handlePrint, loader }, ref) => {
const { userDetails } = useSelector((state) => state.userDetails);
return (
<div
className="w-full lg:min-h-[500px] h-full flex flex-col items-center justify-center"
className="w-full lg:min-h-[538px] p-3 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 ">
@@ -32,12 +35,19 @@ const FamilyAccount = forwardRef(({ familyData, myRef, handlePrint }, ref) => {
<p className="text-xl tracking-wide mb-[15px] text-center font-bold text-dark-gray dark:text-white">
Scan the code from mobile app
</p>
{loader ?
<div className="w-full">
<LoadingSpinner size='8' color='sky-blue' />
</div>
:
<QRCode
size={256}
style={{ height: "auto", maxWidth: "100%", width: "100%" }}
value={`https://www.google.com`}
// value={`https://www.google.com`}
value={`${userDetails?.uid}@${familyData?.username}@${familyData?.uid}`}
viewBox={`0 0 256 256`}
/>
}
</div>
</div>
<div className="h-[50px] w-full flex justify-center items-center">
@@ -53,4 +63,4 @@ const FamilyAccount = forwardRef(({ familyData, myRef, handlePrint }, ref) => {
);
});
export default FamilyAccount;
export default FamilyAccount;
+10 -11
View File
@@ -1,9 +1,8 @@
import { useMemo, useState } from "react";
import { PaginatedList, handlePagingFunc } from "../../Pagination";
import { PriceFormatter } from "../../Helpers/PriceFormatter";
import dataImage2 from "../../../assets/images/data-table-user-2.png";
import PendingJobsPopout from "../../jobPopout/PendingJobsPopout";
import localImgLoad from "../../../lib/localImgLoad";
import { PriceFormatter } from "../../Helpers/PriceFormatter";
import { PaginatedList, handlePagingFunc } from "../../Pagination";
import PendingJobsPopout from "../../jobPopout/PendingJobsPopout";
export default function FamilyPending({
familyData,
@@ -37,7 +36,7 @@ export default function FamilyPending({
return (
<div
className={`update-table w-full p-8 bg-white dark:bg-dark-white overflow-hidden rounded-2xl section-shadow min-h-[520px] ${
className={`update-table w-full p-3 bg-white dark:bg-dark-white overflow-hidden rounded-2xl section-shadow lg:min-h-[538px] ${
className || ""
}`}
>
@@ -55,7 +54,7 @@ export default function FamilyPending({
value?.currency_code,
value?.currency
);
let image = value.banner ? value.banner : "default.jpg";
let image = `${familyData.session_image_server}${localStorage.getItem('session_token')}/job/${value.job_uid}`
return (
<tr
key={index}
@@ -65,9 +64,7 @@ export default function FamilyPending({
<div className="flex space-x-2 items-center w-full">
<div className="w-[60px] h-[60px] p-2 bg-alice-blue rounded-full overflow-hidden flex justify-center items-center">
<img
src={localImgLoad(
`images/taskbanners/${image}`
)}
src={image}
alt="data"
className="w-full h-full rounded-full"
/>
@@ -125,8 +122,10 @@ export default function FamilyPending({
);
})
) : (
<tr className="font-bold text-xl text-dark-gray dark:text-white whitespace-nowrap">
<td className="p-2">No Pending Task!</td>
<tr>
<td className="font-bold text-xl text-dark-gray dark:text-white whitespace-nowrap text-center py-4 px-2">
No Pending Task!
</td>
</tr>
)}
</>
+307 -3
View File
@@ -1,3 +1,307 @@
export default function FamilyProfile() {
return <>Profile</>;
}
import { useState } from "react";
import { useNavigate } from "react-router-dom";
import usersService from "../../../services/UsersService";
import InputCom from "../../Helpers/Inputs/InputCom";
import LoadingSpinner from "../../Spinners/LoadingSpinner";
import { Form, Formik } from "formik";
import * as Yup from "yup";
export default function FamilyProfile({ familyData, className }) {
const validationSchema = Yup.object().shape({
firstname: Yup.string().required("Firstname is required"),
lastname: Yup.string()
// .min(3, "Minimum 3 characters")
// .max(25, "Maximum 25 characters")
.required("Lastname is required"),
year: Yup.number()
.required("Year is required")
.min(
new Date().getFullYear() - process.env.REACT_APP_FAMILY_MAXIMUM_AGE,
`Age must be within ${process.env.REACT_APP_FAMILY_MINIMUM_AGE} to ${process.env.REACT_APP_FAMILY_MAXIMUM_AGE} years`
)
.max(
new Date().getFullYear() - process.env.REACT_APP_FAMILY_MINIMUM_AGE,
`Age must be within ${process.env.REACT_APP_FAMILY_MINIMUM_AGE} to ${process.env.REACT_APP_FAMILY_MAXIMUM_AGE} years`
),
month: Yup.number()
.required("Month is required")
.min(1, "Month is required"),
});
const navigate = useNavigate();
const api = new usersService();
let [requestStatus, setRequestStatus] = useState({
loading: false,
status: false,
message: "",
});
// let [updateDetails, setUpdateDetails] = useState({
// family_uid: familyData.uid,
// firstname: familyData.firstname,
// lastname: familyData.lastname,
// year: familyData.year,
// month: familyData.month,
// action: 22020
// })
// initial values for formik
let initialValues = {
family_uid: familyData?.uid,
firstname: familyData?.firstname,
lastname: familyData?.lastname,
year: familyData?.year,
month: familyData?.month,
enable_traking: familyData?.enable_traking,
};
// const handleChange = ({ target: { name, value } }) => {
// setUpdateDetails((prev) => ({ ...prev, [name]: value }));
// };
const updateFamily = (values, helpers) => {
setRequestStatus({ loading: true, status: false, message: "" });
// let {firstname, lastname, year, month} = updateDetails
// if(!firstname || !lastname || year == 0 || month == 0) {
// setRequestStatus({loading:false, status:false, message: 'Please fill all fields'})
// return setTimeout(()=>{
// setRequestStatus({loading:false, status:false, message: ''})
// }, 5000)
// }
api
.getFamilyUpdate(values)
.then((res) => {
if (res.data.internal_return < 0) {
return setRequestStatus({
loading: false,
status: false,
message: "Failed, try again!",
});
}
setRequestStatus({
loading: false,
status: true,
message: "Family account updated",
});
setTimeout(() => {
navigate("/acc-family", { replace: true });
}, 5000);
})
.catch((error) => {
setRequestStatus({
loading: false,
status: false,
message: "Unable to update, try again!",
});
})
.finally(() => {
setTimeout(() => {
setRequestStatus({ loading: false, status: false, message: "" });
}, 5000);
});
};
return (
<div
className={`w-full bg-white dark:bg-dark-white overflow-hidden rounded-2xl section-shadow lg:min-h-[538px] p-3 ${
className || ""
}`}
>
<Formik
initialValues={initialValues}
validationSchema={validationSchema}
onSubmit={updateFamily}
>
{(props) => {
return (
<Form className="logout-modal-body w-full lg:w-[500px] lg:mx-auto flex flex-col items-center px-10 py-8 gap-4">
<InputCom
placeholder="Firstname"
label="First Name:"
name="firstname"
type="text"
parentClass="flex items-center gap-1 w-full"
labelClass="flex-[0.2] mb-0"
inputClass={`flex-[0.8] input-curve lg border border-[#dce4e9] ${
props.errors.firstname && props.touched.firstname
? "border border-red-500"
: ""
}`}
fieldClass="px-2"
value={props.values.firstname}
inputHandler={props.handleChange}
/>
<InputCom
placeholder="Lastname"
label="Last Name:"
name="lastname"
type="text"
parentClass="flex items-center gap-1 w-full"
labelClass="flex-[0.2] mb-0"
inputClass={`flex-[0.8] input-curve lg border border-[#dce4e9] ${
props.errors.lastname && props.touched.lastname
? "border border-red-500"
: ""
}`}
fieldClass="px-2"
value={props.values?.lastname}
inputHandler={props.handleChange}
/>
<div className="input-com flex flex-col gap-1 w-full">
{/* Age dropdown */}
<div className="">
<label
className="input-label text-[#181c32] dark:text-white text-[15px] font-semibold"
htmlFor="age-selection"
>
Birthday: (Year/Month)
</label>
{((props.errors.year && props.touched.year) ||
(props.errors.month && props.touched.month)) && (
<span className="text-[12px] text-red-500">
{" "}
{props.errors.year || props.errors.month}
</span>
)}
</div>
<div className="flex max-w-[330px] w-full self-end gap-4">
<select
name="year"
id="yearDropdown"
value={props.values.year}
onChange={props.handleChange}
className={`input-wrapper border border-[#f5f8fa] ${
props.errors.year && props.touched.year
? "border border-red-500"
: ""
} dark:border-[#5e6278] w-56 rounded-[35px] h-10 overflow-hidden relative font-medium leading-6 bg-clip-padding text-[#5e6278] dark:text-gray-100 bg-[#f5f8fa] dark:bg-[#5e6278] text-base focus-visible:border-transparent focus-visible:outline-0 focus-visible:ring-transparent px-4`}
>
<option value="">Select a Year</option>
{years.map((year) => (
<option key={year} value={year}>
{year}
</option>
))}
</select>
<select
name="month"
id="monthDropdown"
// value={(months.filter(month => month.id == selectedMonth)[0]?.id) || ''}
value={
months.filter(
(month) => month.id == props.values.month
)[0]?.id || 0
}
onChange={props.handleChange}
className={`input-wrapper border border-[#f5f8fa] ${
props.errors.month && props.touched.month
? "border border-red-500"
: ""
} dark:border-[#5e6278] w-56 rounded-[35px] h-10 overflow-hidden relative font-medium leading-6 bg-clip-padding text-[#5e6278] dark:text-gray-100 bg-[#f5f8fa] dark:bg-[#5e6278] text-base focus-visible:border-transparent focus-visible:outline-0 focus-visible:ring-transparent px-4`}
>
<option value="">Select a Month</option>
{months.map(({ id, name }) => (
<option key={id} value={id}>
{name}
</option>
))}
</select>
</div>
</div>
<div className="input-com mb-7 flex flex-col gap-1 w-full">
{/* Location dropdown */}
<div className="">
<label
className="input-label text-[#181c32] dark:text-white text-[15px] font-semibold"
htmlFor="age-selection"
>
Location Tracking
</label>
{/* {((props.errors.year && props.touched.year) ||
(props.errors.month && props.touched.month)) && (
<span className="text-[12px] text-red-500">
{" "}
{props.errors.year || props.errors.month}
</span>
)} */}
</div>
<div className="flex max-w-[330px] w-full self-end gap-4">
<select
name="enable_traking"
id="enable_traking"
value={props.values.enable_traking}
onChange={props.handleChange}
className={`input-wrapper border border-[#f5f8fa] dark:border-[#5e6278] w-56 rounded-[35px] h-10 overflow-hidden relative font-medium leading-6 bg-clip-padding text-[#5e6278] dark:text-gray-100 bg-[#f5f8fa] dark:bg-[#5e6278] text-base focus-visible:border-transparent focus-visible:outline-0 focus-visible:ring-transparent px-4`}
>
<option value={+0}>Disabled</option>
<option value={+100}>Enabled</option>
</select>
</div>
</div>
{requestStatus.message && (
<div
className={`relative p-2 ${
!requestStatus.status
? "text-[#912741] bg-[#fcd9e2] border-[#fbc6d3]"
: "text-green-700"
} mb-2 rounded-[0.475rem] text-xs font-light leading-[19.5px]`}
>
{requestStatus.message}
</div>
)}
<div className="flex justify-end w-full">
{requestStatus.loading ? (
<>
<div className="signup btn-loader"></div>
<LoadingSpinner size="4" />
</>
) : (
<button
type="submit"
// onClick={updateFamily}
// className={`rounded-[0.475rem] text-white flex justify-center bg-[#4687ba] hover:bg-[#009ef7] transition-all duration-300 items-center h-[42px] py-[0.8875rem] px-[1.81rem] text-[14.95px] btn-login`}
className="text-white btn-gradient text-lg tracking-wide px-6 py-2 rounded-full"
>
Update
</button>
)}
</div>
</Form>
);
}}
</Formik>
</div>
);
}
// Get the current year
// const currentYear = new Date().getFullYear() - process.env.REACT_APP_FAMILY_MINIMUM_AGE;
// Calculate the current year and the year 3 years before the current year
const currentYear = new Date().getFullYear();
const startYear = currentYear - 3;
// Generate an array of years from 4 years before the current year to 18 years before the current year
const years = Array.from({ length: 15 }, (_, index) => startYear - index).reverse();
// const latestYears = years.slice(0, process.env.REACT_APP_FAMILY_YEAR_RANGE);
const months = [
{ id: "1", name: "January" },
{ id: "2", name: "February" },
{ id: "3", name: "March" },
{ id: "4", name: "April" },
{ id: "5", name: "May" },
{ id: "6", name: "June" },
{ id: "7", name: "July" },
{ id: "8", name: "August" },
{ id: "9", name: "September" },
{ id: "10", name: "October" },
{ id: "11", name: "November" },
{ id: "12", name: "December" },
];
+37 -19
View File
@@ -1,20 +1,30 @@
import React, { useState } from "react";
import dataImage2 from "../../../assets/images/data-table-user-2.png";
import { useNavigate, useLocation } from "react-router-dom";
import React, { useMemo, useState } from "react";
import { useLocation, useNavigate } from "react-router-dom";
import localImgLoad from "../../../lib/localImgLoad";
import Icons from "../../Helpers/Icons";
import { PriceFormatter } from "../../Helpers/PriceFormatter";
import { handlePagingFunc } from "../../Pagination/HandlePagination";
import PaginatedList from "../../Pagination/PaginatedList";
import LoadingSpinner from "../../Spinners/LoadingSpinner";
import Icons from "../../Helpers/Icons";
import { PriceFormatter } from "../../Helpers/PriceFormatter";
import ModalCom from "../../Helpers/ModalCom";
import Detail from "../../jobPopout/popoutcomponent/Detail";
import localImgLoad from "../../../lib/localImgLoad";
export default function FamilyTasks({ familyData, className, loader, accountDetails }) {
export default function FamilyTasks({
familyData,
className,
loader,
accountDetails,
}) {
let navigate = useNavigate();
let { pathname } = useLocation();
// ...
const filteredFamilyData = useMemo(
() =>
familyData?.result_list?.filter(
(data) => data?.family_uid === accountDetails?.family_uid
),
[familyData, accountDetails]
);
const [currentPage, setCurrentPage] = useState(0);
const indexOfFirstItem = Number(currentPage);
const indexOfLastItem =
@@ -26,9 +36,10 @@ export default function FamilyTasks({ familyData, className, loader, accountDeta
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={`update-table w-full bg-white dark:bg-dark-white h-full lg:min-h-[538px] overflow-hidden rounded-2xl section-shadow p-3 ${
className || ""
}`}
>
@@ -45,8 +56,8 @@ export default function FamilyTasks({ familyData, className, loader, accountDeta
{
<>
{familyData &&
familyData?.result_list &&
familyData.result_list.length > 0 &&
familyData?.result_list &&
familyData.result_list.length > 0 ? (
currentTask.map((value, index) => {
// find due date
const dueDate = value?.delivery_date.split(" ")[0];
@@ -56,7 +67,7 @@ export default function FamilyTasks({ familyData, className, loader, accountDeta
value?.currency_code,
value?.currency
);
let image = value.banner ? value.banner : 'default.jpg'
let image = `${familyData.session_image_server}${localStorage.getItem('session_token')}/job/${value.job_uid}`
return (
<tr
key={index}
@@ -66,7 +77,7 @@ export default function FamilyTasks({ familyData, className, loader, accountDeta
<div className="flex space-x-2 items-center w-full">
<div className="w-[60px] h-[60px] p-2 bg-alice-blue rounded-full overflow-hidden flex justify-center items-center">
<img
src={localImgLoad(`images/taskbanners/${image}`)}
src={image}
alt="data"
className="w-full h-full rounded-full"
/>
@@ -75,7 +86,7 @@ export default function FamilyTasks({ familyData, className, loader, accountDeta
<h1 className="font-bold text-xl text-dark-gray dark:text-white">
{value.title}
</h1>
<div className="flex gap-4 items-center">
<div className="flex flex-col sm:flex-row items-start gap-1 md:gap-4 md:items-center">
<span className="text-sm text-thin-light-gray flex flex-start gap-1">
Price:{" "}
<span className="text-purple">
@@ -109,8 +120,8 @@ export default function FamilyTasks({ familyData, className, loader, accountDeta
state: {
...value,
pathname,
accountDetails
}
accountDetails,
},
});
}}
className="w-20 h-11 flex justify-center items-center btn-gradient text-base rounded-full text-white"
@@ -120,7 +131,14 @@ export default function FamilyTasks({ familyData, className, loader, accountDeta
</td>
</tr>
);
})}
})
) : (
<tr>
<td colSpan="2" className="text-center py-4 font-bold text-xl text-dark-gray dark:text-white whitespace-nowrap">
No Family Task
</td>
</tr>
)}
</>
}
</tbody>
@@ -48,7 +48,7 @@ const FamilyWaitlist = memo(
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={`update-table w-full bg-white dark:bg-dark-white h-full lg:min-h-[538px] p-3 overflow-hidden rounded-2xl section-shadow ${
className || ""
}`}
>
@@ -67,6 +67,8 @@ const FamilyWaitlist = memo(
const selectedImage = require(`../../../assets/images/family/${
value?.banner || "default.jpg"
}`);
console.log('VALUE', value)
// let image = `${familyData.session_image_server}${localStorage.getItem('session_token')}/job/${value.job_uid}`
return (
<tr
className="bg-white dark:bg-dark-white border-b dark:border-[#5356fb29] hover:bg-gray-50"
@@ -0,0 +1,45 @@
import React, { useEffect, useState } from 'react'
import usersService from '../../../services/UsersService'
import LoadingSpinner from '../../Spinners/LoadingSpinner'
import background from '../../../assets/images/bg-sky-blue.jpg'
import Wallet from './wallet/Wallet'
function FamilyWallet({familyData}) {
const apiUrl = new usersService()
let [familyWallet, setFamilyWallet] = useState({loading:true, data: []})
let [familyWalletReload, setFamilyWalletReload] = useState(false) // STATE TO DETERMINE WHEN TO RELOAD FAMILY WALLET TAB/PAGE
useEffect(()=>{
setFamilyWallet({loading:true, data: []})
apiUrl.getFamilyWallet({family_uid:familyData?.uid}).then(res => {
setFamilyWallet({loading:false, data: res?.data?.result_list || []})
}).catch(error => {
setFamilyWallet({loading:false, data: []})
})
},[familyWalletReload])
return (
<div className='p-3 w-full h-full bg-white dark:bg-dark-white flex flex-col justify-start items-start'>
{familyWallet.loading ?
<div className='w-full h-20 flex justify-center items-center'>
<LoadingSpinner size='8' color='sky-blue' />
</div>
:
familyWallet?.data?.length > 0 ?
<div className='w-full p-4 flex flex-col gap-2'>
{familyWallet?.data?.map((wallet, index)=>(
<Wallet key={index} wallet={wallet} familyData={familyData} setFamilyWalletReload={setFamilyWalletReload} />
))}
</div>
:
<p className='text-lg text-gray-500 dark:text-gray-400'>No Wallet Found!</p>
}
</div>
)
}
export default FamilyWallet
+16 -5
View File
@@ -1,26 +1,32 @@
import React from "react";
import localImgLoad from "../../../lib/localImgLoad";
export default function ProfileInfo({
profileImg,
profileImgInput,
profileImgChangHandler,
profileImgChangeHandler,
browseProfileImg,
accountDetails,
uploadStatus
}) {
// console.log(accountDetails.banner)
return (
<div className="flex flex-col items-center gap-4">
<div className="flex justify-center">
<div className="w-full relative">
<img
src={localImgLoad(`images/icons/${accountDetails.banner}`)}
alt=""
src={
profileImg
? profileImg
: localImgLoad(`images/icons/${accountDetails.banner}`)
}
alt="profile"
className="sm:w-[180px] sm:h-[180px] w-[120px] h-[120px] rounded-full overflow-hidden object-cover"
/>
<input
ref={profileImgInput}
onChange={(e) => profileImgChangHandler(e)}
accept="image/*"
onChange={profileImgChangeHandler}
type="file"
className="hidden"
/>
@@ -47,6 +53,11 @@ export default function ProfileInfo({
</div>
</div>
</div>
{/* DISPLAYS PROFILE UPLOADING STATUS */}
<div className="w-full">
{uploadStatus.message && !uploadStatus.loading && <p className={`text-center ${uploadStatus.status ? 'text-green-500':'text-red-500'}`}>{uploadStatus.message}</p>}
{uploadStatus.loading && <p className="text-center">{uploadStatus.message}</p>}
</div>
<div className="flex flex-col justify-center gap-3 items-center">
<h1 className="font-bold text-xl tracking-wide line-clamp-1 text-dark-gray dark:text-white capitalize">
{accountDetails?.firstname}
@@ -0,0 +1,358 @@
import React, { useEffect, useState } from "react";
import ModalCom from "../../../Helpers/ModalCom";
import InputCom from "../../../Helpers/Inputs/InputCom";
import { Form, Formik } from "formik";
import * as Yup from "yup";
import { AmountTo2DP } from "../../../Helpers/PriceFormatter";
import usersService from "../../../../services/UsersService";
import LoadingSpinner from "../../../Spinners/LoadingSpinner";
import { PriceFormatter } from "../../../Helpers/PriceFormatter";
import { tableReload } from "../../../../store/TableReloads";
import { useDispatch } from "react-redux";
import { apiConst } from "../../../../lib/apiConst";
const validationSchema = Yup.object().shape({
// amount: Yup.string()
// .typeError("Invalid number")
// .min(1, "Price must be greater than 0")
// .test("no-e", "Invalid number", (value) => {
// if (value && /\d+e/.test(value)) {
// return false;
// }
// return true;
// })
// .required("Amount is required"),
amount: Yup.number("Please enter a number")
.min(1, "Price must be greater than 0")
.required("Amount is required"),
comment: Yup.string().required("Comment is required"),
});
function FamilyAddFundPopout({ action, situation, wallet, familyData }) {
const dispatch = useDispatch();
const apiUrl = new usersService();
const [startTransfer, setStartTransfer] = useState({
loading: true,
data: {},
});
const [requestStatus, setRequestStatus] = useState({
loading: false,
status: false,
message: "",
});
// initial values for formik
let initialValues = {
amount: "",
from: AmountTo2DP(startTransfer?.data?.origing_current_balance * 0.01),
to: `${familyData.firstname} ${familyData.lastname}`,
comment: "",
};
// FUNCTION TO PERFORM FAMILY TRANSFER
const handleAddFund = (values) => {
setRequestStatus({ loading: true, status: false, message: "" });
let senderBal = startTransfer?.data?.origing_current_balance || ""; // SENDER'S ACCOUNT BALANCE
let senderLimit = startTransfer?.data?.origing_transfer_limit || ""; // SENDER'S TRANSFER LIMIT
let reqData = {
// API REQUEST DATA
family_uid: familyData.uid,
wallet_uid: wallet.wallet_uid,
origing_wallet_uid: startTransfer?.data?.origing_wallet_uid,
currency: startTransfer?.data?.currency,
amount: values.amount * 100,
description: values.comment,
family_transfer_mode: 100,
action: apiConst.WRENCHBOARD_FAMILY_TRANSFER,
};
if (!senderBal || !senderLimit) {
// RETURNS UNAUTHORIZED, IF SENDER BAL OR LIMIT IS NOT AVAILABLE
setRequestStatus({
loading: false,
status: false,
message: "Unauthorized, try again later",
});
return setTimeout(() => {
setRequestStatus({ loading: false, status: false, message: "" });
}, 5000);
}
if (values.amount > senderBal * 0.01) {
// CHECKS TO SEE IF SENDER IS SENDING MORE THAN HIS BALANCE
setRequestStatus({
loading: false,
status: false,
message: "You cannot send more than your balance",
});
return setTimeout(() => {
setRequestStatus({ loading: false, status: false, message: "" });
}, 5000);
}
if (values.amount > senderLimit * 0.01) {
// CHECKS TO SEE IF SENDER IS SENDING MORE THAN HIS LIMIT
setRequestStatus({
loading: false,
status: false,
message: `You cannot exceed ${senderLimit * 0.01} ${
startTransfer?.data?.origing_currency.charAt(0).toUpperCase() +
startTransfer?.data?.origing_currency.slice(1).toLowerCase()
}`,
});
return setTimeout(() => {
setRequestStatus({ loading: false, status: false, message: "" });
}, 5000);
}
apiUrl
.familyTransfer(reqData)
.then(({ data }) => {
if (
data.internal_return < 0 ||
data.credit_confirm == "" ||
data.pay_confirm == ""
) {
setRequestStatus({
loading: false,
status: false,
message: "Transfer Failed",
});
return setTimeout(() => {
setRequestStatus({ loading: false, status: false, message: "" });
}, 5000);
}
setRequestStatus({
loading: false,
status: true,
message: "Transfer Successful",
});
setTimeout(() => {
setRequestStatus({ loading: false, status: false, message: "" });
dispatch(tableReload({ type: "WALLETTABLE" })); // UPDATES PARENT WALLET ACCOUNT
action(); // TO CLOSE THE MODAL
}, 5000);
})
.catch((error) => {
setRequestStatus({
loading: false,
status: false,
message: "Network Error, try again",
});
setTimeout(() => {
setRequestStatus({ loading: false, status: false, message: "" });
}, 5000);
});
};
// LOAD FAMILY START TRANSFER
useEffect(() => {
let reqData = {
family_uid: familyData.uid,
wallet_uid: wallet.wallet_uid,
action: apiConst.WRENCHBOARD_FAMILY_TRANSFERSTART,
};
apiUrl
.familyTransferStart(reqData)
.then((response) => {
setStartTransfer({ loading: false, data: response?.data });
})
.catch((err) => {
setStartTransfer({ loading: false, data: {} });
});
}, []);
return (
<ModalCom action={action} situation={situation}>
<div className="relative logout-modal-wrapper lg:w-[500px] h-full lg:h-auto bg-white dark:bg-dark-white lg:rounded-2xl">
<div className="logout-modal-header w-full flex items-center justify-between lg:px-10 lg:py-8 px-[30px] py-[23px] border-b border-light-purple dark:border-[#5356fb29] ">
<h1 className="text-26 font-bold text-dark-gray dark:text-white tracking-wide">
Add Fund
</h1>
<button
type="button"
className="text-[#374557] dark:text-red-500"
onClick={action}
>
<svg
width="36"
height="36"
viewBox="0 0 36 36"
fill="none"
className="fill-current"
xmlns="http://www.w3.org/2000/svg"
>
<path
d="M36 16.16C36 17.4399 36 18.7199 36 20.0001C35.7911 20.0709 35.8636 20.2554 35.8385 20.4001C34.5321 27.9453 30.246 32.9248 22.9603 35.2822C21.9006 35.6251 20.7753 35.7657 19.6802 35.9997C18.4003 35.9997 17.1204 35.9997 15.8401 35.9997C15.5896 35.7086 15.2189 35.7732 14.9034 35.7093C7.77231 34.2621 3.08728 30.0725 0.769671 23.187C0.435002 22.1926 0.445997 21.1199 0 20.1599C0 18.7198 0 17.2798 0 15.8398C0.291376 15.6195 0.214408 15.2656 0.270759 14.9808C1.71321 7.69774 6.02611 2.99691 13.0428 0.700951C14.0118 0.383805 15.0509 0.386897 15.9999 0C17.2265 0 18.4532 0 19.6799 0C19.7156 0.124041 19.8125 0.136067 19.9225 0.146719C27.3 0.868973 33.5322 6.21922 35.3801 13.427C35.6121 14.3313 35.7945 15.2484 36 16.16ZM33.011 18.0787C33.0433 9.77105 26.3423 3.00309 18.077 2.9945C9.78479 2.98626 3.00344 9.658 2.98523 17.8426C2.96667 26.1633 9.58859 32.9601 17.7602 33.0079C26.197 33.0577 32.9787 26.4186 33.011 18.0787Z"
fill=""
fillOpacity="0.6"
/>
<path
d="M15.9309 18.023C13.9329 16.037 12.007 14.1207 10.0787 12.2072C9.60071 11.733 9.26398 11.2162 9.51996 10.506C9.945 9.32677 11.1954 9.0811 12.1437 10.0174C13.9067 11.7585 15.6766 13.494 17.385 15.2879C17.9108 15.8401 18.1633 15.7487 18.6375 15.258C20.3586 13.4761 22.1199 11.7327 23.8822 9.99096C24.8175 9.06632 26.1095 9.33639 26.4967 10.517C26.7286 11.2241 26.3919 11.7413 25.9133 12.2178C24.1757 13.9472 22.4477 15.6855 20.7104 17.4148C20.5228 17.6018 20.2964 17.7495 20.0466 17.9485C22.0831 19.974 24.0372 21.8992 25.9689 23.8468C26.9262 24.8119 26.6489 26.1101 25.4336 26.4987C24.712 26.7292 24.2131 26.3441 23.7455 25.8757C21.9945 24.1227 20.2232 22.3892 18.5045 20.6049C18.0698 20.1534 17.8716 20.2269 17.4802 20.6282C15.732 22.4215 13.9493 24.1807 12.1777 25.951C11.7022 26.4262 11.193 26.7471 10.4738 26.4537C9.31345 25.9798 9.06881 24.8398 9.98589 23.8952C11.285 22.5576 12.6138 21.2484 13.9387 19.9355C14.5792 19.3005 15.2399 18.6852 15.9309 18.023Z"
fill="#"
fillOpacity="0.6"
/>
</svg>
</button>
</div>
<div className="logout-modal-body w-full flex flex-col items-center px-10 py-8">
{startTransfer.loading && (
<LoadingSpinner size="16" color="sky-blue" height={"h-64"} />
)}
{!startTransfer.loading && (
<Formik
initialValues={initialValues}
validationSchema={validationSchema}
onSubmit={handleAddFund}
>
{(props) => (
<Form className="w-full">
<div className="flex flex-col-reverse sm:flex-row">
<div className="fields w-full">
{/* AMOUNT */}
<div className="field w-full mb-[0.5rem]">
<InputCom
placeholder="0"
label={`Amount (${startTransfer?.data?.currency})`}
name="amount"
type="text"
parentClass="flex items-center gap-1 w-full"
labelClass="flex-[0.3] mb-0"
inputClass={`flex-[0.7] input-curve lg border border-[#dce4e9] ${
props.errors.amount && props.touched.amount
? "border border-red-500"
: ""
}`}
fieldClass="px-2 text-right"
value={props.values.amount}
inputHandler={props.handleChange}
/>
</div>
{/* FROM */}
<div className="field w-full mb-[0.5rem]">
<InputCom
placeholder="From"
label={`From (${startTransfer?.data?.origing_currency})`}
name="from"
type="text"
parentClass="flex items-center gap-1 w-full"
labelClass="flex-[0.3] mb-0"
inputClass={`flex-[0.7] input-curve lg border border-[#dce4e9]`}
fieldClass="px-2 text-right"
value={props.values.from}
disable={true}
/>
</div>
{/* TO */}
<div className="field w-full mb-[0.5rem]">
<InputCom
placeholder="To"
label="To:"
name="to"
type="text"
parentClass="flex items-center gap-1 w-full"
labelClass="flex-[0.3] mb-0"
inputClass={`flex-[0.7] input-curve lg border border-[#dce4e9]`}
fieldClass="px-2 text-right"
value={props.values.to}
disable={true}
/>
</div>
{/* COMMENT */}
<div className="field w-full mb-[0.5rem]">
<div className="w-full">
<label
htmlFor="Job Delivery Details"
className="input-label text-[#181c32] dark:text-white text-[13.975px] leading-[20.9625px] font-semibold flex items-center gap-1"
>
Comment
{/* {props.errors.comment && props.touched.comment && <span className='text-sm text-red-500'>{' '}{props.errors.comment}</span>} */}
</label>
<textarea
// id="Job Delivery Details"
rows="2"
className={`input-field px-3 py-2 placeholder:text-base text-dark-gray dark:text-white w-full bg-slate-100 dark:bg-[#11131F] focus:ring-0 focus:outline-[#dce4e9] rounded-[10px] border ${
props.errors.comment && props.touched.comment
? "border border-red-500"
: ""
}`}
style={{ resize: "none" }}
name="comment"
value={props.values.comment}
onChange={props.handleChange}
/>
</div>
</div>
{/* inputs ends here */}
</div>
</div>
{/* ERROR DISPLAY AND SUBMIT BUTTON */}
<div className="content-footer w-full">
{/* error or success display */}
{requestStatus.message != "" &&
(!requestStatus.status ? (
<div
className={`relative p-4 text-[#912741] bg-[#fcd9e2] border-[#fbc6d3] rounded-[0.475rem] text-md font-light leading-[19.5px] text-[13px]`}
// className={`pb-1 absolute bottom-0 left-1/2 -translate-x-1/2 text-[#912741] rounded-[0.475rem] text-md font-light leading-[19.5px] text-[13px]`}
>
{requestStatus.message}
</div>
) : (
requestStatus.status && (
<div
className={`relative p-4 text-green-700 bg-slate-200 border-slate-800 rounded-[0.475rem] text-md font-light leading-[19.5px] text-[13px]`}
// className={`pb-1 absolute bottom-0 left-1/2 -translate-x-1/2 text-green-700 rounded-[0.475rem] text-md font-light leading-[19.5px] text-[13px]`}
>
{requestStatus.message}
</div>
)
))}
{/* End of error or success display */}
<div className="pt-2 w-full border-t border-light-purple dark:border-[#5356fb29] flex justify-end items-center">
<div className="w-full flex justify-between gap-2 items-center">
<button
type="button"
onClick={action}
className="w-[150px] h-[48px] rounded-full text-base text-white bg-red-600 hover:bg-red-500"
>
Cancel
</button>
<>
{requestStatus.loading ? (
<LoadingSpinner size="6" color="sky-blue" />
) : (
<button
type="submit"
className={`w-[150px] h-[48px] rounded-full text-base text-white bg-sky-500 hover:bg-sky-400 ${
requestStatus.status ? "opacity-50" : ""
}`}
disabled={requestStatus.status}
>
Send
</button>
)}
</>
</div>
</div>
</div>
</Form>
)}
</Formik>
)}
</div>
</div>
</ModalCom>
);
}
export default FamilyAddFundPopout;

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