diff --git a/src/api/marketplace/accounts/views.py b/src/api/marketplace/accounts/views.py index 013023f0..0fcf9cbd 100644 --- a/src/api/marketplace/accounts/views.py +++ b/src/api/marketplace/accounts/views.py @@ -74,6 +74,7 @@ from nacl.signing import VerifyKey import base58 +from reward.models import UserReferrals from django.utils import timezone # Twitter account API-Endpoint @@ -141,13 +142,15 @@ def get(self, request): ).values_list('influencer__twitter_account', flat=True).distinct() # Filter the TwitterAccount queryset based on the extracted IDs - twitterAccount = twitterAccount.filter(id__in=account_ids_with_published_package) + twitterAccount = twitterAccount.filter( + id__in=account_ids_with_published_package) twitterAccount = twitterAccount[:8] # Paginate the results pagination = Pagination(twitterAccount, request) - serializer = TwitterAccountSerializer(pagination.getData(), many=True) + serializer = TwitterAccountSerializer( + pagination.getData(), many=True) return Response( { "isSuccess": True, @@ -163,6 +166,7 @@ def get(self, request): class TwitterAccountList(APIView): authentication_classes = [JWTAuthenticationOptional] + def get(self, request): try: languages = request.GET.getlist("languages[]", []) @@ -182,14 +186,12 @@ def get(self, request): searchString = request.GET.get("searchString", "") rating = request.GET.get("rating", "0") - + # Default fetch influencers for explore page role = request.GET.get("role", "influencer") # By default Fetch twitter account that have atleast one published service. - packageStatus =request.GET.get("packageStatus", "published") - - + packageStatus = request.GET.get("packageStatus", "published") # Filter based on parameters twitterAccount = TwitterAccount.objects.all() @@ -205,8 +207,8 @@ def get(self, request): ).values_list('influencer__twitter_account', flat=True).distinct() # Filter the TwitterAccount queryset based on the extracted IDs - twitterAccount = twitterAccount.filter(id__in=account_ids_with_published_package) - + twitterAccount = twitterAccount.filter( + id__in=account_ids_with_published_package) # From the account model itself. if upperFollowerLimit: @@ -239,7 +241,8 @@ def get(self, request): ) if regions: - region_ids = RegionMaster.objects.filter(regionName__in=regions).values_list('id', flat=True) + region_ids = RegionMaster.objects.filter( + regionName__in=regions).values_list('id', flat=True) twitterAccount = twitterAccount.filter( Q(user_twitter_account_id__region_user_account__region__in=region_ids) ) @@ -315,24 +318,26 @@ def get(self, request): twitterAccount = twitterAccount.exclude( id__in=twitter_accounts_to_exclude ) - + if rating != "0": - exclude_ids = [] - + exclude_ids = [] + for twitter_account in twitterAccount: - user = get_object_or_404(User, twitter_account=twitter_account) - + user = get_object_or_404( + User, twitter_account=twitter_account) + reviews = Review.objects.filter( order__order_item_order_id__package__influencer=user, order__deleted_at=None, order__status="completed" ) - - total_rating = reviews.aggregate(Avg('rating'))['rating__avg'] or Decimal('0') - + + total_rating = reviews.aggregate(Avg('rating'))[ + 'rating__avg'] or Decimal('0') + if total_rating < Decimal(rating): exclude_ids.append(twitter_account.id) - + twitterAccount = twitterAccount.exclude(id__in=exclude_ids) twitterAccount = twitterAccount.order_by("-followers_count") @@ -447,21 +452,22 @@ def delete(self, request, pk): class CategoryMasterList(APIView): def get(self, request): try: - is_verified = request.GET.get("is_verified" , None) - show_on_main = request.GET.get("show_on_main" , None) - + is_verified = request.GET.get("is_verified", None) + show_on_main = request.GET.get("show_on_main", None) + categoryMaster = CategoryMaster.objects.all() if is_verified: is_verified = bool(is_verified) categoryMaster = categoryMaster.filter(is_verified=is_verified) - if show_on_main: show_on_main = bool(show_on_main) - categoryMaster = categoryMaster.filter(show_on_main=show_on_main) + categoryMaster = categoryMaster.filter( + show_on_main=show_on_main) pagination = Pagination(categoryMaster, request) - serializer = CategoryMasterSerializer(pagination.getData(), many=True) + serializer = CategoryMasterSerializer( + pagination.getData(), many=True) return Response( { "isSuccess": True, @@ -574,7 +580,8 @@ def post(self, request): region_id = request.data.get("region_id") # Check if the user already has an AccountRegion - account_region = AccountRegion.objects.filter(user_account=user_id).first() + account_region = AccountRegion.objects.filter( + user_account=user_id).first() if account_region: # If an AccountRegion exists, update the region account_region.region_id = region_id @@ -589,7 +596,8 @@ def post(self, request): ) # Create a new AccountRegion instance - account_region_data = {"user_account": user_id, "region": region_id} + account_region_data = { + "user_account": user_id, "region": region_id} serializer = AccountRegionSerializer(data=account_region_data) if serializer.is_valid(): @@ -634,7 +642,8 @@ def get(self, request): twitter_account_id=twitter_account_id ) pagination = Pagination(accountCategory, request) - serializer = AccountCategorySerializer(pagination.getData(), many=True) + serializer = AccountCategorySerializer( + pagination.getData(), many=True) return Response( { "isSuccess": True, @@ -958,7 +967,8 @@ def put(self, request, pk): bankAccount = self.get_object(pk) if bankAccount is None: return handleNotFound("Bank Account") - serializer = BankAccountSerializer(instance=bankAccount, data=request.data) + serializer = BankAccountSerializer( + instance=bankAccount, data=request.data) if serializer.is_valid(): serializer.save() return Response( @@ -1108,18 +1118,27 @@ def get(self, request): except Exception as e: return handleServerException(e) + class OTPAuth(APIView): - def get_or_create_user(self, email): + def get_or_create_user(self, email, referral_code, login_type): try: return User.objects.get(email=email) except User.DoesNotExist: + if(login_type == "LOGIN"): + return None user = User.objects.create( email=email, role=Role.objects.get(name="business_owner"), username=email, ) + if referral_code is not None: + referred_by = User.objects.get(referral_code=referral_code) + if referred_by: + # Create mapping between referred_by and new_user_account + UserReferrals.objects.create( + user_account=user, + referred_by=referred_by) user.save() - return user @swagger_auto_schema(request_body=OTPAuthenticationSerializer) @@ -1129,7 +1148,17 @@ def post(self, request): if serializer.is_valid(): # If user exists, send OTP - user = self.get_or_create_user(request.data["email"]) + user = self.get_or_create_user( + request.data["email"], request.data.get("referral_code", None), request.data.get("loginType", None)) + if user is None: + return Response( + { + "isSuccess": False, + "data": None, + "message": "Account not found. Please sign-in using an invite code.", + }, + status=status.HTTP_404_NOT_FOUND, + ) otp_service = OTPAuthenticationService() otp, otp_expiration = otp_service.generateOTP() if user: @@ -1232,8 +1261,6 @@ def post(self, request): samesite="None", ) - - response.data = { "isSuccess": True, "data": UserSerializer(user).data, @@ -1255,6 +1282,7 @@ def post(self, request): except Exception as e: return handleServerException(e) + class OTPAuthV2(APIView): def get_user(self, username, email): try: @@ -1268,7 +1296,6 @@ def get_user(self, username, email): return current_user except User.DoesNotExist: return None - @swagger_auto_schema(request_body=OTPAuthenticationV2Serializer) def post(self, request): @@ -1277,7 +1304,8 @@ def post(self, request): if serializer.is_valid(): # If user exists, send OTP. - user = self.get_user(request.data["username"], request.data["email"]) + user = self.get_user( + request.data["username"], request.data["email"]) otp_service = OTPAuthenticationService() otp, otp_expiration = otp_service.generateOTP() if user: @@ -1312,7 +1340,7 @@ def post(self, request): }, status=status.HTTP_200_OK, ) - else: + else: return Response( { "isSuccess": False, @@ -1325,6 +1353,7 @@ def post(self, request): except Exception as e: return handleServerException(e) + class OTPVerifyV2(APIView): def get_object(self, username): try: @@ -1352,14 +1381,15 @@ def post(self, request): is_valid = otp_service.validateOTP(request.data["otp"], user) if is_valid: # If the OTP is valid, save the data to the user table - + user.email_verified_at = timezone.now() user.email = request.data["email"] if user.login_method == "email": user.username = request.data["email"] user.save() - business_account_meta = BusinessAccountMetaData.objects.get(user_account=user) + business_account_meta = BusinessAccountMetaData.objects.get( + user_account=user) business_account_meta.user_email = request.data["email"] business_account_meta.save() response = Response() # Create a new Response instance @@ -1384,6 +1414,7 @@ def post(self, request): except Exception as e: return handleServerException(e) + class EmailVerification(APIView): # A GET request to this endpoint will extract the user id from the JWT and check if the user exists and then send an email to the user authentication_classes = [JWTAuthentication] @@ -1537,7 +1568,8 @@ def get_or_create_wallet_provider(self, name): wallet_provider = WalletProvider.objects.get(wallet_provider=name) return wallet_provider except WalletProvider.DoesNotExist: - wallet_provider = WalletProvider.objects.create(wallet_provider=name) + wallet_provider = WalletProvider.objects.create( + wallet_provider=name) wallet_provider.save() return wallet_provider @@ -1562,6 +1594,16 @@ def verify_nonce_signature(self, wallet_address, signature, message): return result + def manage_referral(self, request, user): + referral_code = request.data.get("referral_code", None) + if referral_code is not None: + referred_by = User.objects.get(referral_code=referral_code) + if referred_by: + # Create mapping between referred_by and new_user_account + UserReferrals.objects.create( + user_account=user, + referred_by=referred_by) + @swagger_auto_schema(request_body=WalletAuthSerializer) def post(self, request): try: @@ -1586,6 +1628,19 @@ def post(self, request): # Should create a wallet if no wallet is found for the requested user else return the wallet wallet = self.get_wallet(request.data["wallet_address_id"]) + + # Check if logintype is LOGIN so the user should exist already + if wallet is None and request.data.get("loginType", None) == "LOGIN": + return Response( + { + "isSuccess": False, + "data": None, + "message": "Account not found. Please sign-in using an invite code.", + "errors": "Account not found. Please sign-in using an invite code.", + }, + status=status.HTTP_404_NOT_FOUND, + ) + if wallet is None: wallet_provider = self.get_or_create_wallet_provider( serializer.validated_data["wallet_provider_id"] @@ -1606,6 +1661,8 @@ def post(self, request): ) wallet.user_id = user wallet.save() + # Referral entry for new users + self.manage_referral(request, user) is_new_user = True else: user = User.objects.get(username=wallet.user_id) @@ -1656,7 +1713,7 @@ def post(self, request): ) message = "Logged in successfully" if is_new_user: - message="New user? Head to your profile to earn badges!" + message = "New user? Head to your profile to earn badges!" response.data = { "isSuccess": True, "data": UserSerializer(user).data, @@ -1786,6 +1843,7 @@ def post(self, request): except Exception as e: return handleServerException(e) + class WalletList(APIView): authentication_classes = [JWTAuthentication] @@ -1807,6 +1865,7 @@ def get(self, request): except Exception as e: return handleServerException(e) + class DisconnectTwitterAccount(APIView): def get_object(self, pk): try: @@ -1815,11 +1874,12 @@ def get_object(self, pk): return None authentication_classes = [JWTAuthentication] + def delete(self, request, pk): try: # Get the user object user = self.get_object(pk) - + # If user not found, raise NotFound exception if user is None: raise NotFound("User not found") @@ -1877,6 +1937,7 @@ def delete(self, request, pk): except Exception as e: return handleServerException(e) + class BusinessAccountMetaDataDetail(APIView): def get_object(self, userId): try: @@ -1889,7 +1950,8 @@ def get(self, request, userId): businessAccountMetaData = self.get_object(userId) if businessAccountMetaData is None: return handleNotFound("Business Account Meta Data") - serializer = BusinessAccountMetaDataSerializer(businessAccountMetaData) + serializer = BusinessAccountMetaDataSerializer( + businessAccountMetaData) return Response( { "isSuccess": True, diff --git a/src/api/marketplace/marketplace/views.py b/src/api/marketplace/marketplace/views.py index 4470175f..98ff685c 100644 --- a/src/api/marketplace/marketplace/views.py +++ b/src/api/marketplace/marketplace/views.py @@ -7,6 +7,7 @@ HttpResponse, HttpResponseRedirect, ) +from rest_framework import status from decouple import config from accounts.models import AccountCategory, CategoryMaster, Role, TwitterAccount, User import datetime @@ -19,7 +20,7 @@ import hashlib from requests_oauthlib import OAuth2Session import json -from reward.models import RewardConfig, RewardPoints, UserReferrals +from reward.models import RewardConfig,RewardPoints, UserReferrals logger = logging.getLogger(__name__) @@ -31,14 +32,18 @@ twitter = None + def generate_referral_code(): # Generate a unique referral code - code = ''.join(random.choices(string.ascii_uppercase + string.digits, k=10)) + code = ''.join(random.choices( + string.ascii_uppercase + string.digits, k=10)) # Check if the generated code is unique while User.objects.filter(referral_code=code).exists(): - code = ''.join(random.choices(string.ascii_uppercase + string.digits, k=10)) + code = ''.join(random.choices( + string.ascii_uppercase + string.digits, k=10)) return code + def logoutUser(request): response_data = { 'isSuccess': True, @@ -51,19 +56,23 @@ def logoutUser(request): response.content = json.dumps(response_data) return response + def storingCredsPerSession(request): try: global twitter # Creating a new code verifier to update the previous variables. - code_verifier = base64.urlsafe_b64encode(os.urandom(30)).decode("utf-8") + code_verifier = base64.urlsafe_b64encode( + os.urandom(30)).decode("utf-8") code_verifier = re.sub("[^a-zA-Z0-9]+", "", code_verifier) # Sending the code challenge to Twitter authentication. code_challenge = hashlib.sha256(code_verifier.encode("utf-8")).digest() - code_challenge = base64.urlsafe_b64encode(code_challenge).decode("utf-8") + code_challenge = base64.urlsafe_b64encode( + code_challenge).decode("utf-8") code_challenge = code_challenge.replace("=", "") - twitter = OAuth2Session(client_id, redirect_uri=redirect_uri, scope=TWITTER_SCOPES) + twitter = OAuth2Session( + client_id, redirect_uri=redirect_uri, scope=TWITTER_SCOPES) authorization_url, state = twitter.authorization_url( auth_url, code_challenge=code_challenge, code_challenge_method="S256" ) @@ -77,6 +86,7 @@ def storingCredsPerSession(request): logger.error(f"createNewCredentials - {e}") return None + def authTwitterUser(request, role, requestType): try: request.session["role"] = role @@ -84,13 +94,17 @@ def authTwitterUser(request, role, requestType): referral_code = request.GET.get("referral_code", "") if referral_code: request.session["referral_code"] = referral_code + + login_type = request.GET.get("login_type", None) + if login_type: + request.session["login_type"] = login_type # If the request is connect type, store the user id in session - if(requestType == "connect"): + if (requestType == "connect"): payload, token = JWTOperations.getPayload( - req=request, cookie_name="jwt") + req=request, cookie_name="jwt") request.session["user_id"] = payload["id"] - + auth_url = storingCredsPerSession(request) return redirect(auth_url) except Exception as e: @@ -100,7 +114,8 @@ def authTwitterUser(request, role, requestType): else: redirect_uri = f"{config('FRONT_END_URL')}influencer/?authenticationStatus=error" return HttpResponseRedirect(redirect_uri) - + + def connectTwitter(request, role): try: request.session["role"] = role @@ -115,13 +130,13 @@ def connectTwitter(request, role): return HttpResponseRedirect(redirect_uri) - def twitterLoginCallback(request): # Exchange the authorization code for an access token code = request.GET.get("code") role = request.session.get("role", "") requestType = request.session.get("requestType", "") referral_code = request.session.get("referral_code", "") + login_type = request.session.get("login_type", None) try: # Authenticate User @@ -130,10 +145,10 @@ def twitterLoginCallback(request): access_token = authentication_result["access_token"] refresh_token = authentication_result["refresh_token"] # Create USER and JWT and send response - if(requestType == "auth"): - return createJWT(userData, access_token, role, refresh_token, referral_code) + if (requestType == "auth"): + return createJWT(userData, access_token, role, refresh_token, referral_code, login_type) # Connect the user with twitter account - elif(requestType == "connect"): + elif (requestType == "connect"): user_id = request.session.get("user_id", "") return connectUser(userData, access_token, refresh_token, user_id) except Exception as e: @@ -158,7 +173,8 @@ def authenticateUser(request, code): code=code, ) else: - twitter = OAuth2Session(client_id, redirect_uri=redirect_uri, scope=TWITTER_SCOPES) + twitter = OAuth2Session( + client_id, redirect_uri=redirect_uri, scope=TWITTER_SCOPES) token = twitter.fetch_token( token_url=token_url, client_secret=client_secret, @@ -190,8 +206,8 @@ def authenticateUser(request, code): except Exception as e: logger.error("Error in authenticateUser -", e) return HttpResponseRedirect( - config("FRONT_END_URL") + "influencer/?authenticationStatus=error" - ) + config("FRONT_END_URL") + "influencer/?authenticationStatus=error" + ) def manage_categories(hashtags, twitter_account): @@ -216,6 +232,7 @@ def manage_categories(hashtags, twitter_account): ) new_account_category.save() + def manage_referrals(referral_code, new_user_account, current_twitter_user): # Get user account based on referral_code try: @@ -262,13 +279,21 @@ def manage_referrals(referral_code, new_user_account, current_twitter_user): "message": f"No referral given, invalid referral code - {referral_code}.", } -def createUser(userData, access_token, role, refresh_token, referral_code): + +def createUser(userData, access_token, role, refresh_token, referral_code, login_type): try: is_new_user = False - existing_user = TwitterAccount.objects.filter(twitter_id=userData.id).first() + existing_user = TwitterAccount.objects.filter( + twitter_id=userData.id).first() # Operations for twitter user account if existing_user is None: + if login_type == "LOGIN": + return { + "status": "error", + "message": "Account not found. Please sign-in using an invite code.", + "status_code":status.HTTP_404_NOT_FOUND, + } newUser = TwitterAccount.objects.create( twitter_id=userData.id, name=userData.name, @@ -328,13 +353,14 @@ def createUser(userData, access_token, role, refresh_token, referral_code): role=Role.objects.filter(name=role).first(), ) new_user_account.save() - if role=="business_owner": - is_new_user = True - existing_user_account=new_user_account + if role == "business_owner": + is_new_user = True + existing_user_account = new_user_account - # New user login with referral_code - + # New user login with referral_code - if referral_code: - ref = manage_referrals(referral_code, new_user_account, current_twitter_user) + ref = manage_referrals( + referral_code, new_user_account, current_twitter_user) if ref is not None: return ref else: @@ -346,15 +372,16 @@ def createUser(userData, access_token, role, refresh_token, referral_code): else: existing_user_account.last_login = datetime.datetime.now() existing_user_account.save() - + # Creating referral_code for users if not existing_user_account.referral_code: existing_user_account.referral_code = generate_referral_code() existing_user_account.save() - current_user = User.objects.filter(twitter_account=current_twitter_user).first() + current_user = User.objects.filter( + twitter_account=current_twitter_user).first() logger.info("User Successfully created/updated") - return {"status": "success", "current_user": current_user, "is_new_user":is_new_user} + return {"status": "success", "current_user": current_user, "is_new_user": is_new_user} except Exception as e: logger.error("Error creating/updating user account -", e) return { @@ -363,15 +390,18 @@ def createUser(userData, access_token, role, refresh_token, referral_code): } # userData is the twitter API data and user_id is the User's table id + + def connectUser(userData, access_token, refresh_token, user_id): redirect_url = f"{config('FRONT_END_URL')}business/profile?tab=connect_x" try: user = User.objects.get(pk=user_id) except Role.DoesNotExist: return HttpResponseRedirect(f"{config('FRONT_END_URL')}business/profile?tab=connect_x&authenticationStatus=error") - + # Check if the twitter account is already connect to other account or not. - existing_twitter_user = TwitterAccount.objects.filter(twitter_id=userData.id).first() + existing_twitter_user = TwitterAccount.objects.filter( + twitter_id=userData.id).first() if existing_twitter_user: return HttpResponseRedirect(f"{config('FRONT_END_URL')}business/profile?tab=connect_x&authenticationStatus=error&message=Twitter account already connected to a user") else: @@ -402,7 +432,7 @@ def connectUser(userData, access_token, refresh_token, user_id): existing_user_account.twitter_account = newTwitterUser existing_user_account.save() response = redirect(redirect_url) - + response.data = { "isSuccess": True, "data": UserSerializer(user).data, @@ -412,7 +442,9 @@ def connectUser(userData, access_token, refresh_token, user_id): return response # The userData here is from twitter -def createJWT(userData, access_token, role, refresh_token, referral_code): + + +def createJWT(userData, access_token, role, refresh_token, referral_code, login_type): try: # Creating a response object with JWT cookie if role == "business_owner": @@ -420,14 +452,19 @@ def createJWT(userData, access_token, role, refresh_token, referral_code): elif role == "influencer": redirect_url = f"{config('FRONT_END_URL')}influencer/" - current_user_data = createUser(userData, access_token, role, refresh_token, referral_code) + current_user_data = createUser( + userData, access_token, role, refresh_token, referral_code, login_type) + if current_user_data["status"] == "error": + status_code = current_user_data.get("status_code", 400) return redirect( - redirect_url - + f"?authenticationStatus=error&message={current_user_data['message']}" + f"{config('FRONT_END_URL')}login/" + + f"?authenticationStatus=error&message={current_user_data['message']}&status_code={status_code}" ) current_user = current_user_data["current_user"] + + # Redirect influencers to their profile page. if role == "influencer": @@ -467,4 +504,3 @@ def createJWT(userData, access_token, role, refresh_token, referral_code): else: redirect_uri = f"{config('FRONT_END_URL')}influencer/?authenticationStatus=error" return HttpResponseRedirect(redirect_uri) - diff --git a/src/ui/app/business/explore/page.tsx b/src/ui/app/business/explore/page.tsx index 73c5e91c..8c6cf922 100644 --- a/src/ui/app/business/explore/page.tsx +++ b/src/ui/app/business/explore/page.tsx @@ -41,6 +41,7 @@ const formatTwitterFollowers = (followersCount: any) => { export default function Explore({}: Props) { const [influencersData, setInfluencersData] = useState(); const [refresh, setRefresh] = useState(false); + const [pageChanged, setPageChanged] = useState(false); const [pagination, setPagination] = React.useState({ total_data_count: 0, total_page_count: 0, @@ -72,8 +73,11 @@ export default function Explore({}: Props) { }, [formik.values]); useEffect(() => { - getInfluencers(); - }, [pagination.current_page_number]); + if (pageChanged) { + getInfluencers(); + setPageChanged(false); + } + }, [pageChanged]); useEffect(() => { let filterData: any = localStorage.getItem("filterData"); @@ -81,13 +85,20 @@ export default function Explore({}: Props) { if (filterData) { formik.setFieldValue("categories", filterData?.categories); + getInfluencers(filterData); } - getInfluencers(filterData); return () => { localStorage.removeItem("filterData"); }; }, []); + useEffect(() => { + if (refresh) { + getInfluencers(); + setRefresh(false); + } + }, [refresh]); + const getPrice = (inf: any, type = "max") => { if (type == "max") { if (inf.service_types && inf.service_types.length > 0) { @@ -159,19 +170,13 @@ export default function Explore({}: Props) { event: React.ChangeEvent, page: number ) => { + setPageChanged(true); setPagination((prev) => ({ ...prev, current_page_number: page, })); }; - useEffect(() => { - if (refresh) { - getInfluencers(); - setRefresh(false); - } - }, [refresh]); - return ( <> {/* Filters section */} diff --git a/src/ui/app/business/layout.tsx b/src/ui/app/business/layout.tsx index d23eebef..f9de0957 100644 --- a/src/ui/app/business/layout.tsx +++ b/src/ui/app/business/layout.tsx @@ -2,10 +2,11 @@ import { useAppDispatch, useAppSelector } from "@/src/hooks/useRedux"; import { initializeCart, resetCart } from "@/src/reducers/cartSlice"; -import { postService } from "@/src/services/httpServices"; +import { getService, postService } from "@/src/services/httpServices"; import { ORDER_STATUS } from "@/src/utils/consts"; import { useWallet } from "@solana/wallet-adapter-react"; import React, { useEffect } from "react"; +import { useRouter } from "next/navigation"; export default function BusinessLayout({ children, @@ -13,6 +14,7 @@ export default function BusinessLayout({ children: React.ReactNode; }) { const { publicKey } = useWallet(); + const router = useRouter(); const user = useAppSelector((state) => state.user); const cart = useAppSelector((state) => state.cart); const dispatch = useAppDispatch(); @@ -51,5 +53,21 @@ export default function BusinessLayout({ } }, [user?.loggedIn]); + // Restrict access to landing page for logged out user + const loginStatus = async () => { + try { + const { isSuccess } = await getService("account/"); + if (!isSuccess) { + router.push("/login?role=Business"); + } + } catch (error) { + router.push("/login?role=Business"); + } + }; + + useEffect(() => { + loginStatus(); + }, []); + return
{children}
; } diff --git a/src/ui/app/components/navbar/index.tsx b/src/ui/app/components/navbar/index.tsx index cc2f402f..210d3406 100644 --- a/src/ui/app/components/navbar/index.tsx +++ b/src/ui/app/components/navbar/index.tsx @@ -223,32 +223,14 @@ const MenuItemsComponent = ({ items }: { items: string[] }) => { }; export default function Navbar() { - const { isTwitterUserLoggedIn, logoutTwitterUser } = useTwitterAuth(); + const { logoutTwitterUser } = useTwitterAuth(); const router = useRouter(); const pathname = usePathname(); // const [currentUser, setCurrentUser] = React.useState(null); - const params = useSearchParams(); const user = useAppSelector((state) => state.user); - useEffect(() => { - const status = params.get("authenticationStatus"); - const paramMessage = params.get("message"); - if (status) { - let snackBarMessage = - status === "success" ? LOGIN_STATUS_SUCCESS : LOGIN_STATUS_FAILED; - - if (paramMessage) snackBarMessage = paramMessage; - notification( - snackBarMessage, - status === "success" ? "success" : "error", - 3000 - ); - router.push(pathname); - } - }, [isTwitterUserLoggedIn]); - const handleConnect = () => { const roleQueryParams = pathname.includes("business") ? "Business" diff --git a/src/ui/app/influencer/layout.tsx b/src/ui/app/influencer/layout.tsx index e1726083..15e98389 100644 --- a/src/ui/app/influencer/layout.tsx +++ b/src/ui/app/influencer/layout.tsx @@ -1,10 +1,30 @@ "use client"; -import React from "react"; +import React, { useEffect } from "react"; +import { useRouter } from "next/navigation"; +import { getService } from "@/src/services/httpServices"; export default function BusinessLayout({ children, }: { children: React.ReactNode; }) { + const router = useRouter(); + + // Restrict access to landing page for logged out user + const loginStatus = async () => { + try { + const { isSuccess } = await getService("account/"); + if (!isSuccess) { + router.push("/login?role=Influencer"); + } + } catch (error) { + router.push("/login?role=Influencer"); + } + }; + + useEffect(() => { + loginStatus(); + }, []); + return
{children}
; } diff --git a/src/ui/app/login/components/loginAccordion/index.tsx b/src/ui/app/login/components/loginAccordion/index.tsx index 18c7caa9..d4fd0f0a 100644 --- a/src/ui/app/login/components/loginAccordion/index.tsx +++ b/src/ui/app/login/components/loginAccordion/index.tsx @@ -12,7 +12,8 @@ interface LoginAccordionProps { title: string; subtitle: string; defaultExpanded?: boolean; - children: React.ReactNode; + children: React.ReactNode; + isDisabled?: boolean; } const LoginAccordion: React.FC = ({ @@ -20,6 +21,7 @@ const LoginAccordion: React.FC = ({ subtitle, defaultExpanded = false, children, + isDisabled = false, }) => { return ( = ({ content: "none", }, }, + filter: isDisabled ? "blur(3px)" : "none", }} > }> diff --git a/src/ui/app/login/components/loginOptions/index.tsx b/src/ui/app/login/components/loginOptions/index.tsx index df5a8be0..85931a06 100644 --- a/src/ui/app/login/components/loginOptions/index.tsx +++ b/src/ui/app/login/components/loginOptions/index.tsx @@ -1,15 +1,21 @@ -"use client" +"use client"; import { Button } from "@mui/material"; import React from "react"; interface LoginOptionProps { label: string; onClick: () => void; + disabled?: boolean; } -const LoginOptions: React.FC = ({ label, onClick }) => { +const LoginOptions: React.FC = ({ + label, + onClick, + disabled = false, +}) => { return ( + + + + + + + + ); +} diff --git a/src/ui/app/login/page.tsx b/src/ui/app/login/page.tsx index 8735037a..f1560635 100644 --- a/src/ui/app/login/page.tsx +++ b/src/ui/app/login/page.tsx @@ -22,16 +22,20 @@ import EmailLoginModal from "@/src/components/emailLoginModal"; import { getService } from "@/src/services/httpServices"; import { Send } from "@mui/icons-material"; import { notification } from "@/src/components/shared/notification"; +import TypeModal from "./components/typeModal"; + +// Two types of login would be SIGNIN and LOGIN const Login: React.FC = () => { const router = useRouter(); const searchParams = useSearchParams(); - const role = searchParams.get("role"); const [loginAs, setLoginAs] = useState(role ?? "Business"); + const [loginType, setLoginType] = useState(""); const [walletOpen, setWalletOpen] = useState(false); const [emailOpen, setEmailOpen] = useState(false); + const [typeOpen, setTypeOpen] = useState(true); const [referralCode, setReferralCode] = useState(""); const [isReferralCodeValid, setIsReferralCodeValid] = useState(false); const [isUserTyping, setIsUserTyping] = useState(false); @@ -50,6 +54,25 @@ const Login: React.FC = () => { ? "/business/explore" : "/influencer"; router.push(redirectRoute); + } else { + if (!isTwitterUserLoggedIn) { + const status = searchParams.get("authenticationStatus"); + const paramMessage = searchParams.get("message"); + const status_code = searchParams.get("status_code"); + if (status) { + notification( + paramMessage + ? paramMessage + : "Something went wrong, please try again later", + "error", + 5000 + ); + if (status_code == "404") { + setTypeOpen(false) + setLoginType("SIGNIN"); + } + } + } } }, [isTwitterUserLoggedIn]); @@ -79,6 +102,7 @@ const Login: React.FC = () => { }; const handleConnectX = () => { startTwitterAuthentication({ + loginType, role: loginAs == "Business" ? "business_owner" : "influencer", referral_code: isReferralCodeValid ? referralCode : "", }); @@ -90,19 +114,22 @@ const Login: React.FC = () => { `reward/check-referral-validity/?referral_code=${referralCode}` ); if (isSuccess) { - notification("Referral Code is Valid"); + notification("Invite Code is Valid"); setIsReferralCodeValid(true); } else { - notification("Invalid Referral Code", "error"); + notification("Invalid Invite Code", "error"); setIsReferralCodeValid(false); } setIsUserTyping(false); } catch (error) { - console.error("Error during referal code checking:", error); + console.error("Error during Invite code checking:", error); setIsReferralCodeValid(false); } }; + const isSectionDisabled = + loginType == "SIGNIN" && (isUserTyping || !isReferralCodeValid); + return ( { display: "flex", justifyContent: "center", alignItems: "center", + filter: !!loginType ? "none" : "blur(6px)", }} > - + { priority /> - Sign in to XFluencer as a {loginAs} + {loginType == "SIGNIN" + ? `Sign-up to XFluencer as a ${loginAs}` + : `Sign-in to XFluencer as a ${loginAs}`} {loginAs == "Business" @@ -131,13 +161,53 @@ const Login: React.FC = () => { + + { + setReferralCode(e.target.value); + setIsUserTyping(true); + }} + endAdornment={ + + + + + + } + /> + + {loginAs === "Business" && ( @@ -152,66 +222,21 @@ const Login: React.FC = () => { : "Sign in with your email or socials" } defaultExpanded={loginAs === "Influencer"} + isDisabled={isSectionDisabled} > - + - - {isReferralCodeValid && - !isUserTyping && - loginAs === "Influencer" ? ( - - Hurray!!! Referral Code {referralCode} Applied. - - ) : null} + - {loginAs === "Influencer" ? ( - - { - setReferralCode(e.target.value); - setIsUserTyping(true); - }} - endAdornment={ - - - - - - } - /> - - *Only for first time users - - - ) : null} {loginAs === "Business" ? ( @@ -219,26 +244,70 @@ const Login: React.FC = () => { ) : null} - - Login as {loginAs === "Business" ? "Influencer" : "Business"}? - + + {loginType === "SIGNIN" ? ( + setLoginType("LOGIN")} + > + Existing User?{" "} + + Sign-in + + + ) : ( + setLoginType("SIGNIN")} + > + New User?{" "} + + Sign-up + + + )} + + + {loginType == "SIGNIN" ? "Sign-Up as" : "Sign-in as"}{" "} + {loginAs === "Business" ? "Influencer" : "Business"}? + + {/* Wallet Model */} - + {/* Email Modal */} - + + ); }; diff --git a/src/ui/src/components/emailLoginModal/index.tsx b/src/ui/src/components/emailLoginModal/index.tsx index bdd02b16..eba4f529 100644 --- a/src/ui/src/components/emailLoginModal/index.tsx +++ b/src/ui/src/components/emailLoginModal/index.tsx @@ -21,11 +21,17 @@ import CloseIcon from "@mui/icons-material/Close"; type EmailLoginModalProps = { open: boolean; setOpen: React.Dispatch>; + referralCode?: string; + loginType?: string; + setLoginType?: React.Dispatch>; }; export default function EmailLoginModal({ open, setOpen, + referralCode, + loginType, + setLoginType, }: EmailLoginModalProps) { const [email, setEmail] = React.useState(""); const [otp, setOTP] = React.useState(""); @@ -37,15 +43,25 @@ export default function EmailLoginModal({ notification("Please enter email", "error"); return; } - const { isSuccess, message } = await postService("account/otp/", { - email, - }); + const { isSuccess, message, statusCode } = await postService( + "account/otp/", + { + email, + referral_code: referralCode, + loginType, + } + ); + if (isSuccess) { notification(message); setSecondsLeft(60); setStep(2); } else { notification(message, "error"); + if (statusCode == 404) { + setOpen(false); + if (setLoginType) setLoginType("SIGNIN"); + } } }; diff --git a/src/ui/src/components/web3Components/walletConnectModal/index.tsx b/src/ui/src/components/web3Components/walletConnectModal/index.tsx index ad3b94fa..4c5ecfbe 100644 --- a/src/ui/src/components/web3Components/walletConnectModal/index.tsx +++ b/src/ui/src/components/web3Components/walletConnectModal/index.tsx @@ -41,6 +41,9 @@ type WalletConnectModalProps = { setOpen: React.Dispatch>; connect?: boolean; onlyAddress?: boolean; + referralCode?: string; + loginType?: string; + setLoginType?: React.Dispatch>; }; const eampleWallets = [ @@ -61,23 +64,37 @@ export default function WalletConnectModal({ setOpen, connect = false, onlyAddress = false, + referralCode, + loginType, + setLoginType, }: WalletConnectModalProps) { const { publicKey, wallet, wallets } = useWallet(); const dispatch = useAppDispatch(); const [rawWalletAddress, setRawWalletAddress] = React.useState(""); const onSubmit = async (signature?: string, text?: string) => { - const requestBody = { + let requestBody = { wallet_address_id: publicKey?.toBase58(), wallet_provider_id: wallet?.adapter?.name, wallet_network_id: "solana", signature: signature ? signature : undefined, message: text ? text : undefined, + referral_code: !!referralCode ? referralCode : undefined, + loginType: !!loginType ? loginType : undefined, }; - const { isSuccess, data, message } = await postService( + const { isSuccess, data, message, statusCode } = await postService( connect ? "/account/connect-wallet/" : "/account/wallet-auth/", requestBody ); + if (!connect && statusCode == 404) { + notification( + message ? message : "Something went wrong, please try again later", + "error", + 5000 + ); + setOpen(false); + if (setLoginType) setLoginType("SIGNIN"); + } if (isSuccess) { if (!connect) { dispatch(loginReducer(data?.data)); diff --git a/src/ui/src/hooks/useTwitterAuth.ts b/src/ui/src/hooks/useTwitterAuth.ts index a666a516..c4479ad7 100644 --- a/src/ui/src/hooks/useTwitterAuth.ts +++ b/src/ui/src/hooks/useTwitterAuth.ts @@ -32,12 +32,16 @@ export default function useTwitterAuth() { const startTwitterAuthentication = async ({ role = "", referral_code = "", + loginType = "", }: { role: string; referral_code?: string; + loginType?: string; }) => { try { - window.location.href = `${BACKEND_URL}auth-twitter-user/${role}/auth/?referral_code=${referral_code}`; + if (!!loginType) { + window.location.href = `${BACKEND_URL}auth-twitter-user/${role}/auth/?referral_code=${referral_code}&login_type=${loginType}`; + } } catch (error) { console.error("Error initiating Twitter authentication:", error); } @@ -58,9 +62,9 @@ export default function useTwitterAuth() { await disconnect(); } if (pathname.includes(ROLE_NAME.INFLUENCER)) { - router.push(`/${ROLE_NAME.INFLUENCER}`); + router.push(`/login?role=Influencer`); } else { - router.push(`/business`); + router.push(`/login?role=Business`); } } else { notification(