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} - - + + + + {children} + + + ); } diff --git a/src/config/content/Registration/details.js b/src/config/content/Registration/details.js index 21888285..ac41f59d 100644 --- a/src/config/content/Registration/details.js +++ b/src/config/content/Registration/details.js @@ -25,159 +25,199 @@ export const formFields = [ { label: 'Silicon Institute of Technology, Bhubaneswar (SIT-BBSR)', value: 'Silicon Institute of Technology, Bhubaneswar (SIT-BBSR)', + id: '671a5be16748c70b7f893caa', }, { label: 'Odisha University of Technology and Research (OUTR)', value: 'Odisha University of Technology and Research (OUTR)', + id: '671a5be16748c70b7f893cab', }, { label: 'Gandhi Institute of Engineering and Technology, Gunupur (GIET Gunupur)', value: 'Gandhi Institute of Engineering and Technology, Gunupur (GIET Gunupur)', + id: '671a5be16748c70b7f893cac', }, { label: 'Centurion University of Technology and Management', value: 'Centurion University of Technology and Management', + id: '671a5be26748c70b7f893cad', }, { label: 'Biju Patnaik University of Technology (BPUT)', value: 'Biju Patnaik University of Technology (BPUT)', + id: '671a5be26748c70b7f893cae', }, { label: 'National Institute of Science and Technology, Berhampur (NIST Berhampur)', value: 'National Institute of Science and Technology, Berhampur (NIST Berhampur)', + id: '671a5be26748c70b7f893caf', }, { label: 'Kalinga Institute of Industrial Technology, Bhubaneswar (KIIT BBSR)', value: 'Kalinga Institute of Industrial Technology, Bhubaneswar (KIIT BBSR)', + id: '671a5be26748c70b7f893cb0', }, { label: 'C. V. Raman Global University, Bhubaneswar (CV RAMAN)', value: 'C. V. Raman Global University, Bhubaneswar (CV RAMAN)', + id: '671a5be26748c70b7f893cb1', }, { label: 'Parala Maharaja Engineering College', value: 'Parala Maharaja Engineering College', + id: '671a5be36748c70b7f893cb2', }, { label: 'Indira Gandhi Institute of Technology, Sarang', value: 'Indira Gandhi Institute of Technology, Sarang', + id: '671a5be36748c70b7f893cb3', }, { label: 'Veer Surendra Sai University of Technology (VSSUT)', value: 'Veer Surendra Sai University of Technology (VSSUT)', + id: '671a5be36748c70b7f893cb4', }, { label: 'Gandhi Institute for Technological Advancement (GITA)', value: 'Gandhi Institute for Technological Advancement (GITA)', + id: '671a5be36748c70b7f893cb5', }, { label: 'Pragati Maidan Engineering College (PMEC)', value: 'Pragati Maidan Engineering College (PMEC)', + id: '671a5be36748c70b7f893cb6', }, { label: 'Bhubaneswar Engineering College (BEC BBSR)', value: 'Bhubaneswar Engineering College (BEC BBSR)', + id: '671a5be46748c70b7f893cb7', }, { label: 'Indian Institute of Science Education and Research, Berhampur (IISER Berhampur)', value: 'Indian Institute of Science Education and Research, Berhampur (IISER Berhampur)', + id: '671a5be46748c70b7f893cb8', }, { label: 'International Institute of Information Technology, Bhubaneswar (IIIT BBSR)', value: 'International Institute of Information Technology, Bhubaneswar (IIIT BBSR)', + id: '671a5be46748c70b7f893cb9', }, { label: 'Indian Institute of Technology, Bhubaneswar (IIT BBSR)', value: 'Indian Institute of Technology, Bhubaneswar (IIT BBSR)', + id: '671a5be46748c70b7f893cba', }, { label: 'Rajendra Institute of Medical Sciences, Rourkela (RIMS, Rourkela)', value: 'Rajendra Institute of Medical Sciences, Rourkela (RIMS, Rourkela)', + id: '671a5be46748c70b7f893cbb', }, { label: 'Municipal College, Rourkela', value: 'Municipal College, Rourkela', + id: '671a5be56748c70b7f893cbc', }, { label: 'Government Autonomous College, Rourkela', value: 'Government Autonomous College, Rourkela', + id: '671a5be56748c70b7f893cbd', }, { label: 'Badriprasad Institute of Technology, Sambalpur', value: 'Badriprasad Institute of Technology, Sambalpur', + id: '671a5be56748c70b7f893cbe', }, { label: 'Sambalpur University', value: 'Sambalpur University', + id: '671a5be56748c70b7f893cbf', }, { label: 'Utkalmani Gopabandhu College', value: 'Utkalmani Gopabandhu College', + id: '671a5be56748c70b7f893cc0', }, { label: 'Government College of Engineering, Kalahandi, Bhawanipatna (GCEK Bhawanipatna)', value: 'Government College of Engineering, Kalahandi, Bhawanipatna (GCEK Bhawanipatna)', + id: '671a5be66748c70b7f893cc1', }, { label: 'Birla Institute of Technology, Durg (BIT Durg)', value: 'Birla Institute of Technology, Durg (BIT Durg)', + id: '671a5be66748c70b7f893cc2', }, { label: 'Birla Institute of Technology, Mesra (BIT Mesra)', value: 'Birla Institute of Technology, Mesra (BIT Mesra)', + id: '671a5be66748c70b7f893cc3', }, { label: 'National Institute of Technology, Jamshedpur (NIT Jamshedpur)', value: 'National Institute of Technology, Jamshedpur (NIT Jamshedpur)', + id: '671a5be66748c70b7f893cc4', }, { label: 'National Institute of Technology, Durgapur (NIT Durgapur)', value: 'National Institute of Technology, Durgapur (NIT Durgapur)', + id: '671a5be66748c70b7f893cc5', }, { label: 'Indian Institute of Technology, Dhanbad (IIT Dhanbad)', value: 'Indian Institute of Technology, Dhanbad (IIT Dhanbad)', + id: '671a5be66748c70b7f893cc6', }, { label: 'Indian Institute of Technology, Bhilai (IIT Bhilai)', value: 'Indian Institute of Technology, Bhilai (IIT Bhilai)', + id: '671a5be76748c70b7f893cc7', }, { label: 'National Institute of Technology, Raipur (NIT Raipur)', value: 'National Institute of Technology, Raipur (NIT Raipur)', + id: '671a5be76748c70b7f893cc8', }, { label: 'Jadavpur University', value: 'Jadavpur University', + id: '671a5be76748c70b7f893cc9', }, { label: 'National Institute of Technology, Patna (NIT Patna)', value: 'National Institute of Technology, Patna (NIT Patna)', + id: '671a5be76748c70b7f893cca', }, { label: 'National Institute of Technology, Rourkela (NIT Rourkela)', value: 'National Institute of Technology, Rourkela (NIT Rourkela)', + id: '671a5be76748c70b7f893ccb', }, { label: 'National Institute of Technology, Andhra Pradesh (NIT Andhra Pradesh)', value: 'National Institute of Technology, Andhra Pradesh (NIT Andhra Pradesh)', + id: '671a5be76748c70b7f893ccc', }, { label: 'Vellore Institute of Technology, Vellore (VIT Vellore)', value: 'Vellore Institute of Technology, Vellore (VIT Vellore)', + id: '671a5be86748c70b7f893ccc', + }, + { + label: 'Institute of Technical Education and Research SOA', + value: 'Institute of Technical Education and Research SOA', + id: 'notAllowed', }, { label: 'Trident Academy of Technology, Bhubaneswar (Trident BBSR)', value: 'Trident Academy of Technology, Bhubaneswar (Trident BBSR)', + id: '671a5be86748c70b7f893ccd', }, { label: 'Indian Institute of Engineering Science and Technology, Shibpur (IIEST Shibpur)', value: 'Indian Institute of Engineering Science and Technology, Shibpur (IIEST Shibpur)', + id: '671a5be86748c70b7f893cce', }, - { - label: 'Others', - value: 'others', - }, + { label: 'Others', value: 'others' }, ], id: 'institute', className: 'w-full', @@ -194,7 +234,6 @@ export const formFields = [ type: 'text', id: 'rollNumber', }, - { label: 'Upload College ID', type: 'file', @@ -211,11 +250,11 @@ export const formFields = [ type: 'select', options: [ { - value: 'female', + value: 'FEMALE', label: 'Female', }, { - value: 'male', + value: 'MALE', label: 'Male', }, ], @@ -231,12 +270,12 @@ export const formFields = [ { label: 'Upload Payment Screenshot', type: 'file', - id: 'idCard', + id: 'payment', }, { label: 'Transaction ID', type: 'text', - id: 'TranscId', + id: 'transactionID', className: 'w-full', }, { diff --git a/src/config/zodd/userDetailsSchema.js b/src/config/zodd/userDetailsSchema.js index c9eb11f5..242d6f95 100644 --- a/src/config/zodd/userDetailsSchema.js +++ b/src/config/zodd/userDetailsSchema.js @@ -1,4 +1,5 @@ import { z } from 'zod'; + import { notAllowedInstitutes } from '../content/Registration/details'; export const userSchema = z.object({ @@ -6,7 +7,9 @@ export const userSchema = z.object({ .string() .min(1, 'Name is required') .regex(/^[a-zA-Z\s]+$/, 'Name can only contain letters'), - email: z.string().min(1, 'Email is required').email('Invalid email format'), + email: z.string().regex(/^[a-z0-9](\.?[a-z0-9]){5,}@g(oogle)?mail\.com$/, { + message: 'Invalid email address. Please use your Gmail address', + }), phone: z .string() .length(10, 'Phone number must be exactly 10 digits') @@ -18,7 +21,13 @@ export const userSchema = z.object({ message: "Students from this institute have been officially barred from participating in INNO'24", }), - university: z.string().min(1, 'University name is required'), + university: z + .string() + .min(1, 'University name is required') + .refine((val) => notAllowedInstitutes.indexOf(val.toUpperCase()) === -1, { + message: + "Students from this institute have been officially barred from participating in INNO'24", + }), rollNumber: z.string().min(1, 'Roll number is required'), referralCode: z .string() @@ -29,11 +38,12 @@ export const userSchema = z.object({ permission: z.boolean().refine((val) => val === true, { message: "You must have permission from your institute's authority", }), - gender: z.enum(['male', 'female'], { + gender: z.enum(['MALE', 'FEMALE'], { errorMap: () => ({ message: 'Gender selection is required and must be either male or female' }), }), undertaking: z.boolean().refine((val) => val === true, { message: 'You must agree to the terms and conditions', }), campusAmbassador: z.boolean().optional(), + transactionID: z.string().min(1, 'Transaction ID is required'), }); diff --git a/src/context/auth-context.jsx b/src/context/auth-context.jsx index 3567192e..e6cb8505 100644 --- a/src/context/auth-context.jsx +++ b/src/context/auth-context.jsx @@ -1,14 +1,16 @@ import { createContext, useState } from 'react'; + +import Cookies from 'js-cookie'; import { toast } from 'react-hot-toast'; + import { signInWithGoogle, signOutUser } from '@/firebase/auth'; -import Cookies from 'js-cookie'; -import { auth } from '@/firebase/firebase'; export const AuthContext = createContext(); export const AuthProvider = ({ children }) => { const [userInfo, setUserInfo] = useState({}); const [authLoading, setAuthLoading] = useState(false); + const [userMongoId, setUserMongoId] = useState(''); const handleGoogleSignIn = async () => { setAuthLoading(true); @@ -46,6 +48,7 @@ export const AuthProvider = ({ children }) => { await signOutUser(); setUserInfo({}); Cookies.remove('userData'); + Cookies.remove('userDataDB'); toast.success('Successfully signed out.'); } catch (error) { console.error('Error signing out:', error); @@ -55,7 +58,15 @@ export const AuthProvider = ({ children }) => { return ( {children} diff --git a/src/graphql/mutations/eventMutations.js b/src/graphql/mutations/eventMutations.js new file mode 100644 index 00000000..33441d78 --- /dev/null +++ b/src/graphql/mutations/eventMutations.js @@ -0,0 +1,14 @@ +import { gql } from '@apollo/client'; + +export const REGISTER_EVENT = gql` + mutation RegisterEvent($eventRegistration: EventRegistrationCreateInputType!) { + createEventRegistration(eventRegistration: $eventRegistration) { + event { + name + } + user { + name + } + } + } +`; diff --git a/src/graphql/mutations/organizationMutations.js b/src/graphql/mutations/organizationMutations.js new file mode 100644 index 00000000..28124c0e --- /dev/null +++ b/src/graphql/mutations/organizationMutations.js @@ -0,0 +1,9 @@ +import { gql } from '@apollo/client'; + +export const REGISTER_ORG = gql` + mutation CreateMultipleOrgs($orgs: [OrgCreateInputType!]!) { + createMultipleOrgs(orgs: $orgs) { + id + } + } +`; diff --git a/src/graphql/mutations/userMutations.js b/src/graphql/mutations/userMutations.js new file mode 100644 index 00000000..d2242afb --- /dev/null +++ b/src/graphql/mutations/userMutations.js @@ -0,0 +1,10 @@ +import { gql } from '@apollo/client'; + +export const REGISTER_USER = gql` + mutation RegisterUser($user: UserCreateInputType!) { + createUser(user: $user) { + id + hasPaid + } + } +`; diff --git a/src/graphql/queries/eventQueries.js b/src/graphql/queries/eventQueries.js new file mode 100644 index 00000000..fac9ead1 --- /dev/null +++ b/src/graphql/queries/eventQueries.js @@ -0,0 +1,29 @@ +import { gql } from '@apollo/client'; + +export const GET_EVENTS_BY_TYPE = gql` + query GetEventsByType($type: String) { + getEventsByType(type: $type) { + name + id + } + } +`; + +export const GET_USER_REGISTERED_EVENTS = gql` + query GetUserRegisteredEvents($userId: ID) { + eventRegistration(userID: $userId) { + eventID + event { + name + } + } + } +`; + +export const GET_USER_REGISTERED_EVENT_IDS = gql` + query GetUserRegisteredEventIds($userId: ID) { + eventRegistration(userID: $userId) { + eventID + } + } +`; diff --git a/src/graphql/queries/organizationQueries.js b/src/graphql/queries/organizationQueries.js new file mode 100644 index 00000000..3469ce4e --- /dev/null +++ b/src/graphql/queries/organizationQueries.js @@ -0,0 +1,21 @@ +import { gql } from '@apollo/client'; + +export const GET_ORGANIZATION_STATS = gql` + query GetOrganizationStats($orgType: OrgType) { + org(orgType: $orgType) { + name + registrations + studentCount + } + } +`; + +export const GET_GENDER_DISTRIBUTION = gql` + query GetGenderDistribution($orgId: ID) { + user(orgID: $orgId) { + data { + gender + } + } + } +`; diff --git a/src/graphql/queries/userQueries.js b/src/graphql/queries/userQueries.js new file mode 100644 index 00000000..24b48b9e --- /dev/null +++ b/src/graphql/queries/userQueries.js @@ -0,0 +1,12 @@ +import { gql } from '@apollo/client'; + +export const GET_USER_BY_UID = gql` + query GetUserByUid($uid: ID) { + user(uid: $uid) { + data { + name + id + } + } + } +`; diff --git a/src/lib/apollo-client.js b/src/lib/apollo-client.js new file mode 100644 index 00000000..37d159df --- /dev/null +++ b/src/lib/apollo-client.js @@ -0,0 +1,84 @@ +import Cookies from 'js-cookie'; + +import { ApolloClient, from, HttpLink, InMemoryCache } from '@apollo/client'; +import { setContext } from '@apollo/client/link/context'; +import { onError } from '@apollo/client/link/error'; + +const MAX_RETRIES = 3; +const RETRY_TIMEOUT = 8000; + +const reconnectFetch = async (uri, options, retries = 0) => { + const controller = new AbortController(); + const timeoutId = setTimeout(() => controller.abort(), RETRY_TIMEOUT); + + try { + const response = await fetch(uri, { + ...options, + signal: controller.signal, + }); + + clearTimeout(timeoutId); + + if (!response.ok) { + throw new Error(`HTTP error! Status: ${response.status}`); + } + + console.log('Connected to server successfully'); + return response; + } catch (error) { + clearTimeout(timeoutId); + + if (retries < MAX_RETRIES) { + console.log(`Retrying (${retries + 1}/${MAX_RETRIES})...`); + return new Promise((resolve) => + setTimeout(() => resolve(reconnectFetch(uri, options, retries + 1)), RETRY_TIMEOUT), + ); + } + + throw error; + } +}; + +const httpLink = new HttpLink({ + uri: process.env.NEXT_PUBLIC_APOLLO_URI, + fetch: reconnectFetch, +}); + +const authLink = setContext((_, { headers }) => { + let token; + + if (typeof window !== 'undefined') { + try { + const userDataFromCookie = Cookies.get('userData'); + const parsedUserData = JSON.parse(userDataFromCookie); + token = parsedUserData.token; + } catch (error) { + console.error(error); + throw new Error('Error parsing userData from cookie'); + } + } + + return { + headers: { + ...headers, + Authorization: token || '', + }, + }; +}); + +const errorLink = onError(({ graphQLErrors, networkError }) => { + if (graphQLErrors) { + graphQLErrors.forEach(({ message, locations, path }) => + console.error(`[GraphQL error]: Message: ${message}, Location: ${locations}, Path: ${path}`), + ); + } + + if (networkError) { + console.error(`[Network error]: ${networkError}`); + } +}); + +export const client = new ApolloClient({ + link: from([errorLink, authLink.concat(httpLink)]), + cache: new InMemoryCache(), +});
Your payment is being verified! You will be mailed shortly
Use this while referring to your peers.