Compare commits
517 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 30642bba14 | |||
| f5921612b8 | |||
| 38afece043 | |||
| d44447c6b3 | |||
| d942c6641a | |||
| beed565ba0 | |||
| 329e27b83d | |||
| 3145656d80 | |||
| 5098a45bd5 | |||
| 9e65a6e7d5 | |||
| 83ac1087f3 | |||
| 55b2bccdf0 | |||
| 0c51474dd2 | |||
| 8c7ffb52a5 | |||
| 6c698bc399 | |||
| 540ad6f9ad | |||
| 11342c32c6 | |||
| 356c11d029 | |||
| 934d95e822 | |||
| 0aa49b49a9 | |||
| be52792271 | |||
| c23e61e06b | |||
| 24b9a0e348 | |||
| fcb154ba49 | |||
| c0065ed569 | |||
| 05a2ebec61 | |||
| 63f7deaa2b | |||
| d762ef42e8 | |||
| 3e34c81789 | |||
| a2340af314 | |||
| 2a445fd2d4 | |||
| b5e52fb34e | |||
| 87512bbc23 | |||
| 1e59059394 | |||
| e59c83d216 | |||
| 395569dab5 | |||
| 25a537c5bc | |||
| 5244a2875e | |||
| 54560b3fec | |||
| 078d26317f | |||
| 8dbf638c7f | |||
| a68af7c3a4 | |||
| 905d48ceb7 | |||
| 1e5dac0104 | |||
| 11e52a02f6 | |||
| 4e74198ae5 | |||
| 5d673a630c | |||
| adcb33764b | |||
| 305bff1167 | |||
| 1ebe9fe12b | |||
| 3fbaf6b78a | |||
| 421a24c185 | |||
| 6386fbcda6 | |||
| 2e09d77597 | |||
| 10dae9193c | |||
| 3f487337f4 | |||
| 7eabee8855 | |||
| 360f0500a9 | |||
| 9b6c5b72ad | |||
| afc5e25cfc | |||
| 58fe5eb82c | |||
| 2f3fdc0695 | |||
| 0a465fceb0 | |||
| 5e968db2f5 | |||
| 1916bc1a65 | |||
| 1103127cae | |||
| 751369071c | |||
| 4cc8f27c14 | |||
| aab455c2c3 | |||
| 21843c4bc7 | |||
| 8702728ffa | |||
| 79dcd0f2b5 | |||
| 47b25f3dfe | |||
| 89925a6af9 | |||
| 3c0f951b6e | |||
| ffbfb1da35 | |||
| a8416a8433 | |||
| 70c9d1778c | |||
| 930559c96b | |||
| c4af2dfcc8 | |||
| b186549b8d | |||
| ab191b6a72 | |||
| d130687cf9 | |||
| 5e9a442eac | |||
| 1093c25207 | |||
| 0623227807 | |||
| 3a05790125 | |||
| 85b0456011 | |||
| 6ddd16ee24 | |||
| d034f9be45 | |||
| 2f3940f99c | |||
| 5e998e5e42 | |||
| e113f40c5c | |||
| 42a3df2d65 | |||
| 3fb996887b | |||
| 6d2794dd2f | |||
| 8e683f12a2 | |||
| 1910ea2bb6 | |||
| 5f6471a16e | |||
| 98a4baddab | |||
| 67f8de325f | |||
| 594d2a4224 | |||
| edd48e72ec | |||
| 47c9e1bb6e | |||
| 2fe80ecbe3 | |||
| 1640f25d9d | |||
| 28f3bbcad1 | |||
| c4eca264b1 | |||
| 89a9e77380 | |||
| 349b524151 | |||
| 084370b641 | |||
| c2a6cff3eb | |||
| d45e533d91 | |||
| c89de2559a | |||
| a5a267927a | |||
| 557e0acba4 | |||
| 3e4dfadb41 | |||
| b480d49096 | |||
| ece7bf41d3 | |||
| 250dd0b171 | |||
| f2475ddd88 | |||
| f302ca80d7 | |||
| 11b96e56da | |||
| daad9d18ec | |||
| 3c842f6606 | |||
| 23ef007bb1 | |||
| a9b9a17381 | |||
| d5e66618aa | |||
| 2e89f07ee2 | |||
| 1a51bcf0b5 | |||
| f1ee5150a9 | |||
| 9db7f5c985 | |||
| d56363276b | |||
| 5a9b49559b | |||
| fbc8228977 | |||
| a1140f7006 | |||
| f3561ff0fb | |||
| 42b792ab9d | |||
| 1e3a166172 | |||
| e1c6cb357e | |||
| 1aa64fba1f | |||
| 4f0a6f67c3 | |||
| 4f863f7b1d | |||
| b66f9d7ced | |||
| 493f209162 | |||
| 0aa109e280 | |||
| b0a287c6a8 | |||
| 8c00b157ad | |||
| 85bbd25bbb | |||
| 4d7e42fba8 | |||
| f91e8e9910 | |||
| 9e9b0b5557 | |||
| bfcfcdbf16 | |||
| 7e99e7ccab | |||
| fd9adb1c08 | |||
| 7f2448efff | |||
| 82ec224d66 | |||
| 35ea200de5 | |||
| 7cdd387045 | |||
| 6b568ff56c | |||
| 8808a2f81c | |||
| 1991bab675 | |||
| f650097db2 | |||
| c427521ac3 | |||
| 2412059fd0 | |||
| 34dc467e8b | |||
| 9fa16278ab | |||
| cae02d1dc6 | |||
| abbd7b3993 | |||
| 5523511524 | |||
| 35db8ca7ec | |||
| 51e0482a16 | |||
| a615e25fcb | |||
| addede388e | |||
| 76474bb3b5 | |||
| 6337c82374 | |||
| a970411719 | |||
| 10967acf9e | |||
| fb6831d44f | |||
| 7dcae39320 | |||
| a0ba60a2bc | |||
| f377326b48 | |||
| feb8bd718d | |||
| a2a2dbfc24 | |||
| 1c0cb6c97f | |||
| 41c9c60fd1 | |||
| ba5646fe2c | |||
| 1f9ab45206 | |||
| 0ffa1123d2 | |||
| 9420a201e4 | |||
| 1b7ea7321e | |||
| b3e273d712 | |||
| 7fa74d6fd3 | |||
| 02c195500a | |||
| ca8e56c6dd | |||
| c265e9db33 | |||
| 2861473ea3 | |||
| a598aa3898 | |||
| 9d190916a8 | |||
| eca9455f0f | |||
| a1bf0bad0f | |||
| e236ad20f7 | |||
| 330f47baa7 | |||
| 5088aa3ead | |||
| 07a443d082 | |||
| 59a370d763 | |||
| 68e709f34b | |||
| b1bb730c7d | |||
| 20a86f4802 | |||
| 2d918517f8 | |||
| a0c0cbdc98 | |||
| ed309007e5 | |||
| 594c072806 | |||
| 4b9289cb8e | |||
| 5b39e75119 | |||
| e61b2c8f43 | |||
| 4c6952c4b3 | |||
| 6a5cebed5b | |||
| 663e3e8bb7 | |||
| aaa5b9e8ff | |||
| 90ec033b34 | |||
| c3c359cfc8 | |||
| 68b4031752 | |||
| 8a2559571f | |||
| ccae5000ee | |||
| 5f01cb14f9 | |||
| 9f62345a2e | |||
| b9096117bc | |||
| f70659901c | |||
| 451e4624b1 | |||
| 1557ba1040 | |||
| 7133ad8de3 | |||
| 9fd8944987 | |||
| 874276dcba | |||
| be1454f1b8 | |||
| 6a34ca6c18 | |||
| fc09771b8d | |||
| cab5ed0ece | |||
| 8d3dca96b4 | |||
| e71a25630d | |||
| cf79a15837 | |||
| 042e8c2c17 | |||
| 46d286e8f3 | |||
| c2bfcc81ea | |||
| 654ed3741c | |||
| 6cccd1c372 | |||
| 1e6be76449 | |||
| 476ca04f5f | |||
| ce41db474d | |||
| 7ace7e5b5d | |||
| 51e829f7b4 | |||
| 173b2adc66 | |||
| 31cb7a521d | |||
| ad4f68b252 | |||
| 10abf35d4f | |||
| ce767086fd | |||
| 0f44616767 | |||
| bce00b5c0e | |||
| 312cd54f87 | |||
| a0c3437eae | |||
| 711226f913 | |||
| f46c6232b0 | |||
| 172f0ccbce | |||
| a2047cc2de | |||
| 6ea52e6481 | |||
| a7bbcfdc1b | |||
| ff28be3e70 | |||
| e91b4a4424 | |||
| 9960033b72 | |||
| ef135f1a9b | |||
| 1df6380c4a | |||
| fc8cf551e5 | |||
| b1feb0438a | |||
| a89649f774 | |||
| b96e8a3ed5 | |||
| f3226a6cfc | |||
| ff4c503100 | |||
| f543a2d893 | |||
| 41badd52be | |||
| eeddd4e0a5 | |||
| ee4d136834 | |||
| 283efa42b3 | |||
| 5cbab4933c | |||
| 4de2181c18 | |||
| c8f0161a29 | |||
| 60222b6d88 | |||
| 9ea3963239 | |||
| a87592623b | |||
| bfcf53f763 | |||
| df4489c6f2 | |||
| 37185812b4 | |||
| eb3e78244d | |||
| b302d7ba57 | |||
| d6c16169d9 | |||
| 57129da0bd | |||
| 9eaf7123d4 | |||
| 096da29149 | |||
| c8331c51cf | |||
| 3b7618702b | |||
| 84968b4435 | |||
| 98f11a3d80 | |||
| 98d734e869 | |||
| 47004fec8c | |||
| 48ce89489e | |||
| 1ce05a3be3 | |||
| 28ab1116e9 | |||
| 994060d929 | |||
| a5c62564b7 | |||
| 22e61d2b41 | |||
| 6ab5eae0c0 | |||
| 3fbb47ee82 | |||
| 3327b2b0df | |||
| ec204d0389 | |||
| cb1259d2c8 | |||
| 706cf141e7 | |||
| 2229ebb9a0 | |||
| 05022c29b2 | |||
| 9718cfc574 | |||
| aef082ac60 | |||
| 754340fcba | |||
| 16afd4f90c | |||
| b69e2ff53b | |||
| 4ac799f8c9 | |||
| c93d3b61a6 | |||
| 4fb6dbf30f | |||
| 11dc8fc659 | |||
| 4876ba80c1 | |||
| 71f799d157 | |||
| 77c538ca79 | |||
| 6b473b5dc6 | |||
| 6aa11793a5 | |||
| e098eb4626 | |||
| 4f8edc3998 | |||
| 7dd805b804 | |||
| 2aa1219ea9 | |||
| c1a17949f7 | |||
| 24302c7435 | |||
| 27f4fda129 | |||
| 7ddef6d52d | |||
| 187ac61396 | |||
| 31297efb5b | |||
| f8d6475ff8 | |||
| 22a45ac15e | |||
| 0489be5e69 | |||
| 0f8ef167bb | |||
| aa6e3f4b94 | |||
| 68beda891b | |||
| 61bef08a24 | |||
| a6409a037f | |||
| f3ab8241a3 | |||
| f3defdca85 | |||
| f3b1a42819 | |||
| e65d538709 | |||
| 98aef302de | |||
| b3bbf11f0a | |||
| 973517215f | |||
| a3782e2dfc | |||
| 25830d5bf3 | |||
| 1615a7ac8a | |||
| 232211a297 | |||
| eda3d76d4c | |||
| 87f1a1e3e8 | |||
| 5257f89acb | |||
| d4cf7419bf | |||
| 1ffb732bfa | |||
| 9007463f6d | |||
| a4b6b9e493 | |||
| ccf820da7c | |||
| 7c51896bbf | |||
| aa80bab930 | |||
| 1704fc60b4 | |||
| c3e6c90c49 | |||
| 1a65b1daa3 | |||
| ba2c009896 | |||
| bc2b71ecda | |||
| 1d315018a4 | |||
| 56f8b75525 | |||
| 59531df377 | |||
| 06a7386211 | |||
| 4bb3a11261 | |||
| f308eeca8d | |||
| 8e562ed1a5 | |||
| 23dd7571a3 | |||
| 8530b2d1a0 | |||
| acdbddbc79 | |||
| 15fc5f4f14 | |||
| 2ba3b01646 | |||
| 4e960a2f53 | |||
| ab700edcf2 | |||
| 42e80c7a11 | |||
| d4472a881a | |||
| 5d12ab4f62 | |||
| 8b3c586456 | |||
| e456b55e16 | |||
| 89c5936212 | |||
| 7bedf76945 | |||
| 54bf020c55 | |||
| 78145eee77 | |||
| d75b6ee26c | |||
| bf5c9d4671 | |||
| d2db38ba21 | |||
| eecbca6b0e | |||
| dd1430a350 | |||
| 0eca0c52ce | |||
| c2c7ad7bb4 | |||
| 98ccbce4c0 | |||
| 6484640e1a | |||
| ba3c4e1918 | |||
| fa4fe35bdf | |||
| c106e66f44 | |||
| 222c739663 | |||
| 96972dbe2f | |||
| 7146048aee | |||
| 39f1f5bc73 | |||
| fb7913c563 | |||
| feb301c3c0 | |||
| 752fc6a4a8 | |||
| 608d5b92f1 | |||
| 45563cc59b | |||
| b027e20c20 | |||
| bbe0777496 | |||
| 27efbe362b | |||
| 878539a56a | |||
| 6ae408029d | |||
| 8016d1bd12 | |||
| 0e9fef218f | |||
| 8116665045 | |||
| e4addc47d9 | |||
| 1682f11efd | |||
| 80b2abf9e3 | |||
| 5edecb6464 | |||
| 3a479b3573 | |||
| 4034909836 | |||
| 8fc61a6289 | |||
| 940a12a2e9 | |||
| a9f671eeaa | |||
| 481924fa02 | |||
| 6b712089d1 | |||
| 85ca2cb15a | |||
| d01c4928ff | |||
| b81e62988c | |||
| a8b63917fb | |||
| 9cf6d13716 | |||
| c1af2b7fc9 | |||
| 494c1f3271 | |||
| 9f0c33521f | |||
| 1d5875d4e2 | |||
| 6f6b12f4b5 | |||
| f3ad4d576e | |||
| e01d0106ad | |||
| 68472f8c66 | |||
| 8def463d80 | |||
| 0320999f72 | |||
| 78a97d8b0b | |||
| 6bf6c5d2d4 | |||
| 33284600e5 | |||
| 68bf995078 | |||
| fa05f47941 | |||
| 2391857309 | |||
| ed38cadcee | |||
| ea447a9366 | |||
| 4e2f120ab5 | |||
| 20ce9bf749 | |||
| 04e1bcc5f1 | |||
| cbaa8b6f7b | |||
| f6bdb1c299 | |||
| d4061d72da | |||
| 2d9a8b55b5 | |||
| 920eafed29 | |||
| 21d926eb5c | |||
| ec9d793d6b | |||
| 2fd04dc86d | |||
| d7752cb70b | |||
| dc592f60db | |||
| 675ba2989e | |||
| b201224fd6 | |||
| c24013eefd | |||
| 234f04ca8f | |||
| 5f222a2d88 | |||
| 258434a109 | |||
| ed148612a7 | |||
| 2287fb5ebb | |||
| 24545baad5 | |||
| ead7589c92 | |||
| 51bb8fc421 | |||
| d2166d9578 | |||
| c4872f522b | |||
| 0aef8c5e1e | |||
| 767b5c1b32 | |||
| 6fed51443d | |||
| 017ba7bd2f | |||
| 86d876b013 | |||
| fcd8898439 | |||
| 690f496807 | |||
| f804e13b56 | |||
| 5e248bc108 | |||
| 26647b088f | |||
| ae8ada33f4 | |||
| c31dab92e7 | |||
| c16764269e | |||
| afe6a1afcb | |||
| e98929627f | |||
| 3919a2bc4b | |||
| 93e89f996c | |||
| 7ede9883ba | |||
| 7edc7b08e5 | |||
| 7990959e9f | |||
| 6ead632c79 | |||
| 9c575716cd | |||
| cc93d5980d | |||
| e899e5eb2a | |||
| c53ee2833f | |||
| 9475961c2d | |||
| 97ae9dd136 | |||
| ded088c70f | |||
| 4dacee11e8 | |||
| bcca701a6b |
@@ -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
|
||||
@@ -42,13 +41,58 @@ REACT_APP_GOOGLE_CLIENT_SECRET=aozK_2G8UjaCmLgPPkv9abIm
|
||||
REACT_APP_GOOGLE_CLIENT_SCOPE="https://www.googleapis.com/auth/plus.login https://www.googleapis.com/auth/userinfo.email https://www.googleapis.com/auth/userinfo.profile"
|
||||
REACT_APP_GOOGLE_REDIRECT_URL=http://localhost:9082/login/auth/
|
||||
|
||||
REACT_APP_FACEBOOK_CLIENT_ID=390204307987009
|
||||
REACT_APP_FACEBOOK_CLIENT_SECRET=19f778e312f2ab96d147bacb612910c2
|
||||
REACT_APP_FACEBOOK_CLIENT_SCOPE="email, public_profile"
|
||||
#Real Account
|
||||
REACT_APP_FACEBOOK_CLIENT_ID2=390204307987009
|
||||
REACT_APP_FACEBOOK_CLIENT_SECRET2=19f778e312f2ab96d147bacb612910c2
|
||||
|
||||
#development Account Social
|
||||
REACT_APP_FACEBOOK_CLIENT_ID=677857427521030
|
||||
REACT_APP_FACEBOOK_CLIENT_SECRET=4801375f22072d8a75f64483fdd89829
|
||||
|
||||
#my Account
|
||||
REACT_APP_FACEBOOK_CLIENT_ID3=1598725580610908
|
||||
|
||||
|
||||
REACT_APP_FACEBOOK_CLIENT_SCOPE="email,public_profile"
|
||||
REACT_APP_FACEBOOK_REDIRECT_URL="http://localhost:9082/login/auth/flogin"
|
||||
|
||||
REACT_APP_APPLE_CLIENT_ID='com.wrenchboard.users.client'
|
||||
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
|
||||
@@ -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
|
||||
@@ -41,4 +40,27 @@ REACT_APP_GOOGLE_CLIENT_SCOPE="https://www.googleapis.com/auth/plus.login https:
|
||||
REACT_APP_GOOGLE_REDIRECT_URL=http://localhost:9082/login/auth/
|
||||
|
||||
REACT_APP_MAX_FILE_SIZE=1000000
|
||||
REACT_APP_TOTAL_NUM_FILE=4
|
||||
REACT_APP_TOTAL_NUM_FILE=4
|
||||
|
||||
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
|
||||
@@ -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
|
||||
@@ -40,7 +39,34 @@ REACT_APP_GOOGLE_CLIENT_SECRET=aozK_2G8UjaCmLgPPkv9abIm
|
||||
REACT_APP_GOOGLE_CLIENT_SCOPE="https://www.googleapis.com/auth/plus.login https://www.googleapis.com/auth/userinfo.email https://www.googleapis.com/auth/userinfo.profile"
|
||||
REACT_APP_GOOGLE_REDIRECT_URL=https://users.wrenchboard.com/login/auth/
|
||||
|
||||
REACT_APP_FACEBOOK_CLIENT_ID2=390204307987009
|
||||
REACT_APP_FACEBOOK_CLIENT_SECRET2=19f778e312f2ab96d147bacb612910c2
|
||||
REACT_APP_FACEBOOK_CLIENT_SCOPE="email,public_profile"
|
||||
REACT_APP_FACEBOOK_REDIRECT_URL="https://users.wrenchboard.com/login/auth/flogin"
|
||||
|
||||
DISABLE_ESLINT_PLUGIN=true
|
||||
|
||||
REACT_APP_MAX_FILE_SIZE=1000000
|
||||
REACT_APP_TOTAL_NUM_FILE=4
|
||||
REACT_APP_TOTAL_NUM_FILE=4
|
||||
|
||||
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
|
||||
|
||||
@@ -19,6 +19,8 @@
|
||||
.env.test.local
|
||||
.env.production.local
|
||||
|
||||
#package-lock.json
|
||||
|
||||
npm-debug.log*
|
||||
yarn-debug.log*
|
||||
yarn-error.log*
|
||||
|
||||
@@ -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",
|
||||
|
||||
@@ -17,9 +17,11 @@
|
||||
"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",
|
||||
|
||||
|
After Width: | Height: | Size: 32 KiB |
|
After Width: | Height: | Size: 18 KiB |
|
After Width: | Height: | Size: 84 KiB |
|
After Width: | Height: | Size: 16 KiB |
|
After Width: | Height: | Size: 5.3 KiB |
|
After Width: | Height: | Size: 4.1 KiB |
|
After Width: | Height: | Size: 3.6 KiB |
|
After Width: | Height: | Size: 2.9 KiB |
|
After Width: | Height: | Size: 5.2 KiB |
|
After Width: | Height: | Size: 1.7 KiB |
|
After Width: | Height: | Size: 3.8 KiB |
|
After Width: | Height: | Size: 5.3 KiB |
|
After Width: | Height: | Size: 2.3 KiB |
|
After Width: | Height: | Size: 3.1 KiB |
|
After Width: | Height: | Size: 713 B |
|
After Width: | Height: | Size: 1.6 KiB |
|
After Width: | Height: | Size: 15 KiB |
@@ -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"}
|
||||
|
After Width: | Height: | Size: 45 KiB |
|
After Width: | Height: | Size: 59 KiB |
|
After Width: | Height: | Size: 64 KiB |
|
After Width: | Height: | Size: 28 KiB |
|
After Width: | Height: | Size: 64 KiB |
|
After Width: | Height: | Size: 50 KiB |
|
After Width: | Height: | Size: 48 KiB |
|
After Width: | Height: | Size: 54 KiB |
|
After Width: | Height: | Size: 51 KiB |
|
After Width: | Height: | Size: 65 KiB |
|
After Width: | Height: | Size: 31 KiB |
@@ -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"}
|
||||
@@ -48,6 +48,7 @@ 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";
|
||||
|
||||
export default function Routers() {
|
||||
return (
|
||||
@@ -55,9 +56,13 @@ export default function Routers() {
|
||||
<Routes>
|
||||
{/* guest routes */}
|
||||
<Route exact path="/login" element={<LoginPage />} />
|
||||
<Route exact path="/eoffer" element={<LoginPage />} />
|
||||
<Route exact path="/invite" element={<LoginPage />} />
|
||||
|
||||
<Route exact path="/signup" element={<SignupPage />} />
|
||||
<Route exact path="/login/auth" element={<AuthRedirect />} />
|
||||
<Route exact path="/login/auth/flogin" element={<FacebookRedirect />} />
|
||||
<Route exact path="/login/auth/apple" element={<AppleRedirectPage />} />
|
||||
<Route
|
||||
exact
|
||||
path="/forgot-password"
|
||||
@@ -117,7 +122,8 @@ 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 />} />
|
||||
|
||||
|
After Width: | Height: | Size: 172 KiB |
|
After Width: | Height: | Size: 215 B |
@@ -0,0 +1 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 48 48" id="naira"><path fill="#fff" d="M24,5A19,19,0,1,0,43,24,19,19,0,0,0,24,5Zm7,20a1,1,0,0,1,0,2H29v3A1.94,1.94,0,0,1,27.45,32a2,2,0,0,1-2.26-1.08L20,17.05l0,14a1,1,0,0,1-2,0V27H16a1,1,0,0,1,0-2h2V22H16a1,1,0,0,1,0-2h2V17a1.94,1.94,0,0,1,1.55-1.92,2,2,0,0,1,2.26,1.08L27,30,27,16a1,1,0,0,1,2,0v4h2a1,1,0,0,1,0,2H29v3Z" class="color3b3c3d svgShape"></path><path fill="#e3e3e3" d="M24,48A24,24,0,1,1,48,24,24,24,0,0,1,24,48ZM24,2A22,22,0,1,0,46,24,22,22,0,0,0,24,2Z" class="color3b3c3d svgShape"></path></svg>
|
||||
|
After Width: | Height: | Size: 563 B |
@@ -0,0 +1 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16.933 16.933" id="Coin"><path d="M8.452 281.052a7.487 7.487 0 0 0-7.483 7.482 7.485 7.485 0 0 0 7.483 7.48 7.484 7.484 0 0 0 7.48-7.48 7.486 7.486 0 0 0-7.48-7.482zm0 1.232a6.253 6.253 0 0 1 6.248 6.25 6.25 6.25 0 0 1-6.248 6.248c-3.449 0-6.25-2.8-6.25-6.248a6.254 6.254 0 0 1 6.25-6.25zm0 .53a5.717 5.717 0 0 0-5.721 5.72 5.715 5.715 0 0 0 5.72 5.719 5.713 5.713 0 0 0 5.72-5.719 5.715 5.715 0 0 0-5.72-5.72zm-.004 2.011a.265.265 0 0 1 .267.268v.596H9.74a.265.265 0 1 1 0 .529H8.715v2.05h.17c.856 0 1.551.697 1.551 1.555s-.696 1.555-1.549 1.555h-.172v.596a.265.265 0 1 1-.529 0v-.596H7.161a.265.265 0 1 1 0-.53h1.025v-2.05h-.172a1.552 1.552 0 0 1-1.547-1.555c0-.858.694-1.554 1.547-1.554h.172v-.596a.265.265 0 0 1 .262-.268zm-.434 1.393a1.01 1.01 0 0 0-1.018 1.025 1.01 1.01 0 0 0 1.018 1.026h.172v-2.051zm.701 2.58v2.05h.172c.57 0 1.02-.448 1.02-1.025s-.449-1.025-1.022-1.025z" color="#e3e3e3" font-family="sans-serif" font-weight="400" overflow="visible" transform="translate(0 -280.067)" style="line-height:normal;font-variant-ligatures:normal;font-variant-position:normal;font-variant-caps:normal;font-variant-numeric:normal;font-variant-alternates:normal;font-feature-settings:normal;text-indent:0;text-align:start;text-decoration-line:none;text-decoration-style:solid;text-decoration-color:#000;text-transform:none;text-orientation:mixed;shape-padding:0;isolation:auto;mix-blend-mode:normal" fill="#fff" class="color000000 svgShape"></path></svg>
|
||||
|
After Width: | Height: | Size: 1.5 KiB |
|
After Width: | Height: | Size: 24 KiB |
|
After Width: | Height: | Size: 19 KiB |
@@ -0,0 +1 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 1792 1792" id="Bank"><path d="m896 78.526 896 358.4v119.466h-119.467q0 24.267-19.133 42-19.133 17.734-45.267 17.734H183.867q-26.134 0-45.267-17.734-19.133-17.733-19.133-42H0V436.926ZM238.933 675.859h238.934v716.8h119.466v-716.8h238.934v716.8h119.466v-716.8h238.934v716.8h119.466v-716.8h238.934v716.8h55.066q26.134 0 45.267 17.733 19.133 17.734 19.133 42v59.734H119.467v-59.734q0-24.266 19.133-42 19.133-17.733 45.267-17.733h55.066v-716.8zm1488.667 896q26.133 0 45.267 17.733 19.133 17.734 19.133 42v119.467H0v-119.467q0-24.266 19.133-42 19.134-17.733 45.267-17.733h1663.2z" fill="#4687ba" class="color000000 svgShape"></path></svg>
|
||||
|
After Width: | Height: | Size: 684 B |
@@ -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 |
@@ -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 |
@@ -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 |
@@ -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 |
@@ -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 |
@@ -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 |
@@ -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 |
@@ -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 |
@@ -0,0 +1,4 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" enable-background="new 0 0 128 128" viewBox="0 0 128 128" id="Alert"><path fill="#f34093" d="M122.9,100.2L74.3,15.9c-4.6-7.9-16-7.9-20.6,0L5.1,100.2C0.5,108.1,6.2,118,15.4,118h97.3
|
||||
C121.8,118,127.5,108.1,122.9,100.2z M63.9,101c-3.5-0.1-6.7-3.3-6.6-6.8c0.1-3.5,3.3-6.7,6.8-6.6c3.5,0.1,6.7,3.3,6.6,6.8
|
||||
C70.6,98,67.4,101.1,63.9,101z M71.3,51.8c-0.7,7.8-1.5,15.6-2.3,23.4c-0.5,4.4-3.2,6.1-7.3,4.5c-1-0.4-2-2-2.2-3.2
|
||||
C58.6,70,58,63.4,57.3,56.8c-0.3-2.8-0.5-5.6-0.8-8.5c-0.4-3.3,0.6-4.5,4-4.5c1.1,0,2.3,0,3.4,0C72,43.8,72,43.8,71.3,51.8z" class="color343433 svgShape"></path></svg>
|
||||
|
After Width: | Height: | Size: 621 B |
@@ -0,0 +1 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" id="Messages"><path fill="#4687ba" d="m447.372 413.112-17.544-52.629c-1-3.002-.674-6.23.775-9.043 13.184-25.615 18.629-55.858 12.925-87.736-8.746-48.868-44.53-89.475-90.943-105.048 11.976 28.924 15.628 61.084 9.645 92.984-12.155 64.837-64.074 116.246-129.191 127.924-8.429 1.512-16.945 2.248-25.396 2.463 19.776 21.336 46.173 36.503 75.619 41.784 31.682 5.682 61.784.393 87.322-12.594 2.802-1.425 6.007-1.742 8.99-.748l52.83 17.61c9.251 3.084 18.052-5.716 14.968-14.967z" class="color173042 svgShape"></path><path fill="#4687ba" d="M177.161 85.744C122.283 95.941 78.306 140.526 68.472 195.47c-5.704 31.878-.259 62.121 12.925 87.736 1.449 2.813 1.775 6.041.776 9.043l-17.545 52.629c-3.083 9.251 5.717 18.051 14.968 14.968l52.83-17.61c2.984-.994 6.19-.677 8.991.748 25.537 12.987 55.638 18.276 87.321 12.594 54.809-9.829 99.281-53.698 109.541-108.428 18.078-96.414-64.733-179.316-161.118-161.406zm34.938 177.536h-68.234c-6.73 0-12.185-5.454-12.185-12.185 0-6.73 5.455-12.185 12.185-12.185h68.234c6.731 0 12.185 5.455 12.185 12.185 0 6.731-5.454 12.185-12.185 12.185zm48.739-48.739H143.864c-6.73 0-12.185-5.454-12.185-12.185 0-6.73 5.455-12.185 12.185-12.185h116.973c6.731 0 12.185 5.455 12.185 12.185.001 6.731-5.453 12.185-12.184 12.185z" class="color173042 svgShape"></path></svg>
|
||||
|
After Width: | Height: | Size: 1.3 KiB |
@@ -0,0 +1 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" id="phone" x="0" y="0" version="1.1" viewBox="0 0 29 29" xml:space="preserve"><path d="M5.661 23.339c4.882 4.882 12.796 4.882 17.678 0 4.882-4.882 4.882-12.796 0-17.678s-12.796-4.882-17.678 0C.78 10.543.78 18.457 5.661 23.339zm5.545-16.613l1.86 1.86c.372.372.451.947.193 1.406l-.699 1.241a.781.781 0 0 0 .129.937l4.141 4.141a.781.781 0 0 0 .937.128l1.241-.699a1.173 1.173 0 0 1 1.406.193l1.861 1.861a1.175 1.175 0 0 1 0 1.66l-.881.881a3.135 3.135 0 0 1-3.786.493l-.084-.048a24.54 24.54 0 0 1-8.868-8.681l-.423-.706a3.128 3.128 0 0 1 .469-3.823l.844-.844a1.173 1.173 0 0 1 1.66 0z" fill="#4687ba" class="color000000 svgShape"></path></svg>
|
||||
|
After Width: | Height: | Size: 678 B |
@@ -0,0 +1 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" version="1.2" viewBox="0 0 512 512" id="Sms"><circle cx="256" cy="256" r="256" fill="#4687ba" class="color00aebb svgShape"></circle><path fill="#ffffff" d="M256 129.3c-88.4 0-160 52-160 116.2 0 26.6 12.3 51.1 33 70.7-11.6 26.2-22.6 66.2-22.6 66.2 6.1 3.1 60.9-23.9 78.4-32.8 21.4 7.7 45.6 12.1 71.2 12.1 88.4 0 160-52 160-116.2s-71.6-116.2-160-116.2zm-59.2 148.9c-5.2 3.8-12 5.7-20.4 5.7-8.3 0-15.4-2.1-21.4-6.2-6-4.1-8.8-10.3-8.7-18.5l.1-.3h15c0 4.6 1.3 7.9 4 10.1 2.7 2.2 6.3 3.2 10.9 3.2 4.1 0 7.3-.9 9.5-2.6 2.2-1.7 3.3-4 3.3-6.9 0-3-1-5.3-3.1-7.1-2.1-1.8-5.7-3.5-10.9-5.1-8.9-2.7-15.6-5.8-20.3-9.5-4.6-3.6-6.9-8.6-6.9-14.9 0-6.3 2.6-11.5 7.8-15.5 5.2-4 11.8-6 19.9-6 8.5 0 15.4 2.2 20.6 6.5 5.2 4.3 7.7 10 7.5 16.9l-.1.3h-15c0-3.8-1.2-6.8-3.5-8.9-2.3-2.1-5.6-3.1-9.8-3.1-3.8 0-6.8.9-8.8 2.8-2.1 1.8-3.1 4.2-3.1 7 0 2.6 1.1 4.8 3.4 6.4 2.3 1.7 6.2 3.5 11.7 5.3 8.5 2.4 14.9 5.5 19.4 9.4 4.4 3.9 6.7 9 6.7 15.3-.1 6.7-2.7 11.9-7.8 15.7zm99.3 4.7h-15.5v-52.6l-.3-.1-19.1 52.7h-10.5l-19.1-52.8-.3.1v52.7h-15.5v-77.1h20.3l19.6 56.8h.3l19.8-56.8H296v77.1zm62.1-4.7c-5.2 3.8-12 5.7-20.4 5.7-8.3 0-15.4-2.1-21.4-6.2-5.9-4.1-8.8-10.3-8.7-18.5l.1-.3h15c0 4.6 1.3 7.9 4 10.1 2.7 2.2 6.3 3.2 10.9 3.2 4.1 0 7.3-.9 9.5-2.6 2.2-1.7 3.3-4 3.3-6.9 0-3-1-5.3-3.1-7.1-2.1-1.8-5.7-3.5-10.9-5.1-8.9-2.7-15.7-5.8-20.3-9.5-4.6-3.6-6.9-8.6-6.9-14.9 0-6.3 2.6-11.5 7.8-15.5 5.2-4 11.8-6 19.9-6 8.5 0 15.4 2.2 20.6 6.5 5.2 4.3 7.7 10 7.5 16.9l-.1.3h-15c0-3.8-1.2-6.8-3.5-8.9-2.3-2.1-5.6-3.1-9.8-3.1-3.8 0-6.8.9-8.8 2.8-2.1 1.8-3.1 4.2-3.1 7 0 2.6 1.1 4.8 3.4 6.4 2.3 1.7 6.2 3.5 11.7 5.3 8.5 2.4 14.9 5.5 19.4 9.4 4.4 3.9 6.7 9 6.7 15.3-.1 6.7-2.6 11.9-7.8 15.7z" class="colorffffff svgShape"></path></svg>
|
||||
|
After Width: | Height: | Size: 1.7 KiB |
|
After Width: | Height: | Size: 5.5 KiB |
|
After Width: | Height: | Size: 5.1 KiB |
|
After Width: | Height: | Size: 5.0 KiB |
@@ -0,0 +1 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 30 30" id="Visacard"><g fill="#767fad" class="color303c42 svgShape"><path d="M28 4H2C.897 4 0 4.897 0 6v18c0 1.103.897 2 2 2h26c1.103 0 2-.897 2-2V6c0-1.103-.897-2-2-2zm1 20c0 .551-.449 1-1 1H2c-.551 0-1-.449-1-1V6c0-.551.449-1 1-1h26c.551 0 1 .449 1 1v18z" fill="#5218ed" class="color000000 svgShape"></path><path d="M7.677 14.266a.2.2 0 0 0 .228.053.17.17 0 0 0 .106-.194l-.314-1.368a.121.121 0 0 0-.002-.009c-.113-.39-.47-.449-.729-.459H6.96l-1.768-.002c-.091 0-.17.06-.185.143a.172.172 0 0 0 .121.193c1.078.358 1.96.926 2.55 1.643zM23.535 12.303h-1.15c-.46 0-.724.141-.883.473l-2.383 4.981a.162.162 0 0 0 .014.165c.035.05.094.079.158.079h1.35a.189.189 0 0 0 .176-.112c.165-.404.272-.661.307-.744h.974l1.183.002c.039.151.134.552.173.717.019.08.095.137.184.137h1.177c.057 0 .11-.024.146-.065s.05-.095.038-.146l-1.28-5.351a.185.185 0 0 0-.184-.136zm-1.926 3.675.895-2.138.114.492.39 1.646h-1.4z" fill="#5218ed" class="color000000 svgShape"></path><path d="M11.816 12.38a.193.193 0 0 0-.157-.078h-1.37a.188.188 0 0 0-.175.11L8.511 16.19l-.172-.575a.142.142 0 0 0-.009-.023c-.253-.542-.933-1.39-1.881-2.062-.065-.046-.154-.048-.221-.006s-.097.12-.074.192L7.47 17.87a.187.187 0 0 0 .18.124l1.518-.002a.19.19 0 0 0 .173-.104l2.49-5.344a.162.162 0 0 0-.016-.164zM14.088 12.297h-1.303a.184.184 0 0 0-.186.144l-.989 5.355a.165.165 0 0 0 .042.141c.036.04.088.062.144.062h1.302c.092 0 .17-.061.186-.145l.99-5.354a.164.164 0 0 0-.042-.141.195.195 0 0 0-.144-.062zM17.696 13.452h.05c.51 0 .87.107 1.097.187a.202.202 0 0 0 .16-.011.175.175 0 0 0 .093-.122l.152-.826c.016-.085-.039-.168-.128-.194a4.634 4.634 0 0 0-1.3-.182c-1.607 0-2.735.746-2.744 1.814-.01.79.807 1.231 1.422 1.494.632.269.844.441.841.682-.004.369-.506.536-.97.536a3.676 3.676 0 0 1-1.522-.285.202.202 0 0 0-.163.008.175.175 0 0 0-.095.123l-.161.87a.173.173 0 0 0 .123.193c.444.144 1.064.233 1.66.24h.001c1.705-.001 2.815-.739 2.827-1.88.006-.627-.427-1.1-1.36-1.49-.57-.256-.92-.426-.917-.686 0-.234.32-.47.934-.47z" fill="#5218ed" class="color000000 svgShape"></path></g></svg>
|
||||
|
After Width: | Height: | Size: 2.0 KiB |
|
After Width: | Height: | Size: 22 KiB |
|
After Width: | Height: | Size: 94 KiB |
|
After Width: | Height: | Size: 5.0 KiB |
|
After Width: | Height: | Size: 24 KiB |
|
After Width: | Height: | Size: 19 KiB |
|
After Width: | Height: | Size: 100 KiB |
|
After Width: | Height: | Size: 101 KiB |
@@ -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 |
|
After Width: | Height: | Size: 110 KiB |
@@ -1,6 +1,6 @@
|
||||
import { Field, Form, Formik } from "formik";
|
||||
import React, { useEffect, useState } from "react";
|
||||
import { useDispatch } from "react-redux";
|
||||
import { useDispatch, useSelector } from "react-redux";
|
||||
import * as Yup from "yup";
|
||||
import usersService from "../../services/UsersService";
|
||||
import { tableReload } from "../../store/TableReloads";
|
||||
@@ -11,7 +11,7 @@ const validationSchema = Yup.object().shape({
|
||||
country: Yup.string()
|
||||
.min(1, "Minimum 3 characters")
|
||||
.max(25, "Maximum 25 characters")
|
||||
.required("Country is required"),
|
||||
.required("Currency is required"),
|
||||
price: Yup.string()
|
||||
.typeError("Invalid number")
|
||||
.min(1, "Price must be greater than 0")
|
||||
@@ -32,7 +32,7 @@ const validationSchema = Yup.object().shape({
|
||||
.required("Description is required"),
|
||||
job_detail: Yup.string()
|
||||
.min(3, "Minimum 3 characters")
|
||||
.max(1440, "Maximum 1440 characters")
|
||||
.max(499, "Maximum 499 characters")
|
||||
.required("Details is required"),
|
||||
timeline_days: Yup.number()
|
||||
.typeError("you must specify a number")
|
||||
@@ -43,15 +43,9 @@ const validationSchema = Yup.object().shape({
|
||||
|
||||
function AddJob({ popUpHandler, categories }) {
|
||||
const ApiCall = new usersService();
|
||||
|
||||
const { walletDetails } = useSelector((state) => state.walletDetails);
|
||||
let dispatch = useDispatch();
|
||||
|
||||
let [currency, setCurrency] = useState({
|
||||
loading: true,
|
||||
status: false,
|
||||
data: null,
|
||||
}); // To Hold the array of currency getUserCurrency returns
|
||||
|
||||
let initialValues = {
|
||||
// initial values for formik
|
||||
country: "",
|
||||
@@ -69,30 +63,15 @@ function AddJob({ popUpHandler, categories }) {
|
||||
message: "",
|
||||
}); // Holds state when submit button is pressed
|
||||
|
||||
// FUNCTION TO GET Currency
|
||||
const getUserCurrency = () => {
|
||||
setCurrency((prev) => ({ ...prev, loading: true }));
|
||||
ApiCall.getUserWallets()
|
||||
.then((res) => {
|
||||
if (res.data.internal_return < 0) {
|
||||
setCurrency({ loading: false, status: true, data: [] });
|
||||
return;
|
||||
}
|
||||
|
||||
setCurrency({
|
||||
loading: false,
|
||||
status: true,
|
||||
data: res.data.result_list,
|
||||
});
|
||||
})
|
||||
.catch((err) => {
|
||||
setCurrency({ loading: false, status: false, data: [] });
|
||||
});
|
||||
const getWalletDetail = (country) => { // A FUNCTION TO GET USER BALANCE BASED ON COUNTRY SELECTED
|
||||
const walletChecker = walletDetails?.data.find(
|
||||
(item) => item.country === country
|
||||
);
|
||||
return walletChecker ? walletChecker.amount : 0;
|
||||
};
|
||||
|
||||
// FUNCTION TO HANDLE ADD JOB FORM
|
||||
const handleAddJob = (values, helpers) => {
|
||||
let reqData = {
|
||||
const handleAddJob = async (values, helpers) => {
|
||||
const reqData = {
|
||||
country: values?.country,
|
||||
price: Number(values.price) * 100,
|
||||
title: values?.title,
|
||||
@@ -102,20 +81,35 @@ function AddJob({ popUpHandler, categories }) {
|
||||
category: values.category?.join("@"),
|
||||
};
|
||||
|
||||
const walletAmount = getWalletDetail(reqData.country); // GETTING USER BALANCE BASED ON COUNTRY SELECTED
|
||||
|
||||
if (reqData.price > walletAmount) {
|
||||
setRequestStatus({
|
||||
loading: false,
|
||||
status: false,
|
||||
message: "Insufficient Balance",
|
||||
});
|
||||
|
||||
setTimeout(() => {
|
||||
setRequestStatus({ loading: false, status: false, message: "" });
|
||||
}, 1500);
|
||||
return;
|
||||
}
|
||||
|
||||
setRequestStatus({ loading: true, status: false, message: "" });
|
||||
ApiCall.jobManagerCreateJob(reqData)
|
||||
.then((res) => {
|
||||
if (res.data.internal_return < 1) {
|
||||
setRequestStatus({
|
||||
loading: false,
|
||||
status: false,
|
||||
message: "Could not complete your request at the moment",
|
||||
});
|
||||
setTimeout(() => {
|
||||
popUpHandler();
|
||||
}, 1500);
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
const res = await ApiCall.jobManagerCreateJob(reqData);
|
||||
if (res.data.internal_return < 1) {
|
||||
setRequestStatus({
|
||||
loading: false,
|
||||
status: false,
|
||||
message: "Could not complete your request at the moment",
|
||||
});
|
||||
setTimeout(() => {
|
||||
popUpHandler();
|
||||
}, 1500);
|
||||
} else {
|
||||
setRequestStatus({
|
||||
loading: false,
|
||||
status: true,
|
||||
@@ -125,27 +119,22 @@ function AddJob({ popUpHandler, categories }) {
|
||||
dispatch(tableReload({ type: "JOBTABLE" }));
|
||||
popUpHandler();
|
||||
}, 1000);
|
||||
})
|
||||
.catch((err) => {
|
||||
setRequestStatus({
|
||||
loading: false,
|
||||
status: false,
|
||||
message: "Opps! something went wrong. Try Again",
|
||||
});
|
||||
})
|
||||
.finally(() => {
|
||||
setTimeout(() => {
|
||||
setRequestStatus({ loading: false, status: false, message: "" });
|
||||
}, 5000);
|
||||
}
|
||||
} catch (err) {
|
||||
setRequestStatus({
|
||||
loading: false,
|
||||
status: false,
|
||||
message: "Oops! Something went wrong. Try Again",
|
||||
});
|
||||
} finally {
|
||||
setTimeout(() => {
|
||||
setRequestStatus({ loading: false, status: false, message: "" });
|
||||
}, 5000);
|
||||
}
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
getUserCurrency();
|
||||
}, []);
|
||||
|
||||
return (
|
||||
<div className="add-job p-5 w-full bg-white rounded-md flex flex-col justify-between">
|
||||
<div className="add-job p-5 w-full bg-white dark:bg-dark-white dark:text-white rounded-md flex flex-col justify-between">
|
||||
<Formik
|
||||
initialValues={initialValues}
|
||||
validationSchema={validationSchema}
|
||||
@@ -164,26 +153,30 @@ function AddJob({ popUpHandler, categories }) {
|
||||
className="input-label text-[#181c32] dark:text-white text-[13.975px] leading-[20.9625px] font-semibold flex item-center gap-1"
|
||||
>
|
||||
Currency
|
||||
{props.errors.country && props.touched.country && <span className="text-[12px] text-red-500">{props.errors.country}</span>}
|
||||
{props.errors.country && props.touched.country && (
|
||||
<span className="text-[12px] text-red-500">
|
||||
{props.errors.country}
|
||||
</span>
|
||||
)}
|
||||
</label>
|
||||
<select
|
||||
id="country"
|
||||
name="country"
|
||||
value={props.values.country}
|
||||
className={`input-field p-2 mt-3 rounded-md placeholder:text-base text-dark-gray dark:text-white w-full h-10 bg-slate-100 dark:bg-[#11131F] focus:ring-0 focus:outline-none`}
|
||||
className={`input-field p-2 mt-3 rounded-md placeholder:text-base text-dark-gray dark:text-white w-full h-10 bg-slate-100 dark:bg-[#11131F] focus:ring-0 focus:outline-none border`}
|
||||
onChange={props.handleChange}
|
||||
onBlur={props.handleBlur}
|
||||
>
|
||||
{currency.loading ? (
|
||||
{walletDetails.loading ? (
|
||||
<option className="text-slate-500 text-lg" value="">
|
||||
Loading...
|
||||
</option>
|
||||
) : currency.data.length ? (
|
||||
) : walletDetails.data.length ? (
|
||||
<>
|
||||
<option className="text-slate-500 text-lg" value="">
|
||||
Select a currency
|
||||
</option>
|
||||
{currency.data?.map((item, index) => (
|
||||
{walletDetails.data?.map((item, index) => (
|
||||
<option
|
||||
key={index}
|
||||
className="text-slate-500 text-lg"
|
||||
@@ -207,14 +200,17 @@ function AddJob({ popUpHandler, categories }) {
|
||||
fieldClass="px-6 text-right"
|
||||
label="Price"
|
||||
labelClass="tracking-wide"
|
||||
inputBg="bg-slate-100"
|
||||
type="number"
|
||||
name="price"
|
||||
placeholder="0"
|
||||
value={props.values.price}
|
||||
inputHandler={props.handleChange}
|
||||
blurHandler={props.handleBlur}
|
||||
error={props.errors.price && props.touched.price && props.errors.price}
|
||||
error={
|
||||
props.errors.price &&
|
||||
props.touched.price &&
|
||||
props.errors.price
|
||||
}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
@@ -225,13 +221,16 @@ function AddJob({ popUpHandler, categories }) {
|
||||
fieldClass="px-6"
|
||||
label="Title"
|
||||
labelClass="tracking-wide"
|
||||
inputBg="bg-slate-100"
|
||||
type="text"
|
||||
name="title"
|
||||
value={props.values.title}
|
||||
inputHandler={props.handleChange}
|
||||
blurHandler={props.handleBlur}
|
||||
error={props.errors.title && props.touched.title && props.errors.title}
|
||||
error={
|
||||
props.errors.title &&
|
||||
props.touched.title &&
|
||||
props.errors.title
|
||||
}
|
||||
/>
|
||||
</div>
|
||||
|
||||
@@ -241,13 +240,16 @@ function AddJob({ popUpHandler, categories }) {
|
||||
fieldClass="px-6"
|
||||
label="Description"
|
||||
labelClass="tracking-wide"
|
||||
inputBg="bg-slate-100"
|
||||
type="text"
|
||||
name="description"
|
||||
value={props.values.description}
|
||||
inputHandler={props.handleChange}
|
||||
blurHandler={props.handleBlur}
|
||||
error={props.errors.description && props.touched.description && props.errors.description}
|
||||
error={
|
||||
props.errors.description &&
|
||||
props.touched.description &&
|
||||
props.errors.description
|
||||
}
|
||||
/>
|
||||
</div>
|
||||
|
||||
@@ -259,12 +261,17 @@ function AddJob({ popUpHandler, categories }) {
|
||||
className='className="input-label text-[#181c32] dark:text-white text-[13.975px] leading-[20.9625px] font-semibold flex items-center gap-1'
|
||||
>
|
||||
Job Delivery Details
|
||||
{props.errors.job_detail && props.touched.job_detail && <span className="text-[12px] text-red-500">{props.errors.job_detail}</span>}
|
||||
{props.errors.job_detail &&
|
||||
props.touched.job_detail && (
|
||||
<span className="text-[12px] text-red-500">
|
||||
{props.errors.job_detail}
|
||||
</span>
|
||||
)}
|
||||
</label>
|
||||
<textarea
|
||||
id="Job Delivery Details"
|
||||
rows="5"
|
||||
className={`input-field px-3 py-2 placeholder:text-base text-dark-gray dark:text-white w-full h-[100px] bg-slate-100 dark:bg-[#11131F] focus:ring-0 focus:outline-[#dce4e9] rounded-[10px]`}
|
||||
className={`input-field px-3 py-2 placeholder:text-base text-dark-gray dark:text-white w-full h-[100px] bg-slate-100 dark:bg-[#11131F] focus:ring-0 focus:outline-[#dce4e9] rounded-[10px] border`}
|
||||
style={{ resize: "none" }}
|
||||
name="job_detail"
|
||||
value={props.values.job_detail}
|
||||
@@ -286,19 +293,28 @@ function AddJob({ popUpHandler, categories }) {
|
||||
role="group"
|
||||
aria-labelledby="checked-group"
|
||||
>
|
||||
{Object?.entries(categories).map(([key, value]) => (
|
||||
<label
|
||||
key={key}
|
||||
className="flex gap-1 w-full items-center"
|
||||
>
|
||||
<Field
|
||||
type="checkbox"
|
||||
name="category"
|
||||
value={key}
|
||||
/>
|
||||
<span className="text-[13.975px]">{value}</span>
|
||||
{categories ? (
|
||||
<>
|
||||
{Object?.entries(categories).map(([key, value]) => (
|
||||
<label
|
||||
key={key}
|
||||
className="flex gap-1 w-full items-center"
|
||||
>
|
||||
<Field
|
||||
type="checkbox"
|
||||
name="category"
|
||||
value={key}
|
||||
/>
|
||||
<span className="text-[13.975px]">{value}</span>
|
||||
</label>
|
||||
))}
|
||||
</>
|
||||
) : (
|
||||
<label className="flex gap-1 w-full items-center">
|
||||
<Field type="checkbox" name="category" />
|
||||
<span className="text-[13.975px]">null</span>
|
||||
</label>
|
||||
))}
|
||||
)}
|
||||
<span className="h-5 text-sm italic text-[#cf3917]">
|
||||
{props.errors.category &&
|
||||
props.touched.category &&
|
||||
@@ -324,7 +340,7 @@ function AddJob({ popUpHandler, categories }) {
|
||||
<Field
|
||||
component="select"
|
||||
name="timeline_days"
|
||||
className={`input-field p-2 mt-3 rounded-md placeholder:text-base text-dark-gray dark:text-white w-full h-10 bg-slate-100 dark:bg-[#11131F] focus:ring-0 focus:outline-none ${
|
||||
className={`input-field p-2 mt-3 rounded-md placeholder:text-base text-dark-gray dark:text-white w-full h-10 bg-slate-100 dark:bg-[#11131F] focus:ring-0 focus:outline-none border ${
|
||||
props.errors.timeline_days &&
|
||||
props.touched.timeline_days
|
||||
? "border-[#ff0a0a63] shadow-red-500 border-[0.5px] animate-shake"
|
||||
@@ -335,6 +351,7 @@ function AddJob({ popUpHandler, categories }) {
|
||||
<option value="">Select Duration</option>
|
||||
{publicArray.map(({ name, duration }, idx) => (
|
||||
<option
|
||||
key={idx}
|
||||
className="text-slate-500 text-lg"
|
||||
value={duration}
|
||||
>
|
||||
|
||||
@@ -0,0 +1,69 @@
|
||||
import React, { useEffect } from 'react';
|
||||
import { useLocation, useNavigate } from 'react-router-dom';
|
||||
import usersService from '../../../services/UsersService';
|
||||
import {updateUserDetails} from "../../../store/UserDetails";
|
||||
import { useDispatch } from "react-redux";
|
||||
import AuthLayout from "../AuthLayout";
|
||||
|
||||
function AppleRedirect() {
|
||||
const location = useLocation();
|
||||
const navigate = useNavigate();
|
||||
const userApi = new usersService();
|
||||
const dispatch = useDispatch()
|
||||
|
||||
const queryParams = new URLSearchParams(location?.search);
|
||||
const codeResponse = queryParams.get("code");
|
||||
|
||||
useEffect(()=>{
|
||||
if(!codeResponse){
|
||||
navigate('/login', {state: {error: true}})
|
||||
return
|
||||
}
|
||||
console.log(codeResponse);
|
||||
|
||||
setTimeout(()=>{ // remove LATER
|
||||
navigate('/login', {state: {error: true}})
|
||||
},2000)
|
||||
|
||||
/*
|
||||
POST /token HTTP/1.1
|
||||
Host: oauth2.googleapis.com
|
||||
Content-Type: application/x-www-form-urlencoded
|
||||
|
||||
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
|
||||
*/
|
||||
var reqData = {
|
||||
auth_type: "APPLE",
|
||||
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>
|
||||
)
|
||||
}
|
||||
|
||||
export default AppleRedirect
|
||||
@@ -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() {
|
||||
@@ -20,44 +20,52 @@ function FbookRedirect() {
|
||||
return
|
||||
}
|
||||
console.log(codeResponse);
|
||||
|
||||
setTimeout(()=>{ // remove LATER
|
||||
navigate('/login', {state: {error: true}})
|
||||
},2000)
|
||||
|
||||
/*
|
||||
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: "GOOGLE",
|
||||
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
|
||||
@@ -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,19 +24,42 @@ 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) {
|
||||
const reqData = { email };
|
||||
const reqData = { email, action:11013 };
|
||||
setResetLoading(true);
|
||||
try {
|
||||
const res = await userApi.StartResetPassword(reqData);
|
||||
@@ -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>
|
||||
|
||||
@@ -1,26 +1,29 @@
|
||||
import React, { useEffect, useLayoutEffect, useState } from "react";
|
||||
import { Link, useNavigate, useLocation } from "react-router-dom";
|
||||
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 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";
|
||||
// import { GoogleOAuthProvider } from '@react-oauth/google';
|
||||
import { googleLogout, useGoogleLogin } 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() {
|
||||
const dispatch = useDispatch();
|
||||
const {state} = useLocation()
|
||||
const { state } = useLocation();
|
||||
|
||||
let [loginType, setLoginType] = useState('');
|
||||
const [validCaptcha, setValidCaptcha] = useState({show: false, valid:''}); // FOR CAPTCHA
|
||||
|
||||
let [loginType, setLoginType] = useState("");
|
||||
|
||||
const [checked, setValue] = useState(false);
|
||||
const [loginLoading, setLoginLoading] = useState(false);
|
||||
|
||||
const [showPassword, setShowPassword] = useState(false);
|
||||
@@ -30,12 +33,8 @@ export default function Login() {
|
||||
// for the catch error
|
||||
const [msgError, setMsgError] = useState("");
|
||||
|
||||
const rememberMe = () => {
|
||||
setValue(!checked);
|
||||
};
|
||||
|
||||
// To Show and Hide Password
|
||||
const togglePasswordVisibility = () => {
|
||||
// To Show and Hide Password
|
||||
const togglePasswordVisibility = () => {
|
||||
setShowPassword(!showPassword);
|
||||
};
|
||||
|
||||
@@ -43,7 +42,7 @@ export default function Login() {
|
||||
const handleLoginType = ({ target: { name } }) => {
|
||||
setLoginType(name);
|
||||
let currentDate = new Date();
|
||||
let expirationDate = new Date(currentDate.getTime() + (24 * 60 * 60 * 1000));
|
||||
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=/;`;
|
||||
@@ -83,7 +82,9 @@ export default function Login() {
|
||||
if (regEx.test(email) == false) {
|
||||
setLoginLoading(false);
|
||||
setMsgError("Invalid Email");
|
||||
return setTimeout(()=>{setMsgError("");},3000)
|
||||
return setTimeout(() => {
|
||||
setMsgError("");
|
||||
}, 3000);
|
||||
}
|
||||
// Post Data Info for normal Login
|
||||
postData = {
|
||||
@@ -110,20 +111,35 @@ 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) => {
|
||||
if (res.status != 200 || res.data.internal_return < 0 || !res.data.member_id || !res.data.uid || !res.data.session) {
|
||||
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}`);
|
||||
// localStorage.setItem("session", `${res.data.session}`);
|
||||
dispatch(updateUserDetails({...res.data}));
|
||||
dispatch(updateUserDetails({ ...res.data }));
|
||||
setTimeout(() => {
|
||||
navigate("/", { replace: true });
|
||||
setLoginLoading(false);
|
||||
@@ -132,6 +148,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(() => {
|
||||
@@ -142,6 +159,14 @@ export default function Login() {
|
||||
});
|
||||
};
|
||||
|
||||
function captchaChecker(value) { // FUNCTION TO VALIDATE CAPTCHA
|
||||
if(value){
|
||||
setValidCaptcha({show: true, valid:value})
|
||||
}else{
|
||||
setValidCaptcha({show: true, valid:''})
|
||||
}
|
||||
}
|
||||
|
||||
const googleLogin = useGoogleLogin({
|
||||
flow: "auth-code",
|
||||
ux_mode: "redirect",
|
||||
@@ -154,11 +179,12 @@ export default function Login() {
|
||||
|
||||
// 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=/;";
|
||||
// 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
|
||||
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")){
|
||||
@@ -166,33 +192,34 @@ export default function Login() {
|
||||
// }else{
|
||||
// setLoginType('full')
|
||||
// }
|
||||
function readCookie(cname) { // checks the cookie in order to set the login type before components mounts
|
||||
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 carr = decoded_cookie.split(";");
|
||||
for (let i = 0; i < carr.length; i++) {
|
||||
let c = carr[i];
|
||||
while(c.charAt(0)==' '){
|
||||
c=c.substring(1);
|
||||
while (c.charAt(0) == " ") {
|
||||
c = c.substring(1);
|
||||
}
|
||||
if(c.indexOf(name) == 0) {
|
||||
return c.substring(name.length, c.length);
|
||||
if (c.indexOf(name) == 0) {
|
||||
return c.substring(name.length, c.length);
|
||||
}
|
||||
}
|
||||
return 'full'
|
||||
return "full";
|
||||
}
|
||||
let loginValue = readCookie('loginType')
|
||||
setLoginType(loginValue)
|
||||
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
|
||||
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(()=>{
|
||||
setTimeout(() => {
|
||||
setMsgError("");
|
||||
navigate('/login', {replace: true})
|
||||
},4000)
|
||||
navigate("/login", { replace: true });
|
||||
}, 4000);
|
||||
}
|
||||
},[])
|
||||
|
||||
}, []);
|
||||
|
||||
useEffect(() => {
|
||||
setMail("");
|
||||
@@ -234,7 +261,9 @@ export default function Login() {
|
||||
<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]"
|
||||
loginType == "full"
|
||||
? "bg-[#4687ba] border-[2px] border-[#4687ba] text-white"
|
||||
: "bg-white text-[#000] border-t-[2px]"
|
||||
}`}
|
||||
onClick={handleLoginType}
|
||||
>
|
||||
@@ -243,7 +272,9 @@ export default function Login() {
|
||||
<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]"
|
||||
loginType == "family"
|
||||
? "bg-[#4687ba] border-[2px] border-[#4687ba] text-white"
|
||||
: "bg-white text-[#000] border-t-[2px]"
|
||||
}`}
|
||||
onClick={handleLoginType}
|
||||
>
|
||||
@@ -255,7 +286,7 @@ export default function Login() {
|
||||
|
||||
{/* for login component */}
|
||||
{
|
||||
loginType == 'full' ? (
|
||||
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">
|
||||
@@ -275,7 +306,7 @@ export default function Login() {
|
||||
<div className="input-item mb-5">
|
||||
<InputCom
|
||||
labelClass="tracking-wider"
|
||||
fieldClass="sm:px-6 px-2"
|
||||
fieldClass="sm:px-6 px-2 tracking-[0.25em] text-2xl"
|
||||
value={password}
|
||||
inputHandler={handlePassword}
|
||||
placeholder="● ● ● ● ● ●"
|
||||
@@ -287,6 +318,17 @@ export default function Login() {
|
||||
forgotPassword
|
||||
/>
|
||||
</div>
|
||||
|
||||
{/* hCaptha clone for the time being */}
|
||||
{validCaptcha.show &&
|
||||
<div className="mb-5 flex justify-center w-full">
|
||||
<ReCAPTCHA
|
||||
sitekey={process.env.REACT_APP_GOOGLE_RECAPTCHA_SITEKEY}
|
||||
onChange={captchaChecker}
|
||||
/>
|
||||
</div>
|
||||
}
|
||||
|
||||
{loginError && (
|
||||
<div className="relative p-4 text-[#912741] bg-[#fcd9e2] border-[#fbc6d3] mb-4 rounded-[0.475rem] text-md font-thin leading-[19.5px] text-[13px]">
|
||||
Invalid username or password- Please{" "}
|
||||
@@ -311,7 +353,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>
|
||||
@@ -320,33 +362,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=com.wrenchboard.users.client&redirect_uri=https%3A%2F%2Fwork.wrenchboard.com%2Flogin%2Fauth%2Fapple&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=${'1287693041832191'}&redirect_uri=http://localhost:9082/login/auth/flogin&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>
|
||||
@@ -406,7 +466,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>
|
||||
@@ -422,10 +482,11 @@ 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>
|
||||
@@ -434,7 +495,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");
|
||||
// };
|
||||
@@ -454,34 +522,29 @@ const BrandBtn = ({ link, imgSrc, brand, onClick, isAnchor=false }) => {
|
||||
// alert("start facebook");
|
||||
// };
|
||||
return (
|
||||
<div className="w-full sm:w-1/2 flex justify-center bottomMargin">
|
||||
{isAnchor ?
|
||||
(
|
||||
<div className="w-full flex justify-center bottomMargin" style={style}>
|
||||
{isAnchor ? (
|
||||
<a
|
||||
href={link}
|
||||
className="w-full border border-light-purple dark:border-[#5356fb29] rounded-[0.475rem] h-[48px] flex justify-center bg-[#FAFAFA] hover:bg-[#eff2f5] hover:text-[#7e8299] transition duration-300 dark:bg-[#11131F] 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>
|
||||
)
|
||||
:
|
||||
(
|
||||
href={link}
|
||||
className="w-full border border-light-purple dark:border-[#5356fb29] rounded-[0.475rem] h-[48px] flex justify-center bg-[#FAFAFA] hover:bg-[#eff2f5] hover:text-[#7e8299] transition duration-300 dark:bg-[#11131F] 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 dark:border-[#5356fb29] rounded-[0.475rem] h-[48px] flex justify-center bg-[#FAFAFA] hover:bg-[#eff2f5] hover:text-[#7e8299] transition duration-300 dark:bg-[#11131F] 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>
|
||||
)
|
||||
}
|
||||
onClick={onClick}
|
||||
// href="#dd"
|
||||
className="w-full border border-light-purple dark:border-[#5356fb29] rounded-[0.475rem] h-[48px] flex justify-center bg-[#FAFAFA] hover:bg-[#eff2f5] hover:text-[#7e8299] transition duration-300 dark:bg-[#11131F] 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>
|
||||
)
|
||||
);
|
||||
};
|
||||
|
||||
|
||||
@@ -1,21 +1,26 @@
|
||||
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() {
|
||||
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: "",
|
||||
@@ -46,9 +51,18 @@ export default function SignUp() {
|
||||
try {
|
||||
if (res.status === 200) {
|
||||
const { signup_country } = await res.data;
|
||||
setCountries(signup_country);
|
||||
// setCountries(signup_country);
|
||||
if(country){ // IF LINK/PATHNAME HAS CNT QUERY VALUE
|
||||
let cnt = signup_country.filter(item => item[0]==country) // test to see country passed in query param exist from list of countries supplied by API
|
||||
if(!cnt.length){ // IF CNT EMPTY, SET FORMDATA COUNTRY BACK TO EMPTY STRING: RE: THIS IS BCOS WE INITAIL SET COUNTRY VALUE IN FORMDATA, IF COUNTRY PARAM IS PRESENT IN LINK
|
||||
setFormData(prev => ({...prev, country: ''}))
|
||||
return setCountries({loading: false, data: signup_country});
|
||||
}
|
||||
return setCountries({loading: false, data: cnt});
|
||||
}
|
||||
setCountries({loading: false, data:signup_country});
|
||||
} else if (res.data.result !== 100) {
|
||||
setCountries("Nothing see here!");
|
||||
setCountries({loading: false, data:[]});
|
||||
}
|
||||
} catch (error) {
|
||||
throw new Error(error);
|
||||
@@ -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">
|
||||
@@ -205,15 +226,13 @@ export default function SignUp() {
|
||||
</div>
|
||||
<div className="input-item mb-5">
|
||||
<InputCom
|
||||
fieldClass="px-6"
|
||||
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"
|
||||
}
|
||||
passIcon={showPassword ? "show-password" : "hide-password"}
|
||||
value={formData.password}
|
||||
inputHandler={handleInputChange}
|
||||
/>
|
||||
@@ -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) => (
|
||||
{data?.data?.length > 1 ?
|
||||
<>
|
||||
<option value={""}>Select your Country</option>
|
||||
{data?.data?.map((item, idx) => (
|
||||
<option value={item[0]} key={idx}>
|
||||
{item[1]}
|
||||
</option>
|
||||
))}
|
||||
</>
|
||||
:
|
||||
data?.data?.length == 1 ?
|
||||
data?.data?.map((item, idx) => (
|
||||
<option value={item[0]} key={idx}>
|
||||
{item[1]}
|
||||
</option>
|
||||
))
|
||||
:
|
||||
data?.data?.length < 1 && data.loading ?
|
||||
<option value=''>
|
||||
Loading...
|
||||
</option>
|
||||
:
|
||||
<option value=''>
|
||||
No Country Found!
|
||||
</option>
|
||||
}
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -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";
|
||||
|
||||
@@ -1,16 +1,20 @@
|
||||
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 +27,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,46 +35,60 @@ 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,
|
||||
m_uid: requestStatus.data?.m_uid || '',
|
||||
reset_uid: requestStatus.data?.reset_uid || '',
|
||||
step: 300,
|
||||
action: 730,
|
||||
};
|
||||
|
||||
const res = await userApi?.CompleteResetPassword(reqData);
|
||||
|
||||
if (res.status === 200) {
|
||||
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);
|
||||
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);
|
||||
}
|
||||
} else {
|
||||
setLinkLoader(false);
|
||||
setMsgError("Passwords does not match");
|
||||
}
|
||||
} else {
|
||||
setMsgError("Please fill in fields");
|
||||
}
|
||||
} catch (error) {
|
||||
setLinkLoader(false);
|
||||
setLinkSuccess(false);
|
||||
@@ -87,6 +100,31 @@ const VerifyPassword = () => {
|
||||
}
|
||||
};
|
||||
|
||||
const verifyResetPwd = () => { // FUNCTION TO VERIFY RESET PASSWORD LINK
|
||||
setRequestStatus({loading: true, status:false, data: []})
|
||||
var reqData = {
|
||||
sessionid: "DUMMY-CANNOT_BE_EMPTY",
|
||||
reset_link: token,
|
||||
step: 200,
|
||||
action: 730,
|
||||
};
|
||||
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 +139,59 @@ 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">
|
||||
{requestStatus.loading ?
|
||||
<LoadingSpinner color='sky-blue' size='16' height='h-300px' />
|
||||
:
|
||||
!requestStatus.loading && requestStatus.status ?
|
||||
<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>
|
||||
)}
|
||||
</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")}
|
||||
/>
|
||||
) : (
|
||||
<ErrorComponent onClick={() => navigate("/login")} />
|
||||
)}
|
||||
{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="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>
|
||||
@@ -158,7 +216,7 @@ const SuccessfulComponent = ({
|
||||
{/* INPUT */}
|
||||
<div className="mb-5">
|
||||
<InputCom
|
||||
fieldClass="px-6"
|
||||
fieldClass="sm:px-6 px-2 tracking-[0.25em] text-2xl"
|
||||
value={password}
|
||||
inputHandler={handlePassword}
|
||||
placeholder="● ● ● ● ● ●"
|
||||
@@ -171,7 +229,7 @@ const SuccessfulComponent = ({
|
||||
</div>
|
||||
<div className="mb-5">
|
||||
<InputCom
|
||||
fieldClass="px-6"
|
||||
fieldClass="sm:px-6 px-2 tracking-[0.25em] text-2xl"
|
||||
value={confirmPassword}
|
||||
inputHandler={handlePassword}
|
||||
placeholder="● ● ● ● ● ●"
|
||||
@@ -209,25 +267,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>
|
||||
);
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -1,38 +1,77 @@
|
||||
import React, { useState } from "react";
|
||||
import { Link } from "react-router-dom";
|
||||
import React, { useEffect, useState } from "react";
|
||||
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";
|
||||
|
||||
export default function BlogItem(props) {
|
||||
|
||||
const apiCall = new usersService()
|
||||
const navigate = useNavigate()
|
||||
|
||||
const [blogdata, setBlogdata] = useState({loading: true, data:{}})
|
||||
|
||||
const [selectTab, setValue] = useState("today");
|
||||
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");
|
||||
|
||||
useEffect(()=>{
|
||||
if(!blog_id){
|
||||
navigate('/',{replace:true})
|
||||
}
|
||||
apiCall.getSingleBlogData({blog_id}).then(res => {
|
||||
setBlogdata({loading: false, data:res.data})
|
||||
}).catch(error => {
|
||||
setBlogdata({loading: false, data:{}})
|
||||
console.log('ERROR', error)
|
||||
})
|
||||
},[blog_id])
|
||||
return (
|
||||
<Layout>
|
||||
<CommonHead
|
||||
commonHeadData={props.commonHeadData}
|
||||
/>
|
||||
<div className="notification-page w-full mb-10">
|
||||
<div className="notification-wrapper w-full">
|
||||
{/* heading */}
|
||||
<div className="sm:flex justify-between items-center mb-6">
|
||||
<div className="mb-5 sm:mb-0">
|
||||
<h1 className="text-26 font-bold text-dark-gray dark:text-white">
|
||||
<span
|
||||
className={`${selectTab === "today" ? "block" : "hidden"}`}
|
||||
>
|
||||
Title of this Blog Items
|
||||
</span>
|
||||
</h1>
|
||||
</div>
|
||||
<div className="slider-btns flex space-x-4">
|
||||
|
||||
</div>
|
||||
<div className="mb-5">
|
||||
<h1 className="text-26 font-bold text-dark-gray dark:text-white">
|
||||
<span
|
||||
className={`${selectTab === "today" ? "block" : "hidden"}`}
|
||||
>
|
||||
{blogdata.data?.blogdata?.[0]?.post_title}
|
||||
</span>
|
||||
</h1>
|
||||
</div>
|
||||
<div className="notification-wrapper w-full bg-white p-8 rounded-2xl">
|
||||
{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">
|
||||
<h1 className="text-26 font-bold text-dark-gray dark:text-white">
|
||||
<span
|
||||
className={`${selectTab === "today" ? "block" : "hidden"}`}
|
||||
>
|
||||
{blogdata.data?.blogdata?.[0]?.post_title}
|
||||
</span>
|
||||
</h1>
|
||||
</div> */}
|
||||
{/* <div className="slider-btns flex space-x-4">
|
||||
</div> */}
|
||||
</div>
|
||||
<div dangerouslySetInnerHTML={{__html: blogdata.data?.blogdata?.[0]?.post_content}}>
|
||||
</div>
|
||||
</div>
|
||||
<div>
|
||||
Blog Items Details
|
||||
</div>
|
||||
:
|
||||
<h1 className="text-26 font-bold text-dark-gray dark:text-white">No Blog Found!</h1>
|
||||
}
|
||||
</div>
|
||||
</div>
|
||||
</Layout>
|
||||
|
||||
@@ -1,9 +1,7 @@
|
||||
import { useCallback, useEffect, useMemo, useState } from "react";
|
||||
import { Link, useNavigate } from "react-router-dom";
|
||||
import MarketPopUp from "../MarketPlace/PopUp/MarketPopUp";
|
||||
import usersService from "../../services/UsersService";
|
||||
import { useEffect, useState } from "react";
|
||||
import dataImage2 from "../../assets/images/taskbanners/default.jpg";
|
||||
import { PriceFormatter } from "../Helpers/PriceFormatter";
|
||||
import dataImage2 from "../../assets/images/data-table-user-2.png";
|
||||
import MarketPopUp from "../MarketPlace/PopUp/MarketPopUp";
|
||||
|
||||
export default function AvailableJobsCard({
|
||||
className,
|
||||
@@ -156,27 +154,6 @@ export default function AvailableJobsCard({
|
||||
</p>
|
||||
</div>
|
||||
|
||||
{/* <div className="card-two-info flex gap-2 items-center">
|
||||
<div className="owned-by flex space-x-2 items-center">
|
||||
<div>
|
||||
<p className="text-thin-light-gray text-sm leading-3">Added</p>
|
||||
<p className="text-base text-dark-gray dark:text-white">
|
||||
{datas.offer_added}
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
<div className="w-[1px] bg-light-purple dark:bg-dark-light-purple h-7"></div>
|
||||
<div className="created-by flex space-x-2 items-center flex-row-reverse">
|
||||
<div>
|
||||
<p className="text-thin-light-gray text-sm leading-3 text-right">
|
||||
Expires
|
||||
</p>
|
||||
<p className="text-base text-dark-gray dark:text-white text-right">
|
||||
{datas.expire}
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</div> */}
|
||||
<div className="block sm:flex flex-wrap gap-4">
|
||||
<p className="text-sm text-thin-light-gray flex flext-start gap-1">
|
||||
Price: <span className="text-purple">{thePrice}</span>
|
||||
|
||||
@@ -16,7 +16,8 @@ export default function HomeBannerOffersCard(props) {
|
||||
|
||||
return (
|
||||
<Link
|
||||
to={link_result}
|
||||
// to={link_result}
|
||||
to={link_result == '/blog-page' ? `${link_result}?blog_id=${props.itemData.blog_id}` : `${link_result}`}
|
||||
className="item p-2 w-full flex items-center min-h-[340px] bg-alice-blue bg-cover bg-center"
|
||||
style={{
|
||||
backgroundImage: `url('${imageUrl}')`,
|
||||
|
||||
@@ -19,7 +19,7 @@ export default function OfferCard({ datas, hidden = false, setOfferPopout }) {
|
||||
className="thumbnail w-full h-full rounded-xl overflow-hidden px-4 pt-4"
|
||||
style={{
|
||||
background: `url(${localImgLoad(
|
||||
`images/taskbanners/${datas.banner}`
|
||||
`images/taskbanners/${datas?.banner || "default.jpg"}`
|
||||
)}) center / contain no-repeat`,
|
||||
}}
|
||||
>
|
||||
|
||||
@@ -50,22 +50,23 @@ export default function ProductCardStyleTwo({
|
||||
</div>
|
||||
<div className="details-area">
|
||||
{/* title */}
|
||||
<a href={datas.guid} target="_blank" className="mb-2.5" rel="noreferrer">
|
||||
<Link to={`/blog-page?blog_id=${datas.ID}`} className="mb-2.5" rel="noreferrer">
|
||||
<h1 className="font-bold text-xl tracking-wide line-clamp-1 text-dark-gray dark:text-white capitalize">
|
||||
{datas.post_title || "dummy title..."}
|
||||
</h1>
|
||||
</a>
|
||||
</Link>
|
||||
<div className="flex justify-between">
|
||||
<div className="flex items-center space-x-2"></div>
|
||||
<div className="my-1">
|
||||
<a
|
||||
href={datas.guid}
|
||||
target="_blank"
|
||||
<Link
|
||||
// href={datas.guid}
|
||||
// target="_blank"
|
||||
to={`/blog-page?blog_id=${datas.ID}`}
|
||||
className="px-4 py-2.5 text-white text-sm bg-pink rounded-full tracking-wide"
|
||||
rel="noreferrer"
|
||||
>
|
||||
View
|
||||
</a>
|
||||
</Link>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -1,19 +1,18 @@
|
||||
import React, {
|
||||
Suspense,
|
||||
lazy,
|
||||
useCallback,
|
||||
useEffect,
|
||||
useMemo,
|
||||
useRef,
|
||||
useState,
|
||||
useTransition,
|
||||
} from "react";
|
||||
import { useReactToPrint } from "react-to-print";
|
||||
import profile from "../../assets/images/profile-info-profile.png";
|
||||
import profile from "../../assets/images/icons/family.svg";
|
||||
import localImgLoad from "../../lib/localImgLoad";
|
||||
import usersService from "../../services/UsersService";
|
||||
import LoadingSpinner from "../Spinners/LoadingSpinner";
|
||||
import AssignTaskPopout from "./FamilyPopout/AssignTaskPopout";
|
||||
import localImgLoad from "../../lib/localImgLoad";
|
||||
import FamilyWallet from "./Tabs/FamilyWallet";
|
||||
|
||||
// Lazy Imports for components
|
||||
const FamilyWaitlist = lazy(() => import("./Tabs/FamilyWaitlist"));
|
||||
@@ -29,113 +28,113 @@ export default function FamilyManageTabs({
|
||||
listReload,
|
||||
loader,
|
||||
}) {
|
||||
// Initial state for family details
|
||||
const initialDetailState = {
|
||||
loading: false,
|
||||
data: null,
|
||||
};
|
||||
|
||||
// State for family details, tasks, waitlist, and pending
|
||||
const [details, setDetails] = useState({
|
||||
familyDetails: { loading: false, data: null },
|
||||
familyTasks: { loading: false, data: null },
|
||||
familyWaitList: { loading: false, data: null },
|
||||
familyPending: { loading: false, data: null },
|
||||
familyDetails: { ...initialDetailState },
|
||||
familyTasks: { ...initialDetailState },
|
||||
familyWaitList: { ...initialDetailState },
|
||||
familyPending: { ...initialDetailState },
|
||||
});
|
||||
|
||||
// Function to reset family details, tasks, waitlist, and pending
|
||||
const resetDetails = () => {
|
||||
setDetails({
|
||||
familyDetails: { ...initialDetailState },
|
||||
familyTasks: { ...initialDetailState },
|
||||
familyWaitList: { ...initialDetailState },
|
||||
familyPending: { ...initialDetailState },
|
||||
});
|
||||
};
|
||||
|
||||
const [updatePage, setUpdatePage] = useState(false) // State to determine when to update the page
|
||||
|
||||
// State for family task data
|
||||
const [familyTask, setFamilyTask] = useState({ loading: false, data: [] });
|
||||
|
||||
// State for active task
|
||||
const [activeTask, setActiveTask] = useState({ id: 0, data: {} });
|
||||
|
||||
// State for error messages
|
||||
const [errMsg, setErrMsg] = useState("");
|
||||
|
||||
// State for family task popout
|
||||
const [familyTaskPopout, setFamilyTaskPopout] = useState(false);
|
||||
|
||||
// State for profile image
|
||||
const [profileImg, setProfileImg] = useState(profile);
|
||||
|
||||
// Ref for profile image input
|
||||
const profileImgInput = useRef(null);
|
||||
const [isPending, startTransition] = useTransition();
|
||||
|
||||
// Create an instance of the usersService class
|
||||
const apiCall = useMemo(() => new usersService(), []);
|
||||
|
||||
// Function to handle toggling the family task popout
|
||||
const familyPopUpHandler = () => {
|
||||
setFamilyTaskPopout((prev) => !prev);
|
||||
};
|
||||
|
||||
// Function to trigger a click on the hidden profile image input
|
||||
const browseProfileImg = () => {
|
||||
profileImgInput.current.click();
|
||||
};
|
||||
|
||||
/**
|
||||
* Handles the change event of the profile image input field.
|
||||
* Checks if the selected file exceeds the maximum file size limit and displays an alert if it does.
|
||||
* If the file is within the size limit, it reads the file using the FileReader API and sets the profile image state with the result.
|
||||
*/
|
||||
const profileImgChangeHandler = (e) => {
|
||||
if (e.target.value !== "") {
|
||||
const MAX_FILE_SIZE = 5 * 1024 * 1024; // 5MB
|
||||
|
||||
const file = e.target.files[0];
|
||||
if (file && file.size > MAX_FILE_SIZE) {
|
||||
alert("File size exceeds the limit.");
|
||||
return;
|
||||
}
|
||||
|
||||
if (file) {
|
||||
const imgReader = new FileReader();
|
||||
imgReader.onload = (event) => {
|
||||
setProfileImg(event.target.result);
|
||||
imgReader.onload = () => {
|
||||
const imageDataUrl = imgReader.result;
|
||||
|
||||
// Set the profile image
|
||||
setProfileImg(imageDataUrl);
|
||||
};
|
||||
imgReader.readAsDataURL(e.target.files[0]);
|
||||
imgReader.readAsDataURL(file);
|
||||
}
|
||||
};
|
||||
|
||||
const manageFamily = useCallback(async () => {
|
||||
try {
|
||||
setDetails((prevDetails) => ({
|
||||
...prevDetails,
|
||||
familyDetails: { loading: true },
|
||||
familyTasks: { loading: true },
|
||||
familyWaitList: { loading: true },
|
||||
familyPending: { loading: true },
|
||||
}));
|
||||
|
||||
const { family_uid } = accountDetails;
|
||||
const reqData = { family_uid };
|
||||
|
||||
const [familyRes, tasksRes, familyWaitRes, familyPending] =
|
||||
await Promise.all([
|
||||
apiCall.ManageFamily(reqData),
|
||||
apiCall.ManageTasks(reqData),
|
||||
apiCall.ManageFamilyWaitlist(),
|
||||
apiCall.ManageFamilyPending(),
|
||||
]);
|
||||
|
||||
const familyData = familyRes.data;
|
||||
const tasksData = tasksRes.data;
|
||||
const familyWaitData = familyWaitRes.data;
|
||||
const familyPendingData = familyPending.data;
|
||||
|
||||
if (
|
||||
familyData?.internal_return < 0 ||
|
||||
tasksData?.internal_return < 0 ||
|
||||
familyWaitData?.internal_return < 0 ||
|
||||
familyPendingData?.internal_return < 0
|
||||
) {
|
||||
return;
|
||||
}
|
||||
|
||||
startTransition(() => {
|
||||
setDetails({
|
||||
familyDetails: { loading: false, data: familyData },
|
||||
familyTasks: { loading: false, data: tasksData },
|
||||
familyWaitList: { loading: false, data: familyWaitData },
|
||||
familyPending: { loading: false, data: familyPendingData },
|
||||
});
|
||||
});
|
||||
} catch (error) {
|
||||
setDetails((prevDetails) => ({
|
||||
...prevDetails,
|
||||
familyDetails: { loading: false },
|
||||
familyTasks: { loading: false },
|
||||
familyWaitList: { loading: false },
|
||||
familyPending: { loading: false },
|
||||
}));
|
||||
setErrMsg("An error occurred");
|
||||
throw new Error(error);
|
||||
}
|
||||
}, [apiCall, accountDetails]);
|
||||
|
||||
// Ref for the account section
|
||||
const accountRef = useRef();
|
||||
|
||||
// React-to-Print hook for handling printing
|
||||
const useHandlePrint = useReactToPrint({
|
||||
content: () => accountRef.current,
|
||||
});
|
||||
|
||||
// Array of tab names
|
||||
const tabs = [
|
||||
{ id: 1, name: "Tasks" },
|
||||
{ id: 2, name: "Waiting" },
|
||||
{ id: 3, name: "Pending" },
|
||||
];
|
||||
|
||||
// State for the currently selected tab
|
||||
const [tab, setTab] = useState(tabs[0].name);
|
||||
|
||||
// Function to handle tab changes
|
||||
const tabHandler = (value) => {
|
||||
startTransition(() => {
|
||||
setTab(value);
|
||||
});
|
||||
setTab(value);
|
||||
};
|
||||
|
||||
// Object that maps tab names to their corresponding components
|
||||
const tabComponents = {
|
||||
Tasks: (
|
||||
<FamilyTasks
|
||||
@@ -156,7 +155,7 @@ export default function FamilyManageTabs({
|
||||
<FamilyPending
|
||||
familyData={details.familyPending.data}
|
||||
accountDetails={accountDetails}
|
||||
loader={details.familyWaitList.loading}
|
||||
loader={details.familyPending.loading}
|
||||
/>
|
||||
),
|
||||
Account: (
|
||||
@@ -167,9 +166,11 @@ export default function FamilyManageTabs({
|
||||
handlePrint={useHandlePrint}
|
||||
/>
|
||||
),
|
||||
Profile: <FamilyProfile />,
|
||||
Profile: <FamilyProfile familyData={details.familyDetails.data} />,
|
||||
wallet: <FamilyWallet familyData={details.familyDetails.data} />,
|
||||
};
|
||||
|
||||
// Default tab component
|
||||
const defaultTabComponent = (
|
||||
<FamilyTasks
|
||||
className={className}
|
||||
@@ -179,33 +180,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: 13005,
|
||||
};
|
||||
|
||||
if (checkFamilyTask) {
|
||||
setFamilyTask({ loading: true });
|
||||
apiCall
|
||||
.getMyJobList(reqData)
|
||||
.then((res) => {
|
||||
setFamilyTask({ loading: false, data: res?.data?.result_list });
|
||||
if (res?.data?.result_list?.length) {
|
||||
setActiveTask((prev) => ({
|
||||
...prev,
|
||||
data: res?.data?.result_list[0],
|
||||
}));
|
||||
}
|
||||
})
|
||||
.catch((err) => {
|
||||
setFamilyTask({ loading: false, data: [] });
|
||||
console.log("Error", err);
|
||||
});
|
||||
}
|
||||
|
||||
// Cleanup function to prevent memory leaks
|
||||
return () => {
|
||||
checkFamilyTask = false;
|
||||
};
|
||||
}, []);
|
||||
|
||||
return (
|
||||
<div
|
||||
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}
|
||||
@@ -214,35 +299,46 @@ export default function FamilyManageTabs({
|
||||
browseProfileImg={browseProfileImg}
|
||||
accountDetails={accountDetails}
|
||||
/>
|
||||
<div className="mt-4 flex justify-center 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"
|
||||
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="w-[70px] h-[70px]"
|
||||
className="max-w-[30px]"
|
||||
alt="Settings-Icon"
|
||||
/>
|
||||
<p className="mt-2 text-lg text-sky-blue">Account</p>
|
||||
<p className="text-[16px] text-sky-blue">Acc.</p>
|
||||
</button>
|
||||
<button
|
||||
onClick={() => tabHandler("Profile")}
|
||||
className="family-icon p-2 border-2 border-sky-blue rounded-2xl flex flex-col justify-between items-center"
|
||||
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/profile.svg")}
|
||||
className="w-[70px] h-[70px]"
|
||||
className="max-w-[30px]"
|
||||
alt="Settings-Icon"
|
||||
/>
|
||||
<p className="mt-2 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 +361,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 +381,12 @@ export default function FamilyManageTabs({
|
||||
<AssignTaskPopout
|
||||
action={familyPopUpHandler}
|
||||
situation={familyTaskPopout}
|
||||
familyDetails={details.familyDetails.data}
|
||||
familyTask={familyTask}
|
||||
setFamilyTask={setFamilyTask}
|
||||
setActiveTask={setActiveTask}
|
||||
activeTask={activeTask}
|
||||
familyDetailsData={details.familyDetails.data}
|
||||
setUpdatePage={setUpdatePage}
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
|
||||
@@ -1,441 +1,420 @@
|
||||
import React, { useState, useEffect, useTransition } from "react";
|
||||
import ModalCom from "../../Helpers/ModalCom";
|
||||
import Detail from "../../jobPopout/popoutcomponent/Detail";
|
||||
import React, { useState } from "react";
|
||||
import usersService from "../../../services/UsersService";
|
||||
import LoadingSpinner from "../../Spinners/LoadingSpinner";
|
||||
import ModalCom from "../../Helpers/ModalCom";
|
||||
import { PriceFormatter } from "../../Helpers/PriceFormatter";
|
||||
import LoadingSpinner from "../../Spinners/LoadingSpinner";
|
||||
import Detail from "../../jobPopout/popoutcomponent/Detail";
|
||||
import { NewTasks } from "./forms";
|
||||
|
||||
const AssignTaskPopout = React.memo(({ action, details, situation, familyDetails }) => {
|
||||
const apiCall = new usersService();
|
||||
const AssignTaskPopout = React.memo(
|
||||
({
|
||||
action,
|
||||
details,
|
||||
situation,
|
||||
familyDetailsData,
|
||||
familyTask,
|
||||
activeTask,
|
||||
setActiveTask,
|
||||
setUpdatePage
|
||||
}) => {
|
||||
const apiCall = new usersService();
|
||||
|
||||
let [requestStatus, setRequestStatus] = useState({
|
||||
loading: false,
|
||||
status: false,
|
||||
message: "",
|
||||
}); // HOLDS RESPONSE FOR SENDING API REQUEST
|
||||
let [requestStatus, setRequestStatus] = useState({
|
||||
loading: false,
|
||||
status: false,
|
||||
message: "",
|
||||
}); // HOLDS RESPONSE FOR SENDING API REQUEST
|
||||
|
||||
let [familyTask, setFamilyTask] = useState({ loading: false, data: [] });
|
||||
let [taskType, setTaskType] = useState(details ? "new" : "select"); // SWITCHES BTW SELECT TASK AND NEW TASK
|
||||
|
||||
let [taskType, setTaskType] = useState(details ? "new" : "select"); // SWITCHES BTW SELECT TASK AND NEW TASK
|
||||
const switchTaskType = ({ target: { value } }) => {
|
||||
// FUNCTION TO CHANGE SELECTED ACTIVE TASK
|
||||
setTaskType(value);
|
||||
};
|
||||
|
||||
let [activeTask, setActiveTask] = useState({ id: 0, data: {} }); // HOLDS SELECTED TASK
|
||||
const handleActiveTask = (id = 0, data = {}) => {
|
||||
// FUNCTION TO CHANGE SELECTED ACTIVE TASK
|
||||
setActiveTask({ id, data });
|
||||
};
|
||||
|
||||
const switchTaskType = ({ target: { value } }) => {
|
||||
// FUNCTION TO CHANGE SELECTED ACTIVE TASK
|
||||
setTaskType(value);
|
||||
};
|
||||
// New Task
|
||||
const [formState, setFormState] = useState({
|
||||
// Initialize form state with desired fields
|
||||
banner: details?.banner || "default.jpg",
|
||||
country: details?.country || "",
|
||||
price: details?.price || "",
|
||||
title: details?.title || "",
|
||||
description: details?.description || "",
|
||||
job_detail: details?.job_detail || "",
|
||||
timeline_days: details?.timeline_days || "",
|
||||
category: details?.category || "",
|
||||
});
|
||||
|
||||
const handleActiveTask = (id = 0, data = {}) => {
|
||||
// FUNCTION TO CHANGE SELECTED ACTIVE TASK
|
||||
setActiveTask({ id, data });
|
||||
};
|
||||
|
||||
// New Task
|
||||
const [formState, setFormState] = useState({
|
||||
// Initialize form state with desired fields
|
||||
banner: details?.banner || "default.jpg",
|
||||
country: details?.country || "",
|
||||
price: details?.price || "",
|
||||
title: details?.title || "",
|
||||
description: details?.description || "",
|
||||
job_detail: details?.job_detail || "",
|
||||
timeline_days: details?.timeline_days || "",
|
||||
category: details?.category || "",
|
||||
});
|
||||
|
||||
const assignFamilyTask = () => {
|
||||
setRequestStatus({ loading: true, status: false, message: "" });
|
||||
let reqData = {};
|
||||
if (taskType == "select") {
|
||||
// RUNS HERE IF TASK TYPE IS SELECT
|
||||
if (!Object.keys(activeTask.data).length) {
|
||||
setRequestStatus({
|
||||
loading: false,
|
||||
status: false,
|
||||
message: "No Task is seleted",
|
||||
});
|
||||
return setTimeout(() => {
|
||||
setRequestStatus({ loading: false, status: false, message: "" });
|
||||
}, 3000);
|
||||
}
|
||||
reqData = {
|
||||
// API PAYLOADS
|
||||
job_id: activeTask.data?.job_id,
|
||||
job_uid: activeTask.data?.job_uid,
|
||||
family_uid: familyDetails.uid,
|
||||
job_description: activeTask.data?.description,
|
||||
assign_mode: 110011,
|
||||
};
|
||||
}
|
||||
|
||||
if (taskType === "new") {
|
||||
const {
|
||||
banner,
|
||||
category,
|
||||
country,
|
||||
description,
|
||||
job_detail,
|
||||
price,
|
||||
timeline_days,
|
||||
title,
|
||||
} = formState;
|
||||
|
||||
const requiredFields = {
|
||||
banner,
|
||||
// category,
|
||||
country,
|
||||
description,
|
||||
job_detail,
|
||||
price,
|
||||
timeline_days,
|
||||
title,
|
||||
};
|
||||
|
||||
for (let field in requiredFields) {
|
||||
if (requiredFields[field] == "") {
|
||||
// let currencyErrMsg = field == "country" && "currency"
|
||||
const assignFamilyTask = () => {
|
||||
setRequestStatus({ loading: true, status: false, message: "" });
|
||||
let reqData = {};
|
||||
if (taskType == "select") {
|
||||
// RUNS HERE IF TASK TYPE IS SELECT
|
||||
if (!Object.keys(activeTask.data).length) {
|
||||
setRequestStatus({
|
||||
loading: false,
|
||||
status: false,
|
||||
message: `${field} is empty`,
|
||||
message: "No Task is seleted",
|
||||
});
|
||||
return setTimeout(() => {
|
||||
setRequestStatus({ loading: false, status: false, message: "" });
|
||||
}, 3000);
|
||||
}
|
||||
reqData = {
|
||||
// API PAYLOADS
|
||||
job_id: activeTask.data?.job_id,
|
||||
job_uid: activeTask.data?.job_uid,
|
||||
family_uid: familyDetailsData?.uid || details?.family_uid,
|
||||
job_description: activeTask.data?.description,
|
||||
assign_mode: 110011,
|
||||
};
|
||||
}
|
||||
|
||||
reqData = {
|
||||
banner,
|
||||
category,
|
||||
country,
|
||||
description,
|
||||
job_detail,
|
||||
price: price * 100,
|
||||
timeline_days,
|
||||
title,
|
||||
assign_mode: 110055,
|
||||
family_uid: details?.family_uid || familyDetails?.uid,
|
||||
};
|
||||
}
|
||||
if (taskType === "new") {
|
||||
const {
|
||||
banner,
|
||||
category,
|
||||
country,
|
||||
description,
|
||||
job_detail,
|
||||
price,
|
||||
timeline_days,
|
||||
title,
|
||||
} = formState;
|
||||
|
||||
apiCall
|
||||
.assignFamilyTask(reqData)
|
||||
.then((res) => {
|
||||
if (res.status != 200 || res.data.internal_return < 0) {
|
||||
const requiredFields = {
|
||||
banner,
|
||||
// category,
|
||||
country,
|
||||
description,
|
||||
job_detail,
|
||||
price,
|
||||
timeline_days,
|
||||
title,
|
||||
};
|
||||
|
||||
for (let field in requiredFields) {
|
||||
if (requiredFields[field] == "") {
|
||||
// let currencyErrMsg = field == "country" && "currency"
|
||||
setRequestStatus({
|
||||
loading: false,
|
||||
status: false,
|
||||
message: `${field} is empty`,
|
||||
});
|
||||
return setTimeout(() => {
|
||||
setRequestStatus({ loading: false, status: false, message: "" });
|
||||
}, 3000);
|
||||
}
|
||||
}
|
||||
|
||||
reqData = {
|
||||
banner,
|
||||
category,
|
||||
country,
|
||||
description,
|
||||
job_detail,
|
||||
price: price * 100,
|
||||
timeline_days,
|
||||
title,
|
||||
assign_mode: 110055,
|
||||
family_uid: details?.family_uid || familyDetailsData?.uid,
|
||||
};
|
||||
}
|
||||
|
||||
apiCall
|
||||
.assignFamilyTask(reqData)
|
||||
.then((res) => {
|
||||
if (res.status != 200 || res.data.internal_return < 0) {
|
||||
setRequestStatus({
|
||||
loading: false,
|
||||
status: false,
|
||||
message: "failed to assign task",
|
||||
});
|
||||
return setTimeout(() => {
|
||||
setRequestStatus({ loading: false, status: false, message: "" });
|
||||
}, 5000);
|
||||
}
|
||||
setRequestStatus({
|
||||
loading: false,
|
||||
status: true,
|
||||
message: "action successful",
|
||||
});
|
||||
setUpdatePage(prev => !prev) // Updates family task page by calling the useeffect hook
|
||||
setTimeout(() => {
|
||||
setRequestStatus({ loading: false, status: false, message: "" });
|
||||
action(); // FUNCTION THAT CLOSES THE MODAL BOX
|
||||
}, 5000);
|
||||
})
|
||||
.catch((err) => {
|
||||
setRequestStatus({
|
||||
loading: false,
|
||||
status: false,
|
||||
message: "failed to assign task",
|
||||
message: "An Error occured, try again",
|
||||
});
|
||||
return setTimeout(() => {
|
||||
setTimeout(() => {
|
||||
setRequestStatus({ loading: false, status: false, message: "" });
|
||||
}, 5000);
|
||||
}
|
||||
setRequestStatus({
|
||||
loading: false,
|
||||
status: true,
|
||||
message: "action successful",
|
||||
});
|
||||
setTimeout(() => {
|
||||
setRequestStatus({ loading: false, status: false, message: "" });
|
||||
action(); // FUNCTION THAT CLOSES THE MODAL BOX
|
||||
}, 5000);
|
||||
})
|
||||
.catch((err) => {
|
||||
setRequestStatus({
|
||||
loading: false,
|
||||
status: false,
|
||||
message: "An Error occured, try again",
|
||||
});
|
||||
setTimeout(() => {
|
||||
setRequestStatus({ loading: false, status: false, message: "" });
|
||||
}, 5000);
|
||||
});
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
let checkFamilyTask = true;
|
||||
const reqData = {
|
||||
limit: 30,
|
||||
offset: 0,
|
||||
job_type: "FAMILY",
|
||||
action: 13005,
|
||||
};
|
||||
|
||||
apiCall
|
||||
.getMyJobList(reqData)
|
||||
.then((res) => {
|
||||
if (checkFamilyTask) {
|
||||
setFamilyTask({ loading: false, data: res?.data?.result_list });
|
||||
if (res?.data?.result_list?.length) {
|
||||
setActiveTask((prev) => ({
|
||||
...prev,
|
||||
data: res?.data?.result_list[0],
|
||||
}));
|
||||
}
|
||||
}
|
||||
})
|
||||
.catch((err) => {
|
||||
setFamilyTask({ loading: false, data: [] });
|
||||
console.log("Error", err);
|
||||
});
|
||||
|
||||
return () => {
|
||||
checkFamilyTask = false;
|
||||
};
|
||||
}, []);
|
||||
|
||||
return (
|
||||
<>
|
||||
<ModalCom
|
||||
action={action}
|
||||
situation={situation}
|
||||
className="assign-task-popup"
|
||||
>
|
||||
<div className="w-full h-full lg:w-[700px] lg:h-auto bg-white dark:bg-dark-white lg:rounded-2xl">
|
||||
<div className="w-full flex items-center justify-between lg:px-10 lg:py-8 px-[30px] py-[23px] border-b dark:border-[#5356fb29] border-light-purple">
|
||||
<h1 className="text-26 font-bold text-dark-gray dark:text-white tracking-wide">
|
||||
Assign task to {familyDetails?.firstname || details.firstName}
|
||||
</h1>
|
||||
<button
|
||||
type="button"
|
||||
className="text-[#374557] dark:text-red-500"
|
||||
onClick={action}
|
||||
>
|
||||
<svg
|
||||
width="36"
|
||||
height="36"
|
||||
viewBox="0 0 36 36"
|
||||
fill="none"
|
||||
className="fill-current"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
return (
|
||||
<>
|
||||
<ModalCom
|
||||
action={action}
|
||||
situation={situation}
|
||||
className="assign-task-popup"
|
||||
>
|
||||
<div className="w-full h-full lg:w-[700px] lg:h-auto bg-white dark:bg-dark-white lg:rounded-2xl">
|
||||
<div className="w-full flex items-center justify-between lg:px-10 lg:py-8 px-[30px] py-[23px] border-b dark:border-[#5356fb29] border-light-purple">
|
||||
<h1 className="text-26 font-bold text-dark-gray dark:text-white tracking-wide">
|
||||
Assign task to{" "}
|
||||
{familyDetailsData?.firstname || details?.firstName}
|
||||
</h1>
|
||||
<button
|
||||
type="button"
|
||||
className="text-[#374557] dark:text-red-500"
|
||||
onClick={action}
|
||||
>
|
||||
<path
|
||||
d="M36 16.16C36 17.4399 36 18.7199 36 20.0001C35.7911 20.0709 35.8636 20.2554 35.8385 20.4001C34.5321 27.9453 30.246 32.9248 22.9603 35.2822C21.9006 35.6251 20.7753 35.7657 19.6802 35.9997C18.4003 35.9997 17.1204 35.9997 15.8401 35.9997C15.5896 35.7086 15.2189 35.7732 14.9034 35.7093C7.77231 34.2621 3.08728 30.0725 0.769671 23.187C0.435002 22.1926 0.445997 21.1199 0 20.1599C0 18.7198 0 17.2798 0 15.8398C0.291376 15.6195 0.214408 15.2656 0.270759 14.9808C1.71321 7.69774 6.02611 2.99691 13.0428 0.700951C14.0118 0.383805 15.0509 0.386897 15.9999 0C17.2265 0 18.4532 0 19.6799 0C19.7156 0.124041 19.8125 0.136067 19.9225 0.146719C27.3 0.868973 33.5322 6.21922 35.3801 13.427C35.6121 14.3313 35.7945 15.2484 36 16.16ZM33.011 18.0787C33.0433 9.77105 26.3423 3.00309 18.077 2.9945C9.78479 2.98626 3.00344 9.658 2.98523 17.8426C2.96667 26.1633 9.58859 32.9601 17.7602 33.0079C26.197 33.0577 32.9787 26.4186 33.011 18.0787Z"
|
||||
fill=""
|
||||
fillOpacity="0.6"
|
||||
/>
|
||||
<path
|
||||
d="M15.9309 18.023C13.9329 16.037 12.007 14.1207 10.0787 12.2072C9.60071 11.733 9.26398 11.2162 9.51996 10.506C9.945 9.32677 11.1954 9.0811 12.1437 10.0174C13.9067 11.7585 15.6766 13.494 17.385 15.2879C17.9108 15.8401 18.1633 15.7487 18.6375 15.258C20.3586 13.4761 22.1199 11.7327 23.8822 9.99096C24.8175 9.06632 26.1095 9.33639 26.4967 10.517C26.7286 11.2241 26.3919 11.7413 25.9133 12.2178C24.1757 13.9472 22.4477 15.6855 20.7104 17.4148C20.5228 17.6018 20.2964 17.7495 20.0466 17.9485C22.0831 19.974 24.0372 21.8992 25.9689 23.8468C26.9262 24.8119 26.6489 26.1101 25.4336 26.4987C24.712 26.7292 24.2131 26.3441 23.7455 25.8757C21.9945 24.1227 20.2232 22.3892 18.5045 20.6049C18.0698 20.1534 17.8716 20.2269 17.4802 20.6282C15.732 22.4215 13.9493 24.1807 12.1777 25.951C11.7022 26.4262 11.193 26.7471 10.4738 26.4537C9.31345 25.9798 9.06881 24.8398 9.98589 23.8952C11.285 22.5576 12.6138 21.2484 13.9387 19.9355C14.5792 19.3005 15.2399 18.6852 15.9309 18.023Z"
|
||||
fill="#"
|
||||
fillOpacity="0.6"
|
||||
/>
|
||||
</svg>
|
||||
</button>
|
||||
</div>
|
||||
{familyTask.loading ? (
|
||||
<div className="h-[100px] w-full flex justify-center items-center">
|
||||
<LoadingSpinner color="sky-blue" size="16" />
|
||||
<svg
|
||||
width="36"
|
||||
height="36"
|
||||
viewBox="0 0 36 36"
|
||||
fill="none"
|
||||
className="fill-current"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
>
|
||||
<path
|
||||
d="M36 16.16C36 17.4399 36 18.7199 36 20.0001C35.7911 20.0709 35.8636 20.2554 35.8385 20.4001C34.5321 27.9453 30.246 32.9248 22.9603 35.2822C21.9006 35.6251 20.7753 35.7657 19.6802 35.9997C18.4003 35.9997 17.1204 35.9997 15.8401 35.9997C15.5896 35.7086 15.2189 35.7732 14.9034 35.7093C7.77231 34.2621 3.08728 30.0725 0.769671 23.187C0.435002 22.1926 0.445997 21.1199 0 20.1599C0 18.7198 0 17.2798 0 15.8398C0.291376 15.6195 0.214408 15.2656 0.270759 14.9808C1.71321 7.69774 6.02611 2.99691 13.0428 0.700951C14.0118 0.383805 15.0509 0.386897 15.9999 0C17.2265 0 18.4532 0 19.6799 0C19.7156 0.124041 19.8125 0.136067 19.9225 0.146719C27.3 0.868973 33.5322 6.21922 35.3801 13.427C35.6121 14.3313 35.7945 15.2484 36 16.16ZM33.011 18.0787C33.0433 9.77105 26.3423 3.00309 18.077 2.9945C9.78479 2.98626 3.00344 9.658 2.98523 17.8426C2.96667 26.1633 9.58859 32.9601 17.7602 33.0079C26.197 33.0577 32.9787 26.4186 33.011 18.0787Z"
|
||||
fill=""
|
||||
fillOpacity="0.6"
|
||||
/>
|
||||
<path
|
||||
d="M15.9309 18.023C13.9329 16.037 12.007 14.1207 10.0787 12.2072C9.60071 11.733 9.26398 11.2162 9.51996 10.506C9.945 9.32677 11.1954 9.0811 12.1437 10.0174C13.9067 11.7585 15.6766 13.494 17.385 15.2879C17.9108 15.8401 18.1633 15.7487 18.6375 15.258C20.3586 13.4761 22.1199 11.7327 23.8822 9.99096C24.8175 9.06632 26.1095 9.33639 26.4967 10.517C26.7286 11.2241 26.3919 11.7413 25.9133 12.2178C24.1757 13.9472 22.4477 15.6855 20.7104 17.4148C20.5228 17.6018 20.2964 17.7495 20.0466 17.9485C22.0831 19.974 24.0372 21.8992 25.9689 23.8468C26.9262 24.8119 26.6489 26.1101 25.4336 26.4987C24.712 26.7292 24.2131 26.3441 23.7455 25.8757C21.9945 24.1227 20.2232 22.3892 18.5045 20.6049C18.0698 20.1534 17.8716 20.2269 17.4802 20.6282C15.732 22.4215 13.9493 24.1807 12.1777 25.951C11.7022 26.4262 11.193 26.7471 10.4738 26.4537C9.31345 25.9798 9.06881 24.8398 9.98589 23.8952C11.285 22.5576 12.6138 21.2484 13.9387 19.9355C14.5792 19.3005 15.2399 18.6852 15.9309 18.023Z"
|
||||
fill="#"
|
||||
fillOpacity="0.6"
|
||||
/>
|
||||
</svg>
|
||||
</button>
|
||||
</div>
|
||||
) : (
|
||||
<>
|
||||
<div
|
||||
className={`job-action-modal-body w-full md:grid ${
|
||||
taskType !== "new" ? "md:grid-cols-2" : "md:grid-cols-1"
|
||||
}`}
|
||||
>
|
||||
<div className="p-4">
|
||||
<div className="mb-2 w-full flex items-center gap-4">
|
||||
<div className="flex items-center gap-2 text-sky-blue text-base">
|
||||
<input
|
||||
type="radio"
|
||||
name="task-type"
|
||||
value="select"
|
||||
className="w-[20px] h-[20px] cursor-pointer"
|
||||
checked={taskType == "select"}
|
||||
onChange={switchTaskType}
|
||||
/>
|
||||
<span>Select Task</span>
|
||||
{familyTask.loading ? (
|
||||
<div className="h-[100px] w-full flex justify-center items-center">
|
||||
<LoadingSpinner color="sky-blue" size="16" />
|
||||
</div>
|
||||
) : (
|
||||
<>
|
||||
<div
|
||||
className={`job-action-modal-body w-full md:grid ${
|
||||
taskType !== "new" ? "md:grid-cols-2" : "md:grid-cols-1"
|
||||
}`}
|
||||
>
|
||||
<div className="p-4">
|
||||
<div className="mb-2 w-full flex items-center gap-4">
|
||||
<div className="flex items-center gap-2 text-sky-blue text-base">
|
||||
<input
|
||||
type="radio"
|
||||
name="task-type"
|
||||
value="select"
|
||||
className="w-[20px] h-[20px] cursor-pointer"
|
||||
checked={taskType == "select"}
|
||||
onChange={switchTaskType}
|
||||
/>
|
||||
<span>Select Task</span>
|
||||
</div>
|
||||
<div className="flex items-center gap-2 text-sky-blue text-base">
|
||||
<input
|
||||
type="radio"
|
||||
name="task-type"
|
||||
value="new"
|
||||
className="w-[20px] h-[20px] cursor-pointer"
|
||||
checked={taskType == "new"}
|
||||
onChange={switchTaskType}
|
||||
/>
|
||||
<span>New Task</span>
|
||||
</div>
|
||||
</div>
|
||||
<div className="flex items-center gap-2 text-sky-blue text-base">
|
||||
<input
|
||||
type="radio"
|
||||
name="task-type"
|
||||
value="new"
|
||||
className="w-[20px] h-[20px] cursor-pointer"
|
||||
checked={taskType == "new"}
|
||||
onChange={switchTaskType}
|
||||
/>
|
||||
<span>New Task</span>
|
||||
</div>
|
||||
</div>
|
||||
{/* Task Type === select */}
|
||||
{taskType == "select" && (
|
||||
<div className="p-4 w-full h-[400px] overflow-y-auto bg-slate-100">
|
||||
{familyTask?.data?.length ? (
|
||||
familyTask?.data?.map((item, index) => (
|
||||
<div
|
||||
key={item.job_uid}
|
||||
className="mb-2 flex justify-start items-center gap-2 text-sky-blue text-base cursor-pointer"
|
||||
onClick={() => handleActiveTask(item.job_uid, item)}
|
||||
>
|
||||
<input
|
||||
type="radio"
|
||||
name="task-list"
|
||||
checked={
|
||||
activeTask.id == item.job_uid ||
|
||||
(activeTask.id == index && true)
|
||||
}
|
||||
onChange={() =>
|
||||
{/* Task Type === select */}
|
||||
{taskType == "select" && (
|
||||
<div className="p-4 w-full h-[400px] overflow-y-auto bg-slate-100 rounded-md dark:bg-[#11131f] dark:text-white">
|
||||
{familyTask?.data?.length ? (
|
||||
familyTask?.data?.map((item, index) => (
|
||||
<div
|
||||
key={item.job_uid}
|
||||
className="mb-2 flex justify-start items-center gap-2 text-sky-blue text-base cursor-pointer"
|
||||
onClick={() =>
|
||||
handleActiveTask(item.job_uid, item)
|
||||
}
|
||||
className="w-[15px] h-[15px] cursor-pointer"
|
||||
/>
|
||||
<p className="w-full text-dark-gray tracking-wide">
|
||||
{item?.title}
|
||||
>
|
||||
<input
|
||||
type="radio"
|
||||
name="task-list"
|
||||
checked={
|
||||
activeTask.id == item.job_uid ||
|
||||
(activeTask.id == index && true)
|
||||
}
|
||||
onChange={() =>
|
||||
handleActiveTask(item.job_uid, item)
|
||||
}
|
||||
className="w-[15px] h-[15px] cursor-pointer"
|
||||
/>
|
||||
<p className="w-full text-dark-gray dark:text-white tracking-wide">
|
||||
{item?.title}
|
||||
</p>
|
||||
</div>
|
||||
))
|
||||
) : (
|
||||
<p className="p-8 text-lg text-dark-gray dark:text-white tracking-wide text-center cursor-default">
|
||||
No Task found!
|
||||
</p>
|
||||
)}
|
||||
</div>
|
||||
)}
|
||||
{taskType == "new" && (
|
||||
<div className="p-4 w-full h-[400px]">
|
||||
<NewTasks
|
||||
formState={formState}
|
||||
setFormState={setFormState}
|
||||
/>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
|
||||
{/*Right Hand Side for details && Task Type === select */}
|
||||
{taskType == "select" && (
|
||||
<>
|
||||
{familyTask?.data?.length > 0 ? (
|
||||
<div className="p-4">
|
||||
<div className="w-full">
|
||||
<p className="text-lg font-bold text-dark-gray dark:text-white tracking-wide border-b-2">
|
||||
{activeTask?.data?.title}
|
||||
</p>
|
||||
<div className="my-3">
|
||||
<Detail
|
||||
label="Description"
|
||||
value={activeTask?.data?.description}
|
||||
/>
|
||||
</div>
|
||||
<div className="flex items-center">
|
||||
<div className="my-3 w-full flex items-center gap-1">
|
||||
<label className="text-slate-900 dark:text-white tracking-wide font-semibold">
|
||||
Price
|
||||
</label>
|
||||
<p className="p-1 text-sm text-slate-900 dark:text-white">
|
||||
{PriceFormatter(
|
||||
activeTask?.data?.price * 0.01,
|
||||
activeTask?.data?.currency,
|
||||
activeTask?.data?.curreny_code
|
||||
)}
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div className="my-3 w-full flex items-center gap-1">
|
||||
<label className="text-slate-900 dark:text-white tracking-wide font-semibold">
|
||||
Timeline
|
||||
</label>
|
||||
<p className="p-1 text-sm text-slate-900 dark:text-white">{`${activeTask?.data?.timeline_days} day(s)`}</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="my-3 sm:flex items-center">
|
||||
<Detail
|
||||
label="Created"
|
||||
value={`Dummy, no value found for created!`}
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div className="my-3">
|
||||
<label className="w-full text-slate-900 dark:text-white tracking-wide font-semibold">
|
||||
Delivery Detail
|
||||
</label>
|
||||
<textarea
|
||||
className={`p-1 w-full text-sm text-slate-900 dark:text-white bg-transparent outline-none border border-slate-300 rounded-md`}
|
||||
rows="5"
|
||||
style={{ resize: "none" }}
|
||||
value={activeTask?.data?.job_detail}
|
||||
readOnly
|
||||
// onChange={handleInputChange}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
))
|
||||
</div>
|
||||
) : (
|
||||
<p className="p-8 text-lg text-dark-gray dark:text-white tracking-wide text-center cursor-default">
|
||||
No Task found!
|
||||
</p>
|
||||
<></>
|
||||
)}
|
||||
</div>
|
||||
)}
|
||||
{taskType == "new" && (
|
||||
<div className="p-4 w-full h-[400px]">
|
||||
<NewTasks
|
||||
formState={formState}
|
||||
setFormState={setFormState}
|
||||
/>
|
||||
</div>
|
||||
</>
|
||||
)}
|
||||
</div>
|
||||
|
||||
{/*Right Hand Side for details && Task Type === select */}
|
||||
{taskType == "select" && (
|
||||
<>
|
||||
{familyTask?.data?.length > 0 ? (
|
||||
<div className="p-4">
|
||||
<div className="w-full">
|
||||
<p className="text-lg font-bold text-dark-gray dark:text-white tracking-wide border-b-2">
|
||||
{activeTask?.data?.title}
|
||||
</p>
|
||||
<div className="my-3">
|
||||
<Detail
|
||||
label="Description"
|
||||
value={activeTask?.data?.description}
|
||||
/>
|
||||
</div>
|
||||
<div className="flex items-center">
|
||||
<div className="my-3 w-full flex items-center gap-1">
|
||||
<label className="text-slate-900 dark:text-white tracking-wide font-semibold">
|
||||
Price
|
||||
</label>
|
||||
<p className="p-1 text-sm text-slate-900 dark:text-white">
|
||||
{PriceFormatter(
|
||||
activeTask?.data?.price * 0.01,
|
||||
activeTask?.data?.currency,
|
||||
activeTask?.data?.curreny_code
|
||||
)}
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div className="my-3 w-full flex items-center gap-1">
|
||||
<label className="text-slate-900 dark:text-white tracking-wide font-semibold">
|
||||
Timeline
|
||||
</label>
|
||||
<p className="p-1 text-sm text-slate-900 dark:text-white">{`${activeTask?.data?.timeline_days} day(s)`}</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="my-3 sm:flex items-center">
|
||||
<Detail
|
||||
label="Created"
|
||||
value={`Dummy, no value found for created!`}
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div className="my-3">
|
||||
<label className="w-full text-slate-900 dark:text-white tracking-wide font-semibold">
|
||||
Delivery Detail
|
||||
</label>
|
||||
<textarea
|
||||
className={`p-1 w-full text-sm text-slate-900 outline-none border border-slate-300 rounded-md`}
|
||||
rows="5"
|
||||
style={{ resize: "none" }}
|
||||
value={activeTask?.data?.job_detail}
|
||||
readOnly
|
||||
// onChange={handleInputChange}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
) : (
|
||||
<></>
|
||||
)}
|
||||
</>
|
||||
)}
|
||||
</div>
|
||||
|
||||
{/* BTN */}
|
||||
<div className="py-2 px-4 border-t-2 flex justify-between items-center">
|
||||
{/* error or success display */}
|
||||
<div className="w-auto h-auto flex items-center">
|
||||
{requestStatus.message != "" &&
|
||||
(!requestStatus.status ? (
|
||||
<div
|
||||
className={`relative p-2 text-[#912741] bg-[#fcd9e2] border-[#fbc6d3] rounded-[0.475rem] text-md font-light leading-[19.5px] text-[13px] self-start`}
|
||||
>
|
||||
{requestStatus.message}
|
||||
</div>
|
||||
) : (
|
||||
requestStatus.status && (
|
||||
{/* BTN */}
|
||||
<div className="py-2 px-4 border-t-2 flex justify-between items-center">
|
||||
{/* error or success display */}
|
||||
<div className="w-auto h-auto flex items-center">
|
||||
{requestStatus.message != "" &&
|
||||
(!requestStatus.status ? (
|
||||
<div
|
||||
className={`relative p-2 text-green-700 bg-slate-200 border-slate-800 mb-4 rounded-[0.475rem] text-md font-light leading-[19.5px] text-[13px]`}
|
||||
className={`relative p-2 text-[#912741] bg-[#fcd9e2] border-[#fbc6d3] rounded-[0.475rem] text-md font-light leading-[19.5px] text-[13px] self-start`}
|
||||
>
|
||||
{requestStatus.message}
|
||||
</div>
|
||||
)
|
||||
))}
|
||||
</div>
|
||||
) : (
|
||||
requestStatus.status && (
|
||||
<div
|
||||
className={`relative p-2 text-green-700 bg-slate-200 border-slate-800 mb-4 rounded-[0.475rem] text-md font-light leading-[19.5px] text-[13px]`}
|
||||
>
|
||||
{requestStatus.message}
|
||||
</div>
|
||||
)
|
||||
))}
|
||||
</div>
|
||||
|
||||
{/* End of error or success display */}
|
||||
<div className="w-auto h-auto flex items-center gap-3">
|
||||
<button
|
||||
disabled={requestStatus.loading}
|
||||
onClick={action}
|
||||
type="button"
|
||||
className="w-20 h-11 flex justify-center items-center border-gradient text-base rounded-full text-white cursor-pointer"
|
||||
>
|
||||
<span className="text-gradient">Close</span>
|
||||
</button>
|
||||
<div className="">
|
||||
{requestStatus.loading ? (
|
||||
<LoadingSpinner color="sky-blue" size="8" />
|
||||
) : taskType == "select" ? (
|
||||
<button
|
||||
type="button"
|
||||
disabled={requestStatus.loading}
|
||||
onClick={assignFamilyTask}
|
||||
className="px-1 w-20 h-11 flex justify-center items-center btn-gradient text-base rounded-full text-white cursor-pointer"
|
||||
>
|
||||
Assign
|
||||
</button>
|
||||
) : (
|
||||
<button
|
||||
type="button"
|
||||
disabled={requestStatus.loading}
|
||||
onClick={assignFamilyTask}
|
||||
className="px-1 w-40 h-11 flex justify-center items-center btn-gradient text-base rounded-full text-white cursor-pointer"
|
||||
>
|
||||
{`Assign to ${
|
||||
familyDetails?.firstname || details?.firstName
|
||||
}`}
|
||||
</button>
|
||||
)}
|
||||
{/* End of error or success display */}
|
||||
<div className="w-auto h-auto flex items-center gap-3">
|
||||
<button
|
||||
disabled={requestStatus.loading}
|
||||
onClick={action}
|
||||
type="button"
|
||||
className="w-20 h-11 flex justify-center items-center border-gradient text-base rounded-full text-white cursor-pointer"
|
||||
>
|
||||
<span className="text-gradient">Close</span>
|
||||
</button>
|
||||
<div className="">
|
||||
{requestStatus.loading ? (
|
||||
<LoadingSpinner color="sky-blue" size="8" />
|
||||
) : taskType == "select" ? (
|
||||
<button
|
||||
type="button"
|
||||
disabled={requestStatus.loading}
|
||||
onClick={assignFamilyTask}
|
||||
className="px-1 w-20 h-11 flex justify-center items-center btn-gradient text-base rounded-full text-white cursor-pointer"
|
||||
>
|
||||
Assign
|
||||
</button>
|
||||
) : (
|
||||
<button
|
||||
type="button"
|
||||
disabled={requestStatus.loading}
|
||||
onClick={assignFamilyTask}
|
||||
className="px-1 w-40 h-11 flex justify-center items-center btn-gradient text-base rounded-full text-white cursor-pointer"
|
||||
>
|
||||
{`Assign to ${
|
||||
familyDetailsData?.firstname || details?.firstName
|
||||
}`}
|
||||
</button>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</>
|
||||
)}
|
||||
</div>
|
||||
</ModalCom>
|
||||
</>
|
||||
);
|
||||
})
|
||||
</>
|
||||
)}
|
||||
</div>
|
||||
</ModalCom>
|
||||
</>
|
||||
);
|
||||
}
|
||||
);
|
||||
|
||||
export default AssignTaskPopout;
|
||||
export default AssignTaskPopout;
|
||||
|
||||
@@ -1,28 +1,124 @@
|
||||
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 }) {
|
||||
/**
|
||||
* Renders a list of family members that can be managed.
|
||||
* It has its current maximum members at 8 and it comes with pagination and loading spinner functionality.
|
||||
|
||||
* @returns {JSX.Element} - The rendered component.
|
||||
*/
|
||||
export default function FamilyTable({
|
||||
className,
|
||||
familyList,
|
||||
loader,
|
||||
popUpHandler,
|
||||
}) {
|
||||
const navigate = useNavigate();
|
||||
|
||||
const [currentPage, setCurrentPage] = useState(0);
|
||||
const itemsPerPage = Number(process.env.REACT_APP_ITEM_PER_PAGE);
|
||||
const indexOfFirstItem = Number(currentPage);
|
||||
const indexOfLastItem =
|
||||
Number(indexOfFirstItem) + Number(process.env.REACT_APP_ITEM_PER_PAGE);
|
||||
const currentFamilyList = familyList?.slice(indexOfFirstItem, indexOfLastItem);
|
||||
const currentFamilyList = familyList?.slice(
|
||||
indexOfFirstItem,
|
||||
indexOfLastItem
|
||||
);
|
||||
|
||||
const handleManageClick = (familyMember) => {
|
||||
navigate("/manage-family", { state: familyMember });
|
||||
};
|
||||
|
||||
const handlePagination = (e) => {
|
||||
handlePagingFunc(e, setCurrentPage);
|
||||
};
|
||||
|
||||
/**
|
||||
* Renders a table row for a family member.
|
||||
* @returns {JSX.Element} - The table row component.
|
||||
*/
|
||||
const FamilyRow = ({
|
||||
firstname,
|
||||
lastname,
|
||||
age,
|
||||
added,
|
||||
last_login,
|
||||
task_count,
|
||||
family_uid,
|
||||
banner,
|
||||
}) => {
|
||||
// Check for valid dates
|
||||
const addedDate = added ? added.split(" ")[0] : "N/A";
|
||||
const loginDate = last_login ? formatDateString(last_login) : "N/A";
|
||||
|
||||
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={localImgLoad(`images/icons/${banner}`)}
|
||||
alt={`Avatar of ${firstname} ${lastname}`}
|
||||
className="w-full h-full"
|
||||
/>
|
||||
</div>
|
||||
<div className="flex flex-col flex-[0.9]">
|
||||
<h1 className="font-bold text-xl text-dark-gray dark:text-white whitespace-nowrap">
|
||||
{`${firstname} ${lastname} (${age})`}
|
||||
</h1>
|
||||
<span className="text-sm text-thin-light-gray">
|
||||
Added: <span className="text-purple ml-1">{addedDate}</span>
|
||||
</span>
|
||||
<span className="text-sm text-thin-light-gray">
|
||||
Last Login:{" "}
|
||||
<span className="text-purple ml-1">{loginDate}</span>
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</td>
|
||||
<td className="text-center py-4 px-2">
|
||||
<div className="flex space-x-1 items-center justify-center">
|
||||
<span className="text-base text-dark-gray dark:text-white font-medium whitespace-nowrap">
|
||||
{task_count}
|
||||
</span>
|
||||
</div>
|
||||
</td>
|
||||
<td className="text-right py-4 px-2 flex items-center justify-center">
|
||||
<button
|
||||
onClick={() =>
|
||||
handleManageClick({
|
||||
firstname,
|
||||
lastname,
|
||||
age,
|
||||
added,
|
||||
last_login,
|
||||
task_count,
|
||||
family_uid,
|
||||
banner,
|
||||
})
|
||||
}
|
||||
type="button"
|
||||
className="w-20 h-11 flex justify-center items-center btn-gradient text-base rounded-full text-white"
|
||||
>
|
||||
Manage
|
||||
</button>
|
||||
</td>
|
||||
</tr>
|
||||
);
|
||||
};
|
||||
|
||||
return (
|
||||
<div
|
||||
className={`update-table w-full 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 || ""
|
||||
}`}
|
||||
>
|
||||
@@ -31,123 +127,51 @@ 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,
|
||||
} = 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-full h-[60px] rounded-full overflow-hidden flex justify-center items-center flex-[0.1] min-w-[60px]">
|
||||
<img
|
||||
src={dataImage1}
|
||||
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">No of Tasks</th>
|
||||
<th className="py-4 text-right"></th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody className="h-full">
|
||||
{currentFamilyList?.map((familyMember, index) => {
|
||||
return <FamilyRow key={index} {...familyMember} />;
|
||||
})}
|
||||
</tbody>
|
||||
</table>
|
||||
) : (
|
||||
<div className="font-bold text-center text-xl md:text-2xl lg:text-4xl text-dark-gray md:flex items-center justify-between">
|
||||
<div className="p-2 w-full md:w-1/2">
|
||||
<p className="mb-4 p-3 md:p-16">
|
||||
Add your family, assign tasks, and get the whole team
|
||||
engaged.
|
||||
</p>
|
||||
<button
|
||||
onClick={popUpHandler}
|
||||
type="button"
|
||||
className="text-white btn-gradient text-lg tracking-wide px-5 py-2 rounded-full"
|
||||
>
|
||||
Add Family
|
||||
</button>
|
||||
</div>
|
||||
<div className="p-2 w-full md:w-1/2">
|
||||
<img className="w-full" src={familyImage} alt="Add Family" />
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
</>
|
||||
)}
|
||||
</div>
|
||||
{/* PAGINATION BUTTON */}
|
||||
<PaginatedList
|
||||
{/* PAGINATION BUTTON */}
|
||||
<PaginatedList
|
||||
onClick={handlePagination}
|
||||
prev={currentPage == 0 ? true : false}
|
||||
next={
|
||||
currentPage + Number(process.env.REACT_APP_ITEM_PER_PAGE) >=
|
||||
familyList?.length
|
||||
? true
|
||||
: false
|
||||
}
|
||||
prev={currentPage == 0}
|
||||
next={currentPage + itemsPerPage >= familyList.length}
|
||||
data={familyList}
|
||||
start={indexOfFirstItem}
|
||||
stop={indexOfLastItem}
|
||||
|
||||
@@ -1,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;
|
||||
|
||||
@@ -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 || ""
|
||||
}`}
|
||||
>
|
||||
@@ -125,8 +124,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>
|
||||
)}
|
||||
</>
|
||||
|
||||
@@ -1,3 +1,308 @@
|
||||
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,
|
||||
action: 22020,
|
||||
};
|
||||
|
||||
// const handleChange = ({ target: { name, value } }) => {
|
||||
// setUpdateDetails((prev) => ({ ...prev, [name]: value }));
|
||||
// };
|
||||
|
||||
const updateFamily = (values, helpers) => {
|
||||
setRequestStatus({ loading: true, status: false, message: "" });
|
||||
// let {firstname, lastname, year, month} = updateDetails
|
||||
// if(!firstname || !lastname || year == 0 || month == 0) {
|
||||
// setRequestStatus({loading:false, status:false, message: 'Please fill all fields'})
|
||||
// return setTimeout(()=>{
|
||||
// setRequestStatus({loading:false, status:false, message: ''})
|
||||
// }, 5000)
|
||||
// }
|
||||
api
|
||||
.getFamilyUpdate(values)
|
||||
.then((res) => {
|
||||
if (res.data.internal_return < 0) {
|
||||
return setRequestStatus({
|
||||
loading: false,
|
||||
status: false,
|
||||
message: "Failed, try again!",
|
||||
});
|
||||
}
|
||||
setRequestStatus({
|
||||
loading: false,
|
||||
status: true,
|
||||
message: "Family account updated",
|
||||
});
|
||||
setTimeout(() => {
|
||||
navigate("/acc-family", { replace: true });
|
||||
}, 5000);
|
||||
})
|
||||
.catch((error) => {
|
||||
setRequestStatus({
|
||||
loading: false,
|
||||
status: false,
|
||||
message: "Unable to update, try again!",
|
||||
});
|
||||
})
|
||||
.finally(() => {
|
||||
setTimeout(() => {
|
||||
setRequestStatus({ loading: false, status: false, message: "" });
|
||||
}, 5000);
|
||||
});
|
||||
};
|
||||
|
||||
return (
|
||||
<div
|
||||
className={`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" },
|
||||
];
|
||||
|
||||
@@ -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,9 @@ export default function FamilyTasks({ familyData, className, loader, accountDeta
|
||||
value?.currency_code,
|
||||
value?.currency
|
||||
);
|
||||
let image = value.banner ? value.banner : 'default.jpg'
|
||||
let image = value.banner
|
||||
? value.banner
|
||||
: "default.jpg";
|
||||
return (
|
||||
<tr
|
||||
key={index}
|
||||
@@ -66,7 +79,9 @@ 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={localImgLoad(
|
||||
`images/taskbanners/${image}`
|
||||
)}
|
||||
alt="data"
|
||||
className="w-full h-full rounded-full"
|
||||
/>
|
||||
@@ -75,7 +90,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 +124,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 +135,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>
|
||||
|
||||
@@ -1,148 +1,152 @@
|
||||
import { useState, useMemo, memo } from "react";
|
||||
import { handlePagingFunc, PaginatedList } from "../../Pagination";
|
||||
import LoadingSpinner from "../../Spinners/LoadingSpinner";
|
||||
import { memo, useMemo, useState } from "react";
|
||||
import SuggestTask from "../../FamilyPopup/SuggestTask";
|
||||
import { PaginatedList, handlePagingFunc } from "../../Pagination";
|
||||
import LoadingSpinner from "../../Spinners/LoadingSpinner";
|
||||
import AssignTaskPopout from "../FamilyPopout/AssignTaskPopout";
|
||||
|
||||
const FamilyWaitlist = memo(({ familyData, className, accountDetails, loader }) => {
|
||||
const [popUp, setPopUp] = useState({ show: false, data: {} });
|
||||
const [continueTaskPopup, setContinueTaskPopup] = useState({
|
||||
show: false,
|
||||
data: {},
|
||||
});
|
||||
const filteredFamilyData = useMemo(
|
||||
() =>
|
||||
familyData?.result_list?.filter(
|
||||
(data) => data?.family_uid === accountDetails?.family_uid
|
||||
),
|
||||
[familyData, accountDetails]
|
||||
);
|
||||
const FamilyWaitlist = memo(
|
||||
({ familyData, className, accountDetails, loader }) => {
|
||||
const [popUp, setPopUp] = useState({ show: false, data: {} });
|
||||
const [continueTaskPopup, setContinueTaskPopup] = useState({
|
||||
show: false,
|
||||
data: {},
|
||||
});
|
||||
const filteredFamilyData = useMemo(
|
||||
() =>
|
||||
familyData?.result_list?.filter(
|
||||
(data) => data?.family_uid === accountDetails?.family_uid
|
||||
),
|
||||
[familyData, accountDetails]
|
||||
);
|
||||
|
||||
const [currentPage, setCurrentPage] = useState(0);
|
||||
const itemsPerPage = Number(process.env.REACT_APP_ITEM_PER_PAGE);
|
||||
const indexOfFirstItem = currentPage;
|
||||
const indexOfLastItem = currentPage + itemsPerPage;
|
||||
const currentTask = useMemo(
|
||||
() => filteredFamilyData?.slice(indexOfFirstItem, indexOfLastItem),
|
||||
[filteredFamilyData, indexOfFirstItem, indexOfLastItem]
|
||||
);
|
||||
const [currentPage, setCurrentPage] = useState(0);
|
||||
const itemsPerPage = Number(process.env.REACT_APP_ITEM_PER_PAGE);
|
||||
const indexOfFirstItem = currentPage;
|
||||
const indexOfLastItem = currentPage + itemsPerPage;
|
||||
const currentTask = useMemo(
|
||||
() => filteredFamilyData?.slice(indexOfFirstItem, indexOfLastItem),
|
||||
[filteredFamilyData, indexOfFirstItem, indexOfLastItem]
|
||||
);
|
||||
|
||||
const handlePagination = (e) => handlePagingFunc(e, setCurrentPage);
|
||||
const handlePagination = (e) => handlePagingFunc(e, setCurrentPage);
|
||||
|
||||
const openPopUp = (value) => {
|
||||
setPopUp({ show: true, data: { ...value } });
|
||||
};
|
||||
const openPopUp = (value) => {
|
||||
setPopUp({ show: true, data: { ...value } });
|
||||
};
|
||||
|
||||
const closePopUp = () => {
|
||||
setPopUp({ show: false, data: {} });
|
||||
};
|
||||
const closePopUp = () => {
|
||||
setPopUp({ show: false, data: {} });
|
||||
};
|
||||
|
||||
const openContinueTaskPopup = (value) => {
|
||||
setContinueTaskPopup({ show: true, data: { ...value } });
|
||||
};
|
||||
const openContinueTaskPopup = (value) => {
|
||||
setContinueTaskPopup({ show: true, data: { ...value } });
|
||||
};
|
||||
|
||||
const closeContinueTaskPopup = () => {
|
||||
setContinueTaskPopup({ show: false, data: {} });
|
||||
};
|
||||
const closeContinueTaskPopup = () => {
|
||||
setContinueTaskPopup({ show: false, data: {} });
|
||||
};
|
||||
|
||||
return (
|
||||
<div
|
||||
className={`update-table w-full bg-white dark:bg-dark-white h-full lg:min-h-[450px] overflow-hidden rounded-2xl section-shadow ${
|
||||
className || ""
|
||||
}`}
|
||||
>
|
||||
{loader ? (
|
||||
<div className="w-full h-full flex justify-center items-center lg:min-h-[470px]">
|
||||
<LoadingSpinner size={16} color="sky-blue" />
|
||||
</div>
|
||||
) : (
|
||||
<>
|
||||
{filteredFamilyData && (
|
||||
<div className="relative w-full overflow-x-auto sm:rounded-lg flex flex-col justify-between h-full">
|
||||
<table className="w-full text-sm text-left text-gray-500 dark:text-gray-400">
|
||||
<tbody>
|
||||
{currentTask.map((value) => {
|
||||
const addedDate = value?.added.split(" ")[0];
|
||||
const selectedImage = require(`../../../assets/images/family/${
|
||||
value?.banner || "default.jpg"
|
||||
}`);
|
||||
return (
|
||||
<tr
|
||||
className="bg-white dark:bg-dark-white border-b dark:border-[#5356fb29] hover:bg-gray-50"
|
||||
key={value.uid}
|
||||
>
|
||||
<td className="py-4">
|
||||
<div className="w-full flex justify-between items-center">
|
||||
<div className="account-name flex space-x-4 items-center">
|
||||
<div className="icon w-14 h-14 flex justify-center items-center">
|
||||
<img
|
||||
src={selectedImage}
|
||||
alt="task_img"
|
||||
className="w-full object-cover"
|
||||
/>
|
||||
return (
|
||||
<div
|
||||
className={`update-table w-full bg-white dark:bg-dark-white h-full lg:min-h-[538px] p-3 overflow-hidden rounded-2xl section-shadow ${
|
||||
className || ""
|
||||
}`}
|
||||
>
|
||||
{loader ? (
|
||||
<div className="w-full h-full flex justify-center items-center lg:min-h-[470px]">
|
||||
<LoadingSpinner size={16} color="sky-blue" />
|
||||
</div>
|
||||
) : (
|
||||
<>
|
||||
{filteredFamilyData && (
|
||||
<div className="relative w-full overflow-x-auto sm:rounded-lg flex flex-col justify-between h-full">
|
||||
<table className="w-full text-sm text-left text-gray-500 dark:text-gray-400">
|
||||
<tbody>
|
||||
{currentTask.map((value) => {
|
||||
const addedDate = value?.added.split(" ")[0];
|
||||
const selectedImage = require(`../../../assets/images/family/${
|
||||
value?.banner || "default.jpg"
|
||||
}`);
|
||||
return (
|
||||
<tr
|
||||
className="bg-white dark:bg-dark-white border-b dark:border-[#5356fb29] hover:bg-gray-50"
|
||||
key={value.uid}
|
||||
>
|
||||
<td className="py-4">
|
||||
<div className="w-full flex justify-between items-center">
|
||||
<div className="account-name flex space-x-4 items-center">
|
||||
<div className="icon w-14 h-14 flex justify-center items-center">
|
||||
<img
|
||||
src={selectedImage}
|
||||
alt="task_img"
|
||||
className="w-full object-cover"
|
||||
/>
|
||||
</div>
|
||||
<div className="">
|
||||
<p className="text-xl font-bold text-dark-gray dark:text-white mb-2 capitalize line-clamp-1">
|
||||
{value.title}
|
||||
</p>
|
||||
<p className="text-sm text-thin-light-gray font-medium">
|
||||
{value.description}
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
<div className="">
|
||||
<p className="text-xl font-bold text-dark-gray dark:text-white mb-2 capitalize line-clamp-1">
|
||||
{value.title}
|
||||
<div className="px-2 flex flex-col items-center justify-center">
|
||||
<p className="text-sm font-bold text-dark-gray dark:text-white">
|
||||
{addedDate}
|
||||
</p>
|
||||
<p className="text-sm text-thin-light-gray font-medium">
|
||||
{value.description}
|
||||
<p className="text-xs py-1.5 w-[70px] cursor-default tracking-wide rounded-full bg-gold text-white flex justify-center items-center">
|
||||
{value.status_text}
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
<div className="px-2 flex flex-col items-center justify-center">
|
||||
<p className="text-sm font-bold text-dark-gray dark:text-white">
|
||||
{addedDate}
|
||||
</p>
|
||||
<p className="text-xs py-1.5 w-[70px] cursor-default tracking-wide rounded-full bg-gold text-white flex justify-center items-center">
|
||||
{value.status_text}
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</td>
|
||||
<td className="text-right py-4 px-2">
|
||||
<button
|
||||
onClick={() => openPopUp(value)}
|
||||
className="w-20 h-11 flex justify-center items-center btn-gradient text-base rounded-full text-white"
|
||||
>
|
||||
View
|
||||
</button>
|
||||
</td>
|
||||
</tr>
|
||||
);
|
||||
})}
|
||||
</tbody>
|
||||
</table>
|
||||
<PaginatedList
|
||||
onClick={handlePagination}
|
||||
prev={currentPage === 0}
|
||||
next={currentPage + itemsPerPage >= filteredFamilyData?.length}
|
||||
data={filteredFamilyData}
|
||||
start={indexOfFirstItem}
|
||||
stop={indexOfLastItem}
|
||||
/>
|
||||
</div>
|
||||
)}
|
||||
</>
|
||||
)}
|
||||
{popUp.show && (
|
||||
<SuggestTask
|
||||
details={popUp.data}
|
||||
onClose={closePopUp}
|
||||
continuePopupData={openContinueTaskPopup}
|
||||
situation={popUp.show}
|
||||
/>
|
||||
)}
|
||||
</td>
|
||||
<td className="text-right py-4 px-2">
|
||||
<button
|
||||
onClick={() => openPopUp(value)}
|
||||
className="w-20 h-11 flex justify-center items-center btn-gradient text-base rounded-full text-white"
|
||||
>
|
||||
View
|
||||
</button>
|
||||
</td>
|
||||
</tr>
|
||||
);
|
||||
})}
|
||||
</tbody>
|
||||
</table>
|
||||
<PaginatedList
|
||||
onClick={handlePagination}
|
||||
prev={currentPage === 0}
|
||||
next={
|
||||
currentPage + itemsPerPage >= filteredFamilyData?.length
|
||||
}
|
||||
data={filteredFamilyData}
|
||||
start={indexOfFirstItem}
|
||||
stop={indexOfLastItem}
|
||||
/>
|
||||
</div>
|
||||
)}
|
||||
</>
|
||||
)}
|
||||
{popUp.show && (
|
||||
<SuggestTask
|
||||
details={popUp.data}
|
||||
onClose={closePopUp}
|
||||
continuePopupData={openContinueTaskPopup}
|
||||
situation={popUp.show}
|
||||
/>
|
||||
)}
|
||||
|
||||
{continueTaskPopup.show && (
|
||||
<AssignTaskPopout
|
||||
details={continueTaskPopup.data}
|
||||
action={closeContinueTaskPopup}
|
||||
situation={continueTaskPopup.show}
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
});
|
||||
{continueTaskPopup.show && (
|
||||
<AssignTaskPopout
|
||||
details={continueTaskPopup.data}
|
||||
action={closeContinueTaskPopup}
|
||||
situation={continueTaskPopup.show}
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
);
|
||||
|
||||
export default FamilyWaitlist;
|
||||
|
||||
@@ -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
|
||||
@@ -1,24 +1,31 @@
|
||||
import React from "react";
|
||||
import localImgLoad from "../../../lib/localImgLoad";
|
||||
|
||||
export default function ProfileInfo({
|
||||
profileImg,
|
||||
profileImgInput,
|
||||
profileImgChangHandler,
|
||||
profileImgChangeHandler,
|
||||
browseProfileImg,
|
||||
accountDetails,
|
||||
}) {
|
||||
// console.log(accountDetails.banner)
|
||||
return (
|
||||
<div className="flex flex-col items-center gap-6">
|
||||
<div className="flex flex-col items-center gap-4">
|
||||
<div className="flex justify-center">
|
||||
<div className="w-full relative">
|
||||
<img
|
||||
src={profileImg}
|
||||
alt=""
|
||||
className="sm:w-[198px] sm:h-[198px] w-[120px] h-[120px] rounded-full overflow-hidden object-cover"
|
||||
src={
|
||||
profileImg
|
||||
? profileImg
|
||||
: localImgLoad(`images/icons/${accountDetails.banner}`)
|
||||
}
|
||||
alt="profile"
|
||||
className="sm:w-[180px] sm:h-[180px] w-[120px] h-[120px] rounded-full overflow-hidden object-cover"
|
||||
/>
|
||||
<input
|
||||
ref={profileImgInput}
|
||||
onChange={(e) => profileImgChangHandler(e)}
|
||||
accept="image/*"
|
||||
onChange={profileImgChangeHandler}
|
||||
type="file"
|
||||
className="hidden"
|
||||
/>
|
||||
|
||||
@@ -0,0 +1,301 @@
|
||||
import React, { useEffect, useState } from 'react'
|
||||
import ModalCom from '../../../Helpers/ModalCom'
|
||||
import InputCom from '../../../Helpers/Inputs/InputCom'
|
||||
import { Form, Formik } from "formik";
|
||||
import * as Yup from "yup";
|
||||
|
||||
import {AmountTo2DP} from '../../../Helpers/PriceFormatter'
|
||||
import usersService from '../../../../services/UsersService';
|
||||
import LoadingSpinner from '../../../Spinners/LoadingSpinner';
|
||||
import { PriceFormatter } from '../../../Helpers/PriceFormatter';
|
||||
import { tableReload } from '../../../../store/TableReloads';
|
||||
import { useDispatch } from 'react-redux';
|
||||
|
||||
const validationSchema = Yup.object().shape({
|
||||
// amount: Yup.string()
|
||||
// .typeError("Invalid number")
|
||||
// .min(1, "Price must be greater than 0")
|
||||
// .test("no-e", "Invalid number", (value) => {
|
||||
// if (value && /\d+e/.test(value)) {
|
||||
// return false;
|
||||
// }
|
||||
// return true;
|
||||
// })
|
||||
// .required("Amount is required"),
|
||||
amount: Yup.number('Please enter a number')
|
||||
.min(1, "Price must be greater than 0")
|
||||
.required("Amount is required"),
|
||||
comment: Yup.string()
|
||||
.required("Comment is required"),
|
||||
});
|
||||
|
||||
function FamilyAddFundPopout({action, situation, wallet, familyData}) {
|
||||
|
||||
const dispatch = useDispatch()
|
||||
|
||||
const apiUrl = new usersService()
|
||||
|
||||
const [startTransfer, setStartTransfer] = useState({loading:true, data: {}})
|
||||
|
||||
const [requestStatus, setRequestStatus] = useState({loading:false, status:false, message:''})
|
||||
|
||||
|
||||
// initial values for formik
|
||||
let initialValues = {
|
||||
amount: '',
|
||||
from : AmountTo2DP(startTransfer?.data?.origing_current_balance*0.01),
|
||||
to: `${familyData.firstname} ${familyData.lastname}`,
|
||||
comment: ''
|
||||
};
|
||||
// FUNCTION TO PERFORM FAMILY TRANSFER
|
||||
const handleAddFund = (values) => {
|
||||
setRequestStatus({loading:true, status:false, message:''})
|
||||
|
||||
let senderBal = startTransfer?.data?.origing_current_balance || '' // SENDER'S ACCOUNT BALANCE
|
||||
let senderLimit = startTransfer?.data?.origing_transfer_limit || '' // SENDER'S TRANSFER LIMIT
|
||||
|
||||
let reqData = { // API REQUEST DATA
|
||||
family_uid : familyData.uid,
|
||||
wallet_uid : wallet.wallet_uid,
|
||||
origing_wallet_uid : startTransfer?.data?.origing_wallet_uid,
|
||||
currency : startTransfer?.data?.currency,
|
||||
amount : values.amount*100,
|
||||
description : values.comment,
|
||||
family_transfer_mode : 100,
|
||||
action : 22014
|
||||
}
|
||||
|
||||
if(!senderBal || !senderLimit){ // RETURNS UNAUTHORIZED, IF SENDER BAL OR LIMIT IS NOT AVAILABLE
|
||||
setRequestStatus({loading:false, status:false, message:'Unauthorized, try again later'})
|
||||
return setTimeout(()=>{
|
||||
setRequestStatus({loading:false, status:false, message:''})
|
||||
}, 5000)
|
||||
}
|
||||
|
||||
if(values.amount > senderBal*0.01){ // CHECKS TO SEE IF SENDER IS SENDING MORE THAN HIS BALANCE
|
||||
setRequestStatus({loading:false, status:false, message:'You cannot send more than your balance'})
|
||||
return setTimeout(()=>{
|
||||
setRequestStatus({loading:false, status:false, message:''})
|
||||
}, 5000)
|
||||
}
|
||||
|
||||
if(values.amount > senderLimit*0.01){ // CHECKS TO SEE IF SENDER IS SENDING MORE THAN HIS LIMIT
|
||||
setRequestStatus({loading:false, status:false, message:`You cannot exceed ${senderLimit*0.01} ${startTransfer?.data?.origing_currency.charAt(0).toUpperCase() + startTransfer?.data?.origing_currency.slice(1).toLowerCase()}`})
|
||||
return setTimeout(()=>{
|
||||
setRequestStatus({loading:false, status:false, message:''})
|
||||
}, 5000)
|
||||
}
|
||||
|
||||
apiUrl.familyTransfer(reqData).then(({data}) => {
|
||||
if(data.internal_return < 0 || data.credit_confirm == '' || data.pay_confirm == ''){
|
||||
setRequestStatus({loading:false, status:false, message:'Transfer Failed'})
|
||||
return setTimeout(()=>{
|
||||
setRequestStatus({loading:false, status:false, message:''})
|
||||
}, 5000)
|
||||
}
|
||||
setRequestStatus({loading:false, status:true, message:'Transfer Successful'})
|
||||
setTimeout(()=>{
|
||||
setRequestStatus({loading:false, status:false, message:''})
|
||||
dispatch(tableReload({ type: "WALLETTABLE" })); // UPDATES PARENT WALLET ACCOUNT
|
||||
action() // TO CLOSE THE MODAL
|
||||
}, 5000)
|
||||
}).catch(error => {
|
||||
setRequestStatus({loading:false, status:false, message:'Network Error, try again'})
|
||||
setTimeout(()=>{
|
||||
setRequestStatus({loading:false, status:false, message:''})
|
||||
}, 5000)
|
||||
})
|
||||
}
|
||||
|
||||
// LOAD FAMILY START TRANSFER
|
||||
useEffect(()=>{
|
||||
let reqData = {
|
||||
family_uid: familyData.uid,
|
||||
wallet_uid: wallet.wallet_uid,
|
||||
action: 22013
|
||||
}
|
||||
apiUrl.familyTransferStart(reqData).then(response => {
|
||||
setStartTransfer({loading:false, data:response?.data })
|
||||
}).catch(err => {
|
||||
setStartTransfer({loading:false, data: {}})
|
||||
})
|
||||
},[])
|
||||
|
||||
return (
|
||||
<ModalCom action={action} situation={situation}>
|
||||
<div className="relative logout-modal-wrapper lg:w-[500px] h-full lg:h-auto bg-white dark:bg-dark-white lg:rounded-2xl">
|
||||
<div className="logout-modal-header w-full flex items-center justify-between lg:px-10 lg:py-8 px-[30px] py-[23px] border-b border-light-purple dark:border-[#5356fb29] ">
|
||||
<h1 className="text-26 font-bold text-dark-gray dark:text-white tracking-wide">
|
||||
Add Fund
|
||||
</h1>
|
||||
<button
|
||||
type="button"
|
||||
className="text-[#374557] dark:text-red-500"
|
||||
onClick={action}
|
||||
>
|
||||
<svg
|
||||
width="36"
|
||||
height="36"
|
||||
viewBox="0 0 36 36"
|
||||
fill="none"
|
||||
className="fill-current"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
>
|
||||
<path
|
||||
d="M36 16.16C36 17.4399 36 18.7199 36 20.0001C35.7911 20.0709 35.8636 20.2554 35.8385 20.4001C34.5321 27.9453 30.246 32.9248 22.9603 35.2822C21.9006 35.6251 20.7753 35.7657 19.6802 35.9997C18.4003 35.9997 17.1204 35.9997 15.8401 35.9997C15.5896 35.7086 15.2189 35.7732 14.9034 35.7093C7.77231 34.2621 3.08728 30.0725 0.769671 23.187C0.435002 22.1926 0.445997 21.1199 0 20.1599C0 18.7198 0 17.2798 0 15.8398C0.291376 15.6195 0.214408 15.2656 0.270759 14.9808C1.71321 7.69774 6.02611 2.99691 13.0428 0.700951C14.0118 0.383805 15.0509 0.386897 15.9999 0C17.2265 0 18.4532 0 19.6799 0C19.7156 0.124041 19.8125 0.136067 19.9225 0.146719C27.3 0.868973 33.5322 6.21922 35.3801 13.427C35.6121 14.3313 35.7945 15.2484 36 16.16ZM33.011 18.0787C33.0433 9.77105 26.3423 3.00309 18.077 2.9945C9.78479 2.98626 3.00344 9.658 2.98523 17.8426C2.96667 26.1633 9.58859 32.9601 17.7602 33.0079C26.197 33.0577 32.9787 26.4186 33.011 18.0787Z"
|
||||
fill=""
|
||||
fillOpacity="0.6"
|
||||
/>
|
||||
<path
|
||||
d="M15.9309 18.023C13.9329 16.037 12.007 14.1207 10.0787 12.2072C9.60071 11.733 9.26398 11.2162 9.51996 10.506C9.945 9.32677 11.1954 9.0811 12.1437 10.0174C13.9067 11.7585 15.6766 13.494 17.385 15.2879C17.9108 15.8401 18.1633 15.7487 18.6375 15.258C20.3586 13.4761 22.1199 11.7327 23.8822 9.99096C24.8175 9.06632 26.1095 9.33639 26.4967 10.517C26.7286 11.2241 26.3919 11.7413 25.9133 12.2178C24.1757 13.9472 22.4477 15.6855 20.7104 17.4148C20.5228 17.6018 20.2964 17.7495 20.0466 17.9485C22.0831 19.974 24.0372 21.8992 25.9689 23.8468C26.9262 24.8119 26.6489 26.1101 25.4336 26.4987C24.712 26.7292 24.2131 26.3441 23.7455 25.8757C21.9945 24.1227 20.2232 22.3892 18.5045 20.6049C18.0698 20.1534 17.8716 20.2269 17.4802 20.6282C15.732 22.4215 13.9493 24.1807 12.1777 25.951C11.7022 26.4262 11.193 26.7471 10.4738 26.4537C9.31345 25.9798 9.06881 24.8398 9.98589 23.8952C11.285 22.5576 12.6138 21.2484 13.9387 19.9355C14.5792 19.3005 15.2399 18.6852 15.9309 18.023Z"
|
||||
fill="#"
|
||||
fillOpacity="0.6"
|
||||
/>
|
||||
</svg>
|
||||
</button>
|
||||
</div>
|
||||
<div className="logout-modal-body w-full flex flex-col items-center px-10 py-8">
|
||||
{startTransfer.loading && <LoadingSpinner size='16' color='sky-blue' height={'h-64'} />}
|
||||
|
||||
{ !startTransfer.loading &&
|
||||
<Formik
|
||||
initialValues={initialValues}
|
||||
validationSchema={validationSchema}
|
||||
onSubmit={handleAddFund}
|
||||
>
|
||||
{(props) => (
|
||||
<Form className="w-full">
|
||||
<div className="flex flex-col-reverse sm:flex-row">
|
||||
<div className="fields w-full">
|
||||
{/* AMOUNT */}
|
||||
<div className="field w-full mb-[0.5rem]">
|
||||
<InputCom
|
||||
placeholder="0"
|
||||
label={`Amount (${startTransfer?.data?.currency})`}
|
||||
name="amount"
|
||||
type="text"
|
||||
parentClass="flex items-center gap-1 w-full"
|
||||
labelClass="flex-[0.3] mb-0"
|
||||
inputClass={`flex-[0.7] input-curve lg border border-[#dce4e9] ${props.errors.amount && props.touched.amount ? 'border border-red-500' : ''}`}
|
||||
fieldClass="px-2 text-right"
|
||||
value={props.values.amount}
|
||||
inputHandler={props.handleChange}
|
||||
/>
|
||||
</div>
|
||||
|
||||
{/* FROM */}
|
||||
<div className="field w-full mb-[0.5rem]">
|
||||
<InputCom
|
||||
placeholder="From"
|
||||
label={`From (${startTransfer?.data?.origing_currency})`}
|
||||
name="from"
|
||||
type="text"
|
||||
parentClass="flex items-center gap-1 w-full"
|
||||
labelClass="flex-[0.3] mb-0"
|
||||
inputClass={`flex-[0.7] input-curve lg border border-[#dce4e9]`}
|
||||
fieldClass="px-2 text-right"
|
||||
value={props.values.from}
|
||||
disable={true}
|
||||
/>
|
||||
</div>
|
||||
|
||||
{/* TO */}
|
||||
<div className="field w-full mb-[0.5rem]">
|
||||
<InputCom
|
||||
placeholder="To"
|
||||
label="To:"
|
||||
name="to"
|
||||
type="text"
|
||||
parentClass="flex items-center gap-1 w-full"
|
||||
labelClass="flex-[0.3] mb-0"
|
||||
inputClass={`flex-[0.7] input-curve lg border border-[#dce4e9]`}
|
||||
fieldClass="px-2 text-right"
|
||||
value={props.values.to}
|
||||
disable={true}
|
||||
/>
|
||||
</div>
|
||||
|
||||
{/* COMMENT */}
|
||||
<div className="field w-full mb-[0.5rem]">
|
||||
<div className="w-full">
|
||||
<label
|
||||
htmlFor="Job Delivery Details"
|
||||
className="input-label text-[#181c32] dark:text-white text-[13.975px] leading-[20.9625px] font-semibold flex items-center gap-1"
|
||||
>
|
||||
Comment
|
||||
{/* {props.errors.comment && props.touched.comment && <span className='text-sm text-red-500'>{' '}{props.errors.comment}</span>} */}
|
||||
</label>
|
||||
<textarea
|
||||
// id="Job Delivery Details"
|
||||
rows="2"
|
||||
className={`input-field px-3 py-2 placeholder:text-base text-dark-gray dark:text-white w-full bg-slate-100 dark:bg-[#11131F] focus:ring-0 focus:outline-[#dce4e9] rounded-[10px] border ${props.errors.comment && props.touched.comment ? 'border border-red-500' : ''}`}
|
||||
style={{ resize: "none" }}
|
||||
name="comment"
|
||||
value={props.values.comment}
|
||||
onChange={props.handleChange}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
{/* inputs ends here */}
|
||||
</div>
|
||||
</div>
|
||||
{/* ERROR DISPLAY AND SUBMIT BUTTON */}
|
||||
<div className="content-footer w-full">
|
||||
{/* error or success display */}
|
||||
{requestStatus.message != "" &&
|
||||
(!requestStatus.status ? (
|
||||
<div
|
||||
className={`relative p-4 text-[#912741] bg-[#fcd9e2] border-[#fbc6d3] rounded-[0.475rem] text-md font-light leading-[19.5px] text-[13px]`}
|
||||
// className={`pb-1 absolute bottom-0 left-1/2 -translate-x-1/2 text-[#912741] rounded-[0.475rem] text-md font-light leading-[19.5px] text-[13px]`}
|
||||
|
||||
>
|
||||
{requestStatus.message}
|
||||
</div>
|
||||
) : (
|
||||
requestStatus.status && (
|
||||
<div
|
||||
className={`relative p-4 text-green-700 bg-slate-200 border-slate-800 rounded-[0.475rem] text-md font-light leading-[19.5px] text-[13px]`}
|
||||
// className={`pb-1 absolute bottom-0 left-1/2 -translate-x-1/2 text-green-700 rounded-[0.475rem] text-md font-light leading-[19.5px] text-[13px]`}
|
||||
>
|
||||
{requestStatus.message}
|
||||
</div>
|
||||
)
|
||||
))}
|
||||
{/* End of error or success display */}
|
||||
|
||||
<div className="pt-2 w-full border-t border-light-purple dark:border-[#5356fb29] flex justify-end items-center">
|
||||
<div className="w-full flex justify-between gap-2 items-center">
|
||||
<button
|
||||
type="button"
|
||||
onClick={action}
|
||||
className="w-[150px] h-[48px] rounded-full text-base text-white bg-red-600 hover:bg-red-500"
|
||||
>
|
||||
Cancel
|
||||
</button>
|
||||
<>
|
||||
{requestStatus.loading ?
|
||||
<LoadingSpinner size='6' color='sky-blue' />
|
||||
:
|
||||
<button
|
||||
type="submit"
|
||||
className={`w-[150px] h-[48px] rounded-full text-base text-white bg-sky-500 hover:bg-sky-400 ${requestStatus.status ? 'opacity-50' : ''}`}
|
||||
disabled={requestStatus.status}
|
||||
>
|
||||
Send
|
||||
</button>
|
||||
}
|
||||
</>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</Form>
|
||||
)}
|
||||
</Formik>
|
||||
}
|
||||
</div>
|
||||
</div>
|
||||
</ModalCom>
|
||||
)
|
||||
}
|
||||
|
||||
export default FamilyAddFundPopout
|
||||
@@ -0,0 +1,77 @@
|
||||
import React, { useState } from 'react'
|
||||
import { localImgLoad } from '../../../../lib'
|
||||
import { PriceFormatter } from '../../../Helpers/PriceFormatter'
|
||||
import FamilyAddFundPopout from './FamilyAddFundPopout'
|
||||
|
||||
function Wallet({wallet, familyData, setFamilyWalletReload}) {
|
||||
const [addFundPopout, setAddFundPopout] = useState({ show: false, data: {} })
|
||||
return (
|
||||
<div className='w-full p-4 bg-[aliceblue] rounded-lg'
|
||||
// style={{
|
||||
// background: `url(${background}) 0% 0% / cover no-repeat`,
|
||||
// }}
|
||||
>
|
||||
<div className="w-full flex justify-start items-center gap-3">
|
||||
<div className="min-w-[50px] min-h-[50px] max-w-min md:max-w-[80px] max-h-min md:max-h-[80px] rounded-full bg-[#e3e3e3] flex justify-center items-center">
|
||||
<img
|
||||
src={localImgLoad(`images/currency/${(wallet.code).toLowerCase()}.svg`)}
|
||||
className="w-full h-full"
|
||||
alt="currency-icon"
|
||||
/>
|
||||
</div>
|
||||
<div className="w-full flex flex-col">
|
||||
<div className="w-full flex gap-2">
|
||||
<p className="text-base md:text-lg text-thin-light-gray tracking-wide">
|
||||
Balance:
|
||||
</p>
|
||||
<p
|
||||
className="text-base md:text-lg font-bold text-purple tracking-wide leading-10"
|
||||
// className="text-[44px] lg:text-[62px] font-bold text-white tracking-wide leading-10 xxs:scale-100 lg:scale-100 xl:scale-125"
|
||||
>
|
||||
{PriceFormatter(
|
||||
Number(wallet.amount) * 0.01,
|
||||
wallet.code,
|
||||
wallet.country,
|
||||
""
|
||||
)}
|
||||
</p>
|
||||
</div>
|
||||
{/* BTN */}
|
||||
<div className='mt-1 flex justify-end items-center gap-2'>
|
||||
<button
|
||||
className="w-[150px] h-[48px] rounded-full text-base text-white bg-sky-500 hover:bg-sky-400"
|
||||
name="add"
|
||||
onClick={()=>{
|
||||
setAddFundPopout({ show: true, data: {} });
|
||||
}}
|
||||
>
|
||||
Add Money
|
||||
</button>
|
||||
<button
|
||||
className="w-[150px] h-[48px] rounded-full text-base text-white bg-[#4687ba] hover:bg-[#009ef7]"
|
||||
name="plan"
|
||||
// onClick={onClose}
|
||||
>
|
||||
Plan Wallet
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* MODAL TO ADD FUND */}
|
||||
{addFundPopout.show &&
|
||||
<FamilyAddFundPopout
|
||||
action={() => {
|
||||
setAddFundPopout({ show: false, data: {} });
|
||||
setFamilyWalletReload(prev => !prev) // TO RELOAD FAMILY WALLET // DETERMINES WHEN TO RELOAD FAMILY WALLET TAB/PAGE
|
||||
}}
|
||||
situation={addFundPopout.show}
|
||||
wallet={wallet}
|
||||
familyData={familyData}
|
||||
/>
|
||||
}
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
export default Wallet
|
||||
@@ -5,16 +5,18 @@ import React, {
|
||||
useMemo,
|
||||
useState,
|
||||
} from "react";
|
||||
import InputCom from "../Helpers/Inputs/InputCom";
|
||||
import Layout from "../Partials/Layout";
|
||||
import FamilyTable from "./FamilyTable";
|
||||
import SiteService from "../../services/SiteService";
|
||||
import InputCom from "../Helpers/Inputs/InputCom";
|
||||
import ModalCom from "../Helpers/ModalCom";
|
||||
import Layout from "../Partials/Layout";
|
||||
import LoadingSpinner from "../Spinners/LoadingSpinner";
|
||||
import FamilyTable from "./FamilyTable";
|
||||
|
||||
export default function FamilyAcc() {
|
||||
const [selectTab, setValue] = useState("today");
|
||||
const [selectedAge, setSelectedAge] = useState(undefined);
|
||||
// State to store the selected year and month
|
||||
const [selectedYear, setSelectedYear] = useState("");
|
||||
const [selectedMonth, setSelectedMonth] = useState("");
|
||||
const [familyList, setFamilyList] = useState([]);
|
||||
const [loader, setLoader] = useState(false);
|
||||
const [popUp, setPopUp] = useState(false);
|
||||
@@ -35,17 +37,14 @@ export default function FamilyAcc() {
|
||||
setValue(value);
|
||||
};
|
||||
|
||||
const ageRange = useMemo(() => {
|
||||
const startAge = 5;
|
||||
const endAge = 16;
|
||||
return Array.from(
|
||||
{ length: endAge - startAge + 1 },
|
||||
(_, index) => startAge + index
|
||||
);
|
||||
}, []);
|
||||
// Handle year selection
|
||||
const handleYearChange = (e) => {
|
||||
setSelectedYear(e.target.value);
|
||||
};
|
||||
|
||||
const handleAgeSelect = (event) => {
|
||||
setSelectedAge(parseInt(event.target.value));
|
||||
// Handle month selection
|
||||
const handleMonthChange = (e) => {
|
||||
setSelectedMonth(e.target.value);
|
||||
};
|
||||
|
||||
const handleInputChange = (event) => {
|
||||
@@ -53,6 +52,13 @@ export default function FamilyAcc() {
|
||||
setFormData((prevFormData) => ({ ...prevFormData, [name]: value }));
|
||||
};
|
||||
|
||||
// use the useEffect hook to clear the selected month if the year changes (to ensure consistency)
|
||||
useEffect(() => {
|
||||
if (selectedYear === "" && selectedMonth !== "") {
|
||||
setSelectedMonth("");
|
||||
}
|
||||
}, [selectedYear]);
|
||||
|
||||
const addMember = async () => {
|
||||
const { first_name, last_name } = formData;
|
||||
setLoader(true);
|
||||
@@ -61,7 +67,8 @@ export default function FamilyAcc() {
|
||||
const reqData = {
|
||||
firstname: first_name,
|
||||
lastname: last_name,
|
||||
age: selectedAge,
|
||||
year: +selectedYear,
|
||||
month: +selectedMonth,
|
||||
};
|
||||
|
||||
const res = await apiCall.addFamily(reqData);
|
||||
@@ -91,7 +98,8 @@ export default function FamilyAcc() {
|
||||
first_name: "",
|
||||
last_name: "",
|
||||
});
|
||||
setSelectedAge("");
|
||||
setSelectedMonth("");
|
||||
setSelectedYear("");
|
||||
}
|
||||
};
|
||||
|
||||
@@ -143,13 +151,16 @@ export default function FamilyAcc() {
|
||||
>
|
||||
Family Accounts
|
||||
</span>
|
||||
<button
|
||||
onClick={popUpHandler}
|
||||
type="button"
|
||||
className="text-white btn-gradient text-lg tracking-wide px-5 py-2 rounded-full"
|
||||
>
|
||||
Add
|
||||
</button>
|
||||
{familyList.length < process.env.REACT_APP_MAX_FAMILY_MEMBERS &&
|
||||
!loader && (
|
||||
<button
|
||||
onClick={popUpHandler}
|
||||
type="button"
|
||||
className="text-white btn-gradient text-lg tracking-wide px-5 py-2 rounded-full"
|
||||
>
|
||||
Add
|
||||
</button>
|
||||
)}
|
||||
</h1>
|
||||
</div>
|
||||
<div className="slider-btns flex space-x-4">
|
||||
@@ -173,9 +184,10 @@ export default function FamilyAcc() {
|
||||
<FamilyForm
|
||||
popUpHandler={popUpHandler}
|
||||
value={formData}
|
||||
ageHandler={handleAgeSelect}
|
||||
ageRange={ageRange}
|
||||
ageValue={selectedAge}
|
||||
selectedYear={selectedYear}
|
||||
selectedMonth={selectedMonth}
|
||||
monthHandler={handleMonthChange}
|
||||
yearHandler={handleYearChange}
|
||||
inputHandler={handleInputChange}
|
||||
msgErr={msgErr}
|
||||
onClick={addMember}
|
||||
@@ -191,8 +203,10 @@ const FamilyForm = ({
|
||||
value: { first_name, last_name },
|
||||
ageValue,
|
||||
inputHandler,
|
||||
ageHandler,
|
||||
ageRange,
|
||||
selectedMonth,
|
||||
selectedYear,
|
||||
monthHandler,
|
||||
yearHandler,
|
||||
msgErr,
|
||||
loader,
|
||||
onClick,
|
||||
@@ -237,33 +251,22 @@ const FamilyForm = ({
|
||||
value={last_name}
|
||||
inputHandler={inputHandler}
|
||||
/>
|
||||
<div className="input-com mb-7 flex gap-1 items-center w-full justify-between">
|
||||
<div className="input-com mb-7 flex flex-col gap-1 w-full">
|
||||
{/* Age dropdown */}
|
||||
<div className="flex items-center justify-between flex-[0.3]">
|
||||
<div className="">
|
||||
<label
|
||||
className="input-label text-[#181c32] dark:text-white text-[15px] font-semibold"
|
||||
htmlFor="age-selection"
|
||||
>
|
||||
Select your age:
|
||||
Birthday: (Year/Month)
|
||||
</label>
|
||||
</div>
|
||||
<div className=" flex-[0.7] max-w-[150px]">
|
||||
<select
|
||||
name="age-selection"
|
||||
id="age-selection"
|
||||
className="input-wrapper border border-[#f5f8fa] dark:border-[#5e6278] w-full rounded-[35px] h-[42px] overflow-hidden relative font-medium leading-6 bg-clip-padding text-[#5e6278] dark:text-gray-100 bg-[#f5f8fa] dark:bg-[#5e6278] text-base focus-visible:border-transparent focus-visible:outline-0 focus-visible:ring-transparent px-4"
|
||||
onChange={ageHandler}
|
||||
value={ageValue}
|
||||
>
|
||||
<option value={""}>Select age</option>
|
||||
{ageRange?.length > 0 &&
|
||||
ageRange?.map((age) => (
|
||||
<option value={age} key={age}>
|
||||
{age}
|
||||
</option>
|
||||
))}
|
||||
</select>
|
||||
</div>
|
||||
<YearMonthDropdowns
|
||||
handleMonthChange={monthHandler}
|
||||
handleYearChange={yearHandler}
|
||||
selectedMonth={selectedMonth}
|
||||
selectedYear={selectedYear}
|
||||
/>
|
||||
</div>
|
||||
{msgErr && (
|
||||
<div className="relative p-4 text-[#912741] bg-[#fcd9e2] border-[#fbc6d3] mb-4 rounded-[0.475rem] text-xs font-light leading-[19.5px]">
|
||||
@@ -312,3 +315,66 @@ const CloseIcon = () => (
|
||||
/>
|
||||
</svg>
|
||||
);
|
||||
|
||||
function YearMonthDropdowns({
|
||||
selectedMonth,
|
||||
selectedYear,
|
||||
handleMonthChange,
|
||||
handleYearChange,
|
||||
}) {
|
||||
// Get the current year
|
||||
const currentYear = new Date().getFullYear();
|
||||
|
||||
// Generate an array of years from the current year to (currentYear - 19)
|
||||
const years = Array.from({ length: 17 }, (_, index) => currentYear - index);
|
||||
|
||||
// Array of month names
|
||||
const months = [
|
||||
"January",
|
||||
"February",
|
||||
"March",
|
||||
"April",
|
||||
"May",
|
||||
"June",
|
||||
"July",
|
||||
"August",
|
||||
"September",
|
||||
"October",
|
||||
"November",
|
||||
"December",
|
||||
];
|
||||
|
||||
return (
|
||||
<div className="flex max-w-[330px] w-full self-end gap-4">
|
||||
<select
|
||||
id="yearDropdown"
|
||||
value={selectedYear}
|
||||
onChange={handleYearChange}
|
||||
className="input-wrapper border border-[#f5f8fa] dark:border-[#5e6278] w-56 rounded-[35px] h-10 overflow-hidden relative font-medium leading-6 bg-clip-padding text-[#5e6278] dark:text-gray-100 bg-[#f5f8fa] dark:bg-[#5e6278] text-base focus-visible:border-transparent focus-visible:outline-0 focus-visible:ring-transparent px-4"
|
||||
// size="5"
|
||||
>
|
||||
<option value="">Select a Year</option>
|
||||
{years.map((year) => (
|
||||
<option key={year} value={year}>
|
||||
{year}
|
||||
</option>
|
||||
))}
|
||||
</select>
|
||||
|
||||
<select
|
||||
id="monthDropdown"
|
||||
value={selectedMonth}
|
||||
onChange={handleMonthChange}
|
||||
className="input-wrapper border border-[#f5f8fa] dark:border-[#5e6278] w-56 rounded-[35px] h-10 overflow-hidden relative font-medium leading-6 bg-clip-padding text-[#5e6278] dark:text-gray-100 bg-[#f5f8fa] dark:bg-[#5e6278] text-base focus-visible:border-transparent focus-visible:outline-0 focus-visible:ring-transparent px-4"
|
||||
// size="5"
|
||||
>
|
||||
<option value="">Select a Month</option>
|
||||
{months.map((month, index) => (
|
||||
<option key={month} value={index + 1}>
|
||||
{month}
|
||||
</option>
|
||||
))}
|
||||
</select>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -504,11 +504,11 @@ export default function Icons({ name }) {
|
||||
></path>
|
||||
</svg>
|
||||
) : name === "atm-card" ? (
|
||||
<img className="w-[20px]" src={ATMCard} alt="card" />
|
||||
<img className="w-[30px]" src={ATMCard} alt="card" />
|
||||
) : name === "visa-card" ? (
|
||||
<img className="w-[20px]" src={VisaCard} alt="card" />
|
||||
<img className="w-[30px]" src={VisaCard} alt="card" />
|
||||
) : name === "master-card" ? (
|
||||
<img className="w-[20px]" src={MasterCard} alt="card" />
|
||||
<img className="w-[30px]" src={MasterCard} alt="card" />
|
||||
) : name === "new-dashboard" ? (
|
||||
<img className="w-[17px] h-[17px]" src={localImgLoad('images/icons/dashboard.svg')} alt="dashboard" />
|
||||
) : name === "new-family" ? (
|
||||
|
||||
@@ -21,7 +21,11 @@ export default function InputCom({
|
||||
blurHandler,
|
||||
spanTag,
|
||||
inputBg,
|
||||
onInput,
|
||||
maxLength = 45,
|
||||
minLength = 0,
|
||||
direction,
|
||||
tabIndex,
|
||||
error,
|
||||
}) {
|
||||
const inputRef = useRef(null);
|
||||
@@ -29,13 +33,13 @@ export default function InputCom({
|
||||
// for Min Length:
|
||||
const minLengthValidation = () => {
|
||||
const inputConfig = inputConfigs[inputRef?.current?.name]?.minLength;
|
||||
return inputConfig || 0;
|
||||
return inputConfig || minLength;
|
||||
};
|
||||
|
||||
// for MaxLength
|
||||
const maxLengthValidation = () => {
|
||||
const inputConfig = inputConfigs[inputRef?.current?.name]?.maxLength;
|
||||
return inputConfig || 30;
|
||||
return inputConfig || maxLength;
|
||||
};
|
||||
|
||||
// for Patterns
|
||||
@@ -63,7 +67,9 @@ export default function InputCom({
|
||||
</span>
|
||||
)}
|
||||
{/* displays error is any */}
|
||||
{error && <span className="text-[12px] text-red-500">{error}</span>}
|
||||
{error && (
|
||||
<span className="text-[12px] text-red-500 italic">{error}</span>
|
||||
)}
|
||||
</label>
|
||||
)}
|
||||
{forgotPassword && (
|
||||
@@ -76,20 +82,26 @@ export default function InputCom({
|
||||
)}
|
||||
</div>
|
||||
<div
|
||||
className={`input-wrapper w-full rounded-full h-[42px] overflow-hidden relative font-medium leading-6 bg-clip-padding text-base ${inputClass ? inputClass : "text-[#5e6278] dark:text-gray-100 bg-[#f5f8fa] dark:bg-[#5e6278] border"}`}
|
||||
className={`input-wrapper w-full rounded-full h-[42px] overflow-hidden relative font-medium leading-6 bg-clip-padding text-base ${
|
||||
inputClass
|
||||
? inputClass
|
||||
: "text-[#5e6278] dark:text-gray-100 bg-[#f5f8fa] dark:bg-[#5e6278] border"
|
||||
}`}
|
||||
>
|
||||
<input
|
||||
placeholder={placeholder}
|
||||
value={value}
|
||||
onChange={inputHandler}
|
||||
className={`input-field placeholder:text-base text-dark-gray w-full h-full tracking-wide ${
|
||||
inputBg ? inputBg : "bg-[#FAFAFA]"
|
||||
} dark:bg-[#11131F] focus:ring-0 focus:outline-none ${fieldClass}`}
|
||||
className={`input-field placeholder:text-base text-dark-gray w-full h-full ${
|
||||
inputBg ? inputBg : "bg-[#FAFAFA] dark:bg-[#11131F] dark:text-white tracking-wide"
|
||||
} focus:ring-0 focus:outline-none ${fieldClass}`}
|
||||
type={type}
|
||||
id={name}
|
||||
name={name}
|
||||
onInput={onInput}
|
||||
minLength={minLengthValidation()}
|
||||
maxLength={maxLengthValidation()}
|
||||
tabIndex={tabIndex}
|
||||
// pattern={inputPatterns()}
|
||||
ref={inputRef}
|
||||
readOnly={disable}
|
||||
@@ -97,17 +109,14 @@ export default function InputCom({
|
||||
dir={direction}
|
||||
/>
|
||||
{iconName && (
|
||||
<div className="absolute right-6 bottom-[10px] z-10 flex gap-2">
|
||||
<div className="absolute right-6 bottom-3 z-10 flex gap-2">
|
||||
{iconName.split(" ").map((item, index) => (
|
||||
<Icons key={index} name={item} />
|
||||
))}
|
||||
</div>
|
||||
)}
|
||||
{passIcon && (
|
||||
<div
|
||||
className="absolute right-6 bottom-[10px] z-10"
|
||||
onClick={onClick}
|
||||
>
|
||||
<div className="absolute right-6 bottom-3 z-10" onClick={onClick}>
|
||||
<Icons name={passIcon} />
|
||||
</div>
|
||||
)}
|
||||
@@ -117,7 +126,7 @@ export default function InputCom({
|
||||
}
|
||||
|
||||
const inputConfigs = {
|
||||
email: { minLength: 7, maxLength: 35 },
|
||||
email: { minLength: 7, maxLength: 45 },
|
||||
first_name: { minLength: 3, maxLength: 25, pattern: "[a-zA-Z]+" },
|
||||
last_name: { minLength: 3, maxLength: 25, pattern: "[a-zA-Z]+" },
|
||||
address: { minLength: 5, maxLength: 49, pattern: "[a-zA-Z0-9]+" },
|
||||
@@ -128,7 +137,8 @@ const inputConfigs = {
|
||||
amount: { minLength: 1, maxLength: 9, pattern: "[0-9]+" },
|
||||
description: { minLength: 5, maxLength: 299 },
|
||||
title: { minLength: 5, maxLength: 149 },
|
||||
job_detail: { minLength: 4, maxLength: 1440 }
|
||||
job_detail: { minLength: 4, maxLength: 1440 },
|
||||
cardNum: { minLength: 4, maxLength: 19 },
|
||||
};
|
||||
|
||||
/* Numbers Only: <input type="text" pattern="[0-9]*" /> strictly numbers
|
||||
|
||||