Compare commits

..

1 Commits

Author SHA1 Message Date
Chief Bube 57953a604c fixed height issues 2023-10-25 13:46:37 -07:00
224 changed files with 2353 additions and 12361 deletions
+1 -20
View File
@@ -15,9 +15,6 @@ REACT_APP_APPSITE="https://myfitapp.mermsemr.com"
REACT_APP_AUX_ENDPOINT="https://apigate.lotus.g1.wrenchboard.com/en/wrench/api/v1"
REACT_APP_USERS_ENDPOINT="https://apigate.lotus.g1.wrenchboard.com/en/wrench/api/v1"
#SOCKETS ENDS
REACT_APP_PRIMARY_SOCKET="https://socket-dev.wrenchboard.com"
#"https://devapi.mermsemr.com/en/desktop/api/v2/myfituser"
REACT_APP_SESSION_EXPIRE_MINUTES=600000
@@ -98,20 +95,4 @@ REACT_APP_GOOGLE_RECAPTCHA_SITEKEY=6Ld_qKooAAAAADNL1TPzRLmcBA8vlpvx__39Rj39
#FAMILY MEMBER MINIMUM AGE
REACT_APP_FAMILY_MINIMUM_AGE=4
REACT_APP_FAMILY_MAXIMUM_AGE=18
#CHANGE LOGIN LAYOUT
REACT_APP_NEW_LOGIN_LAYOUT=1
#APP DOWNLOAD LINKS
REACT_APP_ANDROID_APP='https://play.google.com/store/apps/details?id=com.wrenchboard.users'
REACT_APP_APPLE_APP='https://itunes.apple.com/us/app/wrenchboard/id1435718367?ls=1&mt=8'
# Displays the new family dashboard with boxes
REACT_APP_SHOW_NEW_FAMILY_DASH=1
# Displays the account dashboard
REACT_APP_SHOW_ACCOUNT_DASH=1
# Displays the slider banners
REACT_APP_SHOW_SLIDER_BANNERS=0
REACT_APP_FAMILY_MAXIMUM_AGE=18
+1 -20
View File
@@ -15,9 +15,6 @@ REACT_APP_APPSITE="https://myfitapp.mermsemr.com"
REACT_APP_AUX_ENDPOINT="https://apigate.lotus.g1.wrenchboard.com/en/wrench/api/v1"
REACT_APP_USERS_ENDPOINT="https://apigate.lotus.g1.wrenchboard.com/en/wrench/api/v1"
#SOCKETS ENDS
REACT_APP_PRIMARY_SOCKET="https://socket-dev.wrenchboard.com"
#"https://devapi.mermsemr.com/en/desktop/api/v2/myfituser"
REACT_APP_SESSION_EXPIRE_MINUTES=600000
@@ -66,20 +63,4 @@ REACT_APP_GOOGLE_RECAPTCHA_SITEKEY=6Ld_qKooAAAAADNL1TPzRLmcBA8vlpvx__39Rj39
#FAMILY MEMBER MINIMUM AGE
REACT_APP_FAMILY_MINIMUM_AGE=4
REACT_APP_FAMILY_MAXIMUM_AGE=18
#CHANGE LOGIN LAYOUT
REACT_APP_NEW_LOGIN_LAYOUT=1
#APP DOWNLOAD LINKS
REACT_APP_ANDROID_APP='https://play.google.com/store/apps/details?id=com.wrenchboard.users'
REACT_APP_APPLE_APP='https://itunes.apple.com/us/app/wrenchboard/id1435718367?ls=1&mt=8'
# Displays the new family dashboard with boxes
REACT_APP_SHOW_NEW_FAMILY_DASH=1
# Displays the account dashboard
REACT_APP_SHOW_ACCOUNT_DASH=1
# Displays the slider banners
REACT_APP_SHOW_SLIDER_BANNERS=0
REACT_APP_FAMILY_MAXIMUM_AGE=18
-19
View File
@@ -15,9 +15,6 @@ REACT_APP_APPSITE="https://myfitapp.mermsemr.com"
REACT_APP_AUX_ENDPOINT="https://apigate.orion.g1.wrenchboard.com/en/wrench/api/v1"
REACT_APP_USERS_ENDPOINT="https://apigate.orion.g1.wrenchboard.com/en/wrench/api/v1"
#SOCKETS ENDS
REACT_APP_PRIMARY_SOCKET="https://socket.wrenchboard.com"
#"https://devapi.mermsemr.com/en/desktop/api/v2/myfituser"
REACT_APP_SESSION_EXPIRE_MINUTES=600000
@@ -73,19 +70,3 @@ REACT_APP_GOOGLE_RECAPTCHA_SITEKEY=6Ld_qKooAAAAADNL1TPzRLmcBA8vlpvx__39Rj39
#FAMILY MEMBER MINIMUM AGE
REACT_APP_FAMILY_MINIMUM_AGE=4
REACT_APP_FAMILY_MAXIMUM_AGE=18
#CHANGE LOGIN LAYOUT
REACT_APP_NEW_LOGIN_LAYOUT=1
#APP DOWNLOAD LINKS
REACT_APP_ANDROID_APP='https://play.google.com/store/apps/details?id=com.wrenchboard.users'
REACT_APP_APPLE_APP='https://itunes.apple.com/us/app/wrenchboard/id1435718367?ls=1&mt=8'
# Displays the new family dashboard with boxes
REACT_APP_SHOW_NEW_FAMILY_DASH=1
# Displays the account dashboard
REACT_APP_SHOW_ACCOUNT_DASH=1
# Displays the slider banners
REACT_APP_SHOW_SLIDER_BANNERS=0
-1
View File
@@ -30,7 +30,6 @@
"react-to-print": "^2.14.12",
"react-toastify": "^9.0.1",
"redux": "^4.2.0",
"socket.io-client": "^4.4.1",
"slick-carousel": "^1.8.1",
"web-vitals": "^1.0.1",
"yup": "^1.1.1"
Binary file not shown.

Before

Width:  |  Height:  |  Size: 12 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 11 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 12 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 11 KiB

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

Before

Width:  |  Height:  |  Size: 71 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 12 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 11 KiB

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

Before

Width:  |  Height:  |  Size: 448 B

After

Width:  |  Height:  |  Size: 452 B

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

Before

Width:  |  Height:  |  Size: 799 B

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

Before

Width:  |  Height:  |  Size: 743 B

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

Before

Width:  |  Height:  |  Size: 1.5 KiB

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

Before

Width:  |  Height:  |  Size: 527 B

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

Before

Width:  |  Height:  |  Size: 214 B

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

Before

Width:  |  Height:  |  Size: 437 B

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

Before

Width:  |  Height:  |  Size: 265 B

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

Before

Width:  |  Height:  |  Size: 492 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 235 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 206 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 245 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 67 KiB

+84 -22
View File
@@ -1,28 +1,74 @@
import {
validationSchema as VS,
useDispatch,
useSelector,
usersService,
initialValues as IV,
initialReqState,
useState,
tableReload,
Formik,
InputCom,
Field,
Form,
LoadingSpinner,
} from "./settings";
import { Field, Form, Formik } from "formik";
import React, { useEffect, useState } from "react";
import { useDispatch, useSelector } from "react-redux";
import * as Yup from "yup";
import usersService from "../../services/UsersService";
import { tableReload } from "../../store/TableReloads";
import InputCom from "../Helpers/Inputs/InputCom";
import LoadingSpinner from "../Spinners/LoadingSpinner";
const validationSchema = VS;
const validationSchema = Yup.object().shape({
country: Yup.string()
.min(1, "Minimum 3 characters")
.max(25, "Maximum 25 characters")
.required("Currency is required"),
price: Yup.string()
.typeError("Invalid number")
.min(1, "Price must be greater than 0")
.test("no-e", "Invalid number", (value) => {
if (value && /\d+e/.test(value)) {
return false;
}
return true;
})
.required("Price is required"),
title: Yup.string()
.min(5, "Minimum 5 characters")
.max(149, "Maximum 149 characters")
.required("Title is required"),
description: Yup.string()
.min(5, "Minimum 5 characters")
.max(299, "Maximum 299 characters")
.required("Description is required"),
job_detail: Yup.string()
.min(3, "Minimum 3 characters")
.max(499, "Maximum 499 characters")
.required("Details is required"),
timeline_days: Yup.number()
.typeError("you must specify a number")
.min(1, "Price must be greater than 0")
.required("Timeline is required"),
category: Yup.array().min(1, "Select at least one checkbox"),
});
function AddJob({ popUpHandler, categories }) {
const ApiCall = new usersService();
const { walletDetails } = useSelector((state) => state.walletDetails);
let dispatch = useDispatch();
const [requestStatus, setRequestStatus] = useState(initialReqState); // Holds state when submit button is pressed
let initialValues = {
// initial values for formik
country: "",
price: "",
title: "",
description: "",
job_detail: "",
timeline_days: "",
category: [],
};
let [requestStatus, setRequestStatus] = useState({
loading: false,
status: false,
message: "",
}); // Holds state when submit button is pressed
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;
};
const handleAddJob = async (values, helpers) => {
const reqData = {
@@ -35,6 +81,21 @@ 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: "" });
try {
@@ -75,7 +136,7 @@ function AddJob({ popUpHandler, categories }) {
return (
<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={IV}
initialValues={initialValues}
validationSchema={validationSchema}
onSubmit={handleAddJob}
>
@@ -106,7 +167,7 @@ function AddJob({ popUpHandler, categories }) {
onChange={props.handleChange}
onBlur={props.handleBlur}
>
{walletDetails?.loading ? (
{walletDetails.loading ? (
<option className="text-slate-500 text-lg" value="">
Loading...
</option>
@@ -197,7 +258,7 @@ function AddJob({ popUpHandler, categories }) {
<div className="sm:w-[60%] 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"
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 &&
@@ -339,12 +400,13 @@ function AddJob({ popUpHandler, categories }) {
</span>
</button>
{requestStatus?.loading ? (
{requestStatus.loading ? (
<LoadingSpinner size="8" color="sky-blue" />
) : (
<button
type="submit"
className="w-[152px] h-[46px] flex justify-center items-center btn-gradient text-base rounded-full text-white"
// className='w-20 h-11 flex justify-center items-center btn-gradient text-base rounded-full text-white'
>
Add Job
</button>
-100
View File
@@ -1,100 +0,0 @@
import { Field, Form, Formik } from "formik";
import { useEffect, useState } from "react";
import { useDispatch, useSelector } from "react-redux";
import * as Yup from "yup";
import usersService from "../../services/UsersService";
import { tableReload } from "../../store/TableReloads";
import InputCom from "../Helpers/Inputs/InputCom";
import LoadingSpinner from "../Spinners/LoadingSpinner";
// Initialize state for request values
const initialReqState = {
loading: false,
status: false,
message: "",
};
// For form initial values
const initialValues = {
// initial values for formik
country: "",
price: "",
title: "",
description: "",
job_detail: "",
timeline_days: "",
category: [],
};
// const getWalletDetail = (country) => { // A FUNCTION TO GET USER BALANCE BASED ON COUNTRY SELECTED
// const walletChecker = walletDetails?.data.find(
// (item) => item.country === country
// );
// return walletChecker ? walletChecker.amount : 0;
// };
// To get the validation schema
const validationSchema = Yup.object().shape({
country: Yup.string()
.min(1, "Minimum 3 characters")
.max(25, "Maximum 25 characters")
.required("Currency is required"),
price: Yup.string()
.typeError("Invalid number")
.min(1, "Price must be greater than 0")
.test("no-e", "Invalid number", (value) => {
if (value && /\d+e/.test(value)) {
return false;
}
return true;
})
.required("Price is required"),
title: Yup.string()
.min(5, "Minimum 5 characters")
.max(149, "Maximum 149 characters")
.required("Title is required"),
description: Yup.string()
.min(5, "Minimum 5 characters")
.max(299, "Maximum 299 characters")
.required("Description is required"),
job_detail: Yup.string()
.min(3, "Minimum 3 characters")
.max(499, "Maximum 499 characters")
.required("Details is required"),
timeline_days: Yup.number()
.typeError("you must specify a number")
.min(1, "Price must be greater than 0")
.required("Timeline is required"),
category: Yup.array().min(1, "Select category"),
});
const getWalletDetail = (countryParams, walletDetails) => {
// A FUNCTION TO GET USER BALANCE BASED ON COUNTRY SELECTED
const walletChecker = walletDetails?.data.find(
(item) => item.country === countryParams
);
return walletChecker
? {
description: walletChecker.description,
country: walletChecker.country,
}
: "";
};
export {
Field,
Form,
Formik,
useState,
useEffect,
useDispatch,
useSelector,
usersService,
InputCom,
LoadingSpinner,
initialReqState,
initialValues,
validationSchema,
getWalletDetail,
tableReload
};
@@ -1,9 +0,0 @@
import React from 'react'
export default function AppDownload() {
return (
<div>
<div className='h-screen flex justify-center items-center'>App Download Content Comes here</div>
</div>
)
}
-75
View File
@@ -1,75 +0,0 @@
import React, { useContext } from "react";
import { Link } from "react-router-dom";
import { localImgLoad } from "../../lib";
import DarkModeContext from "../Contexts/DarkModeContext";
export default function LoginLayout({ slogan, children }) {
const bgImg = localImgLoad("images/left-wrenchboard.jpg");
const bgImgNig = localImgLoad("images/wrench-home-back-nigeria.jpg");
const bgImgCom = localImgLoad("images/wrench-home-back-common.jpg");
const { countryMode } = useContext(DarkModeContext);
return (
<div
className={`h-screen overflow-y-auto bg-cover bg-center`}
style={{
backgroundImage: `url(${countryMode == "NG" ? bgImgNig : bgImgCom})`,
}}
>
<div className={`w-full grid grid-cols-1 lg:grid-cols-2`}>
{/* <div
className={`auth-bg hidden lg:block bg-blue-50 relative bg-cover bg-no-repeat border-0 after:content-[''] after:absolute after:inset-0`}
style={{backgroundImage: `url(${bgImg})`}}
>
</div> */}
<div className="p-5 sm:p-7 flex place-content-center lg:col-start-2">
<div className="py-5 w-full sm:w-11/12 max-w-[550px] shadow-md bg-slate-50 rounded-[0.475rem]">
<div className="w-full flex justify-center items-center">
{children && children}
</div>
<div className="w-full flex flex-col justify-center items-center px-10">
<div className="w-full flex justify-center items-center pt-5">
<div className="flex items-center">
<a
href="https://www.wrenchboard.com/about-us"
className="text-[#a1a5b7] text-[15px] px-2 font-medium hover:text-[#009ef7]"
target="_blank"
rel="noreferrer"
>
About
</a>
<a
href="https://www.wrenchboard.com/service"
className="text-[#a1a5b7] text-[15px] px-2 font-medium hover:text-[#009ef7]"
target="_blank"
rel="noreferrer"
>
Services
</a>
<a
href="https://www.wrenchboard.com/contact"
className="text-[#a1a5b7] text-[15px] px-2 font-medium hover:text-[#009ef7]"
target="_blank"
rel="noreferrer"
>
Contact Us
</a>
</div>
</div>
<p className="py-1 text-black text-[15px] px-2 font-medium flex items-center gap-1">
<span className="dark:text-white">
&copy; {new Date().getFullYear()} -
</span>
<Link to="/" className="text-[#009ef7] ml-1">
WrenchBoard
</Link>{" "}
</p>
</div>
</div>
</div>
</div>
</div>
);
}
@@ -59,7 +59,7 @@ export default function ForgotPassword() {
}
if (email !== "" && checked) {
const reqData = { email };
const reqData = { email, action:11013 };
setResetLoading(true);
try {
const res = await userApi.StartResetPassword(reqData);
@@ -1,211 +0,0 @@
import React, { useState } from "react";
import { Link, useNavigate } from "react-router-dom";
import WrenchBoard from "../../../assets/images/wrenchboard-logo-text.png";
import usersService from "../../../services/UsersService";
import InputCom from "../../Helpers/Inputs/InputCom";
import AuthLayout from "../AuthLayout2";
import EmailValidator from "../../../lib/EmailValidator";
import ForgetPwdResponse from "../ForgetPwdResponse";
import ReCAPTCHA from "react-google-recaptcha";
export default function ForgotPassword() {
const [checked, setValue] = useState(false);
const [resetLoading, setResetLoading] = useState(false);
// email
const [email, setMail] = useState("");
const [msgError, setMsgError] = useState("");
const [msgSuccess, setMsgSuccess] = useState(null);
const navigate = useNavigate();
const userApi = new usersService();
const handleEmail = (e) => {
setMail(e?.target.value);
};
// const humanChecker = () => {
// setValue(!checked);
// };
function humanChecker(value) {
// console.log("Captcha value:", value);
if(value){
setValue(true)
}else{
setValue(false)
}
}
const resetHandler = async () => {
if (email == "") {
setMsgError("An email is required");
return setTimeout(() => {
setMsgError(null);
}, process.env.REACT_APP_RESET_START_ERROR_TIMEOUT);
}
if (!checked) {
setMsgError("Check if you are human");
return setTimeout(() => {
setMsgError(null);
}, process.env.REACT_APP_RESET_START_ERROR_TIMEOUT);
}
if(!EmailValidator(email)){ // CHECKS IF EMAIL IS VALID
setMsgError("Invalid Email");
return setTimeout(() => {
setMsgError(null);
}, process.env.REACT_APP_RESET_START_ERROR_TIMEOUT);
}
if (email !== "" && checked) {
const reqData = { email };
setResetLoading(true);
try {
const res = await userApi.StartResetPassword(reqData);
if (res.status === 200) {
setMsgSuccess(true);
setMail("");
setValue(false);
setResetLoading(false);
}else{
setMsgSuccess(false);
}
} catch (error) {
setMsgSuccess(false);
setResetLoading(false);
setMail("");
setMsgError("An error occurred");
throw new Error(error);
} finally {
setTimeout(() => {
setMsgError(null);
}, process.env.REACT_APP_RESET_START_ERROR_TIMEOUT);
}
}
};
return (
<>
<AuthLayout slogan="Welcome to WrenchBoard">
<div className="w-full">
<div className="mb-12">
<Link to="#">
<img
src={WrenchBoard}
alt="wrenchboard"
className="h-10 mx-auto"
/>
</Link>
</div>
<div className="flex place-content-center">
<div className="w-10/12">
{msgSuccess == null ?
<>
<div className="title-area flex flex-col justify-center items-center relative text-center mb-7">
<h1 className="text-[#181c32] font-semibold dark:text-white mb-3 leading-[27.3px] text-[22.75px]">
Forget Password
</h1>
<span className="text-gray-400 font-medium text-[16.25px] leading-[24.375px]">
Enter your email to reset your password.
</span>
</div>
<div className="input-area">
<div className="input-item mb-10">
<InputCom
fieldClass="px-6"
placeholder="Your Username/Email"
label="Email"
name="email"
type="email"
value={email}
inputHandler={handleEmail}
iconName="message"
/>
</div>
{/* hCaptha clone for the time being */}
<div className="mb-10 flex justify-center w-full">
<ReCAPTCHA
sitekey={process.env.REACT_APP_GOOGLE_RECAPTCHA_SITEKEY}
onChange={humanChecker}
/>
</div>
{/* <div className="mb-10">
<div className="w-[303px] h-[78px] mx-auto overflow-hidden">
<div className="w-[300px] h-[74px] bg-white bottom-[1px] rounded border-gray-100 overflow-hidden cursor-pointer">
<div className="h-full relative inline-block">
<div className="relative table top-0 h-full">
<div className="table-cell align-middle">
<div className="relative w-[30px] h-[30px] mx-[15px]">
<input
type="checkbox"
name="human-checkbox"
id="human-checkbox"
className="w-[28px] h-[28px] border-[1px] rounded border-gray-400 checked:bg-white"
checked={checked}
onChange={humanChecker}
/>
</div>
</div>
</div>
</div>
<div className="h-full relative inline-block w-[170px]">
<label className="relative table top-0 h-full">
<label className="table-cell align-middle">
<label
className="text-800 text-sm"
htmlFor="human-checkbox"
>
I am human
</label>
</label>
</label>
</div>
<div className="h-full relative inline-block w-16"></div>
</div>
</div>
</div> */}
{msgError && (
<div className="relative p-4 text-[#912741] bg-[#fcd9e2] border-[#fbc6d3] mb-4 rounded-[0.475rem] text-md font-light leading-[19.5px] text-[13px]">
{msgError}
</div>
)}
<div className="signin-area mb-3.5">
<div className="flex justify-center items-center gap-4">
<button
type="button"
onClick={() => navigate("/login")}
className={`h-[48px] rounded-full mb-6 text-[15px] font-semibold text-white hover:text-white flex justify-center bg-red-500 hover:bg-red-600 transition-all duration-300 items-center py-[0.8875rem] px-[1.8125rem] `}
>
Cancel
</button>
<button
type="button"
onClick={resetHandler}
className={`h-[48px] rounded-full mb-6 text-[15px] font-semibold text-white flex justify-center bg-[#4687ba] hover:bg-[#009ef7] transition-all duration-300 items-center py-[0.8875rem] px-[1.81rem]`}
>
{resetLoading ? (
<div className="signup btn-loader"></div>
) : (
<span>Send Code</span>
)}
</button>
</div>
</div>
</div>
</>
:
<ForgetPwdResponse
title={'Forget Password'}
message={msgSuccess? `Check your email for the link to continue password reset. Note the reset link will expire short time` : 'We are unable to continue with your request. Please try another username or contact us for help'}
type={msgSuccess}
/>
}
</div>
</div>
</div>
</AuthLayout>
</>
);
}
+33 -70
View File
@@ -2,7 +2,7 @@ import React, { useEffect, useLayoutEffect, useState } from "react";
import { Link, useLocation, useNavigate } from "react-router-dom";
import linkedInLogo from "../../../assets/images/Linkedin.png";
import appleLogo from "../../../assets/images/apple-black.svg";
import facebookLogo from "../../../assets/images/facebook.svg";
import facebookLogo from "../../../assets/images/facebook-4.svg";
import googleLogo from "../../../assets/images/google-logo.svg";
import WrenchBoard from "../../../assets/images/wrenchboard-logo-text.png";
import usersService from "../../../services/UsersService";
@@ -17,16 +17,10 @@ import { updateUserDetails } from "../../../store/UserDetails";
import ReCAPTCHA from "react-google-recaptcha";
export default function Login() {
// eslint-disable-next-line no-restricted-globals
const queryParams = new URLSearchParams(location?.search);
// const sessionExpired = queryParams.get("sessionExpired");
const dispatch = useDispatch();
const { state } = useLocation();
const [sessionExpired, setSessionExpired] = useState(queryParams.get("sessionExpired"))
const [validCaptcha, setValidCaptcha] = useState({ show: false, valid: "" }); // FOR CAPTCHA
const [validCaptcha, setValidCaptcha] = useState({show: false, valid:''}); // FOR CAPTCHA
let [loginType, setLoginType] = useState("");
@@ -98,6 +92,7 @@ export default function Login() {
password: password,
sessionid: "STARTING",
login_mode: 1100,
action: 11025,
};
} else if (name == "family") {
// Post Data Info for family Login
@@ -106,6 +101,7 @@ export default function Login() {
pin: password,
sessionid: "20067A92714",
login_mode: 1105,
action: 11025,
};
} else {
setLoginLoading(false);
@@ -115,8 +111,7 @@ 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
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(() => {
@@ -137,15 +132,12 @@ export default function Login() {
// setMsgError("Wrong, email/password");
setLoginError(true);
setLoginLoading(false);
setValidCaptcha((prev) => ({ ...prev, show: true })); // DISPLAYS CAPTCHA IF ERROR
setValidCaptcha(prev => ({...prev, show:true})) // DISPLAYS CAPTCHA IF ERROR
return;
}
localStorage.setItem("member_id", `${res.data.member_id}`);
localStorage.setItem("uid", `${res.data.uid}`);
localStorage.setItem("session_token", `${res.data.session}`);
if (name === "family") {
sessionStorage.setItem("family_uid", res.data?.family_uid);
}
// localStorage.setItem("session", `${res.data.session}`);
dispatch(updateUserDetails({ ...res.data }));
setTimeout(() => {
@@ -156,7 +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
setValidCaptcha(prev => ({...prev, show:true})) // DISPLAYS CAPTCHA IF ERROR
})
.finally(() => {
setTimeout(() => {
@@ -167,12 +159,11 @@ export default function Login() {
});
};
function captchaChecker(value) {
// FUNCTION TO VALIDATE CAPTCHA
if (value) {
setValidCaptcha({ show: true, valid: value });
} else {
setValidCaptcha({ show: true, valid: "" });
function captchaChecker(value) { // FUNCTION TO VALIDATE CAPTCHA
if(value){
setValidCaptcha({show: true, valid:value})
}else{
setValidCaptcha({show: true, valid:''})
}
}
@@ -235,20 +226,6 @@ export default function Login() {
setPassword("");
}, [loginType]);
// EFFECT TO CLEAR SESSION EXPIRY IF IT EXISTS AFTER SOME SECONDS
useEffect(()=>{
let timer;
if(sessionExpired == "true"){
timer = setTimeout(()=>{
setSessionExpired(false)
},5000)
}
return () => {
clearTimeout(timer)
}
}, [])
return (
<>
<AuthLayout slogan="Welcome to WrenchBoard">
@@ -264,32 +241,20 @@ export default function Login() {
</div>
<div className="content-wrapper login shadow-md w-full lg:max-w-[530px] mx-auto flex justify-center items-center xl:bg-white dark:bg-dark-white 2xl:w-[828px] rounded-[0.475rem] sm:p-7 p-5">
<div className="w-full">
{/* HIDES THIS IF USER SESSION HAS EXPIRED */}
{sessionExpired != "true" && (
<div className="title-area flex flex-col justify-center items-center relative text-center mb-7">
{/* <h1 className="text-[#181c32] font-semibold dark:text-white mb-3 leading-[27.3px] text-[22.75px]">
<div className="title-area flex flex-col justify-center items-center relative text-center mb-7">
{/* <h1 className="text-[#181c32] font-semibold dark:text-white mb-3 leading-[27.3px] text-[22.75px]">
Sign In to WrenchBoard
</h1> */}
<span className="text-gray-400 font-medium text-[16.25px] leading-[24.375px]">
New Here?{" "}
<Link
to="/signup"
className="font-semibold text-[#4687ba] hover:text-[#009ef7] transition"
>
Create an Account
</Link>
</span>
</div>
)}
{/* SHOWS THIS IF USER SESSION HAS EXPIRED */}
{sessionExpired == "true" && (
<div className="w-full p-1 mb-7">
<p className="text-red-500 text-base text-center">
Your session expired and will need to login again
</p>
</div>
)}
<span className="text-gray-400 font-medium text-[16.25px] leading-[24.375px]">
New Here?{" "}
<Link
to="/signup"
className="font-semibold text-[#4687ba] hover:text-[#009ef7] transition"
>
Create an Account
</Link>
</span>
</div>
{/* switch login component */}
<div className="ml-7 flex justify-start items-center gap-3">
@@ -355,16 +320,14 @@ export default function Login() {
</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}
{validCaptcha.show &&
<div className="mb-5 flex justify-center w-full">
<ReCAPTCHA
sitekey={process.env.REACT_APP_GOOGLE_RECAPTCHA_SITEKEY}
onChange={captchaChecker}
/>
</div>
)}
</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]">
@@ -521,8 +484,8 @@ export default function Login() {
{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.
This site is protected by hCaptcha and the our Privacy Policy
and Terms of Service apply.
</div>
)}
</div>
-629
View File
@@ -1,629 +0,0 @@
import React, { useEffect, useLayoutEffect, useState } from "react";
import { Link, useLocation, useNavigate } from "react-router-dom";
import linkedInLogo from "../../../assets/images/Linkedin.png";
import appleLogo from "../../../assets/images/apple-black.svg";
import facebookLogo from "../../../assets/images/facebook.svg";
import googleLogo from "../../../assets/images/google-logo.svg";
import WrenchBoard from "../../../assets/images/wrenchboard-logo-text.png";
import usersService from "../../../services/UsersService";
import InputCom from "../../Helpers/Inputs/InputCom";
import AuthLayout from "../AuthLayout2";
// import { GoogleOAuthProvider } from '@react-oauth/google';
import { useGoogleLogin } from "@react-oauth/google";
import { useDispatch } from "react-redux";
import { updateUserDetails } from "../../../store/UserDetails";
import ReCAPTCHA from "react-google-recaptcha";
export default function Login() {
// eslint-disable-next-line no-restricted-globals
const queryParams = new URLSearchParams(location?.search);
// const sessionExpired = queryParams.get("sessionExpired");
const dispatch = useDispatch();
const { state } = useLocation();
const [sessionExpired, setSessionExpired] = useState(
queryParams.get("sessionExpired")
);
const [validCaptcha, setValidCaptcha] = useState({ show: false, valid: "" }); // FOR CAPTCHA
let [loginType, setLoginType] = useState("");
const [loginLoading, setLoginLoading] = useState(false);
const [showPassword, setShowPassword] = useState(false);
//login error state
const [loginError, setLoginError] = useState(false);
// for the catch error
const [msgError, setMsgError] = useState("");
// To Show and Hide Password
const togglePasswordVisibility = () => {
setShowPassword(!showPassword);
};
//FUNCTION TO DETERMINE/CHANGE LOGIN COMPONENT
const handleLoginType = ({ target: { name } }) => {
setLoginType(name);
let currentDate = new Date();
let expirationDate = new Date(currentDate.getTime() + 24 * 60 * 60 * 1000);
// Convert the expiration date to the appropriate format
let expirationDateString = expirationDate.toUTCString();
document.cookie = `loginType=${name}; expires=${expirationDateString}; path=/;`;
};
// email
const [email, setMail] = useState("");
const handleEmail = (e) => {
setMail(e.target.value);
};
// password
const [password, setPassword] = useState("");
const handlePassword = (e) => {
setPassword(e.target.value);
};
const navigate = useNavigate();
const userApi = new usersService();
// FUNCTION TO HANDLE USER LOGIN
const doLogin = ({ target: { name } }) => {
setMsgError("");
setLoginError(false);
setLoginLoading(true);
let postData; // Post Data for API
if (!email || !password) {
setLoginLoading(false);
setMsgError("Please fill all the fields");
setTimeout(() => {
setMsgError("");
}, Number(process.env.REACT_APP_LOGIN_ERROR_TIMEOUT));
return;
}
if (name == "full") {
//checks if email is a valid email address
let regEx = /^[^0-9][a-zA-Z0-9._%+-]+@[a-zA-Z]+(\.[a-zA-Z]+)+$/;
if (regEx.test(email) == false) {
setLoginLoading(false);
setMsgError("Invalid Email");
return setTimeout(() => {
setMsgError("");
}, 3000);
}
// Post Data Info for normal Login
postData = {
username: email,
password: password,
sessionid: "STARTING",
login_mode: 1100,
};
} else if (name == "family") {
// Post Data Info for family Login
postData = {
username: email,
pin: password,
sessionid: "20067A92714",
login_mode: 1105,
};
} else {
setLoginLoading(false);
setMsgError("Invalid Login Type. Consider refreshing the page");
setTimeout(() => {
setMsgError("");
}, Number(process.env.REACT_APP_LOGIN_ERROR_TIMEOUT));
return;
}
if (name == "full" && !validCaptcha.valid && validCaptcha.show) {
// RUNS AND DISPLAYS CAPTCHA, IF ERROR OCCURED DURING LOGIN FOR FULL LOGIN ALONE
setMsgError("Please Verify Captcha");
setLoginLoading(false);
setTimeout(() => {
setMsgError("");
}, Number(process.env.REACT_APP_LOGIN_ERROR_TIMEOUT));
return;
}
userApi
.logInUser(postData)
.then((res) => {
if (
res.status != 200 ||
res.data.internal_return < 0 ||
!res.data.member_id ||
!res.data.uid ||
!res.data.session
) {
// setMsgError("Wrong, email/password");
setLoginError(true);
setLoginLoading(false);
setValidCaptcha((prev) => ({ ...prev, show: true })); // DISPLAYS CAPTCHA IF ERROR
return;
}
localStorage.setItem("member_id", `${res.data.member_id}`);
localStorage.setItem("uid", `${res.data.uid}`);
localStorage.setItem("session_token", `${res.data.session}`);
if (name === "family") {
sessionStorage.setItem("family_uid", res.data?.family_uid);
}
// localStorage.setItem("session", `${res.data.session}`);
dispatch(updateUserDetails({ ...res.data }));
setTimeout(() => {
navigate("/", { replace: true });
setLoginLoading(false);
}, 2000);
})
.catch((error) => {
setMsgError("Unable to login, try again");
setLoginLoading(false);
setValidCaptcha((prev) => ({ ...prev, show: true })); // DISPLAYS CAPTCHA IF ERROR
})
.finally(() => {
setTimeout(() => {
setLoginError(false);
setMsgError("");
setLoginLoading(false);
}, Number(process.env.REACT_APP_LOGIN_ERROR_TIMEOUT));
});
};
function captchaChecker(value) {
// FUNCTION TO VALIDATE CAPTCHA
if (value) {
setValidCaptcha({ show: true, valid: value });
} else {
setValidCaptcha({ show: true, valid: "" });
}
}
const googleLogin = useGoogleLogin({
flow: "auth-code",
ux_mode: "redirect",
redirect_uri: process.env.REACT_APP_GOOGLE_REDIRECT_URL,
onSuccess: async (codeResponse) => {
console.log("GOOGLE LOGIN GOOD --- ", codeResponse);
},
onError: (errorResponse) => console.log(errorResponse),
});
// In order to update the selected login type whenever the component renders
// useEffect(() => {
// Clear the loginType cookie if the user switches to loginfull
// document.cookie ="loginType=; expires=Thu, 01 Jan 1970 00:00:00 UTC; path=/;";
// }, []);
useLayoutEffect(() => {
// checks the cookie in order to set the login type before components mounts
// if(document.cookie.includes("loginType=family")){
// setLoginType('family')
// }else if(document.cookie.includes("loginType=full")){
// setLoginType('full')
// }else{
// setLoginType('full')
// }
function readCookie(cname) {
// checks the cookie in order to set the login type before components mounts
let name = cname + "=";
let decoded_cookie = decodeURIComponent(document.cookie);
let carr = decoded_cookie.split(";");
for (let i = 0; i < carr.length; i++) {
let c = carr[i];
while (c.charAt(0) == " ") {
c = c.substring(1);
}
if (c.indexOf(name) == 0) {
return c.substring(name.length, c.length);
}
}
return "full";
}
let loginValue = readCookie("loginType");
setLoginType(loginValue);
if (state?.error) {
//check if the login path has an error state indicating any social handle login with error
setMsgError("Unexpected Error, Please try again soon.");
setTimeout(() => {
setMsgError("");
navigate("/login", { replace: true });
}, 4000);
}
}, []);
useEffect(() => {
setMail("");
setPassword("");
}, [loginType]);
// EFFECT TO CLEAR SESSION EXPIRY IF IT EXISTS AFTER SOME SECONDS
useEffect(() => {
let timer;
if (sessionExpired == "true") {
timer = setTimeout(() => {
setSessionExpired(false);
}, 5000);
}
return () => {
clearTimeout(timer);
};
}, []);
return (
<>
<AuthLayout slogan="Welcome to WrenchBoard">
<div className="w-full">
<div className="mb-5">
<Link to="#">
<img
src={WrenchBoard}
alt="wrenchboard"
className="h-10 mx-auto"
/>
</Link>
</div>
{/* <div className="content-wrapper login shadow-md w-10/12 mx-auto flex justify-center items-center xl:bg-white dark:bg-dark-white 2xl:w-[828px] rounded-[0.475rem] sm:p-7 p-5"> */}
<div className="flex place-content-center">
<div className="w-10/12">
{/* HIDES THIS IF USER SESSION HAS EXPIRED */}
{sessionExpired != "true" && (
<div className="title-area flex flex-col justify-center items-center relative text-center mb-7">
{/* <h1 className="text-[#181c32] font-semibold dark:text-white mb-3 leading-[27.3px] text-[22.75px]">
Sign In to WrenchBoard
</h1> */}
<span className="text-gray-400 font-medium text-[16.25px] leading-[24.375px]">
New Here?{" "}
<Link
to="/signup"
className="font-semibold text-[#4687ba] hover:text-[#009ef7] transition"
>
Create an Account
</Link>
</span>
</div>
)}
{/* SHOWS THIS IF USER SESSION HAS EXPIRED */}
{sessionExpired == "true" && (
<div className="w-full p-1 mb-7">
<p className="text-red-500 text-base text-center">
Your session expired and will need to login again
</p>
</div>
)}
{/* switch login component */}
<div className="ml-7 flex justify-start items-center gap-3">
<button
name="full"
className={`login-type-btn px-4 py-1 rounded-t-2xl ${
loginType == "full"
? "bg-[#4687ba] border-[2px] border-[#4687ba] text-white"
: "bg-white text-[#000] border-t-[2px]"
}`}
onClick={handleLoginType}
>
Sign in
</button>
<button
name="family"
className={`login-type-btn px-4 py-1 rounded-t-2xl ${
loginType == "family"
? "bg-[#4687ba] border-[2px] border-[#4687ba] text-white"
: "bg-white text-[#000] border-t-[2px]"
}`}
onClick={handleLoginType}
>
Family Account
</button>
</div>
{/* END of switch login component */}
{/* for login component */}
{
loginType == "full" ? (
//user login component
<div className="p-6 input-area login-area border-2 border-[#4687ba] rounded-2xl">
<div className="input-item mb-5">
<InputCom
labelClass="tracking-wider"
fieldClass="sm:px-6 px-2"
value={email}
inputHandler={handleEmail}
placeholder="Your Email"
label="Email"
name="email"
type="email"
iconName="message"
/>
</div>
<div className="input-item mb-5">
<InputCom
labelClass="tracking-wider"
fieldClass="sm:px-6 px-2 tracking-[0.25em] text-2xl"
value={password}
inputHandler={handlePassword}
placeholder="● ● ● ● ● ●"
label="Password"
name="password"
type={showPassword ? "text" : "password"}
onClick={togglePasswordVisibility}
passIcon={showPassword ? "password" : "password"}
forgotPassword
/>
</div>
{/* hCaptha clone for the time being */}
{validCaptcha.show && (
<div className="mb-5 flex justify-center w-full">
<ReCAPTCHA
sitekey={
process.env.REACT_APP_GOOGLE_RECAPTCHA_SITEKEY
}
onChange={captchaChecker}
/>
</div>
)}
{loginError && (
<div className="relative p-4 text-[#912741] bg-[#fcd9e2] border-[#fbc6d3] mb-4 rounded-[0.475rem] text-md font-thin leading-[19.5px] text-[13px]">
Invalid username or password- Please{" "}
<Link to="/#" className="text-[#009ef7]">
reset your password
</Link>{" "}
or{" "}
<Link to="/signup" className="text-[#009ef7]">
create a new account
</Link>
</div>
)}
{msgError && (
<div className="relative p-4 text-[#912741] bg-[#fcd9e2] border-[#fbc6d3] mb-4 rounded-[0.475rem] text-md font-light leading-[19.5px] text-[13px]">
{msgError}
</div>
)}
<div className="signin-area mb-3.5">
<div className="flex justify-center">
<button
name="full"
onClick={doLogin}
type="button"
disabled={loginLoading}
className={`btn-login rounded-full mb-6 text-xl text-white flex justify-center bg-[#4687ba] hover:bg-[#009ef7] transition-all duration-300 items-center text-[15px]`}
>
{loginLoading ? (
<div className="signup btn-loader"></div>
) : (
<>Continue</>
)}
</button>
</div>
<div className="sm:grid grid-cols-2 gap-1">
<div className="w-full">
<BrandBtn
link="#"
imgSrc={googleLogo}
brand="Google"
onClick={googleLogin}
/>
</div>
<div
className={`w-full ${
process.env.REACT_APP_APPLE_SOCIAL_LOGIN !== 0 &&
"hidden"
}`}
>
<BrandBtn
// link={`https://appleid.apple.com/auth/authorize?response_type=code&response_mode=form_post&client_id=${process.env.REACT_APP_APPLE_CLIENT_ID}&redirect_uri=https%3A%2F%2Fwork.wrenchboard.com%2Flogin%2Fauth%2Fapple&state=4b2c4456b7&scope=name+email`}
link={`https://appleid.apple.com/auth/authorize?response_type=code&response_mode=form_post&client_id=${process.env.REACT_APP_APPLE_CLIENT_ID}&redirect_uri=${process.env.REACT_APP_APPLE_REDIRECT_URL}&state=4b2c4456b7&scope=name+email`}
imgSrc={appleLogo}
brand="Apple"
isAnchor={true}
// style={{visibility: 'hidden'}}
/>
</div>
<div className="w-full">
<BrandBtn
link={`https://www.facebook.com/v14.0/dialog/oauth?client_id=${process.env.REACT_APP_FACEBOOK_CLIENT_ID}&redirect_uri=${process.env.REACT_APP_FACEBOOK_REDIRECT_URL}&scope=${process.env.REACT_APP_FACEBOOK_CLIENT_SCOPE}`}
imgSrc={facebookLogo}
brand="Facebook"
isAnchor={true}
/>
</div>
<div
className={`w-full ${
process.env.REACT_APP_LINKEDIN_SOCIAL_LOGIN !== 0 &&
"hidden"
}`}
>
<BrandBtn
// link="https://www.linkedin.com/oauth/v2/authorization?response_type=code&client_id=YOUR_CLIENT_ID&redirect_uri=YOUR_REDIRECT_URI&scope=comma-separated-list-of-scopes&state=YOUR_STATE_VALUE"
imgSrc={linkedInLogo}
brand="LinkedIn"
isAnchor={true}
/>
</div>
</div>
</div>
</div>
) : (
// END of user login compoenent
// family login compoenent
<div className="p-6 input-area login-area border-2 border-[#4687ba] rounded-2xl">
<div className="input-item mb-5">
<InputCom
labelClass="tracking-wider"
fieldClass="px-6"
value={email}
inputHandler={handleEmail}
placeholder="Account ID"
label="Username"
name="email"
type="email"
iconName="family-id"
/>
</div>
<div className="input-item mb-5">
<InputCom
labelClass="tracking-wider"
fieldClass="px-6"
value={password}
inputHandler={handlePassword}
placeholder="● ● ● ● ● ●"
label="Pin"
name="password"
type={showPassword ? "text" : "password"}
onClick={togglePasswordVisibility}
passIcon={showPassword ? "family-pin" : "family-pin"}
/>
</div>
{loginError && (
<div className="relative p-4 text-[#912741] bg-[#fcd9e2] border-[#fbc6d3] mb-4 rounded-[0.475rem] text-md font-thin leading-[19.5px] text-[13px]">
Invalid username or password{" "}
{/* <Link to="/#" className="text-[#009ef7]">
reset your password
</Link>{" "}
or{" "}
<Link to="/signup" className="text-[#009ef7]">
create a new account
</Link> */}
</div>
)}
{msgError && (
<div className="relative p-4 text-[#912741] bg-[#fcd9e2] border-[#fbc6d3] mb-4 rounded-[0.475rem] text-md font-light leading-[19.5px] text-[13px]">
{msgError}
</div>
)}
<div className="signin-area mb-1.5">
<div className="flex justify-center">
<button
name="family"
onClick={doLogin}
disabled={loginLoading}
type="button"
className={`btn-login rounded-full text-xl text-white flex justify-center bg-[#4687ba] hover:bg-[#009ef7] transition-all duration-300 items-center text-[15px]`}
>
{loginLoading ? (
<div className="signup btn-loader"></div>
) : (
<>Continue</>
)}
</button>
</div>
</div>
</div>
)
// END of family login compoenent
}
{/* END of login component */}
{/* APP DOWNLOAD STORE */}
<div className="w-full mt-4">
<div className="w-full flex justify-center items-center gap-4">
<div className="w-28 lg:w-32">
<a
className="px-1 py-1 lg:py-2 flex justify-center items-center gap-1 w-full rounded-md bg-black text-white hover:text-slate-500 hover:shadow-lg transition-all duration-300"
target="_blank"
rel="noreferrer"
href={process.env.REACT_APP_APPLE_APP}
>
<i className="fa-brands fa-apple text-3xl"></i>
<div className="flex flex-col">
<span className="text-[11px]">Available on the</span>
<span className="text-[12px] lg:text-base">
App Store
</span>
</div>
</a>
</div>
<div className="w-28 lg:w-32">
<a
className="px-1 py-1 lg:py-2 flex justify-center items-center gap-1 w-full rounded-md bg-black text-white hover:text-slate-500 hover:shadow-lg transition-all duration-300"
target="_blank"
rel="noreferrer"
href={process.env.REACT_APP_ANDROID_APP}
>
<i className="fa-brands fa-google-play text-2xl"></i>
<div className="flex flex-col">
<span className="text-[11px]">Available on the</span>
<span className="text-[12px] lg:text-base">
Google Play
</span>
</div>
</a>
</div>
</div>
</div>
{loginType == "full" && (
<>
<div className="pt-5 text-[#181c32] text-center font-semibold text-[13.975px] leading-[20.9625px]">
This site is protected by a Captcha. Our Privacy Policy and
Terms of Service apply.
</div>
</>
)}
</div>
</div>
</div>
</AuthLayout>
</>
);
}
const BrandBtn = ({
link,
imgSrc,
brand,
onClick,
isAnchor = false,
style = { visibility: "visible" },
}) => {
// const doGoogle = async () => {
// alert("start google");
// };
// onSuccess: (codeResponse) => setUser(codeResponse),
// const doGoogle = useGoogleLogin({
// onSuccess: (codeResponse) => console.log('Login onSuccess:', codeResponse),
// onError: (error) => console.log('Login Failed:', error)
// });
// const doApple = async () => {
// alert("start apple");
// };
// const doFacebook = async () => {
// alert("start facebook");
// };
return (
<div className="w-full flex justify-center bottomMargin" style={style}>
{isAnchor ? (
<a
href={link}
className="w-full border border-light-purple rounded-[0.475rem] h-[48px] flex justify-center bg-[#FAFAFA] hover:bg-[#eff2f5] hover:text-[#7e8299] transition duration-300 items-center font-medium cursor-pointer"
>
<img className="mr-3 h-6" src={imgSrc} alt="logo-icon(s)" />
<span className="text-lg text-thin-light-gray font-normal text-[15px]">
Continue with {brand}
</span>
</a>
) : (
<button
onClick={onClick}
// href="#dd"
className="w-full border border-light-purple rounded-[0.475rem] h-[48px] flex justify-center bg-[#FAFAFA] hover:bg-[#eff2f5] hover:text-[#7e8299] transition duration-300 items-center font-medium cursor-pointer"
>
<img className="mr-3 h-6" src={imgSrc} alt="logo-icon(s)" />
<span className="text-lg text-thin-light-gray font-normal text-[15px]">
Continue with {brand}
</span>
</button>
)}
</div>
);
};
+14 -14
View File
@@ -6,7 +6,6 @@ import InputCom from "../../Helpers/Inputs/InputCom";
import AuthLayout from "../AuthLayout";
export default function SignUp() {
// eslint-disable-next-line no-restricted-globals
const queryParams = new URLSearchParams(location?.search);
const country = queryParams.get("cnt")?.toUpperCase();
@@ -47,20 +46,21 @@ export default function SignUp() {
// Get Country Api
const getCountryList = useCallback(async () => {
const res = await userApi.getSignupCountryData();
try {
const res = await userApi.getSignupCountryData();
if (res.status === 200 && res.data.internal_return >= 0) {
const { result_list } = await res.data;
if (res.status === 200) {
const { signup_country } = await res.data;
// setCountries(signup_country);
if(country){ // IF LINK/PATHNAME HAS CNT QUERY VALUE
let cnt = result_list.filter(item => item.code == country) // test to see country passed in query param exist from list of countries supplied by API
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: result_list});
return setCountries({loading: false, data: signup_country});
}
return setCountries({loading: false, data: cnt});
}
setCountries({loading: false, data:result_list});
setCountries({loading: false, data:signup_country});
} else if (res.data.result !== 100) {
setCountries({loading: false, data:[]});
}
@@ -374,17 +374,17 @@ const SelectOption = ({
{data?.data?.length > 1 ?
<>
<option value={""}>Select your Country</option>
{data?.data?.map((item) => (
<option value={item.code} key={item.uid}>
{item.country}
{data?.data?.map((item, idx) => (
<option value={item[0]} key={idx}>
{item[1]}
</option>
))}
</>
:
data?.data?.length == 1 ?
data?.data?.map((item) => (
<option value={item.code} key={item.uid}>
{item.country}
data?.data?.map((item, idx) => (
<option value={item[0]} key={idx}>
{item[1]}
</option>
))
:
-444
View File
@@ -1,444 +0,0 @@
import React, { useCallback, useEffect, useState } from "react";
import { Link, useLocation, useNavigate } from "react-router-dom";
import WrenchBoard from "../../../assets/images/wrenchboard-logo-text.png";
import usersService from "../../../services/UsersService";
import InputCom from "../../Helpers/Inputs/InputCom";
import AuthLayout from "../AuthLayout2";
export default function SignUp() {
// eslint-disable-next-line no-restricted-globals
const queryParams = new URLSearchParams(location?.search);
const country = queryParams.get("cnt")?.toUpperCase();
const { pathname } = useLocation();
const currentPath = country
? `${pathname}?cnt=${country.toLowerCase()}`
: pathname; // Determines the new pathname is country query params exist
const [signUpLoading, setSignUpLoading] = useState(false);
const [checked, setValue] = useState(false);
// for the catch error
const [msgError, setMsgError] = useState("");
const [showPassword, setShowPassword] = useState(false);
const [countries, setCountries] = useState({ loading: true, data: [] });
const [formData, setFormData] = useState({
country: country ? country : "",
first_name: "",
last_name: "",
email: "",
password: "",
});
const handleInputChange = (event) => {
const { name, value } = event?.target;
setFormData({ ...formData, [name]: value });
};
// To Show and Hide Password
const togglePasswordVisibility = () => {
setShowPassword(!showPassword);
};
const rememberMe = () => {
setValue(!checked);
};
const navigate = useNavigate();
const userApi = new usersService();
// Get Country Api
const getCountryList = useCallback(async () => {
try {
const res = await userApi.getSignupCountryData();
if (res.status === 200 && res.data.internal_return >= 0) {
const { result_list } = await res.data;
if (country) {
// IF LINK/PATHNAME HAS CNT QUERY VALUE
let cnt = result_list.filter((item) => item.code == country); // test to see country passed in query param exist from list of countries supplied by API
if (!cnt.length) {
// IF CNT EMPTY, SET FORMDATA COUNTRY BACK TO EMPTY STRING: RE: THIS IS BCOS WE INITAIL SET COUNTRY VALUE IN FORMDATA, IF COUNTRY PARAM IS PRESENT IN LINK
setFormData((prev) => ({ ...prev, country: "" }));
return setCountries({ loading: false, data: result_list });
}
return setCountries({ loading: false, data: cnt });
}
setCountries({ loading: false, data: result_list });
} else if (res.data.result !== 100) {
setCountries({ loading: false, data: [] });
}
} catch (error) {
throw new Error(error);
}
}, []);
const handleSignUp = async () => {
let { country, first_name, last_name, email, password } = formData;
try {
if (
email !== "" &&
password !== "" &&
first_name !== "" &&
last_name !== "" &&
country !== ""
) {
//checks if email is a valid email address
let regEx = /^[^0-9][a-zA-Z0-9._%+-]+@[a-zA-Z]+(\.[a-zA-Z]+)+$/;
if (regEx.test(email) == false) {
setMsgError("Invalid Email");
return setTimeout(() => {
setMsgError("");
}, 3000);
}
//checks if terms and condition is checked
if (!checked) {
setMsgError("Terms and condition required");
return setTimeout(() => {
setMsgError("");
}, 3000);
}
setSignUpLoading(true);
const reqData = {
country: country,
firstname: first_name,
lastname: last_name,
email: email,
username: email,
password: password,
terms: 1,
news: 1,
};
const res = await userApi.CreateUser(reqData);
if (res.status === 200) {
const { data } = res;
if (data && data.acc === "DULPICATE") {
setMsgError("Duplicate username. Please try another email.");
setSignUpLoading(false);
}
if (data && data.status === "1") {
setTimeout(() => {
navigate("/outmessage", { replace: true });
setSignUpLoading(false);
}, 2000);
}
if (data && data.status === "5") {
setMsgError("Something went wrong. Please try another email.");
setSignUpLoading(false);
}
} else {
setSignUpLoading(false);
setMsgError("An error occurred");
}
} else {
setMsgError("Please fill in fields");
}
} catch (error) {
throw new Error(error);
} finally {
setTimeout(() => {
setMsgError(null);
}, process.env.REACT_APP_SIGNUP_ERROR_TIMEOUT);
}
};
useEffect(() => {
getCountryList();
}, []);
return (
<>
<AuthLayout slogan="Welcome to WrenchBoard">
<div className="w-full">
<div className="mb-5">
<Link to={currentPath}>
<img
src={WrenchBoard}
alt="wrenchboard"
className="h-10 mx-auto"
/>
</Link>
</div>
<div className="flex place-content-center">
<div className="w-10/12">
<div className="title-area flex flex-col justify-center items-center relative text-center mb-7">
<h1 className="text-[#181c32] font-semibold dark:text-white mb-3 leading-[27.3px] text-[22.75px]">
Create Account
</h1>
<span className="text-gray-400 font-medium text-[16.25px] leading-[24.375px]">
Already have an account?{" "}
<Link
to="/login"
className="font-semibold text-[#4687ba] hover:text-[#009ef7] transition"
>
Sign in here
</Link>
</span>
</div>
<div className="w-full flex items-center gap-2">
<div className="border-b border-[#eff2f5] max-w-[50%] w-full"></div>
<span className="text-[#b5b5c3] font-medium text-[0.7rem] w-[2%]">
OR
</span>
<div className="border-b border-[#eff2f5] max-w-[50%] w-full"></div>
</div>
<div className="input-area">
<SelectOption
label="Country"
data={countries}
name="country"
value={formData.country}
inputHandler={handleInputChange}
disable={
country && countries?.data?.length <= 1 ? true : false
}
/>
<div className="input-fl-name mb-4 sm:flex w-full sm:space-x-6 ">
<div className="input-item sm:w-1/2 w-full mb-4 sm:mb-0">
<InputCom
fieldClass="px-6"
placeholder="Firstname"
label="First Name"
name="first_name"
type="text"
value={formData.first_name}
inputHandler={handleInputChange}
/>
</div>
<div className="input-item flex-1">
<InputCom
fieldClass="px-6"
placeholder="Lastname"
label="Last Name"
name="last_name"
type="text"
value={formData.last_name}
inputHandler={handleInputChange}
/>
</div>
</div>
<div className="input-item mb-4">
<InputCom
fieldClass="px-6"
placeholder="support@mermsemr.com"
label="Email"
name="email"
type="email"
value={formData.email}
inputHandler={handleInputChange}
/>
</div>
<div className="input-item mb-4">
<InputCom
fieldClass="sm:px-6 px-2 tracking-[0.25em] text-2xl"
placeholder="● ● ● ● ● ●"
label="Password"
name="password"
type={showPassword ? "text" : "password"}
onClick={togglePasswordVisibility}
passIcon={showPassword ? "show-password" : "hide-password"}
value={formData.password}
inputHandler={handleInputChange}
/>
</div>
{msgError && (
<div className="p-4 text-[#912741] bg-[#fcd9e2] border-[#fbc6d3] mb-4 rounded-[0.475rem] text-md font-light leading-[19.5px]">
{msgError}
</div>
)}
<div className="forgot-password-area flex justify-between items-center mb-6">
<div className="remember-checkbox flex items-center space-x-2.5 group cursor-pointer">
<button
onClick={rememberMe}
type="button"
className={`w-6 h-6 border-[#4687ba] text-white flex justify-center items-center border rounded-[.45em] group-checked:text-white transition-all duration-200 group-checked:cursor-default ${
checked && "text-white bg-[#4687ba]"
}`}
>
{checked && (
<svg
xmlns="http://www.w3.org/2000/svg"
className="h-5 w-5"
viewBox="0 0 20 20"
fill="currentColor"
>
<path
fillRule="evenodd"
d="M16.707 5.293a1 1 0 010 1.414l-8 8a1 1 0 01-1.414 0l-4-4a1 1 0 011.414-1.414L8 12.586l7.293-7.293a1 1 0 011.414 0z"
clipRule="evenodd"
/>
</svg>
)}
</button>
<span
onClick={rememberMe}
className="cursor-default text-dark-gray dark:text-white text-[15px] group-checked:text-white transition-all duration-200 group-checked:cursor-default"
>
I agree with all
<Link
href="#"
className="text-base text-[#4687ba] hover:text-[#009ef7] mx-1 inline-block"
>
terms and condition
</Link>
</span>
</div>
</div>
{/* Forgot Password */}
{/* <div className="forgot-password-area flex justify-between items-center mb-6">
<div className="remember-checkbox flex items-center space-x-2.5">
<button
onClick={rememberMe}
type="button"
className="w-6 h-6 bg-[#4687ba] text-white flex justify-center items-center border border-light-gray rounded-[.45em]"
>
{checked && (
<svg
xmlns="http://www.w3.org/2000/svg"
className="h-5 w-5"
viewBox="0 0 20 20"
fill="currentColor"
>
<path
fillRule="evenodd"
d="M16.707 5.293a1 1 0 010 1.414l-8 8a1 1 0 01-1.414 0l-4-4a1 1 0 011.414-1.414L8 12.586l7.293-7.293a1 1 0 011.414 0z"
clipRule="evenodd"
/>
</svg>
)}
</button>
<span
onClick={rememberMe}
className="cursor-default text-dark-gray dark:text-white text-[15px]"
>
I agree with all
<Link
href="#"
className="text-base text-[#4687ba] hover:text-[#009ef7] mx-1 inline-block"
>
terms and condition
</Link>
</span>
</div>
</div> */}
<div className="signin-area mb-1">
<div className="flex justify-center">
<button
disabled={countries.loading}
type="button"
onClick={handleSignUp}
className={`rounded-full text-white flex justify-center bg-[#4687ba] hover:bg-[#009ef7] transition-all duration-300 items-center h-[42px] py-[0.8875rem] px-[1.81rem] text-[14.95px] btn-login`}
>
{signUpLoading ? (
<div className="signup btn-loader"></div>
) : (
<span>Sign Up</span>
)}
</button>
</div>
</div>
{/* APP DOWNLOAD STORE */}
<div className="w-full mt-4">
<div className="w-full flex justify-center items-center gap-4">
<div className="w-28 lg:w-32">
<a
className="px-1 py-1 lg:py-2 flex justify-center items-center gap-1 w-full rounded-md bg-black text-white hover:text-slate-500 hover:shadow-lg transition-all duration-300"
target="_blank"
href={process.env.REACT_APP_APPLE_APP}
rel="noreferrer"
>
<i className="fa-brands fa-apple text-3xl"></i>
<div className="flex flex-col">
<span className="text-[11px]">Available on the</span>
<span className="text-[12px] lg:text-base">
App Store
</span>
</div>
</a>
</div>
<div className="w-28 lg:w-32">
<a
className="px-1 py-1 lg:py-2 flex justify-center items-center gap-1 w-full rounded-md bg-black text-white hover:text-slate-500 hover:shadow-lg transition-all duration-300"
target="_blank"
href={process.env.REACT_APP_ANDROID_APP}
rel="noreferrer"
>
<i className="fa-brands fa-google-play text-2xl"></i>
<div className="flex flex-col">
<span className="text-[11px]">Available on the</span>
<span className="text-[12px] lg:text-base">
Google Play
</span>
</div>
</a>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</AuthLayout>
</>
);
}
const SelectOption = ({
label,
name,
inputHandler,
value,
data, // passing the data from parent
disable,
}) => {
return (
<div className="input-com mb-7">
<div className="flex items-center justify-between">
<label
className="input-label text-[#181c32] dark:text-white text-[15px] font-semibold block mb-2.5"
htmlFor={name}
>
{label}
</label>
</div>
<div>
<select
disabled={disable}
name={name}
id={name}
className="input-wrapper border border-[#f5f8fa] dark:border-[#5e6278] w-full rounded-full h-[42px] overflow-hidden relative font-medium leading-6 bg-clip-padding text-[#5e6278] dark:text-gray-100 bg-[#f5f8fa] dark:bg-[#5e6278] text-base focus-visible:border-transparent focus-visible:outline-0 focus-visible:ring-transparent "
onChange={inputHandler}
value={value}
>
{data?.data?.length > 1 ? (
<>
<option value={""}>Select your Country</option>
{data?.data?.map((item) => (
<option value={item.code} key={item.uid}>
{item.country}
</option>
))}
</>
) : data?.data?.length == 1 ? (
data?.data?.map((item) => (
<option value={item.code} key={item.uid}>
{item.country}
</option>
))
) : data?.data?.length < 1 && data.loading ? (
<option value="">Loading...</option>
) : (
<option value="">No Country Found!</option>
)}
</select>
</div>
</div>
);
};
@@ -1,81 +0,0 @@
import React, { useState } from "react";
import titleShape from "../../../assets/images/shape/title-shape-two.svg";
import InputCom from "../../Helpers/Inputs/InputCom";
import AuthLayout from "../AuthLayout2";
import ThankYou from "../ThankYou";
export default function UpdatePassword() {
const [updated, setValue] = useState(false);
const [message, setMessage] = useState(false);
const updatePassword = () => {
setValue(!updated);
setTimeout(() => {
setMessage(!message);
}, 100);
};
return (
<>
<AuthLayout slogan="Welcome to myFit">
{updated === false ? (
<div className="flex place-content-center">
<div className="w-10/12">
<div className="title-area relative flex flex-col justify-center items-center mb-7">
<h1 className="sm:text-5xl text-4xl font-bold leading-[74px] text-dark-gray dark:text-white">
Update Password
</h1>
{/* w-[341px] absolute top-14 left-12 */}
<div className="shape sm:w-[341px] w-[270px] -mt-5 sm:-mt-1 ml-5">
<img src={titleShape} alt="shape" />
</div>
</div>
<div className="input-area">
<div className="input-item mb-5">
<InputCom
fieldClass="px-6"
placeholder="*********"
label="Old Password"
name="password"
type="password"
iconName="password"
/>
</div>
<div className="input-item mb-5">
<InputCom
fieldClass="px-6"
placeholder="*********"
label="New Password"
name="password"
type="password"
iconName="password"
/>
</div>
<div className="input-item mb-5">
<InputCom
fieldClass="px-6"
placeholder="*********"
label="Re-enter Password"
name="password"
type="password"
iconName="password"
/>
</div>
<div className="signin-area mb-3.5">
<button
onClick={updatePassword}
type="button"
className="w-full rounded-[50px] mb-5 h-[58px] text-xl text-white font-bold flex justify-center bg-purple items-center"
>
Continue
</button>
</div>
</div>
</div>
</div>
) : (
<ThankYou className={`thankyou-section ${message ? "active" : ""}`} />
)}
</AuthLayout>
</>
);
}
+57 -60
View File
@@ -36,7 +36,10 @@ export default function VerifyLink() {
var postData = {
username: email,
password: password,
login_mode: 100,
sessionid: "STARTER-NOTREAL",
verify_link: token,
action: 11012,
};
const res = await userApi?.CompleteSignUp(postData);
@@ -51,7 +54,6 @@ export default function VerifyLink() {
localStorage.setItem("member_id", `${data?.member_id}`);
localStorage.setItem("session_token", `${data?.session}`);
localStorage.setItem("session", `${data?.session}`);
localStorage.setItem("uid", data?.uid);
navigate("/", { replace: true });
setLinkLoader(false);
@@ -79,19 +81,19 @@ export default function VerifyLink() {
};
// for verifying the incoming verification link and render the correct component
const verifyEmail = async (code) => {
const verifyEmail = useCallback(async (code) => {
try {
const verifyRes = await userApi.verifyEmail(code);
if (verifyRes.status === 200) {
let { data } = verifyRes;
console.log("TESTING VERIFY", data);
console.log('TESTING VERIFY',data)
if (
data &&
data.internal_return >= 0 &&
data.status == 0 &&
data.pending_id != "" &&
data.pending_uid != "" &&
data.username != "" &&
data.internal_return >= 0 &&
data.status == 0 &&
data.pending_id != '' &&
data.pending_uid != '' &&
data.username != '' &&
data.status_text === "Link Verified"
) {
setPageLoader(false);
@@ -105,13 +107,12 @@ export default function VerifyLink() {
setLinkSuccess(false);
throw new Error(error);
}
};
}, []);
// delay verify requests by 10000ms
const debouncedEmail = debounce(verifyEmail, 1000);
useEffect(() => {
setEmail("")
debouncedEmail(token);
}, []);
@@ -171,57 +172,53 @@ const SuccessfulComponent = ({
handleEmail,
msgErr,
loader,
}) => {
return (
<div className="input-area">
{/* INPUT */}
<div className="mb-5">
<InputCom
fieldClass="px-6"
value={email}
inputHandler={handleEmail}
placeholder="support@mermsemr.com"
label="Email"
name="email"
type="email"
iconName="message"
autoComplete="off"
/>
</div>
<div className="mb-5">
<InputCom
fieldClass="px-6"
value={password}
inputHandler={handlePassword}
placeholder="● ● ● ● ● ●"
label="Password"
name="password"
type="password"
iconName="password"
autoComplete="off"
/>
</div>
{msgErr && (
<div className="relative p-4 text-[#912741] bg-[#fcd9e2] border-[#fbc6d3] mb-4 rounded-[0.475rem] text-md font-light leading-[19.5px] text-[13px]">
{msgErr}
</div>
)}
<div className="signin-area mb-3.5">
<button
onClick={onSubmit}
type="button"
className={`btn-login rounded-[0.475rem] mb-6 text-xl text-white flex justify-center bg-[#4687ba] hover:bg-[#009ef7] transition-all duration-300 items-center text-[15px]`}
>
{loader ? (
<div className="signup btn-loader"></div>
) : (
<span>Continue</span>
)}
</button>
</div>
}) => (
<div className="input-area">
{/* INPUT */}
<div className="mb-5">
<InputCom
fieldClass="px-6"
value={email}
inputHandler={handleEmail}
placeholder="support@mermsemr.com"
label="Email"
name="email"
type="email"
iconName="message"
/>
</div>
);
};
<div className="mb-5">
<InputCom
fieldClass="px-6"
value={password}
inputHandler={handlePassword}
placeholder="● ● ● ● ● ●"
label="Password"
name="password"
type="password"
iconName="password"
/>
</div>
{msgErr && (
<div className="relative p-4 text-[#912741] bg-[#fcd9e2] border-[#fbc6d3] mb-4 rounded-[0.475rem] text-md font-light leading-[19.5px] text-[13px]">
{msgErr}
</div>
)}
<div className="signin-area mb-3.5">
<button
onClick={onSubmit}
type="button"
className={`btn-login rounded-[0.475rem] mb-6 text-xl text-white flex justify-center bg-[#4687ba] hover:bg-[#009ef7] transition-all duration-300 items-center text-[15px]`}
>
{loader ? (
<div className="signup btn-loader"></div>
) : (
<span>Continue</span>
)}
</button>
</div>
</div>
);
const ErrorComponent = ({ onClick }) => (
<div className="input-area">
@@ -1,244 +0,0 @@
import { useCallback, useEffect, useState } from "react";
import { Link, useLocation, useNavigate } from "react-router-dom";
import WrenchBoard from "../../../assets/images/wrenchboard-logo-text.png";
import debounce from "../../../hooks/debounce";
import usersService from "../../../services/UsersService";
import InputCom from "../../Helpers/Inputs/InputCom";
import AuthLayout from "../AuthLayout2";
export default function VerifyLink() {
const [email, setEmail] = useState("");
const [password, setPassword] = useState("");
const [msgError, setMsgError] = useState("");
const [linkLoader, setLinkLoader] = useState(false);
const [pageLoader, setPageLoader] = useState(true);
const [linkSuccess, setLinkSuccess] = useState(true);
const navigate = useNavigate();
const location = useLocation();
const queryParams = new URLSearchParams(location?.search);
const token = queryParams.get("vlnk");
const userApi = new usersService();
// email
const handleEmail = (e) => {
setEmail(e.target.value);
};
// password
const handlePassword = (e) => {
setPassword(e.target.value);
};
// if verification is okay. set a complete signup form
const completeSignup = async () => {
try {
if (email !== "" && password !== "") {
setLinkLoader(true);
var postData = {
username: email,
password: password,
verify_link: token,
};
const res = await userApi?.CompleteSignUp(postData);
if (res.status === 200) {
const { data } = res;
if (
data?.status > 0 &&
data?.internal_return == 100 &&
data?.session != ""
) {
localStorage.setItem("email", `${data?.email}`);
localStorage.setItem("member_id", `${data?.member_id}`);
localStorage.setItem("session_token", `${data?.session}`);
localStorage.setItem("session", `${data?.session}`);
localStorage.setItem("uid", data?.uid);
navigate("/", { replace: true });
setLinkLoader(false);
} else {
setLinkLoader(false);
setMsgError("Invalid Link or Password Combination");
}
} else {
setLinkLoader(false);
setLinkSuccess(false);
setMsgError("An error occurred");
}
} else {
setMsgError("Please fill in fields");
}
} catch (error) {
setLinkLoader(false);
setLinkSuccess(false);
throw new Error(error);
} finally {
setTimeout(() => {
setMsgError(null);
}, process.env.REACT_APP_SIGNUP_ERROR_TIMEOUT);
}
};
// for verifying the incoming verification link and render the correct component
const verifyEmail = useCallback(async (code) => {
try {
const verifyRes = await userApi.verifyEmail(code);
if (verifyRes.status === 200) {
let { data } = verifyRes;
console.log("TESTING VERIFY", data);
if (
data &&
data.internal_return >= 0 &&
data.status == 0 &&
data.pending_id != "" &&
data.pending_uid != "" &&
data.username != "" &&
data.status_text === "Link Verified"
) {
setEmail(data.username);
setPageLoader(false);
} else {
setPageLoader(false);
setLinkSuccess(false);
}
}
} catch (error) {
setPageLoader(false);
setLinkSuccess(false);
throw new Error(error);
}
}, []);
// delay verify requests by 10000ms
const debouncedEmail = debounce(verifyEmail, 1000);
useEffect(() => {
debouncedEmail(token);
}, []);
return (
<>
<AuthLayout slogan="Welcome to WrenchBoard">
{pageLoader ? (
<img src={WrenchBoard} alt="wrenchboard" className="h-10 mx-auto" />
) : (
<div className="w-full">
<div className="mb-12">
<Link to="#">
<img
src={WrenchBoard}
alt="wrenchboard"
className="h-10 mx-auto"
/>
</Link>
</div>
<div className="flex place-content-center">
<div className="w-10/12">
<div className="title-area flex flex-col justify-center items-center relative text-center mb-7">
<h1 className="text-[#181c32] font-semibold dark:text-white mb-3 leading-[27.3px] text-[22.75px]">
{linkSuccess
? "Sign In to WrenchBoard"
: "Invalid verification link"}
</h1>
</div>
{/* If the verification was a success */}
{linkSuccess ? (
<SuccessfulComponent
email={email}
password={password}
handleEmail={handleEmail}
handlePassword={handlePassword}
onSubmit={completeSignup}
msgErr={msgError}
loader={linkLoader}
/>
) : (
<ErrorComponent onClick={() => navigate("/login")} />
)}
</div>
</div>
</div>
)}
</AuthLayout>
</>
);
}
const SuccessfulComponent = ({
onSubmit,
password,
handlePassword,
email,
handleEmail,
msgErr,
loader,
}) => (
<div className="input-area">
{/* INPUT */}
<div className="mb-5">
<InputCom
fieldClass="px-6"
value={email}
inputHandler={handleEmail}
placeholder="support@mermsemr.com"
label="Email"
name="email"
type="email"
iconName="message"
autoComplete="off"
/>
</div>
<div className="mb-5">
<InputCom
fieldClass="px-6"
value={password}
inputHandler={handlePassword}
placeholder="● ● ● ● ● ●"
label="Password"
name="password"
type="password"
iconName="password"
autoComplete="off"
/>
</div>
{msgErr && (
<div className="relative p-4 text-[#912741] bg-[#fcd9e2] border-[#fbc6d3] mb-4 rounded-[0.475rem] text-md font-light leading-[19.5px] text-[13px]">
{msgErr}
</div>
)}
<div className="signin-area mb-3.5">
<button
onClick={onSubmit}
type="button"
className={`btn-login rounded-[0.475rem] mb-6 text-xl text-white flex justify-center bg-[#4687ba] hover:bg-[#009ef7] transition-all duration-300 items-center text-[15px]`}
>
{loader ? (
<div className="signup btn-loader"></div>
) : (
<span>Continue</span>
)}
</button>
</div>
</div>
);
const ErrorComponent = ({ onClick }) => (
<div className="input-area">
<div className="my-5">
<p className="text-[14px] leading-[19px] text-center text-[#181c32]">
This error occurs because you have already verified this link or the
link has expired. Try login or reset password. If none worked, try to
create the account from the start.
</p>
</div>
<div className="signin-area flex justify-center mb-3.5">
<button
onClick={onClick}
type="button"
className={`rounded-[0.475rem] mb-6 text-[15px] font-semibold text-[#009ef7] hover:text-white flex justify-center bg-[#f1faff] hover:bg-[#009ef7] transition-all duration-300 items-center py-[0.8875rem] px-[1.81rem]`}
>
<span>Return Home</span>
</button>
</div>
</div>
);
+105 -124
View File
@@ -11,11 +11,7 @@ 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 [requestStatus, setRequestStatus] = useState({loading: true, status:false, data: []})
const [msgError, setMsgError] = useState("");
const [linkLoader, setLinkLoader] = useState(false);
const [linkSuccess, setLinkSuccess] = useState(null);
@@ -39,64 +35,60 @@ const VerifyPassword = () => {
};
const completeReset = async () => {
if (!password || !confirmPassword) {
// CHECKS IF PASSWORD IS EMPTY
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
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
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@"
);
if(!PasswordValidator(password)){ // CHECKS IF PASSWORD IS VALID
setMsgError("Password must contain alphanumeric, uppercase and special character: eg: Password1@");
return setTimeout(() => {
setMsgError(null);
}, process.env.REACT_APP_RESET_START_ERROR_TIMEOUT);
}
try {
setLinkLoader(true);
var reqData = {
reset_link: token,
newpass: password,
m_uid: requestStatus.data?.m_uid || "",
reset_uid: requestStatus.data?.reset_uid || "",
step: 300,
};
const res = await userApi?.CompleteResetPassword(reqData);
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) {
const { data } = res;
if (data?.internal_return >= 0) {
// setTimeout(() => {
// navigate("/login", { replace: true });
// setLinkLoader(false);
// }, 2000);
setLinkSuccess(true);
} else {
setLinkLoader(false);
setMsgError("An error occurred");
setLinkSuccess(false);
}
} else {
setLinkLoader(false);
setLinkSuccess(false);
}
if (res.status == 200) {
const { data } = res;
if (data?.internal_return >= 0) {
// setTimeout(() => {
// navigate("/login", { replace: true });
// setLinkLoader(false);
// }, 2000);
setLinkSuccess(true);
} else {
setLinkLoader(false);
setMsgError("An error occurred");
setLinkSuccess(false);
}
} else {
setLinkLoader(false);
setLinkSuccess(false);
}
} catch (error) {
setLinkLoader(false);
setLinkSuccess(false);
@@ -108,33 +100,31 @@ const VerifyPassword = () => {
}
};
const verifyResetPwd = () => {
// FUNCTION TO VERIFY RESET PASSWORD LINK
setRequestStatus({ loading: true, status: false, data: [] });
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: [] });
});
};
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(() => {
useEffect(()=>{
// little checker for the validity of the token
if (token == null || token?.length != 64) {
return setRequestStatus({ loading: false, status: false, data: [] });
}
verifyResetPwd();
}, []);
if (token==null || token?.length != 64) {
return setRequestStatus({loading: false, status:false, data: []});
}
verifyResetPwd()
},[])
return (
<>
<AuthLayout slogan="Welcome to WrenchBoard">
@@ -149,68 +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">
{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}
{requestStatus.loading ?
<LoadingSpinner color='sky-blue' size='16' height='h-300px' />
:
!requestStatus.loading && requestStatus.status ?
<div className="w-full">
{linkSuccess == null ?
<>
<div className="title-area flex flex-col justify-center items-center relative text-center mb-7">
<h1 className="text-[#181c32] font-semibold dark:text-white mb-3 leading-[27.3px] text-[22.75px]">
Password Reset
</h1>
<span className="text-gray-400 font-medium text-[16.25px] leading-[24.375px]">
Enter a new password to reset
</span>
<span className="text-gray-400 font-medium text-[16.25px] leading-[24.375px]">
We'll send an email to confirm reset
</span>
</div>
<div>
<p className="text-red-500 font-semibold mb-3 leading-[27.3px] text-[13px]">
Must include a special, numeric, uppercase and lowercase character
</p>
</div>
<SuccessfulComponent
password={password}
confirmPassword={confirmPassword}
handlePassword={handlePassword}
onSubmit={completeReset}
msgErr={msgError}
loader={linkLoader}
showPassword={showPassword}
onClick={togglePasswordVisibility}
navigateHandler={() => navigate("/login")}
/>
)}
</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>
)}
</>
:
<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>
@@ -1,288 +0,0 @@
import { useEffect, useState } from "react";
import { Link, useLocation, useNavigate } from "react-router-dom";
import WrenchBoard from "../../../assets/images/wrenchboard-logo-text.png";
import usersService from "../../../services/UsersService";
import InputCom from "../../Helpers/Inputs/InputCom";
import AuthLayout from "../AuthLayout2";
import ForgetPwdResponse from "../ForgetPwdResponse";
import PasswordValidator from "../../../lib/PasswordValidator";
import LoadingSpinner from "../../Spinners/LoadingSpinner";
const VerifyPassword = () => {
const [password, setPassword] = useState("");
const [confirmPassword, setConfirmPassword] = useState("");
const [requestStatus, setRequestStatus] = useState({
loading: true,
status: false,
data: [],
});
const [msgError, setMsgError] = useState("");
const [linkLoader, setLinkLoader] = useState(false);
const [linkSuccess, setLinkSuccess] = useState(null);
const [showPassword, setShowPassword] = useState(false);
const navigate = useNavigate();
const location = useLocation();
const queryParams = new URLSearchParams(location?.search);
const token = queryParams.get("passlink");
const userApi = new usersService();
// To Show and Hide Password
const togglePasswordVisibility = () => {
setShowPassword(!showPassword);
};
// Password
const handlePassword = (e) => {
let { name, value } = e?.target;
if (name == "password") setPassword(value);
if (name == "confirm_password") setConfirmPassword(value);
};
const completeReset = async () => {
if (!password || !confirmPassword) {
// CHECKS IF PASSWORD IS EMPTY
setMsgError("Please fill in fields");
return setTimeout(() => {
setMsgError(null);
}, process.env.REACT_APP_RESET_START_ERROR_TIMEOUT);
}
if (password != confirmPassword) {
// CHECKS IF PASSWORD EQUALS CONFIRM PASSWORD
setMsgError("Passwords does not match");
return setTimeout(() => {
setMsgError(null);
}, process.env.REACT_APP_RESET_START_ERROR_TIMEOUT);
}
if (password.length < 6) {
// CHECKS IF PASSWORD LENGTH IS UPTO 6 CHARACTERS
setMsgError("Password must be upto six characters");
return setTimeout(() => {
setMsgError(null);
}, process.env.REACT_APP_RESET_START_ERROR_TIMEOUT);
}
if (!PasswordValidator(password)) {
// CHECKS IF PASSWORD IS VALID
setMsgError(
"Password must contain alphanumeric, uppercase and special character: eg: Password1@"
);
return setTimeout(() => {
setMsgError(null);
}, process.env.REACT_APP_RESET_START_ERROR_TIMEOUT);
}
try {
setLinkLoader(true);
var reqData = {
reset_link: token,
newpass: password,
m_uid: requestStatus.data?.m_uid || "",
reset_uid: requestStatus.data?.reset_uid || "",
step: 300,
};
const res = await userApi?.CompleteResetPassword(reqData);
if (res.status == 200) {
const { data } = res;
if (data?.internal_return >= 0) {
// setTimeout(() => {
// navigate("/login", { replace: true });
// setLinkLoader(false);
// }, 2000);
setLinkSuccess(true);
} else {
setLinkLoader(false);
setMsgError("An error occurred");
setLinkSuccess(false);
}
} else {
setLinkLoader(false);
setLinkSuccess(false);
}
} catch (error) {
setLinkLoader(false);
setLinkSuccess(false);
throw new Error(error);
} finally {
setTimeout(() => {
setMsgError(null);
}, process.env.REACT_APP_SIGNUP_ERROR_TIMEOUT);
}
};
const verifyResetPwd = () => {
// FUNCTION TO VERIFY RESET PASSWORD LINK
setRequestStatus({ loading: true, status: false, data: [] });
var reqData = {
reset_link: token,
step: 200,
};
userApi
.CompleteResetPassword(reqData)
.then((res) => {
if (res.status != 200 || res.data.internal_return < 0) {
return setRequestStatus({ loading: false, status: false, data: [] });
}
setRequestStatus({ loading: false, status: true, data: res.data });
})
.catch((error) => {
setRequestStatus({ loading: false, status: false, data: [] });
});
};
useEffect(() => {
// little checker for the validity of the token
if (token == null || token?.length != 64) {
return setRequestStatus({ loading: false, status: false, data: [] });
}
verifyResetPwd();
}, []);
return (
<>
<AuthLayout slogan="Welcome to WrenchBoard">
<div className="w-full">
<div className="mb-12">
<Link to="#">
<img
src={WrenchBoard}
alt="wrenchboard"
className="h-10 mx-auto"
/>
</Link>
</div>
<div className="flex place-content-center">
{requestStatus.loading ? (
<LoadingSpinner color="sky-blue" size="16" height="h-300px" />
) : !requestStatus.loading && requestStatus.status ? (
<div className="w-10/12">
{linkSuccess == null ? (
<>
<div className="title-area flex flex-col justify-center items-center relative text-center mb-7">
<h1 className="text-[#181c32] font-semibold dark:text-white mb-3 leading-[27.3px] text-[22.75px]">
Password Reset
</h1>
<span className="text-gray-400 font-medium text-[16.25px] leading-[24.375px]">
Enter a new password to reset
</span>
<span className="text-gray-400 font-medium text-[16.25px] leading-[24.375px]">
We'll send an email to confirm reset
</span>
</div>
<div>
<p className="text-red-500 font-semibold mb-3 leading-[27.3px] text-[13px]">
Must include a special, numeric, uppercase and lowercase
character
</p>
</div>
<SuccessfulComponent
password={password}
confirmPassword={confirmPassword}
handlePassword={handlePassword}
onSubmit={completeReset}
msgErr={msgError}
loader={linkLoader}
showPassword={showPassword}
onClick={togglePasswordVisibility}
navigateHandler={() => navigate("/login")}
/>
</>
) : (
<ForgetPwdResponse
title={
linkSuccess
? "Password Reset Complete"
: "Password Reset Error"
}
message={
linkSuccess
? "Password Reset Complete. You can login now with your new credentials"
: "Password Reset Error. Please get in touch with support for further support"
}
type={linkSuccess}
/>
)}
</div>
) : (
<div className="w-11/12 sm:max-w-[500px] title-area flex flex-col justify-center items-center relative text-center mb-7">
<ForgetPwdResponse
title={"Forget Password"}
message={
"We are unable to continue to reset process. This error is usually due to expired links. Please start all over or contact us"
}
type={requestStatus.status}
/>
</div>
)}
</div>
</div>
</AuthLayout>
</>
);
};
export default VerifyPassword;
const SuccessfulComponent = ({
onSubmit,
navigateHandler,
showPassword,
onClick,
password,
confirmPassword,
handlePassword,
msgErr,
loader,
}) => (
<div className="input-area">
{/* INPUT */}
<div className="mb-5">
<InputCom
fieldClass="sm:px-6 px-2 tracking-[0.25em] text-2xl"
value={password}
inputHandler={handlePassword}
placeholder="● ● ● ● ● ●"
label="Password"
name="password"
type={showPassword ? "text" : "password"}
onClick={onClick}
passIcon={showPassword ? "show-password" : "hide-password"}
/>
</div>
<div className="mb-5">
<InputCom
fieldClass="sm:px-6 px-2 tracking-[0.25em] text-2xl"
value={confirmPassword}
inputHandler={handlePassword}
placeholder="● ● ● ● ● ●"
label="Confirm Password"
name="confirm_password"
type="password"
/>
</div>
{msgErr && (
<div className="relative p-4 text-[#912741] bg-[#fcd9e2] border-[#fbc6d3] mb-4 rounded-[0.475rem] text-md font-light leading-[19.5px] text-[13px]">
{msgErr}
</div>
)}
<div className="signin-area mb-3.5">
<button
onClick={onSubmit}
type="button"
className={`btn-login rounded-[0.475rem] mb-6 text-xl text-white flex justify-center bg-[#4687ba] hover:bg-[#009ef7] transition-all duration-300 items-center text-[15px]`}
>
{loader ? (
<div className="signup btn-loader"></div>
) : (
<span>Continue</span>
)}
</button>
</div>
<div className="signin-area mb-3.5">
<button
onClick={navigateHandler}
type="button"
className={`rounded-[0.475rem] mb-6 text-[15px] font-semibold text-[#009ef7] hover:text-white flex justify-center bg-[#f1faff] hover:bg-[#009ef7] transition-all duration-300 items-center py-[0.8875rem] px-[1.81rem] btn-login`}
>
<span>Return Home</span>
</button>
</div>
</div>
);
@@ -1,64 +0,0 @@
import { useNavigate, Link } from "react-router-dom";
import AuthLayout from "../AuthLayout2";
import WrenchBoard from "../../../assets/images/wrenchboard-logo-text.png";
export default function VerifyYou() {
const navigate = useNavigate();
return (
<>
<AuthLayout slogan="Welcome to WrenchBoard">
<div className="w-full">
<div className="mb-12">
<Link to="#">
<img
src={WrenchBoard}
alt="wrenchboard"
className="h-10 mx-auto"
/>
</Link>
</div>
<div className="flex place-content-center">
<div className="w-10/12">
<div className="title-area flex flex-col justify-center items-center relative text-center mb-7">
<h1 className="text-[#181c32] font-semibold dark:text-white mb-3 leading-[27.3px] text-[22.75px]">
Let's verify your email now
</h1>
<span className="text-gray-400 font-medium text-[16.25px] leading-[24.375px]">
Check your email.
</span>
</div>
<div className="input-area">
<div className="mb-5">
<p className="text-[14px] leading-[19px] text-center text-[#181c32]">
<b>Verify Email.</b> Help us secure your WrenchBoard account
by verifying your email registration address. Verification
will let you access all of WrenchBoard's features.
</p>
</div>
<div className="mb-5">
<p className="text-[14px] leading-[19px] text-center text-[#181c32]">
If you do not receive the confirmation message within a few
minutes of signing up, please check your Junk E-mail folder
just in case the confirmation email got delivered there
instead of your inbox. If so, select the confirmation
message and click Not Junk, which will allow future messages
to get through.
</p>
</div>
</div>
<div className="signin-area flex justify-center mb-3.5">
<button
onClick={() => navigate("/")}
type="button"
className={`rounded-[0.475rem] mb-6 text-[15px] font-semibold text-[#009ef7] hover:text-white flex justify-center bg-[#f1faff] hover:bg-[#009ef7] transition-all duration-300 items-center py-[0.8875rem] px-[1.81rem]`}
>
<span>Return Home</span>
</button>
</div>
</div>
</div>
</div>
</AuthLayout>
</>
);
}
+12 -33
View File
@@ -4,15 +4,9 @@ import usersService from "../../services/UsersService";
import Layout from "../Partials/Layout";
import LoadingSpinner from "../Spinners/LoadingSpinner";
import CommonHead from "../UserHeader/CommonHead";
import { useSelector } from "react-redux";
import CustomBreadcrumb from "../Breadcrumb/CustomBreadcrumb";
export default function BlogItem(props) {
const {
userDetails: { account_type },
} = useSelector((state) => state?.userDetails); // CHECKS IF USER Details account type
const apiCall = new usersService()
const navigate = useNavigate()
@@ -39,26 +33,11 @@ export default function BlogItem(props) {
},[blog_id])
return (
<Layout>
{account_type == 'FULL' &&
<CommonHead
commonHeadData={props.commonHeadData}
/>
}
{ account_type == 'FAMILY' &&
<div className="mb-5">
<CustomBreadcrumb
title = {blogdata?.data?.blogdata?.length > 0 ? blogdata?.data?.blogdata[0]?.post_title : 'Blog'}
breadcrumb={
[
{ link: "/", title: "Home" },
{ link: "/fam-blog", title: "Blogs", active: true},
]
}
/>
</div>
}
<CommonHead
commonHeadData={props.commonHeadData}
/>
<div className="notification-page w-full mb-10">
{/* <div className="mb-5">
<div className="mb-5">
<h1 className="text-26 font-bold text-dark-gray dark:text-white">
<span
className={`${selectTab === "today" ? "block" : "hidden"}`}
@@ -66,16 +45,16 @@ export default function BlogItem(props) {
{blogdata.data?.blogdata?.[0]?.post_title}
</span>
</h1>
</div> */}
</div>
<div className="notification-wrapper w-full bg-white p-8 rounded-2xl">
{blogdata?.loading ?
{blogdata.loading ?
<LoadingSpinner size='8' color='sky-blue' height='h-[100px]' />
:
blogdata?.data?.blogdata && blogdata.data?.blogdata.length ?
<div className="w-full">
{/* heading */}
{/* <div className="sm:flex justify-between items-center mb-6">
<div className="mb-5">
<div className="sm:flex justify-between items-center mb-6">
{/* <div className="mb-5">
<h1 className="text-26 font-bold text-dark-gray dark:text-white">
<span
className={`${selectTab === "today" ? "block" : "hidden"}`}
@@ -83,10 +62,10 @@ export default function BlogItem(props) {
{blogdata.data?.blogdata?.[0]?.post_title}
</span>
</h1>
</div>
<div className="slider-btns flex space-x-4">
</div>
</div> */}
</div> */}
{/* <div className="slider-btns flex space-x-4">
</div> */}
</div>
<div dangerouslySetInnerHTML={{__html: blogdata.data?.blogdata?.[0]?.post_content}}>
</div>
</div>
@@ -1,25 +0,0 @@
import React from "react";
import { Link } from "react-router-dom";
export default function CustomBreadcrumb({ title, breadcrumb }) {
return (
<div className="">
<h3 className="text-26 font-bold text-dark-gray dark:text-white">
{title}
</h3>
<nav
aria-label="breadcrumb"
className="text-base text-dark-gray dark:text-white flex items-center"
>
{breadcrumb.map((value, index) => (
<React.Fragment key={index}>
<Link className={value.active ? "opacity-60" : ""} to={value.link}>
{value.title}
</Link>
{index !== breadcrumb.length - 1 && <span className="px-1">\</span>}
</React.Fragment>
))}
</nav>
</div>
);
}
+33 -22
View File
@@ -1,4 +1,5 @@
import { useState } from "react";
import { useEffect, useState } from "react";
import dataImage2 from "../../assets/images/taskbanners/default.jpg";
import { PriceFormatter } from "../Helpers/PriceFormatter";
import MarketPopUp from "../MarketPlace/PopUp/MarketPopUp";
@@ -7,7 +8,6 @@ export default function AvailableJobsCard({
datas,
hidden = false,
contentDisplay,
image_server,
}) {
//debugger;
const [marketPopUp, setMarketPopUp] = useState({ show: false, data: {} });
@@ -19,17 +19,10 @@ export default function AvailableJobsCard({
datas?.currency
);
// useEffect(() => {
// const imagePath = require(`../../assets/images/${datas.thumbnil}`); // Replace with your directory path for local images
// setImageUrl(imagePath);
// }, []);
const image = localStorage.getItem("session_token")
? `${image_server}${localStorage.getItem("session_token")}/job/${
datas.job_uid
}`
: "";
useEffect(() => {
const imagePath = require(`../../assets/images/${datas.thumbnil}`); // Replace with your directory path for local images
setImageUrl(imagePath);
}, []);
return (
<>
{contentDisplay == "grid" ? (
@@ -55,7 +48,7 @@ export default function AvailableJobsCard({
Added
</p>
<p className="text-base text-dark-gray dark:text-white">
{new Date(datas.offer_added).toLocaleDateString()}
{datas.offer_added}
</p>
</div>
</div>
@@ -66,7 +59,7 @@ export default function AvailableJobsCard({
Expires
</p>
<p className="text-base text-dark-gray dark:text-white text-right">
{new Date(datas.expire).toLocaleDateString()}
{datas.expire}
</p>
</div>
</div>
@@ -75,16 +68,34 @@ export default function AvailableJobsCard({
<div
className="w-full h-[236px] p-6 rounded-xl overflow-hidden bg-center bg-cover bg-no-repeat"
style={{
backgroundImage: `url('${image}')`,
backgroundImage: `url('${imageUrl}')`,
}}
>
<div className="flex justify-center bg-slate-100 p-2 rounded-md">
{datas.description}
</div>
<div className="flex justify-center">{datas.description}</div>
</div>
</div>
<div className="details-area">
<div className="product-two-options flex justify-between mb-5 relative"></div>
<div className="product-two-options flex justify-between mb-5 relative">
{/* <div className="status">*/}
{/* {datas.isActive && (*/}
{/* <span className="text-xs px-3 py-1.5 tracking-wide rounded-full bg-gold text-white">*/}
{/* Active*/}
{/*</span>*/}
{/* )}*/}
{/* </div>*/}
{/*<div className=" review flex space-x-2">*/}
{/* <button*/}
{/* onClick={favoriteHandler}*/}
{/* type="button"*/}
{/* className={`w-7 h-7 bg-white rounded-full flex justify-center items-center ${*/}
{/* addFavorite ? "text-red-500" : "text-thin-light-gray"*/}
{/* }`}*/}
{/* >*/}
{/* <Icons name="star" />*/}
{/* </button>*/}
{/*</div>*/}
</div>
<div className="flex justify-between">
<div className="flex items-center space-x-2">
@@ -117,10 +128,10 @@ export default function AvailableJobsCard({
</div>
</div>
) : (
<div className="card-style-two w-full px-4 py-[0.4rem] my-2 flex items-center gap-4 bg-white dark:bg-dark-white rounded-2xl section-shadow">
<div className="card-style-two w-full p-8 my-2 flex items-center gap-4 bg-white dark:bg-dark-white rounded-2xl section-shadow">
<div className="flex gap-5 items-center w-full">
<div className="w-full h-[60px] rounded-full overflow-hidden flex justify-center items-center flex-[0.1] min-w-[60px] max-w-[60px]">
<img src={image} alt="data" className="w-full h-full" />
<img src={dataImage2} alt="data" className="w-full h-full" />
</div>
<div className="flex flex-col flex-[0.9]">
<h1
@@ -5,7 +5,7 @@ import localImgLoad from "../../lib/localImgLoad";
import CountDown from "../Helpers/CountDown";
import Icons from "../Helpers/Icons";
export default function FamilyActiveJobsCard({ datas, hidden = false, image_server }) {
export default function FamilyActiveJobsCard({ datas, hidden = false }) {
let { pathname } = useLocation();
@@ -22,8 +22,7 @@ export default function FamilyActiveJobsCard({ datas, hidden = false, image_serv
}
};
//debugger;
// const bannerName = datas.banner == null ?'default.jpg':datas.banner;
let image = `${image_server}${localStorage.getItem('session_token')}/job/${datas.origin_job_uid}`
const bannerName = datas.banner == null ?'default.jpg':datas.banner;
return (
<div className="card-style-one flex flex-col justify-between w-full h-[387px] bg-white dark:bg-dark-white p-3 pb rounded-2xl">
<div className="content">
@@ -32,13 +31,10 @@ export default function FamilyActiveJobsCard({ datas, hidden = false, image_serv
{/* thumbnail image */}
<div
className="thumbnail w-full h-full rounded-xl overflow-hidden px-4 pt-4"
// style={{
// background: `url(${localImgLoad(
// `images/taskbanners/${bannerName}`
// )}) center / contain no-repeat`,
// }}
style={{
background: `url(${image}) center / contain no-repeat`,
background: `url(${localImgLoad(
`images/taskbanners/${bannerName}`
)}) center / contain no-repeat`,
}}
>
{/* <div className="product-options flex justify-between relative">*/}
@@ -8,7 +8,7 @@ export default function HomeBannerOffersCard(props) {
useEffect(() => {
let { banner, banner_location } = props?.itemData;
if (banner_location === "LOCAL") {
const imagePath = require(`../../assets/images/${banner}`);
const imagePath = require(`../../assets/images/${banner}`); // Replace with your directory path for local images
setImageUrl(imagePath);
} else if (banner_location === "URL") setImageUrl(banner);
else return null;
+25 -11
View File
@@ -2,20 +2,13 @@ import localImgLoad from "../../lib/localImgLoad";
import CountDown from "../Helpers/CountDown";
import { PriceFormatter } from "../Helpers/PriceFormatter";
export default function OfferCard({
datas,
hidden = false,
setOfferPopout,
image_server,
}) {
export default function OfferCard({ datas, hidden = false, setOfferPopout }) {
let thePrice = PriceFormatter(
datas?.price * 0.01,
datas?.currency_code,
datas?.currency
);
let image = `${image_server}${localStorage.getItem("session_token")}/job/${datas.job_uid}`
return (
<div className="card-style-one flex flex-col justify-between w-full h-[387px] bg-white dark:bg-dark-white p-3 pb rounded-2xl">
<div className="content">
@@ -25,7 +18,9 @@ export default function OfferCard({
<div
className="thumbnail w-full h-full rounded-xl overflow-hidden px-4 pt-4"
style={{
background: `url(${image}) center / contain no-repeat`,
background: `url(${localImgLoad(
`images/taskbanners/${datas?.banner || "default.jpg"}`
)}) center / contain no-repeat`,
}}
>
{hidden && <div className="flex justify-center"></div>}
@@ -36,7 +31,26 @@ export default function OfferCard({
<h1 className="text-xl font-bold text-dark-gray dark:text-white mb-2 capitalize line-clamp-1">
{datas.title}
</h1>
{/* countdown */}
{/* <div className="w-full h-[54px] flex justify-evenly items-center p-2 rounded-lg border border-[#E3E4FE] dark:border-[#a7a9b533] ">
<div className="flex flex-col justify-between">
<p className="text-sm text-thin-light-gray dark:text-white tracking-wide">
Task Code
</p>
<p className="text-base font-bold tracking-wide text-dark-gray dark:text-white">
{datas.contract}
</p>
</div>
<div className="w-[1px] h-full bg-[#E3E4FE] dark:bg-[#a7a9b533] "></div>
<div className="flex flex-col justify-between">
<p className="text-sm text-thin-light-gray dark:text-white tracking-wide">
Remaining Time
</p>
<p className="text-base font-bold tracking-wide text-dark-gray dark:text-white">
<CountDown lastDate={datas.expire} />
</p>
</div>
</div> */}
<div className="w-full p-2 rounded-lg border border-[#E3E4FE] dark:border-[#a7a9b533] ">
<div className="grid grid-cols-2 gap-2">
<div className="flex flex-col justify-between items-center border-r-2">
@@ -82,7 +96,7 @@ export default function OfferCard({
<button
type="button"
onClick={() =>
setOfferPopout({ show: true, data: { ...datas, thePrice, image } })
setOfferPopout({ show: true, data: { ...datas, thePrice } })
}
className="btn-shine w-[98px] h-[33px] text-white rounded-full text-sm bg-pink flex justify-center items-center"
>
@@ -1,62 +0,0 @@
import React, { createContext, useContext, useEffect, useState } from "react";
import { tableReload } from "../../store/TableReloads";
import { useDispatch } from "react-redux";
import io from "socket.io-client";
let SocketIOContext = createContext({})
export default function SocketIOContextProvider({children}) {
const dispatch = useDispatch()
const socket = io.connect(process.env.REACT_APP_PRIMARY_SOCKET);
// //Room State
// const [room, setRoom] = useState("");
// // Messages States
// const [message, setMessage] = useState("");
const [socketMsgReceived, setSocketMsgReceived] = useState("");
const joinRoom = (room) => {
if (room !== "") {
socket.emit("join_room", room);
}
};
const sendMessage = (message, room) => {
if(message && room){
socket.emit("send_message", { message, room });
}
};
useEffect(() => {
socket.on("receive_message", (data) => {
// setSocketMsgReceived(data.message);
dispatch(tableReload({type:'CHATMESSAGELIST'}))
});
}, [socket]);
let values = {
socket,
sendMessage,
joinRoom,
setSocketMsgReceived,
socketMsgReceived,
// room,
// setRoom,
// message,
// setMessage,
}
return (
<SocketIOContext.Provider value={values}>
{children}
</SocketIOContext.Provider>
)
}
export const SocketValues = () => {
return useContext(SocketIOContext)
}
@@ -1,204 +0,0 @@
import React from "react";
import { Link } from "react-router-dom";
const AccountDashboard = ({ className, bannerList }) => {
// getting the upper three banners for the home layout
const getUpperBanner = bannerList?.filter((value, idx) => idx <= 2);
const getLowerBanner = bannerList?.filter((value, idx) => idx > 2);
let getImage = ({ banner_location, banner }) => {
if (banner_location == "LOCAL") {
return require(`../../assets/images/${banner}`);
} else {
return banner;
}
};
console.log(getLowerBanner);
return (
<div
className={`w-full min-h-[450px] flex flex-col justify-between items-center gap-4 rounded-2xl overflow-hidden ${
className || ""
}`}
>
<div className="w-full grid xxs:grid-cols-1 sm:grid-cols-2 lg:grid-cols-3 items-center justify-center gap-2 md:gap-4">
{getUpperBanner?.map((props, idx) => {
let image = getImage(props);
let { short_title, short_description, short_button_text, link_path } =
props;
return (
<TopBanner
btn={short_button_text}
image={image}
title={short_title}
desc={short_description}
link_path={link_path}
key={idx}
/>
);
})}
</div>
<div className="w-full grid-cols-1 md:grid-cols-2 2xl::grid-cols-3 grid items-center justify-center gap-2 md:gap-4">
{getLowerBanner?.map((props, idx) => {
let image = getImage(props);
let { short_title, short_description, short_button_text, link_path } =
props;
return (
<LowerBanner
btn={short_button_text}
image={image}
title={short_title}
desc={short_description}
link_path={link_path}
key={idx}
/>
);
})}
</div>
</div>
);
};
export default AccountDashboard;
const TopBanner = ({ image, title = "", desc = "", btn, link_path, key }) => {
return (
<div className="flex flex-col shadow-md rounded-xl dark:border-[#5356fb29]" key={key}>
<Link to={link_path} className="h-[12rem] rounded-t-xl">
<img
src={image}
alt="banner-img"
loading="lazy"
className="w-full h-full rounded-t-xl object-cover"
/>
</Link>
<div className="h-[7rem] rounded-b-xl bg-white dark:bg-dark-white">
<div className="border-b border-slate-300 px-2 py-1 h-[5.4rem] flex flex-col gap-2 dark:text-white">
<Link to={link_path} className="font-bold text-lg">
{title}
</Link>
<Link to={link_path} className="text-sm">
{desc}
</Link>
</div>
<div className="flex justify-between w-full px-2 items-center pt-[0.2rem]">
<Link to={link_path} className="text-slate-300 font-semibold text-sm">
{btn}
</Link>
<button className="flex items-center justify-center gap-2">
<div className="w-[4px] h-[4px] bg-slate-400 rounded-full"></div>
<div className="w-[4px] h-[4px] bg-slate-400 rounded-full"></div>
<div className="w-[4px] h-[4px] bg-slate-400 rounded-full"></div>
</button>
</div>
</div>
</div>
);
};
const LowerBanner = ({ image, title = "", desc = "", btn, link_path, key }) => {
return (
<div
key={key}
className="flex flex-col bg-white shadow-md h-full rounded-xl dark:border-[#5356fb29] dark:bg-dark-white"
>
<div className="w-full flex justify-between border-b border-slate-300 p-2">
<div className="h-[130px] flex justify-between items-center">
<div className="px-2 flex flex-col gap-2 dark:text-white">
<Link to={link_path} className="text-lg font-bold">
{title}
</Link>
<p to={link_path} className="text-sm">
{desc}
</p>
</div>
</div>
<Link to={link_path} className="w-[150px] h-[100px]">
<img
src={image}
alt="banner-img"
loading="lazy"
className="w-full h-full rounded-xl"
/>
</Link>
</div>
<div className="flex justify-between w-full px-2 items-center">
<Link to={link_path} className="text-slate-300 font-semibold text-sm">
{btn}
</Link>
<button className="flex items-center justify-center gap-2">
<div className="w-[4px] h-[4px] bg-slate-400 rounded-full"></div>
<div className="w-[4px] h-[4px] bg-slate-400 rounded-full"></div>
<div className="w-[4px] h-[4px] bg-slate-400 rounded-full"></div>
</button>
</div>
</div>
);
};
const BannerSection = ({ banners, variant }) => {
const getImage = ({ banner_location, banner }) => {
return banner_location === "LOCAL"
? require(`../../assets/images/${banner}`)
: banner;
};
return (
<>
{banners?.map((props, idx) => {
const { short_title, short_description, short_button_text, link_path } =
props;
const image = getImage(props);
return (
<div
key={idx}
className={`flex flex-col shadow-md rounded-xl ${
variant === "top" ? "" : "bg-white"
}`}
>
<div className={`${variant === "top" ? "rounded-t-xl" : ""}`}>
<Link to={link_path}>
<img
src={image}
alt="banner-img"
className="w-full h-full rounded-t-xl object-cover"
/>
</Link>
</div>
<div
className={`${
variant === "top"
? "rounded-b-xl bg-white"
: "border-b border-slate-300"
} h-[7rem]`}
>
<div className="border-b border-slate-300 px-2 py-1 h-[5.4rem] flex flex-col gap-2">
<Link to={link_path} className="font-bold text-lg">
{short_title}
</Link>
<Link to={link_path} className="text-sm">
{short_description}
</Link>
</div>
<div className="flex justify-between w-full px-2 items-center">
<Link to={link_path} className="text-slate-300 font-semibold">
{short_button_text}
</Link>
<button className="flex items-center justify-center gap-2">
<div className="w-[4px] h-[4px] bg-slate-400 rounded-full"></div>
<div className="w-[4px] h-[4px] bg-slate-400 rounded-full"></div>
<div className="w-[4px] h-[4px] bg-slate-400 rounded-full"></div>
</button>
</div>
</div>
</div>
);
})}
</>
);
};
@@ -1,49 +0,0 @@
import React from "react";
// import HomeSliders from "./HomeSliders";
import { useSelector } from "react-redux";
export default function FamilyParentDashboard({
className,
bannerList,
nextDueTask,
}) {
const { userDetails } = useSelector((state) => state?.userDetails);
let loginDate = userDetails?.last_login.split(" ")[0];
let { firstname, lastname, email } = userDetails;
let userEmail = email.split("@")[0];
return (
<div
className={`w-full md:h-[47px] xxs:h-[74px] flex px-[1.05rem] py-[0.35rem] flex-wrap justify-between items-center gap-2 rounded-2xl overflow-hidden bg-sky-blue ${
className || ""
}`}
style={{
// background: `url(${heroBg})`,
backgroundRepeat: "no-repeat",
backgroundSize: "cover",
}}
>
<div className="h-full flex flex-wrap w-full justify-between mb-5 lg:mb-0">
{/* heading */}
<div className="flex gap-8">
<h1 className="text-base font-medium text-white tracking-wide">
Welcome
</h1>
{/* user */}
<div className="flex flex-col gap-1">
<p className="text-base tracking-wide font-bold antise text-white leading-[1]">
{`${firstname} ${lastname}`}
</p>
<p className="text-sm tracking-wide text-white leading-[1]">
@{userEmail}
</p>
</div>
</div>
<span className="text-base font-thin tracking-wide text-white flex items-end">
Last Login : {loginDate}
</span>
</div>
</div>
);
}
@@ -1,46 +0,0 @@
import React from "react";
// import HomeSliders from "./HomeSliders";
import { useSelector } from "react-redux";
export default function HomeDashboard({ className, bannerList, nextDueTask }) {
const { userDetails } = useSelector((state) => state?.userDetails);
let loginDate = userDetails?.last_login.split(" ")[0];
let { firstname, lastname, email } = userDetails;
let userEmail = email.split("@")[0];
return (
<div
className={`w-full md:h-[47px] xxs:h-[74px] flex px-[1.05rem] py-[0.35rem] flex-wrap justify-between items-center gap-2 rounded-2xl overflow-hidden bg-sky-blue ${
className || ""
}`}
style={{
// background: `url(${heroBg})`,
backgroundRepeat: "no-repeat",
backgroundSize: "cover",
}}
>
<div className="h-full flex flex-wrap w-full justify-between mb-5 lg:mb-0">
{/* heading */}
<div className="flex gap-8">
<h1 className="text-base font-medium text-white tracking-wide">
Welcome
</h1>
{/* user */}
<div className="flex flex-col gap-1">
<p className="text-base tracking-wide font-bold antise text-white leading-[1]">
{`${firstname} ${lastname}`}
</p>
<p className="text-sm tracking-wide text-white leading-[1]">
@{userEmail}
</p>
</div>
</div>
<span className="text-base font-thin tracking-wide text-white flex items-end">
Last Login : {loginDate}
</span>
</div>
</div>
);
}
@@ -1,50 +0,0 @@
import React from "react";
// import HomeSliders from "./HomeSliders";
import { useSelector } from "react-redux";
export default function JobOwnerDashboard({
className,
bannerList,
nextDueTask,
}) {
const { userDetails } = useSelector((state) => state?.userDetails);
let loginDate = userDetails?.last_login.split(" ")[0];
let { firstname, lastname, email } = userDetails;
let userEmail = email.split("@")[0];
return (
<div
className={`w-full md:h-[47px] xxs:h-[74px] flex px-[1.05rem] py-[0.35rem] flex-wrap justify-between items-center gap-2 rounded-2xl overflow-hidden bg-sky-blue ${
className || ""
}`}
style={{
// background: `url(${heroBg})`,
backgroundRepeat: "no-repeat",
backgroundSize: "cover",
}}
>
<div className="h-full flex flex-wrap w-full justify-between mb-5 lg:mb-0">
{/* heading */}
<div className="flex gap-8">
<h1 className="text-base font-medium text-white tracking-wide">
Welcome
</h1>
{/* user */}
<div className="flex flex-col gap-1">
<p className="text-base tracking-wide font-bold antise text-white leading-[1]">
{`${firstname} ${lastname}`}
</p>
<p className="text-sm tracking-wide text-white leading-[1]">
@{userEmail}
</p>
</div>
</div>
<span className="text-base font-thin tracking-wide text-white flex items-end">
Last Login : {loginDate}
</span>
</div>
</div>
);
}
-110
View File
@@ -1,110 +0,0 @@
import React from "react";
import { Link } from "react-router-dom";
import heroUser from "../../assets/images/hero-user.png";
import CountDown from "../Helpers/CountDown";
// import HomeSliders from "./HomeSliders";
import { useSelector } from "react-redux";
import HomeSliders from "../Home/HomeSliders";
export default function WorkerDashboard({
className,
bannerList,
nextDueTask,
}) {
const settings = {
autoplay: true,
dots: true,
arrows: false,
infinite: true,
swipe: true,
};
const sildeData = null;
const { userDetails } = useSelector((state) => state?.userDetails);
let loginDate = userDetails?.last_login.split(" ")[0];
let { firstname, lastname, email, profile_pic } = userDetails;
let userEmail = email.split("@")[0];
return (
<div
className={`w-full min-h-[400px] md:grid grid-cols-2 lg:p-8 p-4 justify-between items-center gap-2 rounded-2xl overflow-hidden bg-blue-900 ${
className || ""
}`}
style={{
// background: `url(${heroBg})`,
backgroundRepeat: "no-repeat",
backgroundSize: "cover",
}}
>
<div className="h-full flex flex-col justify-between mb-5 lg:mb-0">
{/* heading */}
<div>
<h1 className="lg:text-2xl text-xl font-medium text-white tracking-wide">
Welcome
</h1>
<span className="text-[18px] font-thin tracking-wide text-white">
Last Login : {loginDate}
</span>
</div>
{/* user */}
<div className="flex items-center space-x-3">
<div className="w-14 h-14 flex justify-center items-center rounded-full overflow-hidden">
<img src={profile_pic != "" ? profile_pic : heroUser} alt="" />
</div>
<div>
<p className="text-xl tracking-wide font-bold antise text-white">
{`${firstname} ${lastname}`}
</p>
<p className="text-sm tracking-wide text-white">@{userEmail}</p>
</div>
</div>
{/* countdown */}
{nextDueTask?.next_due &&
Object.keys(nextDueTask.next_due)?.length != 0 && (
<div className="w-full h-32 flex justify-evenly items-center sm:p-6 p-1 rounded-2xl border back-dark1 border-white-opacity">
<div className="flex flex-col justify-between">
<p className="text-base text-white tracking-wide">
Current Task
</p>
<p className="lg:text-2xl text-lg font-bold tracking-wide text-white">
{nextDueTask.next_due.item_code.substr(0, 4) + "..."}
</p>
<p className="text-base text-white tracking-wide">
{nextDueTask.next_due.price * 0.01} Naira
</p>
</div>
<div className="w-[1px] h-full bg-white-opacity"></div>
<div className="flex flex-col justify-between">
<p className="text-base text-white tracking-wide">
Next due in
</p>
<p className="lg:text-2xl text-lg font-bold tracking-wide text-white">
{/* <CountDown lastDate="2023-04-26 4:00:00" /> */}
<CountDown lastDate={nextDueTask.next_due.due_date} />
</p>
<div className="text-base text-white tracking-wide flex gap-[23px]">
<span>Hrs</span>
<span>Min</span>
<span>Sec</span>
</div>
</div>
</div>
)}
{/* action */}
<div className="flex lg:space-x-3 space-x-1 items-center">
<Link to="/mytask" className="text-white text-base">
<span className=" border-b dark:border-[#5356fb29] border-white">
{" "}
View All Task(s)
</span>
</Link>
</div>
</div>
<HomeSliders
settings={settings}
sideData={sildeData}
bannerList={bannerList}
/>
</div>
);
}
@@ -1,49 +0,0 @@
import React from "react";
// import HomeSliders from "./HomeSliders";
import { useSelector } from "react-redux";
export default function WorkerDashboard({
className,
bannerList,
nextDueTask,
}) {
const { userDetails } = useSelector((state) => state?.userDetails);
let loginDate = userDetails?.last_login.split(" ")[0];
let { firstname, lastname, email } = userDetails;
let userEmail = email.split("@")[0];
return (
<div
className={`w-full md:h-[47px] xxs:h-[74px] flex px-[1.05rem] py-[0.35rem] flex-wrap justify-between items-center gap-2 rounded-2xl overflow-hidden bg-sky-blue ${
className || ""
}`}
style={{
// background: `url(${heroBg})`,
backgroundRepeat: "no-repeat",
backgroundSize: "cover",
}}
>
<div className="h-full flex flex-wrap w-full justify-between mb-5 lg:mb-0">
{/* heading */}
<div className="flex gap-8">
<h1 className="text-base font-medium text-white tracking-wide">
Welcome
</h1>
{/* user */}
<div className="flex flex-col gap-1">
<p className="text-base tracking-wide font-bold antise text-white leading-[1]">
{`${firstname} ${lastname}`}
</p>
<p className="text-sm tracking-wide text-white leading-[1]">
@{userEmail}
</p>
</div>
</div>
<span className="text-base font-thin tracking-wide text-white flex items-end">
Last Login : {loginDate}
</span>
</div>
</div>
);
}
-13
View File
@@ -1,13 +0,0 @@
import FamilyParentDashboard from "./FamilyParentDashboard";
import HomeDashboard from "./HomeDashboard";
import JobOwnerDashboard from "./JobOwnerDashboard";
import WorkerDashboard from "./WorkerDashboard";
import AccountDashboard from "./AccountDashboard";
export {
FamilyParentDashboard,
HomeDashboard,
JobOwnerDashboard,
WorkerDashboard,
AccountDashboard
};
@@ -1,84 +0,0 @@
import React, { Suspense } from "react";
import { Link } from "react-router-dom";
import CustomBreadcrumb from "../Breadcrumb/CustomBreadcrumb";
import Layout from "../Partials/Layout";
import LoadingSpinner from "../Spinners/LoadingSpinner";
import FamilyTableNew from "./FamilyTableNew";
export default function FamilyActivities() {
return (
<Layout>
{/*<CommonHead />*/}
<div className="notification-page w-full mb-10">
<div className="notification-wrapper w-full">
{/* heading */}
<div className="sm:flex justify-between items-center mb-6">
<div className="mb-5 sm:mb-0">
<CustomBreadcrumb
title={"Tasks & Chores"}
breadcrumb={[
{ link: "/", title: "Home" },
{
link: "/acc-family",
title: "Family Account",
},
{
link: "/acc-family/activities",
title: "Tasks & Chores",
active: true,
},
]}
/>
</div>
<Link
className="item-content relative text-[18px] transition-all duration-300 ease-in-out bg-[#76a5df] text-white font-medium dark:text-white h-12 px-2 flex items-center gap-2 rounded-md shadow-sm justify-center cursor-pointer dark:bg-[linear-gradient(134.38deg,#f539f8_0%,#c342f9_43.55%,#5356fb_104.51%)]"
to="/acc-family"
>
<svg
xmlns="http://www.w3.org/2000/svg"
fill="none"
viewBox="0 0 24 24"
strokeWidth={1.5}
stroke="currentColor"
className="w-4 h-4"
>
<path
strokeLinecap="round"
strokeLinejoin="round"
d="M10.5 19.5 3 12m0 0 7.5-7.5M3 12h18"
/>
</svg>
<span>Family</span>
</Link>
</div>
<Suspense fallback={<LoadingSpinner color="sky-blue" size="16" />}>
<FamilyTableNew />
</Suspense>
</div>
</div>
</Layout>
);
}
const CloseIcon = () => (
<svg
width="36"
height="36"
viewBox="0 0 36 36"
fill="none"
className="fill-current"
xmlns="http://www.w3.org/2000/svg"
>
<path
d="M36 16.16C36 17.4399 36 18.7199 36 20.0001C35.7911 20.0709 35.8636 20.2554 35.8385 20.4001C34.5321 27.9453 30.246 32.9248 22.9603 35.2822C21.9006 35.6251 20.7753 35.7657 19.6802 35.9997C18.4003 35.9997 17.1204 35.9997 15.8401 35.9997C15.5896 35.7086 15.2189 35.7732 14.9034 35.7093C7.77231 34.2621 3.08728 30.0725 0.769671 23.187C0.435002 22.1926 0.445997 21.1199 0 20.1599C0 18.7198 0 17.2798 0 15.8398C0.291376 15.6195 0.214408 15.2656 0.270759 14.9808C1.71321 7.69774 6.02611 2.99691 13.0428 0.700951C14.0118 0.383805 15.0509 0.386897 15.9999 0C17.2265 0 18.4532 0 19.6799 0C19.7156 0.124041 19.8125 0.136067 19.9225 0.146719C27.3 0.868973 33.5322 6.21922 35.3801 13.427C35.6121 14.3313 35.7945 15.2484 36 16.16ZM33.011 18.0787C33.0433 9.77105 26.3423 3.00309 18.077 2.9945C9.78479 2.98626 3.00344 9.658 2.98523 17.8426C2.96667 26.1633 9.58859 32.9601 17.7602 33.0079C26.197 33.0577 32.9787 26.4186 33.011 18.0787Z"
fill=""
fillOpacity="0.6"
/>
<path
d="M15.9309 18.023C13.9329 16.037 12.007 14.1207 10.0787 12.2072C9.60071 11.733 9.26398 11.2162 9.51996 10.506C9.945 9.32677 11.1954 9.0811 12.1437 10.0174C13.9067 11.7585 15.6766 13.494 17.385 15.2879C17.9108 15.8401 18.1633 15.7487 18.6375 15.258C20.3586 13.4761 22.1199 11.7327 23.8822 9.99096C24.8175 9.06632 26.1095 9.33639 26.4967 10.517C26.7286 11.2241 26.3919 11.7413 25.9133 12.2178C24.1757 13.9472 22.4477 15.6855 20.7104 17.4148C20.5228 17.6018 20.2964 17.7495 20.0466 17.9485C22.0831 19.974 24.0372 21.8992 25.9689 23.8468C26.9262 24.8119 26.6489 26.1101 25.4336 26.4987C24.712 26.7292 24.2131 26.3441 23.7455 25.8757C21.9945 24.1227 20.2232 22.3892 18.5045 20.6049C18.0698 20.1534 17.8716 20.2269 17.4802 20.6282C15.732 22.4215 13.9493 24.1807 12.1777 25.951C11.7022 26.4262 11.193 26.7471 10.4738 26.4537C9.31345 25.9798 9.06881 24.8398 9.98589 23.8952C11.285 22.5576 12.6138 21.2484 13.9387 19.9355C14.5792 19.3005 15.2399 18.6852 15.9309 18.023Z"
fill="#"
fillOpacity="0.6"
/>
</svg>
);
+15 -54
View File
@@ -13,7 +13,6 @@ import usersService from "../../services/UsersService";
import LoadingSpinner from "../Spinners/LoadingSpinner";
import AssignTaskPopout from "./FamilyPopout/AssignTaskPopout";
import FamilyWallet from "./Tabs/FamilyWallet";
import { apiConst } from "../../lib/apiConst";
// Lazy Imports for components
const FamilyWaitlist = lazy(() => import("./Tabs/FamilyWaitlist"));
@@ -34,7 +33,7 @@ export default function FamilyManageTabs({
loading: false,
data: null,
};
// console.log('accountDetails',accountDetails)
// State for family details, tasks, waitlist, and pending
const [details, setDetails] = useState({
familyDetails: { ...initialDetailState },
@@ -67,10 +66,8 @@ export default function FamilyManageTabs({
// State for family task popout
const [familyTaskPopout, setFamilyTaskPopout] = useState(false);
let [uploadStatus, setUploadStatus] = useState({loading: false, status: false, message:''}) // HOLDS STATE FOR UPLOAD PROFILE PICTURE STATUS
// State for profile image
const [profileImg, setProfileImg] = useState(accountDetails.image ? accountDetails.image : profile);
const [profileImg, setProfileImg] = useState(profile);
// Ref for profile image input
const profileImgInput = useRef(null);
@@ -93,59 +90,24 @@ export default function FamilyManageTabs({
* Checks if the selected file exceeds the maximum file size limit and displays an alert if it does.
* If the file is within the size limit, it reads the file using the FileReader API and sets the profile image state with the result.
*/
const profileImgChangeHandler = (e) => {
setUploadStatus({loading: false, status: false, message:''})
let acceptedFormat = ["jpeg", "jpg", "png", "bmp", "gif"] // ARRAY OF SUPPORTED FORMATS
let uploadedFile = e.target.files[0] //UPLOADED FILE
const MAX_FILE_SIZE = 5 * 1024 * 1024; // 5MB
const fileFormat = uploadedFile?.type?.split("/")[1]?.toLowerCase();
if(!acceptedFormat.includes(fileFormat)){ //CHECKING FOR CORRECT UPLOAD FORMAT
const msg = `Please select ${acceptedFormat.slice(0, -1).join(', ')} or ${acceptedFormat.slice(-1)}`;
setUploadStatus({loading: false, status: false, message:msg})
return setTimeout(()=>{
profileImgInput.current.value = '' // clear the input
setUploadStatus({loading: false, status: false, message:''})
},5000)
const file = e.target.files[0];
if (file && file.size > MAX_FILE_SIZE) {
alert("File size exceeds the limit.");
return;
}
if(uploadedFile.size > 5*1048576){ // CHECKING FOR CORRECT FILE SIZE
setUploadStatus({loading: false, status: false, message:'File must not exceed 5MB'})
return setTimeout(()=>{
profileImgInput.current.value = '' // clear the input
setUploadStatus({loading: false, status: false, message:''})
},5000)
}
if (e.target.value !== "") {
if (file) {
const imgReader = new FileReader();
imgReader.onload = (event) => {
let base64Img = imgReader.result.split(",")[1];
let reqData = { // PAYLOAD FOR API CALL
family_uid: accountDetails?.family_uid,
file_name: uploadedFile?.name,
file_size: uploadedFile?.size,
file_type: uploadedFile?.type?.split("/")[0]?.toLowerCase(),
file_data: base64Img,
msg_type: 'FILE',
action: apiConst.WRENCHBOARD_PICTURE_FAMMEMBER
}
setUploadStatus({loading: true, status: false, message:'Loading...'})
apiCall.sendFiles(reqData).then(res=>{
if(res.status != 200 || res.data.internal_return < 0){
return setUploadStatus({loading: false, status: false, message: 'Something went wrong, try again'})
}
setUploadStatus({loading: false, status: true, message: 'Uploaded successfully'})
setProfileImg(event.target.result);
}).catch(error=>{
setUploadStatus({loading: false, status: false, message: 'Network error, try again'})
}).finally(()=>{
setTimeout(()=>{
setUploadStatus({loading: false, status: false, message: ''})
},5000)
})
imgReader.onload = () => {
const imageDataUrl = imgReader.result;
// Set the profile image
setProfileImg(imageDataUrl);
};
imgReader.readAsDataURL(e.target.files[0]);
imgReader.readAsDataURL(file);
}
};
@@ -286,7 +248,7 @@ export default function FamilyManageTabs({
limit: 30,
offset: 0,
job_type: "FAMILY",
action: apiConst.WRENCHBOARD_PICTURE_FAMMEMBER,
action: 13005,
};
if (checkFamilyTask) {
@@ -336,7 +298,6 @@ export default function FamilyManageTabs({
profileImgChangeHandler={profileImgChangeHandler}
browseProfileImg={browseProfileImg}
accountDetails={accountDetails}
uploadStatus={uploadStatus}
/>
<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
+1 -13
View File
@@ -4,7 +4,6 @@ import SearchCom from "../Helpers/SearchCom";
import FamilyMarketCard from "../Cards/FamilyMarketCard";
import usersService from "../../services/UsersService";
import SuggestTask from "../FamilyPopup/SuggestTask";
import CustomBreadcrumb from "../Breadcrumb/CustomBreadcrumb";
export default function FamilyMarket() {
const [popUp, setPopUp] = useState(false);
@@ -54,23 +53,12 @@ export default function FamilyMarket() {
<div className="notification-page w-full mb-10">
<div className="notification-wrapper w-full">
{/* heading */}
{/* <div className="sm:flex justify-between items-center mb-6">
<div className="sm:flex justify-between items-center mb-6">
<div className="mb-5 sm:mb-0">
<h1 className="text-26 font-bold inline-flex gap-3 text-dark-gray dark:text-white items-center">
<span>Suggest Task to the Parents</span>
</h1>
</div>
</div> */}
<div className="mb-5">
<CustomBreadcrumb
title = {'Suggest Task to the Parents'}
breadcrumb={
[
{ link: "/", title: "Home" },
{ link: "/familymarket", title: "Family Market", active: true},
]
}
/>
</div>
{/* Body */}
<div className="filter-section w-full items-center sm:flex justify-between mb-6">
@@ -1,60 +0,0 @@
import React, { useEffect, useState } from 'react'
import Layout from '../Partials/Layout'
import MyOffersFamilyTable from '../MyTasks/MyOffersFamilyTable'
import LoadingSpinner from '../Spinners/LoadingSpinner';
import usersService from '../../services/UsersService';
import CustomBreadcrumb from '../Breadcrumb/CustomBreadcrumb';
export default function FamilyPendingOffer() {
const userApi = new usersService();
const [myOffersList, setMyOffersList] = useState({loading: true, data: []});
const getMyOffersList = async () => {
try {
const res = await userApi.getOffersList();
setMyOffersList({loading:false, data:res.data});
console.log('SAME', res.data)
} catch (error) {
setMyOffersList({loading:false, data:[]});
console.log("Error getting offers", error);
}
};
useEffect(()=>{
getMyOffersList()
},[])
return (
<Layout>
{myOffersList.loading ?
<div className='w-full flex justify-center items-center rounded-2xl bg-white'>
<LoadingSpinner size='10' color='sky-blue' height='h-[20rem]' />
</div>
:
myOffersList?.data?.result_list && myOffersList?.data?.result_list.length > 0 ?
<MyOffersFamilyTable
familyOffers={myOffersList?.data?.result_list}
image_server={myOffersList?.data?.session_image_server}
className="mb-10"
/>
:
<>
<div className="mb-6">
<CustomBreadcrumb
title = {'Ready to Start'}
breadcrumb={
[
{ link: "/", title: "Home" },
{ link: "/pending", title: "Pending", active: true},
]
}
/>
</div>
<div className='w-full h-[30rem] bg-white dark:bg-dark-white flex justify-center items-center rounded-2xl'>
<p className='text-black dark:text-white'>No Record Found!</p>
</div>
</>
}
</Layout>
)
}
@@ -1,8 +1,5 @@
import React, { useState } from "react";
import { useDispatch } from "react-redux";
import { useLocation } from "react-router-dom";
import usersService from "../../../services/UsersService";
import { tableReload } from "../../../store/TableReloads";
import ModalCom from "../../Helpers/ModalCom";
import { PriceFormatter } from "../../Helpers/PriceFormatter";
import LoadingSpinner from "../../Spinners/LoadingSpinner";
@@ -18,27 +15,9 @@ const AssignTaskPopout = React.memo(
familyTask,
activeTask,
setActiveTask,
setUpdatePage,
assignTaskChecker,
setUpdatePage
}) => {
const apiCall = new usersService();
let { pathname, state } = useLocation();
const [selectedFamilyUid, setSelectedFamilyUid] = useState(null);
const handleFamChange = (event) => {
setSelectedFamilyUid(event.target.value);
};
const dispatch = useDispatch();
const getFamilySession = JSON.parse(sessionStorage.getItem("family_list"));
const familyList = getFamilySession?.map((member) => (
<option key={member?.family_uid} value={member?.family_uid}>
{member?.firstname} {member?.lastname}
</option>
));
let [requestStatus, setRequestStatus] = useState({
loading: false,
@@ -90,9 +69,7 @@ const AssignTaskPopout = React.memo(
// API PAYLOADS
job_id: activeTask.data?.job_id,
job_uid: activeTask.data?.job_uid,
family_uid: selectedFamilyUid
? selectedFamilyUid
: familyDetailsData?.uid || details?.family_uid,
family_uid: familyDetailsData?.uid || details?.family_uid,
job_description: activeTask.data?.description,
assign_mode: 110011,
};
@@ -113,11 +90,11 @@ const AssignTaskPopout = React.memo(
const requiredFields = {
banner,
// category,
currency: country,
country,
description,
"job detail": job_detail,
job_detail,
price,
timeline: timeline_days,
timeline_days,
title,
};
@@ -127,9 +104,7 @@ const AssignTaskPopout = React.memo(
setRequestStatus({
loading: false,
status: false,
message: `${
field[0].toUpperCase() + field.slice(1).toLowerCase()
} is empty`,
message: `${field} is empty`,
});
return setTimeout(() => {
setRequestStatus({ loading: false, status: false, message: "" });
@@ -169,8 +144,7 @@ const AssignTaskPopout = React.memo(
status: true,
message: "action successful",
});
setUpdatePage((prev) => !prev); // Updates family task page by calling the useeffect hook
dispatch(tableReload({ type: "WALLETTABLE" })); // RELOADS USER WALLET
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
@@ -188,35 +162,18 @@ const AssignTaskPopout = React.memo(
});
};
console.log(details);
return (
<>
<ModalCom action={action} situation={situation}>
<div className="w-11/12 lg:w-[700px] bg-white dark:bg-dark-white lg:rounded-2xl overflow-y-auto">
<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 flex items-center">
<>
Assign task to{" "}
{pathname === "/acc-family/activities" ? (
<div className="w-[270px] h-[40px] ml-2">
<select
name=""
id=""
className="text-black/70 bg-transparent px-2 transition-all cursor-pointer focus:outline-none border border-gray-200 rounded-lg w-full h-full py-1"
onChange={handleFamChange}
value={selectedFamilyUid}
>
<option value="" disabled selected>
Select a member
</option>
{familyList}
</select>
</div>
) : (
<>{familyDetailsData?.firstname || details?.firstName}</>
)}
</>
<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"
@@ -244,7 +201,7 @@ const AssignTaskPopout = React.memo(
</svg>
</button>
</div>
{familyTask?.loading ? (
{familyTask.loading ? (
<div className="h-[100px] w-full flex justify-center items-center">
<LoadingSpinner color="sky-blue" size="16" />
</div>
@@ -69,7 +69,7 @@ export default function NewTasks({ formState, setFormState }) {
onChange={handleInputChange}
// onBlur={props.handleBlur}
>
{currency?.loading ? (
{currency.loading ? (
<option className="text-slate-500 text-[13.975px]" value="">
Loading...
</option>
@@ -1,9 +0,0 @@
import React from 'react'
const AddFamily = () => {
return (
<div>Add Family</div>
)
}
export default AddFamily
@@ -1,154 +0,0 @@
import React, { useMemo, useRef, useState } from "react";
import usersService from "../../../../services/UsersService";
import { apiConst } from "../../../../lib/apiConst";
const FamilyBanner = ({ imageServer }) => {
const uploadedImage = `${imageServer}${localStorage.getItem(
"session_token"
)}/familybanner/${localStorage.getItem("uid")}`;
const familyBannerRef = useRef(null);
const jobApi = useMemo(() => new usersService(), []);
const [familyBannerImage, setFamilyBannerImage] = useState(uploadedImage);
const [uploadStatus, setUploadStatus] = useState({
loading: false,
status: false,
message: "",
});
/**
* Handles the change event of the family image input field.
* Checks if the selected file exceeds the maximum file size limit and displays an alert if it does.
* If the file is within the size limit, it reads the file using the FileReader API and sets the profile image state with the result.
*/
const familyBannerHandler = (e) => {
setUploadStatus({ loading: false, status: false, message: "" });
let acceptedFormat = ["jpeg", "jpg", "png", "bmp"]; // ARRAY OF SUPPORTED FORMATS
let uploadedFile = e.target.files[0]; //UPLOADED FILE
const fileFormat = uploadedFile?.type?.split("/")[1]?.toLowerCase();
if (!acceptedFormat.includes(fileFormat)) {
//CHECKING FOR CORRECT UPLOAD FORMAT
const msg = `Please select ${acceptedFormat
.slice(0, -1)
.join(", ")} or ${acceptedFormat.slice(-1)}`;
setUploadStatus({ loading: false, status: false, message: msg });
return setTimeout(() => {
// profileImgInput.current.value = '' // clear the input
setUploadStatus({ loading: false, status: false, message: "" });
}, 5000);
}
if (uploadedFile.size > 5 * 1048576) {
// CHECKING FOR CORRECT FILE SIZE
setUploadStatus({
loading: false,
status: false,
message: "File must not exceed 5MB",
});
return setTimeout(() => {
// profileImgInput.current.value = '' // clear the input
setUploadStatus({ loading: false, status: false, message: "" });
}, 5000);
}
if (e.target.value !== "") {
const imgReader = new FileReader();
imgReader.onload = (event) => {
let base64Img = imgReader.result.split(",")[1];
let reqData = {
// PAYLOAD FOR API CALL
job_uid: localStorage.getItem("uid"),
file_name: uploadedFile?.name.slice(0, 19),
file_size: uploadedFile?.size,
file_type: uploadedFile?.type?.split("/")[0]?.toLowerCase(),
file_data: base64Img,
msg_type: "FILE",
action: apiConst.WRENCHBOARD_PICTURE_JOB,
};
setUploadStatus({
loading: true,
status: false,
message: "Loading...",
});
jobApi
.sendFiles(reqData)
.then((res) => {
if (res.status != 200 || res.data.internal_return < 0) {
return setUploadStatus({
loading: false,
status: false,
message: "Something went wrong, try again",
});
}
setUploadStatus({
loading: false,
status: true,
message: "Uploaded successfully",
});
setFamilyBannerImage(event.target.result);
})
.catch((error) => {
setUploadStatus({
loading: false,
status: false,
message: "Network error, try again",
});
})
.finally(() => {
setTimeout(() => {
setUploadStatus({ loading: false, status: false, message: "" });
}, 5000);
});
};
imgReader.readAsDataURL(e.target.files[0]);
}
};
return (
<div className="flex flex-col gap-4">
<div className="w-full lg:h-[400px] rounded-2xl">
<img
src={familyBannerImage}
alt="familyBanner"
className="w-full h-full object-cover rounded-2xl"
/>
</div>
<div className="flex justify-between w-full">
<div>
{uploadStatus.message && !uploadStatus.loading && (
<p
className={`text-center ${
uploadStatus.status ? "text-green-500" : "text-red-500"
}`}
>
{uploadStatus.message}
</p>
)}
{uploadStatus.loading && (
<p className="text-center">{uploadStatus.message}</p>
)}
</div>
<div>
<input
ref={familyBannerRef}
className="hidden"
type="file"
accept="image/*"
onChange={familyBannerHandler}
/>
<button
className="btn-gradient w-[153px] h-[46px] rounded-full text-white lg:flex hidden justify-center items-center"
onClick={() => familyBannerRef.current.click()}
>
Change Banner
</button>
</div>
</div>
</div>
);
};
export default FamilyBanner;
@@ -1,275 +0,0 @@
import React, { useState } from 'react'
import ModalCom from '../../../Helpers/ModalCom';
import LoadingSpinner from '../../../Spinners/LoadingSpinner';
import InputCom from '../../../Helpers/Inputs/InputCom/index';
import { Formik, Form } from 'formik';
import * as Yup from "yup";
import usersService from '../../../../services/UsersService';
const validationSchema = Yup.object().shape({
email: Yup.string()
.email("Wrong email format")
.matches(
/^[^0-9][a-zA-Z0-9._%+-]+@[a-zA-Z]+(\.[a-zA-Z]+)+$/,
"Invalid email format"
)
.min(3, "Minimum 3 characters")
.max(50, "Maximum 50 characters")
.required("Email is required"),
firstname: Yup.string()
.min(3, "Minimum 3 characters")
.max(25, "Maximum 25 characters")
.required("Firstname is required"),
lastname: Yup.string()
.min(3, "Minimum 3 characters")
.max(25, "Maximum 25 characters")
.required("Lastname is required"),
family_type: Yup.string()
.min(3, "Minimum 3 characters")
.max(25, "Maximum 25 characters")
.required("Family Type is required"),
});
const initialValues = {
firstname: '',
lastname: '',
family_type: '',
email: ''
};
export default function InviteRelative({action, situation, setReloadRelList, relativeList}) {
const api = new usersService()
let [requestStatus, setRequestStatus] = useState({
loading: false,
status: false,
message: "",
}); // STATE FOR KNOWING WHEN A REQUEST IS MADE TO THE SERVER
const handleInvite = (values) => {
setRequestStatus({loading: true, status: false, message: ""})
api.inviteFamilyRelative(values).then(response => {
let {status, data} = response
if(data?.internal_return < 0){
setRequestStatus({loading: false, status: false, message: "Unable to complete"})
return setTimeout(()=>{
setRequestStatus({ loading: false, status: false, message: ""})
},3000)
}
setRequestStatus({loading: false, status: true, message: "Relative Added"})
//RELOAD RELATIVE LSIT TABLE
setReloadRelList(prev => !prev)
setTimeout(()=>{
action()
},3000)
}).catch(error => {
setRequestStatus({loading: false, status: false, message: "Something went wrong, try again!"})
setTimeout(()=>{
setRequestStatus({ loading: false, status: false, message: ""})
},3000)
})
}
return (
<ModalCom
action={action}
situation={situation}
>
<div className="lg:w-[600px] w-11/12 lg:overflow-hidden lg:rounded-2xl bg-white dark:bg-dark-white dark:text-white">
<div className="logout-modal-header w-full flex items-center justify-between lg:px-10 lg:py-8 px-[30px] py-[23px] border-b border-light-purple dark:border-[#5356fb29] ">
<h1 className="text-26 font-bold text-dark-gray dark:text-white tracking-wide">
Invite Parent/Relative
</h1>
<button
type="button"
className="text-[#374557] dark:text-red-500"
onClick={action}
>
<svg
width="36"
height="36"
viewBox="0 0 36 36"
fill="none"
className="fill-current"
xmlns="http://www.w3.org/2000/svg"
>
<path
d="M36 16.16C36 17.4399 36 18.7199 36 20.0001C35.7911 20.0709 35.8636 20.2554 35.8385 20.4001C34.5321 27.9453 30.246 32.9248 22.9603 35.2822C21.9006 35.6251 20.7753 35.7657 19.6802 35.9997C18.4003 35.9997 17.1204 35.9997 15.8401 35.9997C15.5896 35.7086 15.2189 35.7732 14.9034 35.7093C7.77231 34.2621 3.08728 30.0725 0.769671 23.187C0.435002 22.1926 0.445997 21.1199 0 20.1599C0 18.7198 0 17.2798 0 15.8398C0.291376 15.6195 0.214408 15.2656 0.270759 14.9808C1.71321 7.69774 6.02611 2.99691 13.0428 0.700951C14.0118 0.383805 15.0509 0.386897 15.9999 0C17.2265 0 18.4532 0 19.6799 0C19.7156 0.124041 19.8125 0.136067 19.9225 0.146719C27.3 0.868973 33.5322 6.21922 35.3801 13.427C35.6121 14.3313 35.7945 15.2484 36 16.16ZM33.011 18.0787C33.0433 9.77105 26.3423 3.00309 18.077 2.9945C9.78479 2.98626 3.00344 9.658 2.98523 17.8426C2.96667 26.1633 9.58859 32.9601 17.7602 33.0079C26.197 33.0577 32.9787 26.4186 33.011 18.0787Z"
fill=""
fillOpacity="0.6"
/>
<path
d="M15.9309 18.023C13.9329 16.037 12.007 14.1207 10.0787 12.2072C9.60071 11.733 9.26398 11.2162 9.51996 10.506C9.945 9.32677 11.1954 9.0811 12.1437 10.0174C13.9067 11.7585 15.6766 13.494 17.385 15.2879C17.9108 15.8401 18.1633 15.7487 18.6375 15.258C20.3586 13.4761 22.1199 11.7327 23.8822 9.99096C24.8175 9.06632 26.1095 9.33639 26.4967 10.517C26.7286 11.2241 26.3919 11.7413 25.9133 12.2178C24.1757 13.9472 22.4477 15.6855 20.7104 17.4148C20.5228 17.6018 20.2964 17.7495 20.0466 17.9485C22.0831 19.974 24.0372 21.8992 25.9689 23.8468C26.9262 24.8119 26.6489 26.1101 25.4336 26.4987C24.712 26.7292 24.2131 26.3441 23.7455 25.8757C21.9945 24.1227 20.2232 22.3892 18.5045 20.6049C18.0698 20.1534 17.8716 20.2269 17.4802 20.6282C15.732 22.4215 13.9493 24.1807 12.1777 25.951C11.7022 26.4262 11.193 26.7471 10.4738 26.4537C9.31345 25.9798 9.06881 24.8398 9.98589 23.8952C11.285 22.5576 12.6138 21.2484 13.9387 19.9355C14.5792 19.3005 15.2399 18.6852 15.9309 18.023Z"
fill="#"
fillOpacity="0.6"
/>
</svg>
</button>
</div>
<div className="logout-modal-body w-full flex flex-col items-center px-10 py-8">
<div className="personal-info-tab w-full flex flex-col justify-between">
<Formik
initialValues={initialValues}
validationSchema={validationSchema}
onSubmit={handleInvite}
>
{(props) => {
return (
<Form>
<div className="w-full flex flex-col-reverse sm:flex-row">
<div className="fields w-full">
{/* inputs starts here */}
{/* Email */}
<div className="field w-full mb-6">
<InputCom
fieldClass="px-6"
label="Email"
type="text"
name="email"
placeholder="Email"
value={props.values.email}
inputHandler={props.handleChange}
blurHandler={props.handleBlur}
error={(props.errors.email && props.touched.email) ? props.errors.email : '' }
/>
</div>
{/* first name and last name */}
<div className="md:flex md:space-x-7 mb-6">
<div className="field w-full mb-6 md:mb-0">
<InputCom
fieldClass="px-6"
label="First Name"
type="text"
name="firstname"
placeholder="First Name"
value={props.values.firstname}
inputHandler={props.handleChange}
blurHandler={props.handleBlur}
error={(props.errors.firstname && props.touched.firstname) ? props.errors.firstname : '' }
/>
</div>
<div className="field w-full">
<InputCom
fieldClass="px-6"
label="Last Name"
type="text"
name="lastname"
placeholder="Last Name"
value={props.values.lastname}
inputHandler={props.handleChange}
blurHandler={props.handleBlur}
error={(props.errors.lastname && props.touched.lastname) ? props.errors.lastname : '' }
/>
</div>
</div>
{/* Type */}
<div className="md:flex md:space-x-7 items-end mb-6">
{/* <div className="field w-full mb-6 md:mb-0">
<InputCom
fieldClass="px-6"
label="Type"
type="text"
name="family_type"
placeholder="Family Type"
value={props.values.family_type}
inputHandler={props.handleChange}
blurHandler={props.handleBlur}
error={(props.errors.family_type && props.touched.family_type) ? props.errors.family_type : '' }
/>
</div> */}
<div className="field w-full mb-6 xl:mb-0">
<label
htmlFor="family_type"
className="input-label text-[#181c32] dark:text-white text-[13.975px] leading-[20.9625px] font-semibold flex item-center gap-1"
>
Family Type
{props.errors.family_type && props.touched.family_type && (
<span className="text-[12px] text-red-500">
{props.errors.family_type}
</span>
)}
</label>
<select
id="family_type"
name="family_type"
value={props.values.family_type}
className={`input-field p-2 mt-3 rounded-full placeholder:text-base text-dark-gray dark:text-white w-full h-10 bg-slate-100 dark:bg-[#11131F] focus:ring-0 focus:outline-none border`}
onChange={props.handleChange}
onBlur={props.handleBlur}
>
{relativeList?.loading ? (
<option className="text-slate-500 text-lg" value="">
Loading...
</option>
) : relativeList?.family_types?.length ? (
<>
<option className="text-slate-500 text-lg" value="">
Select Family Type
</option>
{relativeList?.family_types?.map((item, index) => (
<option
key={index}
className="text-slate-500 text-lg"
value={item?.ty}
>
{item?.ty}
</option>
))}
</>
) : (
<option className="text-slate-500 text-lg" value="">
No Options Found! Try Again
</option>
)}
</select>
</div>
<div className="field w-full flex justify-end">
<div className="flex">
{requestStatus.loading ? (
<LoadingSpinner size="8" color="sky-blue" />
) : (
<button
type="submit"
className={`text-white btn-gradient text-lg tracking-wide px-5 py-2 rounded-full ${(requestStatus.status || requestStatus.loading) && 'opacity-50'}`}
disabled={requestStatus.status || requestStatus.loading}
>
Send Message
</button>
)}
</div>
</div>
</div>
</div>
</div>
</Form>
);
}}
</Formik>
</div>
{/* ERROR DISPLAY AND SUBMIT BUTTON */}
{requestStatus.message != "" &&
(!requestStatus.status ? (
<div
className={`w-full relative p-4 my-4 text-[#912741] bg-[#fcd9e2] border-[#fbc6d3] mb-4 rounded-[0.475rem] text-md font-light leading-[19.5px] text-[13px]`}
>
{requestStatus.message}
</div>
) : (
requestStatus.status && (
<div
className={`relative p-4 my-4 text-green-700 bg-slate-200 border-slate-800 mb-4 rounded-[0.475rem] text-md font-light leading-[19.5px] text-[13px]`}
>
{requestStatus.message}
</div>
)
))}
{/* End of error or success display */}
</div>
</div>
</ModalCom>
)
}
@@ -1,73 +0,0 @@
import React, { useState } from 'react'
import { handlePagingFunc } from '../../../Pagination/HandlePagination';
import PaginatedList from '../../../Pagination/PaginatedList';
export default function RelativeTable({relativeList}) {
// Handle Pagination
const [currentPage, setCurrentPage] = useState(0);
const indexOfFirstItem = Number(currentPage);
const indexOfLastItem =Number(indexOfFirstItem) + Number(process.env.REACT_APP_ITEM_PER_PAGE);
const currentRelativeList = relativeList?.slice(indexOfFirstItem, indexOfLastItem);
const handlePagination = (e) => {
handlePagingFunc(e, setCurrentPage);
};
return (
<div className={`w-full overflow-hidden rounded-2xl`}>
<div className="relative w-full overflow-x-auto sm:rounded-lg flex flex-col justify-between min-h-[400px]">
<table className="w-full text-sm text-left text-gray-500 dark:text-gray-400">
<tbody>
<>
{relativeList && relativeList?.length > 0 ? (
currentRelativeList.map((value, index) => (
<tr key={value.uid || index} className="border-b dark:border-[#5356fb29] hover:bg-gray-50">
<td className='p-2'>
<div className="flex flex-col">
<h1 className="font-bold text-xl text-dark-gray dark:text-white">
{value.firstname && value.firstname} {value.lastname && value.lastname}
</h1>
<span className="text-sm text-thin-light-gray">
{value.email && value.email}
</span>
</div>
</td>
<td className='p-2'>
{/* <span>Family Type</span> */}
<span>{value.family_type && value.family_type.toUpperCase()}</span>
</td>
<td className='p-2 text-right'>
{value.status && value.status}
</td>
</tr>
))
) : (
<tr className="font-bold text-xl text-dark-gray dark:text-white whitespace-nowrap">
<td className="p-2">No Members Found</td>
</tr>
)}
</>
</tbody>
</table>
{/* PAGINATION BUTTON */}
<PaginatedList
onClick={handlePagination}
prev={currentPage == 0 ? true : false}
next={
currentPage + Number(process.env.REACT_APP_ITEM_PER_PAGE) >=
relativeList?.length
? true
: false
}
data={relativeList}
start={indexOfFirstItem}
stop={indexOfLastItem}
/>
{/* END OF PAGINATION BUTTON */}
</div>
</div>
);
};
@@ -1,69 +0,0 @@
import React, { useEffect, useState } from 'react'
import RelativeTable from './RelativeTable'
import InviteRelative from './InviteRelative'
import usersService from '../../../../services/UsersService'
import LoadingSpinner from '../../../Spinners/LoadingSpinner'
const Relatives = () => {
const api = new usersService()
const [reloadRelList, setReloadRelList] = useState(false)
const [invitePopout, setInvitePopout] = useState(false)
const [relativeList, setRelativeList] = useState({loading: true, result_list:[], family_types:[]})
const showInviteMemberPopout = () => {
setInvitePopout(true)
}
const getRelativeList = () => {
setRelativeList(prev => ({...prev, loading: true}))
api.getFamilyRelativeList().then(response => {
let {status, data} = response
if(status != 200 || !data){
setRelativeList({loading:false, result_list:[], family_types:[]})
return
}
setRelativeList({loading:false, result_list:data?.result_list, family_types:data?.family_types})
}).catch(error => {
setRelativeList({loading:false, result_list:[], family_types:[]})
})
}
useEffect(()=>{
getRelativeList()
},[reloadRelList])
return (
<>
<div className=''>
<div className='py-4'>
<button onClick={showInviteMemberPopout} className='text-white btn-gradient text-lg tracking-wide px-5 py-2 rounded-full'>Invite</button>
<div className='my-4 border-b-2'></div>
</div>
<div className='min-h-[500px]'>
{relativeList.loading ?
<LoadingSpinner size='8' height='h-full' />
:
<RelativeTable relativeList={relativeList.result_list} />
}
</div>
</div>
{/* INVITE RELATIVE POPOUT */}
{invitePopout &&
<InviteRelative
action={()=>setInvitePopout(false)}
situation={invitePopout}
setReloadRelList={setReloadRelList}
relativeList={relativeList}
/>
}
{/* END OF INVITE RELATIVE POPOUT */}
</>
)
}
export default Relatives
@@ -1,5 +0,0 @@
import AddFamily from "./AddFamily";
import FamilyBanner from "./FamilyBanner";
import Relatives from "./Relatives";
export {AddFamily, FamilyBanner, Relatives}
@@ -1,135 +0,0 @@
import React, { useEffect, useState } from "react";
import { Link, useLocation } from "react-router-dom";
import CustomBreadcrumb from "../../Breadcrumb/CustomBreadcrumb";
import Icons from "../../Helpers/Icons";
import Layout from "../../Partials/Layout";
import { AddFamily, FamilyBanner, Relatives } from "./Tabs";
const FamilySettings = () => {
let { state } = useLocation();
const tabs = [
{
id: 1,
name: "add_family",
title: "Add Family",
iconName: "new-family",
},
{
id: 2,
name: "relatives",
title: "Relatives",
iconName: "people-hover",
},
{
id: 3,
name: "family_banner",
title: "Family Banner",
iconName: "people-hover",
},
];
const [tab, setTab] = useState(() => {
// Retrieve the active tab from local storage or use the default tab
return localStorage.getItem("activeTab") || tabs[0].name;
});
const tabHandler = (value) => {
setTab(value);
};
// Update local storage when the tab changes
useEffect(() => {
localStorage.setItem("activeTab", tab);
}, [tab]);
const tabComponents = {
add_family: <AddFamily />,
relatives: <Relatives />,
family_banner: <FamilyBanner imageServer={state.imageServer} />,
};
const defaultTabComponent = Array(tabComponents)[0].add_family;
const selectedComponent = tabComponents[tab] || defaultTabComponent;
return (
<Layout>
<div className="notification-page w-full mb-10">
<div className="notification-wrapper w-full">
{/* heading */}
<div className="sm:flex justify-between items-center mb-6">
<div className="mb-5 sm:mb-0">
<CustomBreadcrumb
title={"Family Settings"}
breadcrumb={[
{ link: "/", title: "Home" },
{
link: "/acc-family",
title: "Family Account",
},
{
link: "/familysettings",
title: "Family Settings",
active: true,
},
]}
/>
</div>
<Link
className="item-content relative text-[18px] transition-all duration-300 ease-in-out bg-[#76a5df] text-white font-medium dark:text-white h-12 px-2 flex items-center gap-2 rounded-md shadow-sm justify-center cursor-pointer dark:bg-[linear-gradient(134.38deg,#f539f8_0%,#c342f9_43.55%,#5356fb_104.51%)]"
to="/acc-family"
>
<svg
xmlns="http://www.w3.org/2000/svg"
fill="none"
viewBox="0 0 24 24"
strokeWidth={1.5}
stroke="currentColor"
className="w-4 h-4"
>
<path
strokeLinecap="round"
strokeLinejoin="round"
d="M10.5 19.5 3 12m0 0 7.5-7.5M3 12h18"
/>
</svg>
<span>Family</span>
</Link>
</div>
<div className="w-full bg-white dark:bg-dark-white overflow-y-auto rounded-2xl section-shadow h-full ">
<div className="update-table w-full h-full p-4 bg-white dark:bg-dark-white overflow-y-auto rounded-2xl section-shadow min-h-[520px] lg:flex lg:px-10 px-4 justify-between">
<div className="content-tab-items lg:w-[230px] w-full mr-2">
<ul className="overflow-hidden mb-10 lg:mb-0 py-8">
{tabs.map(({ name, id, title, iconName }) => (
<li
onClick={() => tabHandler(name)}
key={id}
className={`flex lg:space-x-4 space-x-2 hover:text-purple transition-all duration-300 ease-in-out items-center cursor-pointer lg:mb-11 mb-2 mr-6 lg:mr-0 float-left lg:float-none overflow-hidden ${
tab === name ? "text-purple" : " text-thin-light-gray"
}`}
>
<div>
<Icons name={iconName} />
</div>
<div>
<p className="text-18 tracking-wide">{title}</p>
</div>
</li>
))}
</ul>
</div>
<div className="w-[1px] bg-[#E3E4FE] dark:bg-[#a7a9b533] mr-10"></div>
<div className="flex-1">
<div className="tab-item">{selectedComponent}</div>
</div>
</div>
</div>
</div>
</div>
</Layout>
);
};
export default FamilySettings;
+10 -72
View File
@@ -19,7 +19,6 @@ export default function FamilyTable({
familyList,
loader,
popUpHandler,
imageServer,
}) {
const navigate = useNavigate();
const [currentPage, setCurrentPage] = useState(0);
@@ -53,51 +52,28 @@ export default function FamilyTable({
task_count,
family_uid,
banner,
enable_traking,
profile_picture,
imageServer,
username,
}) => {
// Check for valid dates
const addedDate = added ? added.split(" ")[0] : "N/A";
const loginDate = last_login ? formatDateString(last_login) : "N/A";
const key = `family-${family_uid}`; // Assign a unique key
const image = localStorage.getItem("session_token")
? `${imageServer}${localStorage.getItem(
"session_token"
)}/family/${family_uid}`
: "";
const trackingStatus =
enable_traking === "0"
? "Stopped"
: enable_traking === "100"
? "Active"
: "";
return (
<tr
className="bg-white dark:bg-dark-white border-b dark:border-[#5356fb29] hover:bg-gray-50"
key={key}
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">
<div className="w-[60px] h-[60px] rounded-full overflow-hidden flex justify-center items-center flex-[0.1]">
<img
// src={profile_picture}
src={
image ||
profile_picture ||
localImgLoad(`images/icons/${banner}`)
}
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-lg text-dark-gray dark:text-white whitespace-nowrap">
{`${firstname} ${lastname}`}{" "}
<span className="ml-1 text-sm">{`[${username}]`}</span>
<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>
@@ -109,23 +85,6 @@ export default function FamilyTable({
</div>
</div>
</td>
<td className="text-center py-4 px-2">
<div className="flex space-x-1 items-center justify-center">
<span
className={`text-base font-medium whitespace-nowrap ${
enable_traking === "0"
? "text-[#FF0000] dark:text-[#FF6666]"
: enable_traking === "100"
? "text-[#00A000] dark:text-[#00FF00]"
: "text-dark-gray dark:text-white"
}`}
>
{trackingStatus}
</span>
</div>
</td>
<td className="text-center py-4 px-2">
<div className="flex space-x-1 items-center justify-center">
<span className="text-base text-dark-gray dark:text-white font-medium whitespace-nowrap">
@@ -133,7 +92,6 @@ export default function FamilyTable({
</span>
</div>
</td>
<td className="text-right py-4 px-2 flex items-center justify-center">
<button
onClick={() =>
@@ -146,25 +104,12 @@ export default function FamilyTable({
task_count,
family_uid,
banner,
image,
})
}
type="button"
className="w-12 h-11 flex justify-center items-center btn-gradient text-base rounded-full text-white"
className="w-20 h-11 flex justify-center items-center btn-gradient text-base rounded-full text-white"
>
<svg
xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 11 20"
id="Arrow"
className="w-[0.7rem]"
>
<path
fillRule="evenodd"
d="M.366 19.708c.405.39 1.06.39 1.464 0l8.563-8.264a1.95 1.95 0 0 0 0-2.827L1.768.292A1.063 1.063 0 0 0 .314.282a.976.976 0 0 0-.011 1.425l7.894 7.617a.975.975 0 0 1 0 1.414L.366 18.295a.974.974 0 0 0 0 1.413"
// fill=""
className="color000000 svgShape fill-[#fff]"
></path>
</svg>
Manage
</button>
</td>
</tr>
@@ -189,20 +134,13 @@ export default function FamilyTable({
<thead className="sticky top-0">
<tr className="text-base text-thin-light-gray whitespace-nowrap border-b dark:border-[#5356fb29] default-border-bottom ">
<th className="py-4">Name</th>
<th className="py-4 text-center">Tracking</th>
<th className="py-4 text-center">No of Tasks</th>
<th className="py-4 text-right"></th>
</tr>
</thead>
<tbody className="h-full">
{currentFamilyList?.map((familyMember, index) => {
return (
<FamilyRow
key={index}
{...familyMember}
imageServer={imageServer}
/>
);
return <FamilyRow key={index} {...familyMember} />;
})}
</tbody>
</table>
@@ -233,7 +171,7 @@ export default function FamilyTable({
<PaginatedList
onClick={handlePagination}
prev={currentPage == 0}
next={currentPage + itemsPerPage >= familyList?.length}
next={currentPage + itemsPerPage >= familyList.length}
data={familyList}
start={indexOfFirstItem}
stop={indexOfLastItem}
-286
View File
@@ -1,286 +0,0 @@
import React, { Suspense, lazy, useEffect, useMemo, useState } from "react";
import { useLocation } from "react-router-dom";
import { apiConst } from "../../lib/apiConst";
import usersService from "../../services/UsersService";
import LoadingSpinner from "../Spinners/LoadingSpinner";
import AssignTaskPopout from "./FamilyPopout/AssignTaskPopout";
// Lazy Imports for components
const FamilyWaitlist = lazy(() => import("./Tabs/FamilyNewWaitlist"));
const FamilyTasks = lazy(() => import("./Tabs/FamilyNewTasks"));
const FamilyPending = lazy(() => import("./Tabs/FamilyNewPending"));
export default function FamilyTableNew() {
let { pathname } = useLocation();
// Initial state for family details
const initialDetailState = {
loading: false,
data: null,
link: "",
};
const [assignTaskChecker, setAssignTaskChecker] = useState(false);
// console.log('accountDetails',accountDetails)
// State for family details, tasks, waitlist, and pending
const [details, setDetails] = useState({
familyDetails: { ...initialDetailState },
familyTasks: { ...initialDetailState },
familyWaitList: { ...initialDetailState },
familyPending: { ...initialDetailState },
});
// Function to reset family details, tasks, waitlist, and pending
const resetDetails = () => {
setDetails({
familyDetails: { ...initialDetailState },
familyTasks: { ...initialDetailState },
familyWaitList: { ...initialDetailState },
familyPending: { ...initialDetailState },
});
};
const [updatePage, setUpdatePage] = useState(false); // State to determine when to update the page
// State for family task data
const [familyTask, setFamilyTask] = useState({ loading: false, data: [] });
// State for active task
const [activeTask, setActiveTask] = useState({ id: 0, data: {} });
// State for error messages
const [errMsg, setErrMsg] = useState("");
// State for family task popout
const [familyTaskPopout, setFamilyTaskPopout] = useState(false);
// Create an instance of the usersService class
const apiCall = useMemo(() => new usersService(), []);
// Function to handle toggling the family task popout
const familyPopUpHandler = () => {
setFamilyTaskPopout((prev) => !prev);
};
// Array of tab names
const tabs = [
{ id: 1, name: "Tasks" },
{ id: 2, name: "Waiting" },
{ id: 3, name: "Pending" },
];
// State for the currently selected tab
const [tab, setTab] = useState(tabs[0].name);
// Function to handle tab changes
const tabHandler = (value) => {
setTab(value);
};
// Object that maps tab names to their corresponding components
const tabComponents = {
Tasks: (
<FamilyTasks
image_link={details.familyTasks.link}
loader={details.familyTasks.loading}
familyData={details.familyTasks.data}
action={familyPopUpHandler}
setAssignTaskChecker={setAssignTaskChecker}
/>
),
Waiting: (
<FamilyWaitlist
image_link={details.familyWaitList.link}
familyData={details.familyWaitList.data}
loader={details.familyWaitList.loading}
/>
),
Pending: (
<FamilyPending
image_link={details.familyPending.link}
familyData={details.familyPending.data}
loader={details.familyPending.loading}
/>
),
};
const defaultTabComponent = tabComponents.Tasks;
// Selected tab component based on the current 'tab'
const selectedTabComponent = tabComponents[tab] || defaultTabComponent;
// Effect to manage family details and related data
useEffect(() => {
const manageFamily = async () => {
try {
resetDetails();
setDetails({
familyTasks: { loading: true },
familyWaitList: { loading: true },
familyPending: { loading: true },
});
// const { family_uid } = accountDetails;
// const reqData = { family_uid };
const [familyTasksData, familyWaitingRes, familyPendingRes] =
await Promise.all([
apiCall.getMyActiveJobList(),
apiCall.ManageFamilyNewWaitlist(),
apiCall.ManageFamilyPending(),
]);
let tasksData = familyTasksData?.data?.result_list;
let _familyWaitData = familyWaitingRes?.data?.result_list;
let familyPendingData = familyPendingRes?.data?.result_list;
// Getting the image server link
let imageServerLink = familyWaitingRes.data?.session_image_server;
// Function to check for errors in data
const checkDataError = (data) => data?.internal_return < 0;
if (
checkDataError(tasksData) ||
checkDataError(_familyWaitData) ||
checkDataError(familyPendingData)
) {
return;
}
setDetails({
familyTasks: {
loading: false,
data: tasksData,
link: imageServerLink,
},
familyWaitList: {
loading: false,
data: _familyWaitData,
link: imageServerLink,
},
familyPending: {
loading: false,
data: familyPendingData,
link: imageServerLink,
},
});
} catch (error) {
resetDetails();
setErrMsg("An error occurred");
throw new Error(error);
}
};
// Invoke the manageFamily function when the component mounts
manageFamily();
}, [updatePage]);
// Effect to manage family tasks
useEffect(() => {
let checkFamilyTask = true;
const reqData = {
limit: 30,
offset: 0,
job_type: "FAMILY",
action: apiConst.WRENCHBOARD_PICTURE_FAMMEMBER,
};
if (checkFamilyTask) {
setFamilyTask({ loading: true });
apiCall
.getMyJobList(reqData)
.then((res) => {
setFamilyTask({ loading: false, data: res?.data?.result_list });
if (res?.data?.result_list?.length) {
setActiveTask((prev) => ({
...prev,
data: res?.data?.result_list[0],
}));
}
})
.catch((err) => {
setFamilyTask({ loading: false, data: [] });
console.log("Error", err);
});
}
// Cleanup function to prevent memory leaks
return () => {
checkFamilyTask = false;
};
}, []);
return (
<div
className={`w-full bg-white dark:bg-dark-white overflow-y-auto rounded-2xl section-shadow h-full`}
>
<div className="relative w-full sm:rounded-lg overflow-x-auto">
<Suspense
fallback={
<div className="h-full min-h-[609px] w-full overflow-hidden flex justify-center items-center">
<LoadingSpinner size="16" color="sky-blue" />
</div>
}
>
<div className="w-full h-full text-sm text-left text-gray-500 dark:text-gray-400 relative grid grid-cols-3 min-h-[575px]">
<div className="col-span-3 h-full w-full">
<div className="flex flex-col w-full">
<div className="w-full pr-8 flex items-center gap-1 border-b border-b-[#FAFAF]">
<ul className="flex gap-2 items-center w-full">
{tabs.map(({ name, id }) => (
<li
onClick={() => tabHandler(name)}
className={`p-4 flex hover:text-purple transition-all ease-in-out items-center cursor-pointer overflow-hidden text-xl relative top-[2px] ${
tab === name
? "text-purple border-r"
: "text-thin-light-gray"
}`}
key={id}
>
<h1>{name}</h1>
</li>
))}
</ul>
<button
type="button"
onClick={familyPopUpHandler}
className="p-1 my-1 w-[100px] flex justify-center items-center btn-gradient text-base rounded-full text-white"
>
Add task
</button>
</div>
<div className="flex-[0.9] h-full">
<div className="h-full relative overflow-y-auto">
<Suspense
fallback={<LoadingSpinner size="16" color="sky-blue" />}
>
{selectedTabComponent}
</Suspense>
</div>
</div>
</div>
</div>
</div>
</Suspense>
</div>
{familyTaskPopout && (
<AssignTaskPopout
action={familyPopUpHandler}
situation={familyTaskPopout}
familyTask={familyTask}
setFamilyTask={setFamilyTask}
setActiveTask={setActiveTask}
activeTask={activeTask}
setUpdatePage={setUpdatePage}
pathname={pathname}
assignTaskChecker={assignTaskChecker}
/>
)}
</div>
);
}
@@ -1,12 +0,0 @@
import React from 'react'
import Layout from '../Partials/Layout'
import ParentWaiting from '../MyOffers/ParentWaiting'
export default function ParentWaitingLayout() {
return (
<Layout>
<ParentWaiting />
</Layout>
)
}
@@ -1,154 +0,0 @@
import { useMemo, useState } from "react";
import { PriceFormatter } from "../../Helpers/PriceFormatter";
import { PaginatedList, handlePagingFunc } from "../../Pagination";
import PendingJobsPopout from "../../jobPopout/PendingJobsPopout";
export default function FamilyPending({ familyData, image_link, loader }) {
let [jobPopout, setJobPopout] = useState({ show: false, data: {} }); // STATE TO HOLD THE VALUE OF THE ALERT DETAILS AND DETERMINE WHEN TO SHOW
let filteredFamilyData = useMemo(
() => familyData?.filter((data) => data.family_uid !== ""),
[familyData]
);
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 currentPendingTasks = filteredFamilyData?.slice(
indexOfFirstItem,
indexOfLastItem
);
const handlePagination = (e) => {
handlePagingFunc(e, setCurrentPage);
};
console.log(image_link);
return (
<div
className={`update-table w-full p-3 bg-white dark:bg-dark-white overflow-hidden rounded-2xl section-shadow lg:min-h-[538px]`}
>
{familyData && (
<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>
{
<>
{currentPendingTasks.length > 0 ? (
currentPendingTasks.map((value, index) => {
let deliveryDate = value?.expire?.split(" ")[0];
let thePrice = PriceFormatter(
value?.price * 0.01,
value?.currency_code,
value?.currency
);
let image = `${image_link}${localStorage.getItem(
"session_token"
)}/job/${value.job_uid}`;
return (
<tr
key={index}
className="bg-white dark:bg-dark-white border-b dark:border-[#5356fb29] hover:bg-gray-50"
>
<td className=" py-4">
<div className="flex space-x-2 items-center w-full">
<div className="w-[60px] h-[60px] p-2 bg-alice-blue rounded-full overflow-hidden flex justify-center items-center">
<img
src={image}
alt="data"
className="w-full h-full rounded-full"
/>
</div>
<div className="flex flex-col flex-[0.9]">
<h1 className="font-bold text-xl text-dark-gray dark:text-white">
{value.title}
</h1>
<div>{value.description}</div>
<span className="text-sm text-thin-light-gray flex items-start gap-1">
Price:{" "}
<span className="text-purple">
{thePrice}
</span>
</span>
<div className="flex items-center gap-4">
<span className="text-sm text-thin-light-gray">
Duration:{" "}
<span className="text-purple">
{" "}
{value.timeline_days} day(s)
</span>
</span>
<span className="text-sm text-thin-light-gray">
Expire:{" "}
<span className="text-purple">
{" "}
{deliveryDate}
</span>
</span>
<span className="text-sm text-thin-light-gray">
Sent to:{" "}
<span className="text-purple">
{" "}
{value.job_to}
</span>
</span>
</div>
</div>
</div>
</td>
<td className="text-right py-4 px-2 flex justify-end items-center">
<button
type="button"
onClick={() => {
setJobPopout({ show: true, data: value });
}}
className="w-20 h-11 flex justify-center items-center btn-gradient text-base rounded-full text-white"
>
View
</button>
</td>
</tr>
);
})
) : (
<tr>
<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>
)}
</>
}
</tbody>
</table>
{/* PAGINATION BUTTON */}
<PaginatedList
onClick={handlePagination}
prev={currentPage == 0}
next={currentPage + itemsPerPage >= filteredFamilyData.length}
data={filteredFamilyData}
start={indexOfFirstItem}
stop={indexOfLastItem}
/>
{/* END OF PAGINATION BUTTON */}
</div>
)}
{/* Active Job Popout */}
{jobPopout.show && (
<PendingJobsPopout
details={jobPopout.data}
onClose={() => {
setJobPopout({ show: false, data: {} });
}}
situation={jobPopout.show}
/>
)}
{/* End of Active Job Popout */}
</div>
);
}
@@ -1,171 +0,0 @@
import React, { useMemo, useState } from "react";
import { useLocation, useNavigate } from "react-router-dom";
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";
export default function FamilyNewTasks({
familyData,
className,
loader,
action,
image_link,
}) {
let navigate = useNavigate();
let { pathname } = useLocation();
// ...
let filteredFamilyData = useMemo(
() => familyData?.filter((data) => data.family_uid !== ""),
[familyData]
);
const [currentPage, setCurrentPage] = useState(0);
const indexOfFirstItem = Number(currentPage);
const indexOfLastItem =
Number(indexOfFirstItem) + Number(process.env.REACT_APP_ITEM_PER_PAGE);
const currentTasks = filteredFamilyData?.slice(
indexOfFirstItem,
indexOfLastItem
);
const handlePagination = (e) => handlePagingFunc(e, setCurrentPage);
return (
<div
className={`update-table w-full bg-white dark:bg-dark-white h-full lg:min-h-[538px] overflow-hidden rounded-2xl section-shadow p-3 ${
familyData?.length <= 0 && "flex items-center justify-center"
}`}
>
{loader ? (
<div className="w-full h-full flex justify-center items-center lg:min-h-[470px]">
<LoadingSpinner size={16} color="sky-blue" />
</div>
) : (
<>
{familyData && (
<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>
{
<>
{familyData?.length <= 0 ? (
<tr>
<td
colSpan="2"
className="text-center py-4 font-bold text-xl text-dark-gray dark:text-white whitespace-nowrap flex items-center justify-center gap-1"
>
You currently have no active tasks{" "}
<span
className="text-blue-400 cursor-pointer hover:underline transition duration-150"
onClick={action}
>
add new
</span>
</td>
</tr>
) : (
currentTasks.map((value, index) => {
// find due date
const dueDate = value?.delivery_date.split(" ")[0];
// the price
let thePrice = PriceFormatter(
value?.price * 0.01,
value?.currency_code,
value?.currency
);
let image = `${image_link}${localStorage.getItem(
"session_token"
)}/job/${value.job_uid}`;
return (
<tr
key={index}
className="bg-white dark:bg-dark-white border-b dark:border-[#5356fb29] hover:bg-gray-50"
>
<td className=" py-4">
<div className="flex space-x-2 items-center w-full">
<div className="w-[60px] h-[60px] p-2 bg-alice-blue rounded-full overflow-hidden flex justify-center items-center">
<img
src={image}
alt="data"
className="w-full h-full rounded-full"
/>
</div>
<div className="flex flex-col flex-[0.9]">
<h1 className="font-bold text-xl text-dark-gray dark:text-white">
{value.title}
</h1>
<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">
{thePrice}
</span>
</span>
<span className="text-sm text-thin-light-gray">
Duration:{" "}
<span className="text-purple">
{" "}
{value.timeline_days} day(s)
</span>
</span>
<span className="text-sm text-thin-light-gray">
Due Date:{" "}
<span className="text-purple">
{" "}
{dueDate}
</span>
</span>
</div>
</div>
</div>
</td>
<td className="text-right py-4 px-2">
<button
type="button"
onClick={() => {
navigate("/manage-active-job", {
state: {
...value,
pathname,
},
});
}}
className="w-20 h-11 flex justify-center items-center btn-gradient text-base rounded-full text-white"
>
<Icons name="right-arrow" />
</button>
</td>
</tr>
);
})
)}
</>
}
</tbody>
</table>
{/* PAGINATION BUTTON */}
<PaginatedList
onClick={handlePagination}
prev={currentPage == 0 ? true : false}
next={
currentPage + Number(process.env.REACT_APP_ITEM_PER_PAGE) >=
filteredFamilyData?.length
? true
: false
}
data={filteredFamilyData}
start={indexOfFirstItem}
stop={indexOfLastItem}
/>
{/* END OF PAGINATION BUTTON */}
</div>
)}
</>
)}
</div>
);
}
@@ -1,152 +0,0 @@
import { useState } from "react";
import SuggestTask from "../../FamilyPopup/SuggestTask";
import { PaginatedList, handlePagingFunc } from "../../Pagination";
import LoadingSpinner from "../../Spinners/LoadingSpinner";
import AssignTaskPopout from "../FamilyPopout/AssignTaskPopout";
import Icons from "../../Helpers/Icons";
const FamilyNewWaitlist = ({
familyData,
className,
accountDetails,
loader,
}) => {
const [popUp, setPopUp] = useState({ show: false, data: {} });
const [continueTaskPopup, setContinueTaskPopup] = useState({
show: false,
data: {},
});
const [currentPage, setCurrentPage] = useState(0);
const itemsPerPage = Number(process.env.REACT_APP_ITEM_PER_PAGE);
const indexOfFirstItem = currentPage;
const indexOfLastItem = currentPage + itemsPerPage;
const currentTask = familyData?.slice(
indexOfFirstItem,
indexOfLastItem
);
const handlePagination = (e) => handlePagingFunc(e, setCurrentPage);
const openPopUp = (value) => {
setPopUp({ show: true, data: { ...value } });
};
const closePopUp = () => {
setPopUp({ show: false, data: {} });
};
const openContinueTaskPopup = (value) => {
setContinueTaskPopup({ show: true, data: { ...value } });
};
const closeContinueTaskPopup = () => {
setContinueTaskPopup({ show: false, data: {} });
};
console.log("Check this >>",continueTaskPopup)
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>
) : (
<>
{familyData && (
<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"
}`);
// console.log("VALUE", value);
// let image = `${familyData.session_image_server}${localStorage.getItem('session_token')}/job/${value.job_uid}`
return (
<tr
className="bg-white dark:bg-dark-white border-b dark:border-[#5356fb29] hover:bg-gray-50"
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="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.firstname}
</p>
</div>
</div>
</td>
<td className="text-right py-4 px-2 flex justify-end">
<button
onClick={() => openPopUp(value)}
className="w-20 h-11 flex justify-center items-center btn-gradient text-base rounded-full text-white"
>
{/* View */}
<Icons name="right-arrow" />
</button>
</td>
</tr>
);
})}
</tbody>
</table>
<PaginatedList
onClick={handlePagination}
prev={currentPage === 0}
next={currentPage + itemsPerPage >= familyData?.length}
data={familyData}
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>
);
};
export default FamilyNewWaitlist;
@@ -54,7 +54,7 @@ export default function FamilyPending({
value?.currency_code,
value?.currency
);
let image = `${familyData.session_image_server}${localStorage.getItem('session_token')}/job/${value.job_uid}`
let image = value.banner ? value.banner : "default.jpg";
return (
<tr
key={index}
@@ -64,7 +64,9 @@ export default function FamilyPending({
<div className="flex space-x-2 items-center w-full">
<div className="w-[60px] h-[60px] p-2 bg-alice-blue rounded-full overflow-hidden flex justify-center items-center">
<img
src={image}
src={localImgLoad(
`images/taskbanners/${image}`
)}
alt="data"
className="w-full h-full rounded-full"
/>
+153 -239
View File
@@ -1,42 +1,35 @@
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 usersService from "../../../services/UsersService";
import { useNavigate } from "react-router-dom";
import { Form, Formik } from "formik";
import { Formik, Form } from "formik";
import * as Yup from "yup";
export default function FamilyProfile({ familyData, className }) {
export default function FamilyProfile({familyData, className }) {
const validationSchema = Yup.object().shape({
firstname: Yup.string().required("Firstname is required"),
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`
),
.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();
const navigate = useNavigate()
const api = new usersService()
let [requestStatus, setRequestStatus] = useState({
loading: false,
status: false,
message: "",
});
let [requestStatus, setRequestStatus] = useState({loading:false, status:false, message: ''})
// let [updateDetails, setUpdateDetails] = useState({
// family_uid: familyData.uid,
@@ -49,20 +42,20 @@ export default function FamilyProfile({ familyData, className }) {
// 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,
family_uid: familyData.uid,
firstname: familyData.firstname,
lastname: familyData.lastname,
year: familyData.year,
month: familyData.month,
action: 22020
};
// const handleChange = ({ target: { name, value } }) => {
// setUpdateDetails((prev) => ({ ...prev, [name]: value }));
// };
const handleChange = ({target:{name, value}})=>{
setUpdateDetails(prev => ({...prev, [name]:value}))
}
const updateFamily = (values, helpers) => {
setRequestStatus({ loading: true, status: false, message: "" });
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'})
@@ -70,38 +63,22 @@ export default function FamilyProfile({ familyData, className }) {
// 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);
});
};
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
@@ -116,163 +93,102 @@ export default function FamilyProfile({ familyData, className }) {
>
{(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]`}
<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 mb-7 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"
>
{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>
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>
</Form>
<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>
{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>
@@ -280,28 +196,26 @@ export default function FamilyProfile({ familyData, className }) {
);
}
// Get the current year
// const currentYear = new Date().getFullYear() - process.env.REACT_APP_FAMILY_MINIMUM_AGE;
// Get the current year
// const currentYear = new Date().getFullYear() - process.env.REACT_APP_FAMILY_MINIMUM_AGE;
const currentYear = new Date().getFullYear();
// 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 the current year to (currentYear - 19)
const years = Array.from({ length: 19 }, (_, index) => currentYear - index);
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"},
];
// 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" },
];
@@ -67,7 +67,9 @@ export default function FamilyTasks({
value?.currency_code,
value?.currency
);
let image = `${familyData.session_image_server}${localStorage.getItem('session_token')}/job/${value.job_uid}`
let image = value.banner
? value.banner
: "default.jpg";
return (
<tr
key={index}
@@ -77,7 +79,9 @@ export default function FamilyTasks({
<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={image}
src={localImgLoad(
`images/taskbanners/${image}`
)}
alt="data"
className="w-full h-full rounded-full"
/>
@@ -67,8 +67,6 @@ const FamilyWaitlist = memo(
const selectedImage = require(`../../../assets/images/family/${
value?.banner || "default.jpg"
}`);
console.log('VALUE', value)
// let image = `${familyData.session_image_server}${localStorage.getItem('session_token')}/job/${value.job_uid}`
return (
<tr
className="bg-white dark:bg-dark-white border-b dark:border-[#5356fb29] hover:bg-gray-50"
+36 -6
View File
@@ -3,24 +3,22 @@ import React, { useEffect, useState } from 'react'
import usersService from '../../../services/UsersService'
import LoadingSpinner from '../../Spinners/LoadingSpinner'
import { PriceFormatter } from '../../Helpers/PriceFormatter'
import { localImgLoad } from '../../../lib'
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'>
@@ -32,7 +30,39 @@ function FamilyWallet({familyData}) {
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 key={index} 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="balance mt-2 flex justify-center">
<div className="flex gap-2">
<p className="text-base md:text-lg text-thin-light-gray tracking-wide mb-2 sm:mb-6">
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>
</div>
</div>
</div>
))}
</div>
:
@@ -7,7 +7,6 @@ export default function ProfileInfo({
profileImgChangeHandler,
browseProfileImg,
accountDetails,
uploadStatus
}) {
// console.log(accountDetails.banner)
return (
@@ -53,11 +52,6 @@ export default function ProfileInfo({
</div>
</div>
</div>
{/* DISPLAYS PROFILE UPLOADING STATUS */}
<div className="w-full">
{uploadStatus.message && !uploadStatus.loading && <p className={`text-center ${uploadStatus.status ? 'text-green-500':'text-red-500'}`}>{uploadStatus.message}</p>}
{uploadStatus.loading && <p className="text-center">{uploadStatus.message}</p>}
</div>
<div className="flex flex-col justify-center gap-3 items-center">
<h1 className="font-bold text-xl tracking-wide line-clamp-1 text-dark-gray dark:text-white capitalize">
{accountDetails?.firstname}
@@ -1,358 +0,0 @@
import React, { useEffect, useState } from "react";
import ModalCom from "../../../Helpers/ModalCom";
import InputCom from "../../../Helpers/Inputs/InputCom";
import { Form, Formik } from "formik";
import * as Yup from "yup";
import { AmountTo2DP } from "../../../Helpers/PriceFormatter";
import usersService from "../../../../services/UsersService";
import LoadingSpinner from "../../../Spinners/LoadingSpinner";
import { PriceFormatter } from "../../../Helpers/PriceFormatter";
import { tableReload } from "../../../../store/TableReloads";
import { useDispatch } from "react-redux";
import { apiConst } from "../../../../lib/apiConst";
const validationSchema = Yup.object().shape({
// amount: Yup.string()
// .typeError("Invalid number")
// .min(1, "Price must be greater than 0")
// .test("no-e", "Invalid number", (value) => {
// if (value && /\d+e/.test(value)) {
// return false;
// }
// return true;
// })
// .required("Amount is required"),
amount: Yup.number("Please enter a number")
.min(1, "Price must be greater than 0")
.required("Amount is required"),
comment: Yup.string().required("Comment is required"),
});
function FamilyAddFundPopout({ action, situation, wallet, familyData }) {
const dispatch = useDispatch();
const apiUrl = new usersService();
const [startTransfer, setStartTransfer] = useState({
loading: true,
data: {},
});
const [requestStatus, setRequestStatus] = useState({
loading: false,
status: false,
message: "",
});
// initial values for formik
let initialValues = {
amount: "",
from: AmountTo2DP(startTransfer?.data?.origing_current_balance * 0.01),
to: `${familyData.firstname} ${familyData.lastname}`,
comment: "",
};
// FUNCTION TO PERFORM FAMILY TRANSFER
const handleAddFund = (values) => {
setRequestStatus({ loading: true, status: false, message: "" });
let senderBal = startTransfer?.data?.origing_current_balance || ""; // SENDER'S ACCOUNT BALANCE
let senderLimit = startTransfer?.data?.origing_transfer_limit || ""; // SENDER'S TRANSFER LIMIT
let reqData = {
// API REQUEST DATA
family_uid: familyData.uid,
wallet_uid: wallet.wallet_uid,
origing_wallet_uid: startTransfer?.data?.origing_wallet_uid,
currency: startTransfer?.data?.currency,
amount: values.amount * 100,
description: values.comment,
family_transfer_mode: 100,
action: apiConst.WRENCHBOARD_FAMILY_TRANSFER,
};
if (!senderBal || !senderLimit) {
// RETURNS UNAUTHORIZED, IF SENDER BAL OR LIMIT IS NOT AVAILABLE
setRequestStatus({
loading: false,
status: false,
message: "Unauthorized, try again later",
});
return setTimeout(() => {
setRequestStatus({ loading: false, status: false, message: "" });
}, 5000);
}
if (values.amount > senderBal * 0.01) {
// CHECKS TO SEE IF SENDER IS SENDING MORE THAN HIS BALANCE
setRequestStatus({
loading: false,
status: false,
message: "You cannot send more than your balance",
});
return setTimeout(() => {
setRequestStatus({ loading: false, status: false, message: "" });
}, 5000);
}
if (values.amount > senderLimit * 0.01) {
// CHECKS TO SEE IF SENDER IS SENDING MORE THAN HIS LIMIT
setRequestStatus({
loading: false,
status: false,
message: `You cannot exceed ${senderLimit * 0.01} ${
startTransfer?.data?.origing_currency.charAt(0).toUpperCase() +
startTransfer?.data?.origing_currency.slice(1).toLowerCase()
}`,
});
return setTimeout(() => {
setRequestStatus({ loading: false, status: false, message: "" });
}, 5000);
}
apiUrl
.familyTransfer(reqData)
.then(({ data }) => {
if (
data.internal_return < 0 ||
data.credit_confirm == "" ||
data.pay_confirm == ""
) {
setRequestStatus({
loading: false,
status: false,
message: "Transfer Failed",
});
return setTimeout(() => {
setRequestStatus({ loading: false, status: false, message: "" });
}, 5000);
}
setRequestStatus({
loading: false,
status: true,
message: "Transfer Successful",
});
setTimeout(() => {
setRequestStatus({ loading: false, status: false, message: "" });
dispatch(tableReload({ type: "WALLETTABLE" })); // UPDATES PARENT WALLET ACCOUNT
action(); // TO CLOSE THE MODAL
}, 5000);
})
.catch((error) => {
setRequestStatus({
loading: false,
status: false,
message: "Network Error, try again",
});
setTimeout(() => {
setRequestStatus({ loading: false, status: false, message: "" });
}, 5000);
});
};
// LOAD FAMILY START TRANSFER
useEffect(() => {
let reqData = {
family_uid: familyData.uid,
wallet_uid: wallet.wallet_uid,
action: apiConst.WRENCHBOARD_FAMILY_TRANSFERSTART,
};
apiUrl
.familyTransferStart(reqData)
.then((response) => {
setStartTransfer({ loading: false, data: response?.data });
})
.catch((err) => {
setStartTransfer({ loading: false, data: {} });
});
}, []);
return (
<ModalCom action={action} situation={situation}>
<div className="relative logout-modal-wrapper lg:w-[500px] h-full lg:h-auto bg-white dark:bg-dark-white lg:rounded-2xl">
<div className="logout-modal-header w-full flex items-center justify-between lg:px-10 lg:py-8 px-[30px] py-[23px] border-b border-light-purple dark:border-[#5356fb29] ">
<h1 className="text-26 font-bold text-dark-gray dark:text-white tracking-wide">
Add Fund
</h1>
<button
type="button"
className="text-[#374557] dark:text-red-500"
onClick={action}
>
<svg
width="36"
height="36"
viewBox="0 0 36 36"
fill="none"
className="fill-current"
xmlns="http://www.w3.org/2000/svg"
>
<path
d="M36 16.16C36 17.4399 36 18.7199 36 20.0001C35.7911 20.0709 35.8636 20.2554 35.8385 20.4001C34.5321 27.9453 30.246 32.9248 22.9603 35.2822C21.9006 35.6251 20.7753 35.7657 19.6802 35.9997C18.4003 35.9997 17.1204 35.9997 15.8401 35.9997C15.5896 35.7086 15.2189 35.7732 14.9034 35.7093C7.77231 34.2621 3.08728 30.0725 0.769671 23.187C0.435002 22.1926 0.445997 21.1199 0 20.1599C0 18.7198 0 17.2798 0 15.8398C0.291376 15.6195 0.214408 15.2656 0.270759 14.9808C1.71321 7.69774 6.02611 2.99691 13.0428 0.700951C14.0118 0.383805 15.0509 0.386897 15.9999 0C17.2265 0 18.4532 0 19.6799 0C19.7156 0.124041 19.8125 0.136067 19.9225 0.146719C27.3 0.868973 33.5322 6.21922 35.3801 13.427C35.6121 14.3313 35.7945 15.2484 36 16.16ZM33.011 18.0787C33.0433 9.77105 26.3423 3.00309 18.077 2.9945C9.78479 2.98626 3.00344 9.658 2.98523 17.8426C2.96667 26.1633 9.58859 32.9601 17.7602 33.0079C26.197 33.0577 32.9787 26.4186 33.011 18.0787Z"
fill=""
fillOpacity="0.6"
/>
<path
d="M15.9309 18.023C13.9329 16.037 12.007 14.1207 10.0787 12.2072C9.60071 11.733 9.26398 11.2162 9.51996 10.506C9.945 9.32677 11.1954 9.0811 12.1437 10.0174C13.9067 11.7585 15.6766 13.494 17.385 15.2879C17.9108 15.8401 18.1633 15.7487 18.6375 15.258C20.3586 13.4761 22.1199 11.7327 23.8822 9.99096C24.8175 9.06632 26.1095 9.33639 26.4967 10.517C26.7286 11.2241 26.3919 11.7413 25.9133 12.2178C24.1757 13.9472 22.4477 15.6855 20.7104 17.4148C20.5228 17.6018 20.2964 17.7495 20.0466 17.9485C22.0831 19.974 24.0372 21.8992 25.9689 23.8468C26.9262 24.8119 26.6489 26.1101 25.4336 26.4987C24.712 26.7292 24.2131 26.3441 23.7455 25.8757C21.9945 24.1227 20.2232 22.3892 18.5045 20.6049C18.0698 20.1534 17.8716 20.2269 17.4802 20.6282C15.732 22.4215 13.9493 24.1807 12.1777 25.951C11.7022 26.4262 11.193 26.7471 10.4738 26.4537C9.31345 25.9798 9.06881 24.8398 9.98589 23.8952C11.285 22.5576 12.6138 21.2484 13.9387 19.9355C14.5792 19.3005 15.2399 18.6852 15.9309 18.023Z"
fill="#"
fillOpacity="0.6"
/>
</svg>
</button>
</div>
<div className="logout-modal-body w-full flex flex-col items-center px-10 py-8">
{startTransfer.loading && (
<LoadingSpinner size="16" color="sky-blue" height={"h-64"} />
)}
{!startTransfer.loading && (
<Formik
initialValues={initialValues}
validationSchema={validationSchema}
onSubmit={handleAddFund}
>
{(props) => (
<Form className="w-full">
<div className="flex flex-col-reverse sm:flex-row">
<div className="fields w-full">
{/* AMOUNT */}
<div className="field w-full mb-[0.5rem]">
<InputCom
placeholder="0"
label={`Amount (${startTransfer?.data?.currency})`}
name="amount"
type="text"
parentClass="flex items-center gap-1 w-full"
labelClass="flex-[0.3] mb-0"
inputClass={`flex-[0.7] input-curve lg border border-[#dce4e9] ${
props.errors.amount && props.touched.amount
? "border border-red-500"
: ""
}`}
fieldClass="px-2 text-right"
value={props.values.amount}
inputHandler={props.handleChange}
/>
</div>
{/* FROM */}
<div className="field w-full mb-[0.5rem]">
<InputCom
placeholder="From"
label={`From (${startTransfer?.data?.origing_currency})`}
name="from"
type="text"
parentClass="flex items-center gap-1 w-full"
labelClass="flex-[0.3] mb-0"
inputClass={`flex-[0.7] input-curve lg border border-[#dce4e9]`}
fieldClass="px-2 text-right"
value={props.values.from}
disable={true}
/>
</div>
{/* TO */}
<div className="field w-full mb-[0.5rem]">
<InputCom
placeholder="To"
label="To:"
name="to"
type="text"
parentClass="flex items-center gap-1 w-full"
labelClass="flex-[0.3] mb-0"
inputClass={`flex-[0.7] input-curve lg border border-[#dce4e9]`}
fieldClass="px-2 text-right"
value={props.values.to}
disable={true}
/>
</div>
{/* COMMENT */}
<div className="field w-full mb-[0.5rem]">
<div className="w-full">
<label
htmlFor="Job Delivery Details"
className="input-label text-[#181c32] dark:text-white text-[13.975px] leading-[20.9625px] font-semibold flex items-center gap-1"
>
Comment
{/* {props.errors.comment && props.touched.comment && <span className='text-sm text-red-500'>{' '}{props.errors.comment}</span>} */}
</label>
<textarea
// id="Job Delivery Details"
rows="2"
className={`input-field px-3 py-2 placeholder:text-base text-dark-gray dark:text-white w-full bg-slate-100 dark:bg-[#11131F] focus:ring-0 focus:outline-[#dce4e9] rounded-[10px] border ${
props.errors.comment && props.touched.comment
? "border border-red-500"
: ""
}`}
style={{ resize: "none" }}
name="comment"
value={props.values.comment}
onChange={props.handleChange}
/>
</div>
</div>
{/* inputs ends here */}
</div>
</div>
{/* ERROR DISPLAY AND SUBMIT BUTTON */}
<div className="content-footer w-full">
{/* error or success display */}
{requestStatus.message != "" &&
(!requestStatus.status ? (
<div
className={`relative p-4 text-[#912741] bg-[#fcd9e2] border-[#fbc6d3] rounded-[0.475rem] text-md font-light leading-[19.5px] text-[13px]`}
// className={`pb-1 absolute bottom-0 left-1/2 -translate-x-1/2 text-[#912741] rounded-[0.475rem] text-md font-light leading-[19.5px] text-[13px]`}
>
{requestStatus.message}
</div>
) : (
requestStatus.status && (
<div
className={`relative p-4 text-green-700 bg-slate-200 border-slate-800 rounded-[0.475rem] text-md font-light leading-[19.5px] text-[13px]`}
// className={`pb-1 absolute bottom-0 left-1/2 -translate-x-1/2 text-green-700 rounded-[0.475rem] text-md font-light leading-[19.5px] text-[13px]`}
>
{requestStatus.message}
</div>
)
))}
{/* End of error or success display */}
<div className="pt-2 w-full border-t border-light-purple dark:border-[#5356fb29] flex justify-end items-center">
<div className="w-full flex justify-between gap-2 items-center">
<button
type="button"
onClick={action}
className="w-[150px] h-[48px] rounded-full text-base text-white bg-red-600 hover:bg-red-500"
>
Cancel
</button>
<>
{requestStatus.loading ? (
<LoadingSpinner size="6" color="sky-blue" />
) : (
<button
type="submit"
className={`w-[150px] h-[48px] rounded-full text-base text-white bg-sky-500 hover:bg-sky-400 ${
requestStatus.status ? "opacity-50" : ""
}`}
disabled={requestStatus.status}
>
Send
</button>
)}
</>
</div>
</div>
</div>
</Form>
)}
</Formik>
)}
</div>
</div>
</ModalCom>
);
}
export default FamilyAddFundPopout;
@@ -1,77 +0,0 @@
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
+30 -64
View File
@@ -5,21 +5,19 @@ import React, {
useMemo,
useState,
} from "react";
import { Link } from "react-router-dom";
import usersService from "../../services/UsersService";
import Icons from "../Helpers/Icons";
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";
import CustomBreadcrumb from "../Breadcrumb/CustomBreadcrumb";
export default function FamilyAcc() {
const [selectTab, setValue] = useState("today");
// State to store the selected year and month
const [selectedYear, setSelectedYear] = useState("");
const [selectedMonth, setSelectedMonth] = useState("");
const [familyList, setFamilyList] = useState({});
const [familyList, setFamilyList] = useState([]);
const [loader, setLoader] = useState(false);
const [popUp, setPopUp] = useState(false);
const [listReload, setListReload] = useState(false);
@@ -29,12 +27,16 @@ export default function FamilyAcc() {
last_name: "",
});
const apiCall = useMemo(() => new usersService(), []);
const apiCall = useMemo(() => new SiteService(), []);
const popUpHandler = () => {
setPopUp((prev) => !prev);
};
const filterHandler = (value) => {
setValue(value);
};
// Handle year selection
const handleYearChange = (e) => {
setSelectedYear(e.target.value);
@@ -104,12 +106,17 @@ export default function FamilyAcc() {
const memberList = useCallback(async () => {
setLoader(true);
try {
const res = await apiCall.familyListings();
const reqData = {
limit: 20,
offset: 0,
action: 22010,
};
const res = await apiCall.familyListings(reqData);
const { data } = res;
if (data?.internal_return >= 0 && data?.status === "OK") {
const { result_list, session_image_server } = data;
setFamilyList({ result_list, session_image_server });
sessionStorage.setItem("family_list", JSON.stringify(result_list))
const { result_list } = data;
setFamilyList(result_list);
setLoader(false);
} else {
return;
@@ -139,20 +146,12 @@ export default function FamilyAcc() {
<div className="sm:flex justify-between items-center mb-6">
<div className="mb-5 sm:mb-0">
<h1 className="text-26 font-bold inline-flex gap-3 text-dark-gray dark:text-white items-center">
{/* <span className={``}>Family Accounts</span> */}
<div className="mb-5">
<CustomBreadcrumb
title = {'Family Account'}
breadcrumb={
[
{ link: "/", title: "Home" },
{ link: "/acc-family", title: "Family Account", active: true},
]
}
/>
</div>
{familyList?.result_list?.length <
process.env.REACT_APP_MAX_FAMILY_MEMBERS &&
<span
className={`${selectTab === "today" ? "block" : "hidden"}`}
>
Family Accounts
</span>
{familyList.length < process.env.REACT_APP_MAX_FAMILY_MEMBERS &&
!loader && (
<button
onClick={popUpHandler}
@@ -164,51 +163,18 @@ export default function FamilyAcc() {
)}
</h1>
</div>
<div className="flex gap-4 items-center">
<Link state={familyList} to="/acc-family/activities" className={`nav-item flex items-center rounded-md shadow-sm justify-center cursor-pointer dark:bg-[linear-gradient(134.38deg,#f539f8_0%,#c342f9_43.55%,#5356fb_104.51%)] bg-[#76a5df] text-white px-2 gap-2`}>
<span className="item-icon group-hover:bg-purple group-hover:text-white w-8 h-8 flex justify-center items-center transition-all duration-300 ease-in-out bg-light-purple dark:bg-dark-light-purple rounded-full text-dark-gray dark:text-white dark:text-lighter-gray">
<Icons name="pending-job" />
</span>
<span
className={`item-content relative text-[18px] transition-all duration-300 ease-in-out font-medium dark:text-white h-12 flex items-center`}
>
Activities
</span>
</Link>
<Link
to={`/familysettings`}
state={{ imageServer: familyList?.session_image_server }}
className="slider-btns flex space-x-4 w-12 h-12 rounded-md shadow-sm justify-center items-center cursor-pointer dark:bg-[linear-gradient(134.38deg,#f539f8_0%,#c342f9_43.55%,#5356fb_104.51%)] bg-[#76a5df] text-white"
>
<svg
xmlns="http://www.w3.org/2000/svg"
fill="none"
viewBox="0 0 24 24"
strokeWidth={1.5}
stroke="currentColor"
className="w-6 h-6 dark:stroke-white"
>
<path
strokeLinecap="round"
strokeLinejoin="round"
d="M9.594 3.94c.09-.542.56-.94 1.11-.94h2.593c.55 0 1.02.398 1.11.94l.213 1.281c.063.374.313.686.645.87.074.04.147.083.22.127.324.196.72.257 1.075.124l1.217-.456a1.125 1.125 0 011.37.49l1.296 2.247a1.125 1.125 0 01-.26 1.431l-1.003.827c-.293.24-.438.613-.431.992a6.759 6.759 0 010 .255c-.007.378.138.75.43.99l1.005.828c.424.35.534.954.26 1.43l-1.298 2.247a1.125 1.125 0 01-1.369.491l-1.217-.456c-.355-.133-.75-.072-1.076.124a6.57 6.57 0 01-.22.128c-.331.183-.581.495-.644.869l-.213 1.28c-.09.543-.56.941-1.11.941h-2.594c-.55 0-1.02-.398-1.11-.94l-.213-1.281c-.062-.374-.312-.686-.644-.87a6.52 6.52 0 01-.22-.127c-.325-.196-.72-.257-1.076-.124l-1.217.456a1.125 1.125 0 01-1.369-.49l-1.297-2.247a1.125 1.125 0 01.26-1.431l1.004-.827c.292-.24.437-.613.43-.992a6.932 6.932 0 010-.255c.007-.378-.138-.75-.43-.99l-1.004-.828a1.125 1.125 0 01-.26-1.43l1.297-2.247a1.125 1.125 0 011.37-.491l1.216.456c.356.133.751.072 1.076-.124.072-.044.146-.087.22-.128.332-.183.582-.495.644-.869l.214-1.281z"
/>
<path
strokeLinecap="round"
strokeLinejoin="round"
d="M15 12a3 3 0 11-6 0 3 3 0 016 0z"
/>
</svg>
</Link>
<div className="slider-btns flex space-x-4">
<div
onClick={() => filterHandler("today")}
className="relative"
></div>
</div>
</div>
<Suspense fallback={<LoadingSpinner color="sky-blue" size="16" />}>
<FamilyTable
familyList={familyList?.result_list}
familyList={familyList}
loader={loader}
popUpHandler={popUpHandler}
imageServer={familyList?.session_image_server}
/>
</Suspense>
</div>
@@ -247,7 +213,7 @@ const FamilyForm = ({
popUpHandler,
}) => {
return (
<div className="logout-modal-wrapper w-11/12 lg:w-[460px] bg-white dark:bg-dark-white lg:rounded-2xl overflow-y-auto">
<div className="logout-modal-wrapper lg:w-[460px] h-full lg:h-auto bg-white dark:bg-dark-white lg:rounded-2xl">
<div className="logout-modal-header w-full flex items-center justify-between lg:px-10 lg:py-8 px-[30px] py-[23px] border-b dark:border-[#5356fb29] border-light-purple dark:border-[#5356fb29] ">
<h1 className="text-26 font-bold text-dark-gray dark:text-white tracking-wide">
Add Members
+31 -79
View File
@@ -1,9 +1,10 @@
import { Form, Formik } from "formik";
import { useState } from "react";
import { useLocation } from "react-router-dom";
import usersService from "../../services/UsersService";
import InputCom from "../Helpers/Inputs/InputCom";
import ModalCom from "../Helpers/ModalCom";
import { Form, Formik } from "formik";
import InputCom from "../Helpers/Inputs/InputCom";
import usersService from "../../services/UsersService";
import Icons from "../Helpers/Icons";
const DEFAULT_IMAGE = require("../../assets/images/family/default.jpg");
const SuggestTask = ({ details, onClose, situation, continuePopupData }) => {
@@ -50,47 +51,21 @@ const SuggestTask = ({ details, onClose, situation, continuePopupData }) => {
};
const handleParentSuggestion = (values) => {
if (suggestedNextStep === "Send Task") {
let firstname = state?.firstname || details?.firstname;
let family_uid = state?.family_uid || details?.family_uid;
continuePopupData({
...details,
firstname,
family_uid,
});
if (suggestedNextStep == "Send Task") {
let firstName = state?.firstname;
let family_uid = state?.family_uid;
continuePopupData({ ...details, firstName, family_uid });
}
onClose();
};
const isActivitiesPage = pathname === "/acc-family/activities";
const isManageFamilyPage = pathname === "/manage-family";
const getButtonText = () => {
if (isActivitiesPage) {
return suggestedNextStep === "Send Task" ? "Continue" : "Complete";
} else {
if (!isManageFamilyPage) {
if (submitTask.loading) return "Submitting Task";
if (submitTask.state === "success") return "Task Submitted";
if (submitTask.state === "bad") return "An Error Occurred";
return "Send to Parents";
} else {
return suggestedNextStep === "Send Task" ? "Continue" : "Complete";
}
}
};
console.log(details);
return (
<ModalCom action={onClose} situation={situation}>
<div className="logout-modal-wrapper lw-[90%] md:w-[768px] h-full lg:h-auto bg-white dark:bg-dark-white lg:rounded-2xl overflow-y-auto">
<div className="logout-modal-header w-full flex items-center justify-between lg:p-6 px-[30px] py-[23px] border-b dark:border-[#5356fb29] border-light-purple">
<h1 className="text-base md:text-lg font-bold text-dark-gray dark:text-white tracking-wide">
{isManageFamilyPage
{pathname === "/manage-family"
? `${state?.firstname}'s Suggested Task`
: isActivitiesPage
? `${details?.firstname}'s Suggested Task`
: "Suggest to Parent"}
</h1>
<button
@@ -122,11 +97,9 @@ const SuggestTask = ({ details, onClose, situation, continuePopupData }) => {
<Formik
initialValues={initialValues}
onSubmit={
isActivitiesPage
? handleParentSuggestion
: isManageFamilyPage
? handleParentSuggestion
: handleSuggestedTask
pathname !== "/manage-family"
? handleSuggestedTask
: handleParentSuggestion
}
>
{(props) => {
@@ -149,16 +122,12 @@ const SuggestTask = ({ details, onClose, situation, continuePopupData }) => {
<div className="field w-full mb-[15px]">
<InputCom
fieldClass={
pathname === "/manage-family" ||
pathname === "/acc-family/activities"
? "px-2"
: "px-6"
pathname === "/manage-family" ? "px-2" : "px-6"
}
label="Title"
labelClass="tracking-wide"
inputBg={
pathname === "/manage-family" ||
pathname === "/acc-family/activities"
pathname === "/manage-family"
? "bg-white"
: "bg-slate-100"
}
@@ -195,8 +164,7 @@ const SuggestTask = ({ details, onClose, situation, continuePopupData }) => {
id="description"
rows="5"
className={`input-field pt-2 placeholder:text-base text-dark-gray dark:text-white w-full ${
pathname === "/manage-family" ||
pathname === "/acc-family/activities"
pathname === "/manage-family"
? "px-2 h-[110px]"
: "bg-slate-100 px-3 dark:bg-[#11131F] focus:ring-0 focus:outline-[#dce4e9] rounded-[10px] h-[130px]"
}`}
@@ -209,8 +177,7 @@ const SuggestTask = ({ details, onClose, situation, continuePopupData }) => {
</div>
{/* Radio buttons for family */}
{pathname === "/manage-family" ||
pathname === "/acc-family/activities" ? (
{pathname === "/manage-family" ? (
<div className="h-[20px] w-full border-t dark:border-[#5356fb29] border-light-purple relative">
<div id="my-radio-group" className="sr-only">
Parent suggested next step
@@ -245,9 +212,9 @@ const SuggestTask = ({ details, onClose, situation, continuePopupData }) => {
id={`parent-suggested-${idx}`}
name="parent-suggested"
className={`ml-1 ${
title === "Not Now"
title == "Not Now"
? "text-red-500"
: title === "Duplicate"
: title == "Duplicate"
? "text-purple"
: "text-black"
} font-semibold`}
@@ -276,9 +243,19 @@ const SuggestTask = ({ details, onClose, situation, continuePopupData }) => {
disabled={props.isSubmitting}
className="text-white primary-gradient text-18 tracking-wide px-4 py-3 rounded-full transition duration-150 ease-in-out flex items-center"
>
{/* {pathname === "/acc-family/activities" ? (
{pathname !== "/manage-family" ? (
<>
{suggestedNextStep === "Send Task" ? (
{submitTask.loading
? "Submitting Task"
: submitTask.state == "success"
? "Task Submitted"
: submitTask.state == "bad"
? "An Error Occurred"
: "Send to Parents"}
</>
) : (
<>
{suggestedNextStep == "Send Task" ? (
<>
Continue <Icons name="chevron-right" />
</>
@@ -286,32 +263,7 @@ const SuggestTask = ({ details, onClose, situation, continuePopupData }) => {
"Complete"
)}
</>
) : (
<>
{pathname !== "/manage-family" ? (
<>
{submitTask.loading
? "Submitting Task"
: submitTask.state === "success"
? "Task Submitted"
: submitTask.state === "bad"
? "An Error Occurred"
: "Send to Parents"}
</>
) : (
<>
{suggestedNextStep === "Send Task" ? (
<>
Continue <Icons name="chevron-right" />
</>
) : (
"Complete"
)}
</>
)}
</>
)} */}
{getButtonText()}
)}
</button>
</div>
</div>
+5 -12
View File
@@ -2,8 +2,6 @@ import React from "react";
// import Lottie from "react-lottie";
import { useNavigate } from "react-router-dom";
import * as animationData from "../../assets/images/Lotties/77618-website-404-error-animation.json";
import { localImgLoad } from "../../lib";
// import BgError from '../../assets/images/wrench-page-notfound.jpg'
export default function FourZeroFour() {
const navigate = useNavigate();
@@ -16,21 +14,16 @@ export default function FourZeroFour() {
},
};
return (
<div className={`my-custom-bg-class flex justify-center items-center w-full min-h-screen before:content-[''] before:absolute before:inset-0 before:bg-[#cdcdcd]/[.8]`}>
<div className="relative pt-32 max-w-3xl">
<div className="flex justify-center items-center w-full h-screen bg-[#232247]">
<div>
{/* <Lottie options={defaultOptions} width={600} height={600} /> */}
<div className="px-16 md:px-24 flex flex-col items-center justify-center gap-4">
<img src={localImgLoad('images/404.png')} className="w-72" alt='404Image' />
<p className="mt-8 text-red-600 font-black text-4xl md:text-5xl tracking-wider text-center">Sorry!</p>
<h1 className="text-black text-4xl md:text-5xl font-black tracking-wide text-center leading-tight">The page cant be found.</h1>
<p className="px-2 md:px-8 text-slate-700 text-base md:text-lg text-center">The page you're looking for isn't available. Use the go back button below</p>
<div className="flex justify-center">
<button
onClick={() => navigate(-1)}
type="button"
className="px-4 md:px-8 border-2 border-[#2b70fa] rounded-lg text-[#007bff] text-base md:text-lg h-[48px] flex justify-center items-center gap-1"
className="btn-gradient text-white text-lg w-[150px] h-[50px] rounded-full"
>
<span>Go Back</span>
<span className="pb-1">&rarr;</span>
Go Back
</button>
</div>
</div>
+11 -66
View File
@@ -462,15 +462,6 @@ export default function Icons({ name }) {
clipRule="evenodd"
/>
</svg>
) : name === "my-page" ? (
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 68 50" id="Page">
<path
fillRule="evenodd"
d="M0 13V2a2 2 0 0 1 2-2h64a2 2 0 0 1 2 2v11H0Zm0 4v31a2 2 0 0 0 2 2h20V17H0Zm26 33h40a2 2 0 0 0 2-2V17H26v33Z"
fill="#ffffff"
className="color000000 svgShape"
></path>
</svg>
) : name === "accordion-minus" ? (
<svg
width="20"
@@ -487,13 +478,13 @@ export default function Icons({ name }) {
xmlns="http://www.w3.org/2000/svg"
fill="none"
viewBox="0 0 24 24"
strokeWidth="1.5"
stroke-width="1.5"
stroke="currentColor"
className="w-4 h-4"
>
<path
strokeLinecap="round"
strokeLinejoin="round"
stroke-linecap="round"
stroke-linejoin="round"
d="M8.25 4.5l7.5 7.5-7.5 7.5"
/>
</svg>
@@ -519,66 +510,20 @@ export default function Icons({ name }) {
) : name === "master-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"
/>
<img className="w-[17px] h-[17px]" src={localImgLoad('images/icons/dashboard.svg')} alt="dashboard" />
) : name === "new-family" ? (
<img
className="w-[17px] h-[17px]"
src={localImgLoad("images/icons/family.svg")}
alt="family"
/>
<img className="w-[17px] h-[17px]" src={localImgLoad('images/icons/family.svg')} alt="family" />
) : name === "new-logout" ? (
<img
className="w-[45px] rounded-full bg-white p-1"
src={localImgLoad("images/icons/logout.svg")}
alt="logout"
/>
<img className="w-[45px] rounded-full bg-white p-1" src={localImgLoad('images/icons/logout.svg')} alt="logout" />
) : name === "new-market" ? (
<img
className="w-[17px] h-[17px]"
src={localImgLoad("images/icons/market.svg")}
alt="market"
/>
<img className="w-[17px] h-[17px]" src={localImgLoad('images/icons/market.svg')} alt="market" />
) : name === "new-mytask" ? (
<img
className="w-[17px] h-[17px]"
src={localImgLoad("images/icons/my-task.svg")}
alt="task"
/>
<img className="w-[17px] h-[17px]" src={localImgLoad('images/icons/my-task.svg')} alt="task" />
) : name === "family-id" ? (
<img
className="w-[20px] h-[20px]"
src={localImgLoad("images/icons/family-id.svg")}
alt="family-id"
/>
<img className="w-[20px] h-[20px]" src={localImgLoad('images/icons/family-id.svg')} alt="family-id" />
) : name === "family-pin" ? (
<img
className="w-[20px] h-[20px]"
src={localImgLoad("images/icons/family-pin.svg")}
alt="family-pin"
/>
) : name === "pending-job" ? (
<img
className="w-[19px] h-[19px]"
src={localImgLoad("images/icons/job_pending.svg")}
alt="Pending Job"
/>
) : name === "active-job" ? (
<img
className="w-[17px] h-[17px]"
src={localImgLoad("images/icons/job_active.svg")}
alt="Active Job"
/>
) : name === "job-list" ? (
<img
className="w-[17px] h-[17px]"
src={localImgLoad("images/icons/job_list.svg")}
alt="Job List"
/>
) : (
<img className="w-[20px] h-[20px]" src={localImgLoad('images/icons/family-pin.svg')} alt="family-pin" />
): (
""
)}
</>
@@ -14,7 +14,6 @@ export default function InputCom({
forgotPassword,
parentClass,
labelClass,
labalClass,
inputClass,
fieldClass,
onClick,
@@ -28,7 +27,6 @@ export default function InputCom({
direction,
tabIndex,
error,
autoComplete="on"
}) {
const inputRef = useRef(null);
// Entry Validation
@@ -54,7 +52,7 @@ export default function InputCom({
<div className={`flex items-center justify-between mb-2.5 ${labelClass}`}>
{label && (
<label
className={`input-label text-[#181c32] text-[13.975px] leading-[20.9625px] font-semibold flex items-center gap-1 ${labalClass}`}
className="input-label text-[#181c32] dark:text-white text-[13.975px] leading-[20.9625px] font-semibold flex items-center gap-1"
htmlFor={name}
>
{label}
@@ -87,7 +85,7 @@ export default function InputCom({
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] border"
: "text-[#5e6278] dark:text-gray-100 bg-[#f5f8fa] dark:bg-[#5e6278] border"
}`}
>
<input
@@ -109,7 +107,6 @@ export default function InputCom({
readOnly={disable}
onBlur={blurHandler}
dir={direction}
autoComplete={autoComplete}
/>
{iconName && (
<div className="absolute right-6 bottom-3 z-10 flex gap-2">
+1 -1
View File
@@ -16,7 +16,7 @@ export default function ModalCom({ action, children, situation, isOpen, classNam
onClick={action || isOpen}
className="fixed top-0 left-0 w-full lg:h-[100vh] h-full bg-black bg-opacity-40 backdrop-filter backdrop-blur-sm z-50"
></div>
<div className={`fixed lg:h-100vh h-full z-[99999999999999] w-full lg:w-auto inset-0 flex flex-col justify-center items-center ${className}`}>
<div className={`children-element fixed lg:h-100vh h-full z-[99999999999999] w-full lg:w-auto ${className}`}>
{children && children}
</div>
</div>
-26
View File
@@ -50,29 +50,3 @@ export const PriceFormatter = (
</span>
);
};
// FUNCTION TO RETURN AMOUNT TO TWO DECIMAL PLACES
export const AmountTo2DP = (
amount = "00",
) => {
// Convert the number to a string
let numStr = String(amount);
// Split the string into integer and decimal parts
let parts = numStr.split(".");
let integerPart = parts[0] || "";
let decimalPart = parts[1] || "";
// Add thousands separators to the integer part
let formattedInteger = integerPart.replace(/\B(?=(\d{3})+(?!\d))/g, ',');
// Truncate or pad the decimal part to two decimal points
let formattedDecimal = decimalPart.slice(0, 2).padEnd(2, "0");
// Combine the formatted integer and decimal parts
let formattedNumber = formattedInteger + '.' + formattedDecimal;
// return formattedNumber;
return formattedNumber;
};
-106
View File
@@ -1,106 +0,0 @@
import React, {useEffect, useState} from 'react'
import Image from '../../assets/images/taskbanners/default.jpg'
import usersService from '../../services/UsersService';
import { handlePagingFunc } from '../../components/Pagination/HandlePagination';
import PaginatedList from '../../components/Pagination/PaginatedList';
import LoadingSpinner from '../Spinners/LoadingSpinner';
import { AmountTo2DP } from '../Helpers/PriceFormatter';
function JobsCompleted() {
const apiCall = new usersService()
let [familyRewardHistory, setFamilyRewardHistory] = useState({ // FOR PURCHASE HISTORY
loading: true,
data: [],
error: false
})
const [currentPage, setCurrentPage] = useState(0);
const indexOfFirstItem = Number(currentPage);
const indexOfLastItem = Number(indexOfFirstItem)+Number(process.env.REACT_APP_ITEM_PER_PAGE);
const currentReward = familyRewardHistory?.data?.slice(indexOfFirstItem, indexOfLastItem);
const handlePagination = (e) => {
handlePagingFunc(e,setCurrentPage)
}
//FUNCTION TO GET FAMILY REWARD HISTORY
const getJobCompletedHistory = ()=>{
// apiCall.getFamilyRewardHx().then((res)=>{
// if(res.data.internal_return < 0){ // success but no data
// setFamilyRewardHistory(prev => ({...prev, loading: false}))
// return
// }
// setFamilyRewardHistory(prev => ({...prev, loading: false, data: res.data.result_list}))
// }).catch((error)=>{
// setFamilyRewardHistory(prev => ({...prev, loading: false, error: true}))
// })
setTimeout(()=>{
setFamilyRewardHistory(prev => ({...prev, loading: false, error:true}))
},3000)
}
useEffect(()=>{
getJobCompletedHistory()
}, [])
return (
<div className='flex flex-col justify-between min-h-[500px]'>
{familyRewardHistory.loading ?
<LoadingSpinner size='16' color='sky-blue' height='h-[500px]' />
: familyRewardHistory.data.length ?
<table className="wallet-activity w-full table-auto border-collapse text-left">
<thead className='border-b-2'>
<tr className='text-slate-600'>
<th className="p-2"></th>
<th className="p-2">Amount</th>
<th className="p-2">Date</th>
<th className="p-2">Confirmation</th>
</tr>
</thead>
<tbody>
{currentReward.map((item, index) => {
let date = new Date(item.added).toLocaleDateString()
return (
<tr key={index} className='text-slate-500'>
<td className="p-2">
<div className='flex items-center gap-2'>
<img src={item.icon} className='min-w-[60px] max-w-[60px] min-h-[60px] max-h-[60px] rounded-full bg-slate-500' alt='Reward Logo' />
<div className='flex flex-col'>
<h1 className='text-lg font-bold'>Reward to {item.rec_firstname} {item.rec_lastname}</h1>
<p className='text-sm'>{item.description}</p>
</div>
</div>
</td>
<td className="p-2">{AmountTo2DP(item.amount*0.01)} {item.currency}</td>
<td className="p-2">{date}</td>
<td className="p-2">{item.confirmation}</td>
</tr>
)
}
)}
</tbody>
</table>
:familyRewardHistory.error ?
<div className="p-2 text-slate-500 flex flex-col grow justify-center items-center">
<span>Opps! an error occurred. Please try again!</span>
</div>
:
<div className="p-2 text-slate-500 flex flex-col grow justify-center items-center">
<span>No Completed Job History Found!</span>
</div>
}
{/* PAGINATION BUTTON */}
<PaginatedList onClick={handlePagination} prev={currentPage == 0 ? true : false} next={currentPage+Number(process.env.REACT_APP_ITEM_PER_PAGE) >= familyRewardHistory?.data?.length ? true : false} data={familyRewardHistory?.data} start={indexOfFirstItem} stop={indexOfLastItem} />
{/* END OF PAGINATION BUTTON */}
</div>
)
}
export default JobsCompleted
-103
View File
@@ -1,103 +0,0 @@
import React, {useEffect, useState} from 'react'
import Image from '../../assets/images/taskbanners/default.jpg'
import usersService from '../../services/UsersService';
import { handlePagingFunc } from '../../components/Pagination/HandlePagination';
import PaginatedList from '../../components/Pagination/PaginatedList';
import LoadingSpinner from '../Spinners/LoadingSpinner';
import { AmountTo2DP } from '../Helpers/PriceFormatter';
function RewardsTable() {
const apiCall = new usersService()
let [familyRewardHistory, setFamilyRewardHistory] = useState({ // FOR PURCHASE HISTORY
loading: true,
data: [],
error: false
})
const [currentPage, setCurrentPage] = useState(0);
const indexOfFirstItem = Number(currentPage);
const indexOfLastItem = Number(indexOfFirstItem)+Number(process.env.REACT_APP_ITEM_PER_PAGE);
const currentReward = familyRewardHistory?.data?.slice(indexOfFirstItem, indexOfLastItem);
const handlePagination = (e) => {
handlePagingFunc(e,setCurrentPage)
}
//FUNCTION TO GET FAMILY REWARD HISTORY
const getFamilyRewardHistory = ()=>{
apiCall.getFamilyRewardHx().then((res)=>{
if(res.data.internal_return < 0){ // success but no data
setFamilyRewardHistory(prev => ({...prev, loading: false}))
return
}
setFamilyRewardHistory(prev => ({...prev, loading: false, data: res.data.result_list}))
}).catch((error)=>{
setFamilyRewardHistory(prev => ({...prev, loading: false, error: true}))
})
}
useEffect(()=>{
getFamilyRewardHistory()
}, [])
return (
<div className='flex flex-col justify-between min-h-[500px]'>
{familyRewardHistory.loading ?
<LoadingSpinner size='16' color='sky-blue' height='h-[500px]' />
: familyRewardHistory.data.length ?
<table className="wallet-activity w-full table-auto border-collapse text-left">
<thead className='border-b-2'>
<tr className='text-slate-600'>
<th className="p-2"></th>
<th className="p-2">Amount</th>
<th className="p-2">Date</th>
<th className="p-2">Confirmation</th>
</tr>
</thead>
<tbody>
{currentReward.map((item, index) => {
let date = new Date(item.added).toLocaleDateString()
return (
<tr key={index} className='text-slate-500'>
<td className="p-2">
<div className='flex items-center gap-2'>
<img src={item.icon} className='min-w-[60px] max-w-[60px] min-h-[60px] max-h-[60px] rounded-full bg-slate-500' alt='Reward Logo' />
<div className='flex flex-col'>
<h1 className='text-lg font-bold'>Reward to {item.rec_firstname} {item.rec_lastname}</h1>
<p className='text-sm'>{item.description}</p>
</div>
</div>
</td>
<td className="p-2">{AmountTo2DP(item.amount*0.01)} {item.currency}</td>
<td className="p-2">{date}</td>
<td className="p-2">{item.confirmation}</td>
</tr>
)
}
)}
</tbody>
</table>
:familyRewardHistory.error ?
<div className="p-2 text-slate-500 flex flex-col grow justify-center items-center">
<span>Opps! an error occurred. Please try again!</span>
</div>
:
<div className="p-2 text-slate-500 flex flex-col grow justify-center items-center">
<span>No Rewards History Found!</span>
</div>
}
{/* PAGINATION BUTTON */}
<PaginatedList onClick={handlePagination} prev={currentPage == 0 ? true : false} next={currentPage+Number(process.env.REACT_APP_ITEM_PER_PAGE) >= familyRewardHistory?.data?.length ? true : false} data={familyRewardHistory?.data} start={indexOfFirstItem} stop={indexOfLastItem} />
{/* END OF PAGINATION BUTTON */}
</div>
)
}
export default RewardsTable
+4 -43
View File
@@ -10,8 +10,6 @@ import usersService from "../../services/UsersService";
import PurchasesTable from "../MyWallet/WalletComponent/PurchasesTable";
import RecentActivityTable from "../MyWallet/WalletComponent/RecentActivityTable";
import LoadingSpinner from "../Spinners/LoadingSpinner";
import RewardsTable from "./RewardsTable";
import JobsCompleted from "./JobsCompleted";
export default function History() {
@@ -60,9 +58,6 @@ export default function History() {
useEffect(()=>{
getPaymentHistory()
}, [])
useEffect(()=>{
getPurchaseHistory()
}, [])
@@ -241,33 +236,15 @@ export default function History() {
>
Recent Activity
</button>
<button
name="reward"
onClick={(e) => setTab(e.target.name)}
className={`px-4 py-1 rounded-t-2xl ${
tab == "reward" ? "bg-[#4687ba] border-[2px] border-[#4687ba] text-white" : "bg-white text-[#000] border-t-[2px]"
}`}
>
Rewards
</button>
<button
name="jobs_completed"
onClick={(e) => setTab(e.target.name)}
className={`px-4 py-1 rounded-t-2xl ${
tab == "jobs_completed" ? "bg-[#4687ba] border-[2px] border-[#4687ba] text-white" : "bg-white text-[#000] border-t-[2px]"
}`}
>
Jobs Completed
</button>
</div>
{/* END OF switch button */}
<div className="history-tables w-full">
{/* PURCHASE SECTION */}
{tab == 'purchases' &&
<div className="wallet w-full border-t">
{/* <h1 className="p-2 text-xl font-bold text-dark-gray dark:text-white tracking-wide">Purchases</h1> */}
<h1 className="p-2 text-xl font-bold text-dark-gray dark:text-white tracking-wide">Purchases</h1>
{purchaseHistory.loading ?
<LoadingSpinner size='16' color='sky-blue' height='h-[500px]' />
<LoadingSpinner size='16' color='sky-blue' />
:
<PurchasesTable purchase={purchaseHistory} />
}
@@ -278,32 +255,16 @@ export default function History() {
{/* RECENT ACTIVITY SECTION */}
{tab == 'recent' &&
<div className="wallet w-full border-t">
{/* <h1 className="p-2 text-xl font-bold text-dark-gray dark:text-white tracking-wide">Recent Activity</h1> */}
<h1 className="p-2 text-xl font-bold text-dark-gray dark:text-white tracking-wide">Recent Activity</h1>
{/* <p className='text-base text-slate-500 dark:text-white'>Activity Report</p> */}
{paymentHistory.loading ?
<LoadingSpinner size='16' color='sky-blue' height='h-[500px]' />
<LoadingSpinner size='16' color='sky-blue' />
:
<RecentActivityTable payment={paymentHistory} />
}
</div>
}
{/* END OF RECENT ACTIVITY SECTION */}
{/* REWARD SECTION */}
{tab == 'reward' &&
<div className="wallet w-full border-t">
<RewardsTable />
</div>
}
{/* END OF REWARD SECTION */}
{/* JOBS COMPLETED SECTION */}
{tab == 'jobs_completed' &&
<div className="wallet w-full border-t">
<JobsCompleted />
</div>
}
{/* END OF JOBS COMPLETED SECTION */}
</div>
</div>
{/*<HistoryTable />*/}
+2 -2
View File
@@ -4,7 +4,7 @@ import Icons from "../Helpers/Icons";
import SliderCom from "../Helpers/SliderCom";
import FamilyActiveJobsCard from "../Cards/FamilyActiveJobsCard";
export default function FamilyActiveLSlde({ className, trending, image_server }) {
export default function FamilyActiveLSlde({ className, trending }) {
const settings = {
arrows: false,
slidesToShow: 3,
@@ -98,7 +98,7 @@ export default function FamilyActiveLSlde({ className, trending, image_server })
{trending &&
trending.length > 0 &&
trending.map((item) => (
<FamilyActiveJobsCard key={item.id} datas={item} image_server={image_server} />
<FamilyActiveJobsCard key={item.id} datas={item} />
))}
</SliderCom>
</div>
+14 -130
View File
@@ -1,141 +1,25 @@
import React, { useEffect, useState } from "react";
import { Link } from "react-router-dom";
import usersService from "../../services/UsersService";
import React from "react";
import ParentWaiting from "../MyPendingJobs/ParentWaiting";
import MyOffersFamilyTable from "../MyTasks/MyOffersFamilyTable";
import FamilyActiveLSlde from "./FamilyActiveLSlde";
import { useDispatch, useSelector } from "react-redux";
import { tableReload } from "../../store/TableReloads";
import LoadingSpinner from "../Spinners/LoadingSpinner";
export default function FamilyDash({ MyActiveJobList=[], serverImg }) {
// console.log("PROPS IN FAMILY DASH->", familyOffers?.result_list);
const dispatch = useDispatch();
const userApi = new usersService();
// const trending = MyActiveJobList;
const { familyBannersList } = useSelector((state) => state.familyBannersList);
const { familyResources:{ tab_categories } } = useSelector((state) => state.familyResources);
const { userDetails } = useSelector((state) => state?.userDetails);
let [reloadBanner, setReloadBanner] = useState(0)
useEffect(()=>{
if(reloadBanner >= 2){
dispatch(tableReload({ type: "FAMILYBANNERSLIST" })); // RELOAD FAMILY BANNERS LIST EVERY 10 MINS
setReloadBanner(0)
}
const timer = setInterval(()=>{
setReloadBanner(prev => prev+1)
},300000)
return ()=>{
clearInterval(timer)
}
},[reloadBanner])
export default function FamilyDash({ familyOffers, MyActiveJobList }) {
console.log("PROPS IN FAMILY DASH->", familyOffers);
const trending = MyActiveJobList;
return (
<div>
<div className="home-page-wrapper">
{/* Header */}
<div className="text-white mb-4 min-h-[3rem] px-2 w-full flex justify-between items-center rounded-xl bg-family-header-bg">
<div className="w-full">
<div className="w-full flex flex-wrap gap-x-4 ">
<p className="text-lg font-normal leading-5">Welcome</p>
<div className="">
<h1 className="text-lg font-normal leading-5">{`${userDetails?.firstname} ${userDetails?.lastname}`}</h1>
</div>
</div>
</div>
<div className="py-1 w-full text-sm text-right self-end">
<p className="leading-4">Last Login: {`${userDetails?.last_login.split(' ')[0]}`}</p>
</div>
</div>
{/* <CommonHead commonHeadData={props.commonHeadData} /> */}
{familyOffers && familyOffers.length > 0 && (
<MyOffersFamilyTable familyOffers={familyOffers} className="mb-10" />
)}
{trending && trending.length > 0 && (
<FamilyActiveLSlde trending={trending} className="mb-10" />
)}
{process.env.REACT_APP_SHOW_NEW_FAMILY_DASH == '1' &&
<>
{familyBannersList?.loading ?
<div className="w-full bg-white rounded-2xl">
<LoadingSpinner size='10' color='sky-blue' height='h-[20rem]' />
</div>
:
familyBannersList?.result_list && Object.keys(familyBannersList?.result_list).length > 0 ?
// Loop for Family Banners
<div className="w-full mb-4 grid grid-cols-2 md:grid-cols-3 gap-2 md:gap-4">
{Object.keys(familyBannersList?.result_list).map((item, index) => {
let content = familyBannersList?.result_list[item]
// let action = item == 'recommend' ? 'familymarket' : 'mytask'
return (
<Link key={item} to={`/${content.banner.action}`} className={`rounded-xl bg-white dark:bg-dark-white shadow-md flex justify-center items-center transition-all duration-300 hover:shadow-sm`}>
<div className="h-full w-full">
<img className="w-full h-[10rem] object-cover rounded-t-xl" src={content.banner.image} alt='banner image' />
<div className="flex flex-col justify-between">
<div className="px-2 py-2 border-b border-transparent min-h-[4rem] flex flex-col gap-1">
<h1 className="text-lg text-[#083e21] dark:text-white font-bold tracking-wide">{content.banner.text}</h1>
<p className="text-sm text-black dark:text-white">{content.banner.description}</p>
</div>
</div>
</div>
</Link>
)
})}
</div>
:
null
}
{tab_categories?.data &&
<div className="mb-4">
<h1 className="my-4 text-26 font-bold text-dark-gray dark:text-white tracking-wide">Resources</h1>
<div className="w-full grid grid-cols-2 md:grid-cols-4 gap-2 md:gap-4">
{tab_categories.data.map((item) => {
// if(item.enabled){
// }
return (
<Link key={item.uid} to={`/${item?.action}`} className={`group rounded-xl bg-white dark:bg-dark-white shadow-md flex justify-center items-center transition-all duration-300 hover:shadow-sm`}>
<div className="h-full w-full">
<div className="w-full h-[8rem] rounded-t-xl overflow-hidden">
<img className="w-full h-full group-hover:scale-110 object-cover transition-all duration-300" src={item?.banner} alt='banner image' />
</div>
<div className="flex flex-col justify-between">
<div className="px-2 py-1 border-b border-transparent min-h-[2rem] flex justify-between items-center gap-1">
<h1 className="text-lg text-[#083e21] dark:text-white font-bold tracking-wide">{item?.content}</h1>
<div className="flex justify-center gap-1">
<div className="w-1 h-1 bg-slate-400 rounded-full"></div>
<div className="w-1 h-1 bg-slate-400 rounded-full"></div>
<div className="w-1 h-1 bg-slate-400 rounded-full"></div>
</div>
</div>
</div>
</div>
</Link>
)
})}
</div>
</div>
}
</>
}
{/* {familyOffers?.result_list && familyOffers?.result_list.length > 0 && (
<MyOffersFamilyTable
familyOffers={familyOffers?.result_list}
image_server={familyOffers?.session_image_server}
className="mb-10"
/>
)} */}
{/* {trending && trending.length > 0 && (
<FamilyActiveLSlde
trending={trending}
className="mb-10"
image_server={serverImg}
/>
)} */}
{/*<TopSellerTopBuyerSliderSection className="mb-10" />*/}
<ParentWaiting className="mb-10" />
</div>
</div>
);
+27 -68
View File
@@ -1,72 +1,25 @@
import { useSelector } from "react-redux";
import {
AccountDashboard,
FamilyParentDashboard,
HomeDashboard,
JobOwnerDashboard,
WorkerDashboard,
} from "../Dashboards";
import MyJobTable from "../MyTasks/MyJobTable";
import MyOffersTable from "../MyTasks/MyOffersTable";
import LoadingSpinner from "../Spinners/LoadingSpinner";
import Hero from "./Hero";
import HomeActivities from "./HomeActivities";
export default function FullAccountDash(props) {
// console.log("PROPS IN HOME->", props);
const { userDetails } = useSelector((state) => state?.userDetails);
const renderDashboard = () => {
switch (props.dashTypes) {
case "DEFAULT_HOME_DASH":
return (
<HomeDashboard
className="mb-4"
data={userDetails}
bannerList={props.bannerList}
nextDueTask={props.nextDueTask}
/>
);
case "FAMILY_PARENT_DASH":
return (
<FamilyParentDashboard
className="mb-4"
data={userDetails}
bannerList={props.bannerList}
nextDueTask={props.nextDueTask}
/>
);
case "WORKER_HOME_DASH":
return (
<WorkerDashboard
className="mb-4"
data={userDetails}
bannerList={props.bannerList}
nextDueTask={props.nextDueTask}
/>
);
case "JOBOWNER_HOME_DASH":
return (
<JobOwnerDashboard
className="mb-4"
data={userDetails}
bannerList={props.bannerList}
nextDueTask={props.nextDueTask}
/>
);
default:
return null;
}
};
return (
<>
<div className="home-page-wrapper">
{renderDashboard()}
{process.env.REACT_APP_SHOW_ACCOUNT_DASH == "1" && (
<AccountDashboard className="mb-4" bannerList={props.bannerList} />
)}
{props?.dashTypes !== "undefined" &&
props.offersList?.data?.result_list?.length ? (
<Hero
className="mb-10"
data={userDetails}
bannerList={props.bannerList}
nextDueTask={props.nextDueTask}
/>
{props.offersList?.data?.result_list?.length ? (
<MyOffersTable
MyActiveOffersList={props.offersList?.data}
className="mb-10"
@@ -81,20 +34,26 @@ export default function FullAccountDash(props) {
<MyJobTable
ActiveJobList={props.MyActiveJobList}
Account={userDetails}
imageServer={props.offersList?.data?.session_image_server}
/>
</>
) : null}
) : !props.offersList?.loading && !props.MyActiveJobList?.loading ? (
<HomeActivities className="mb-10" />
) : (
<div className="w-full h-[220px] flex items-center justify-center">
<LoadingSpinner size="16" color="sky-blue" />
</div>
)}
{/*<UpdateTable className="mb-10"/>*/}
{/*<SellHistoryMarketVisitorAnalytic className="mb-10"/>*/}
{/*<TopSellerTopBuyerSliderSection className="mb-10" />*/}
{/*<HomeTaskDisplay*/}
{/* jobData={jobData}*/}
{/* className="mb-10"*/}
{/* bannerList={props.bannerList}*/}
{/*/>*/}
</div>
</>
);
}
// : !props.offersList?.loading && !props.MyActiveJobList?.loading ? (
// <HomeActivities className="mb-10" />
// )
// : (
// <div className="w-full h-[220px] flex items-center justify-center">
// <LoadingSpinner size="16" color="sky-blue" />
// </div>
// )
+1 -1
View File
@@ -53,7 +53,7 @@ export default function HomeActivities({ className }) {
return (
<div
className={`update-table w-full p-8 bg-white dark:bg-dark-white overflow-hidden rounded-2xl relative min-h-[520px] ${
className={`update-table w-full p-8 bg-white dark:bg-dark-white overflow-hidden rounded-2xl section-shadow relative min-h-[520px] ${
className || ""
}`}
>
+27 -8
View File
@@ -58,11 +58,14 @@ export default function Home(props) {
useEffect(() => {
const fetchData = async () => {
await Promise.all([getHomeDate(), getMyOffersList(), getMyActiveJobList()]);
await Promise.all([getHomeDate(), getMyOffersList()]);
};
if(userDetails?.account_type == 'FULL'){
fetchData();
}
fetchData();
}, []);
useEffect(() => {
getMyActiveJobList();
}, []);
return (
@@ -72,15 +75,13 @@ export default function Home(props) {
<FamilyDash
account={userDetails}
commonHeadData={props.bannerList}
// familyOffers={MyOffersList?.data}
serverImg = {userDetails?.session_image_server}
// MyActiveJobList={MyActiveJobList?.data}
familyOffers={MyOffersList?.data?.result_list}
MyActiveJobList={MyActiveJobList?.data}
/>
) : userDetails && userDetails?.account_type == "FULL" ? (
<FullAccountDash
nextDueTask={nextDueTask}
bannerList={props.bannerList}
dashTypes={props.dashTypes}
offersList={MyOffersList}
MyActiveJobList={MyActiveJobList}
/>
@@ -93,3 +94,21 @@ export default function Home(props) {
</Layout>
);
}
// /*
// <Layout>
// <div className="home-page-wrapper">
// <Hero className="mb-10" data={userDetails} />
// {/* <CreateNft />
// <TrendingSection trending={trending} className="mb-10" />*/}
// <HomeTaskDisplay
// jobData={jobData}
// className="mb-10"
// bannerList={props.bannerList}
// />
{
/* <SellHistoryMarketVisitorAnalytic className="mb-10"/>
<TopSellerTopBuyerSliderSection className="mb-10" />
<UpdateTable className="mb-10"/>*/
}
// </div>
// </Layout>
-183
View File
@@ -1,183 +0,0 @@
import React, { useState } from "react";
import ModalCom from "../Helpers/ModalCom";
import LoadingSpinner from "../Spinners/LoadingSpinner";
import InputCom from "../Helpers/Inputs/InputCom/index";
import usersService from "../../services/UsersService";
import { apiConst } from "../../lib/apiConst";
export default function AddGroup({ action, situation, setUpdateList }) {
const api = new usersService();
const [name, setName] = useState("");
const handleChange = ({ target: { name, value } }) => {
setName(value);
};
let [requestStatus, setRequestStatus] = useState({
loading: false,
status: false,
message: "",
}); // STATE FOR KNOWING WHEN A REQUEST IS MADE TO THE SERVER
const addGroup = () => {
setRequestStatus({ loading: true, status: false, message: "" });
if (name == "") {
setRequestStatus({
loading: false,
status: false,
message: "Please enter a group name",
});
return setTimeout(() => {
setRequestStatus({ loading: false, status: false, message: "" });
}, 3000);
}
if (name.length < 6) {
setRequestStatus({
loading: false,
status: false,
message: "Group name must be up to six characters",
});
return setTimeout(() => {
setRequestStatus({ loading: false, status: false, message: "" });
}, 3000);
}
api
.jobGroupAdd({
group_name: name,
action: apiConst.WRENCHBOARD_JOB_OFFER_SYSTEM,
})
.then((response) => {
let { status, data } = response;
if (status != 200 || data.internal_return < 0) {
setRequestStatus({
loading: false,
status: false,
message: "Unable to add Group",
});
return;
}
setRequestStatus({
loading: false,
status: true,
message: "Group Added",
});
setTimeout(() => {
setRequestStatus({ loading: false, status: false, message: "" });
// close modal
action();
// reload group page
setUpdateList((prev) => !prev);
}, 3000);
})
.catch((error) => {
setRequestStatus({
loading: false,
status: false,
message: "Unable to add Group, try again later",
});
})
.finally(() => {
setTimeout(() => {
setRequestStatus({ loading: false, status: false, message: "" });
}, 3000);
});
};
return (
<ModalCom action={action} situation={situation}>
<div className="logout-modal-wrapper lg:w-[500px] h-full lg:h-auto bg-white dark:bg-dark-white lg:rounded-2xl">
<div className="logout-modal-header w-full flex items-center justify-between lg:px-10 lg:py-8 px-[30px] py-[23px] border-b border-light-purple dark:border-[#5356fb29] ">
<h1 className="text-26 font-bold text-dark-gray dark:text-white tracking-wide">
Add Group
</h1>
<button
type="button"
className="text-[#374557] dark:text-red-500"
onClick={action}
>
<svg
width="36"
height="36"
viewBox="0 0 36 36"
fill="none"
className="fill-current"
xmlns="http://www.w3.org/2000/svg"
>
<path
d="M36 16.16C36 17.4399 36 18.7199 36 20.0001C35.7911 20.0709 35.8636 20.2554 35.8385 20.4001C34.5321 27.9453 30.246 32.9248 22.9603 35.2822C21.9006 35.6251 20.7753 35.7657 19.6802 35.9997C18.4003 35.9997 17.1204 35.9997 15.8401 35.9997C15.5896 35.7086 15.2189 35.7732 14.9034 35.7093C7.77231 34.2621 3.08728 30.0725 0.769671 23.187C0.435002 22.1926 0.445997 21.1199 0 20.1599C0 18.7198 0 17.2798 0 15.8398C0.291376 15.6195 0.214408 15.2656 0.270759 14.9808C1.71321 7.69774 6.02611 2.99691 13.0428 0.700951C14.0118 0.383805 15.0509 0.386897 15.9999 0C17.2265 0 18.4532 0 19.6799 0C19.7156 0.124041 19.8125 0.136067 19.9225 0.146719C27.3 0.868973 33.5322 6.21922 35.3801 13.427C35.6121 14.3313 35.7945 15.2484 36 16.16ZM33.011 18.0787C33.0433 9.77105 26.3423 3.00309 18.077 2.9945C9.78479 2.98626 3.00344 9.658 2.98523 17.8426C2.96667 26.1633 9.58859 32.9601 17.7602 33.0079C26.197 33.0577 32.9787 26.4186 33.011 18.0787Z"
fill=""
fillOpacity="0.6"
/>
<path
d="M15.9309 18.023C13.9329 16.037 12.007 14.1207 10.0787 12.2072C9.60071 11.733 9.26398 11.2162 9.51996 10.506C9.945 9.32677 11.1954 9.0811 12.1437 10.0174C13.9067 11.7585 15.6766 13.494 17.385 15.2879C17.9108 15.8401 18.1633 15.7487 18.6375 15.258C20.3586 13.4761 22.1199 11.7327 23.8822 9.99096C24.8175 9.06632 26.1095 9.33639 26.4967 10.517C26.7286 11.2241 26.3919 11.7413 25.9133 12.2178C24.1757 13.9472 22.4477 15.6855 20.7104 17.4148C20.5228 17.6018 20.2964 17.7495 20.0466 17.9485C22.0831 19.974 24.0372 21.8992 25.9689 23.8468C26.9262 24.8119 26.6489 26.1101 25.4336 26.4987C24.712 26.7292 24.2131 26.3441 23.7455 25.8757C21.9945 24.1227 20.2232 22.3892 18.5045 20.6049C18.0698 20.1534 17.8716 20.2269 17.4802 20.6282C15.732 22.4215 13.9493 24.1807 12.1777 25.951C11.7022 26.4262 11.193 26.7471 10.4738 26.4537C9.31345 25.9798 9.06881 24.8398 9.98589 23.8952C11.285 22.5576 12.6138 21.2484 13.9387 19.9355C14.5792 19.3005 15.2399 18.6852 15.9309 18.023Z"
fill="#"
fillOpacity="0.6"
/>
</svg>
</button>
</div>
<div className="logout-modal-body w-full flex flex-col items-center px-10 py-8">
<div className="mb-6 w-full">
<div className="input-item">
<InputCom
labelClass="tracking-wider"
fieldClass="sm:px-6 px-2"
value={name}
placeholder="Group Name"
label="Group Name"
name="name"
type="text"
inputHandler={handleChange}
// iconName="message"
/>
</div>
</div>
<div className="w-full flex justify-between items-center gap-4">
<button
onClick={action}
type="button"
className="text-base text-light-red tracking-wide "
>
<span className="border-b dark:border-[#5356fb29] border-light-red">
Cancel
</span>
</button>
{requestStatus.loading ? (
<LoadingSpinner size="8" color="sky-blue" />
) : (
<button
onClick={() => addGroup()}
type="button"
className="w-[152px] h-[46px] flex justify-center items-center btn-gradient text-base rounded-full text-white"
>
Add Group
</button>
)}
</div>
{/* ERROR DISPLAY AND SUBMIT BUTTON */}
{requestStatus.message != "" &&
(!requestStatus.status ? (
<div
className={`relative p-4 my-4 text-[#912741] bg-[#fcd9e2] border-[#fbc6d3] mb-4 rounded-[0.475rem] text-md font-light leading-[19.5px] text-[13px]`}
>
{requestStatus.message}
</div>
) : (
requestStatus.status && (
<div
className={`relative p-4 my-4 text-green-700 bg-slate-200 border-slate-800 mb-4 rounded-[0.475rem] text-md font-light leading-[19.5px] text-[13px]`}
>
{requestStatus.message}
</div>
)
))}
{/* End of error or success display */}
</div>
</div>
</ModalCom>
);
}

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