diff --git a/.env.sample b/.env.sample new file mode 100644 index 00000000..f2817e41 --- /dev/null +++ b/.env.sample @@ -0,0 +1,13 @@ +NEXT_PUBLIC_CLOUDINARY_CLOUD_NAME="" +NEXT_PUBLIC_CLOUDINARY_UPLOAD_PRESET="" + + +NEXT_PUBLIC_FIREBASE_API_KEY="" +NEXT_PUBLIC_FIREBASE_AUTH_DOMAIN="" +NEXT_PUBLIC_FIREBASE_PROJECT_ID= "" +NEXT_PUBLIC_FIREBASE_STORAGE_BUCKET= "" +NEXT_PUBLIC_FIREBASE_MESSAGING_SENDER_ID= "" +NEXT_PUBLIC_FIREBASE_APP_ID= "" +NEXT_PUBLIC_FIREBASE_MEASUREMENT_ID= "" + +NEXT_PUBLIC_APOLLO_URI="" diff --git a/package.json b/package.json index 89f139b5..9f3f7f0d 100644 --- a/package.json +++ b/package.json @@ -33,6 +33,7 @@ "prettier:fix": "prettier --write \"./**/*.{ts,tsx,js,jsx,css,scss,md}\" --ignore-pattern node_modules/" }, "dependencies": { + "@apollo/client": "^3.11.8", "@babel/core": "^7.25.2", "@nextui-org/react": "^2.4.8", "@react-three/drei": "^9.112.1", @@ -42,6 +43,7 @@ "firebase": "^10.14.1", "flowbite-react": "^0.10.2", "framer-motion": "^11.5.6", + "graphql": "^16.9.0", "hamburger-react": "^2.5.1", "js-cookie": "^3.0.5", "lucide-react": "^0.452.0", @@ -53,6 +55,7 @@ "swiper": "^11.1.14", "three": "^0.168.0", "twin.macro": "^3.4.1", + "uuidv4": "^6.2.13", "zod": "^3.23.8" }, "devDependencies": { diff --git a/src/app/register/page.jsx b/src/app/register/page.jsx index e7937d16..6f3dd303 100644 --- a/src/app/register/page.jsx +++ b/src/app/register/page.jsx @@ -1,32 +1,39 @@ 'use client'; -import { useState, useContext } from 'react'; +import { useContext, useEffect, useState } from 'react'; + +import Cookies from 'js-cookie'; +import Link from 'next/link'; +import { useRouter } from 'next/navigation'; +import toast from 'react-hot-toast'; +import { v4 } from 'uuid'; + +import CampusAmbassador from '@/components/Register/CampusAmbassador/CampusAmbassador'; +import FileInput from '@/components/Register/FileInput/FileInput'; +import CheckBox from '@/components/Register/InputCheckBox/CheckBox'; +import InputField from '@/components/Register/InputField/InputField'; +import SelectField from '@/components/Register/SelectField/SelectField'; +import { PrimaryButton } from '@/components/shared/Typography/Buttons'; +import { formFields, undertakingContent } from '@/config/content/Registration/details'; +import { userSchema } from '@/config/zodd/userDetailsSchema'; +import { AuthContext } from '@/context/auth-context'; +import { REGISTER_ORG } from '@/graphql/mutations/organizationMutations'; +import { REGISTER_USER } from '@/graphql/mutations/userMutations'; +import { useIsLoggedIn } from '@/hooks/useIsLoggedIn'; +import { useUserDetails } from '@/hooks/useUserDetails'; +import handleLoadingAndToast from '@/utils/handleLoadingToast'; +import { uploadToCloudinary } from '@/utils/uploadToCloudinary'; +import { useMutation } from '@apollo/client'; + import { + Moon, + PaymentPolicyInfo, RegisterContainer, RegisterForm, RegisterHeading, - RegsiterButton, RegisterInnerContainer, - Moon, + RegsiterButton, UndertakingLink, - PaymentPolicyInfo, - PaymentHeading, } from './register.styles'; -import Link from 'next/link'; -import InputField from '@/components/Register/InputField/InputField'; -import SelectField from '@/components/Register/SelectField/SelectField'; -import CheckBox from '@/components/Register/InputCheckBox/CheckBox'; -import FileInput from '@/components/Register/FileInput/FileInput'; -import { formFields, undertakingContent } from '@/config/content/Registration/details'; -import { uploadToCloudinary } from '../../utils/uploadToCloudinary'; -import handleLoadingAndToast from '../../utils/handleLoadingToast'; -import { userSchema } from '@/config/zodd/userDetailsSchema'; -import { useIsLoggedIn } from '@/hooks/useIsLoggedIn'; -import CampusAmbassador from '@/components/Register/CampusAmbassador/CampusAmbassador'; -import { PrimaryButton } from '@/components/shared/Typography/Buttons'; -import { AuthContext } from '@/context/auth-context'; -import { RegistrationModal } from './RegistrationModal'; -import toast from 'react-hot-toast'; -import { QrButton } from '@/components/Register/Qrscanner/QrButton'; function Page() { const [userDetails, setUserDetails] = useState({ @@ -34,6 +41,7 @@ function Page() { email: '', phone: '', institute: '', + instituteId: '', university: '', rollNumber: '', idCard: '', @@ -43,27 +51,33 @@ function Page() { payment: '', undertaking: false, campusAmbassador: false, + payment: '', + transactionID: '', }); - const [loading, setLoading] = useState(false); + const getUserDetails = useUserDetails(); const [errors, setErrors] = useState({}); const isLoggedIn = useIsLoggedIn(); - + const [loading, setLoading] = useState(false); const { handleGoogleSignIn, authLoading } = useContext(AuthContext); + const [registerUser] = useMutation(REGISTER_USER); + const [registerCollege] = useMutation(REGISTER_ORG); + const router = useRouter(); + const storedUserId = getUserDetails().uid; async function handleChange(event) { const { name, value, type, checked } = event.target; if (type === 'file') { try { - // const imageUrl = await handleLoadingAndToast( - // uploadToCloudinary(event.target.files[0]), - // 'Uploading Image...', - // `${name.toUpperCase()} uploaded successfully`, - // `${name.toUpperCase()} upload failed!`, - // setLoading, - // ); + const imageUrl = await handleLoadingAndToast( + uploadToCloudinary(event.target.files[0]), + 'Uploading Image...', + `${name.toUpperCase()} uploaded successfully`, + `${name.toUpperCase()} upload failed!`, + setLoading, + ); setUserDetails((prev) => ({ ...prev, - [name]: 'imageUrl', + [name]: imageUrl, })); setErrors((prev) => ({ ...prev, [name]: '' })); } catch (error) { @@ -83,12 +97,6 @@ function Page() { })); } - function handleSubmit() { - const isFormValid = validateForm(); - if (!isFormValid) return; - console.log(userDetails); - } - function validateForm() { const validationResult = userSchema.safeParse(userDetails); if (!validationResult.success) { @@ -151,13 +159,7 @@ function Page() { error={errors[field.id]} /> ); - case 'button': - return ( - <> - Payment Section - - - ); + case 'checkbox': return ( { + router.push('/'); + }, 1000); + } catch (error) { + console.error(error); + toast.error('Registration failed! Please try again'); + } finally { + setLoading(false); + } } + useEffect(() => { + if (Cookies.get('userDataDB')) { + router.push('/'); + toast.success('You have been already registered!', { + duration: 5000, + }); + } + }, []); + return ( - {!isLoggedIn ? ( + {isLoggedIn ? ( Register @@ -209,7 +276,7 @@ function Page() { isCampusAmbassador={userDetails.campusAmbassador} /> - Submit + {loading ? 'Loading...' : 'Register'} ) : ( @@ -217,8 +284,6 @@ function Page() { {authLoading ? 'Loading...' : 'Sign In with Google'} )} - - setModalOpen(false)} /> ); } diff --git a/src/app/register/register.styles.jsx b/src/app/register/register.styles.jsx index 57af1929..746c7fa4 100644 --- a/src/app/register/register.styles.jsx +++ b/src/app/register/register.styles.jsx @@ -1,7 +1,8 @@ import styled from 'styled-components'; import tw from 'twin.macro'; -import { Heading1, Heading2 } from '@/components/shared/Typography/Headings'; + import { PrimaryButton } from '@/components/shared/Typography/Buttons'; +import { Heading1, Heading2 } from '@/components/shared/Typography/Headings'; export const RegisterHeading = styled(Heading1)` ${tw`text-center text-2xl xxs:text-3xl ssm:text-4xl md:text-5xl lg:text-6xl my-0 py-0 font-spaceX text-white`} @@ -57,7 +58,7 @@ export const Moon = styled.div` `; export const UndertakingLink = styled.a` - ${tw`text-base font-prompt font-semibold hover:underline transition-all duration-200 ease-in-out`} + ${tw`text-base font-prompt font-semibold text-blue-400 hover:underline transition-all duration-200 ease-in-out`} `; export const PaymentPolicyInfo = styled.div` diff --git a/src/components/EventsPage/CardComponents/PreviewCard.jsx b/src/components/EventsPage/CardComponents/PreviewCard.jsx index 70637311..4ca6ca8d 100644 --- a/src/components/EventsPage/CardComponents/PreviewCard.jsx +++ b/src/components/EventsPage/CardComponents/PreviewCard.jsx @@ -1,3 +1,5 @@ +import toast from 'react-hot-toast'; + import { PreviewButtonContainer, PreviewCardContainer, @@ -12,13 +14,18 @@ function PreviewCard({ ImageURL, PreviewDescription = '' }) { const truncatedDescription = words.length > 30 ? words.slice(0, 50).join(' ') + '...' : PreviewDescription; + function handleToast() { + toast('You can register after you are verified!', { + icon: '🚀', + }); + } return ( {truncatedDescription} Rulebook - Register + Register ); diff --git a/src/components/ProfileMenu/ProfileMenu.jsx b/src/components/ProfileMenu/ProfileMenu.jsx index bc0de82a..c3fa3598 100644 --- a/src/components/ProfileMenu/ProfileMenu.jsx +++ b/src/components/ProfileMenu/ProfileMenu.jsx @@ -1,26 +1,40 @@ 'use client'; -import { useUserDetails } from '@/hooks/useUserDetails'; +import { useContext, useEffect, useState } from 'react'; + +import Cookies from 'js-cookie'; +import { usePathname, useRouter } from 'next/navigation'; +import { toast } from 'react-hot-toast'; + import { AuthContext } from '@/context/auth-context'; -import { useContext } from 'react'; +import { useUserDetails } from '@/hooks/useUserDetails'; + import { + CloseButton, Container, + LogoutButton, MenuCard, - CloseButton, - UserName, - UserEmail, MenuContent, MenuLinks, - StyledLink, - LogoutButton, - menuVariants, menuTransition, + menuVariants, ProfileImage, + StyledLink, + UserEmail, + UserName, } from './ProfileMenu.styles'; function ProfileMenu({ handleProfileToggle, handleNavClose }) { const { handleSignOut } = useContext(AuthContext); + const [isRegistered, setIsRegistered] = useState(false); const getUserDetails = useUserDetails(); const user = getUserDetails(); + const router = useRouter(); + const path = usePathname(); + + // const { data: userDataInDb } = useSuspenseQuery( + // GET_USER_BY_UID, + // user.uid ? { variables: { uid: user.uid } } : skipToken, + // ); const handleLogout = () => { handleSignOut(); @@ -33,6 +47,24 @@ function ProfileMenu({ handleProfileToggle, handleNavClose }) { handleNavClose(false); }; + useEffect(() => { + const mongoId = Cookies.get('userDataDB'); + + console.log('mongoId:', mongoId); + // userDataInDb?.user.data.length > 0; + if (mongoId) { + if (path === '/register') { + toast.success('You are already registered!'); + router.push('/'); + } + setIsRegistered(true); + } else { + setIsRegistered(false); + } + + // console.log('userDataInDb:', userDataInDb); + }, []); + return ( {user?.name} {user?.email} - - Complete Your Registration - + {isRegistered ? ( +

Your payment is being verified! You will be mailed shortly

+ ) : ( + + Complete Your Registration + + )} Logout
diff --git a/src/components/Register/CampusAmbassador/CampusAmbassador.jsx b/src/components/Register/CampusAmbassador/CampusAmbassador.jsx index fa8b53dc..d310139e 100644 --- a/src/components/Register/CampusAmbassador/CampusAmbassador.jsx +++ b/src/components/Register/CampusAmbassador/CampusAmbassador.jsx @@ -1,16 +1,18 @@ -import CheckBox from '../InputCheckBox/CheckBox'; import { beamImage, - campusAmbassadorInput, campusAbassadorPara, + campusAmbassadorInput, } from '@/config/content/Registration/details'; + +import CheckBox from '../InputCheckBox/CheckBox'; import { - Container, BeamImage, - Title, - Description, CheckBoxWrapper, + Container, + Description, + Title, } from './CampusAmbassador.styles'; + function CampusAmbassador({ handleChange, userReferral, isCampusAmbassador }) { function isUserRefferalCorrect(referralCode) { return referralCode.length === 10 && /^\d+$/.test(String(referralCode)); @@ -38,7 +40,7 @@ function CampusAmbassador({ handleChange, userReferral, isCampusAmbassador }) { > Your referral code is {userReferral}
-

Use this while referring to your peers.

+ Use this while referring to your peers. )}
diff --git a/src/components/Register/SelectField/SelectField.jsx b/src/components/Register/SelectField/SelectField.jsx index 18dc44b5..0c71278e 100644 --- a/src/components/Register/SelectField/SelectField.jsx +++ b/src/components/Register/SelectField/SelectField.jsx @@ -1,6 +1,9 @@ 'use client'; +import { useEffect, useRef, useState } from 'react'; + +import { toast } from 'react-hot-toast'; + import { Label } from '../FileInput/FileInput.styles'; -import { useState, useEffect, useRef } from 'react'; import InputField from '../InputField/InputField'; import { ErrorMessage } from '../InputField/InputField.styles'; import { @@ -40,7 +43,13 @@ function SelectField({ const handleToggle = () => setIsOpen(!isOpen); - const handleSelectChange = (option) => { + const handleSelectChange = (option, id) => { + if (id === 'notAllowed') { + toast.error( + "Students from this institute have been officially barred from participating in INNO'24", + ); + return; + } setSelectedOption(option); setIsOpen(false); setErrors((prevState) => ({ @@ -53,6 +62,11 @@ function SelectField({ [name]: otherInstituteName, })); } else { + handleSelect((prevState) => ({ + ...prevState, + instituteId: id, + })); + handleSelect((prevState) => ({ ...prevState, [name]: option, @@ -116,7 +130,10 @@ function SelectField({ {isOpen && ( {sortedOptions.map((option, index) => ( - handleSelectChange(option.value)}> + handleSelectChange(option.value, option.id)} + > {option.label} ))} diff --git a/src/components/shared/HOC/Hoc.jsx b/src/components/shared/HOC/Hoc.jsx index 9ba7bfff..724e3f7c 100644 --- a/src/components/shared/HOC/Hoc.jsx +++ b/src/components/shared/HOC/Hoc.jsx @@ -2,14 +2,18 @@ import { AuthProvider } from '@/context/auth-context'; import { Footer } from '../../Marginals/Footer/Footer'; import Navbar from '../../Marginals/navbar/navbar'; +import { ApolloProvider } from '@apollo/client'; +import { client } from '@/lib/apollo-client'; function Hoc({ children }) { return ( - - -
{children}
-