From f73ce79eb1adb09a8ab468199e402b772697052e Mon Sep 17 00:00:00 2001 From: Vignesh Duraisamy Date: Fri, 8 Dec 2023 22:00:06 +0530 Subject: [PATCH 01/14] feat: Auth UI --- src/app/login/page.tsx | 9 + src/assets/images/arrow_back_auth.svg | 11 + src/assets/images/auth-bg-vert.svg | 1 + src/assets/images/auth-bg.svg | 35 +++ src/assets/images/button-bg.svg | 4 + .../AuthLayout/AuthLayout.module.css | 162 ++++++++++++ src/components/AuthLayout/AuthLayout.tsx | 50 ++++ src/components/AuthLayout/form.module.css | 246 ++++++++++++++++++ src/components/AuthLayout/input.tsx | 24 ++ src/components/AuthLayout/login.tsx | 174 +++++++++++++ src/components/AuthLayout/type.d.ts | 17 ++ src/components/index.ts | 1 + 12 files changed, 734 insertions(+) create mode 100644 src/app/login/page.tsx create mode 100644 src/assets/images/arrow_back_auth.svg create mode 100644 src/assets/images/auth-bg-vert.svg create mode 100644 src/assets/images/auth-bg.svg create mode 100644 src/assets/images/button-bg.svg create mode 100644 src/components/AuthLayout/AuthLayout.module.css create mode 100644 src/components/AuthLayout/AuthLayout.tsx create mode 100644 src/components/AuthLayout/form.module.css create mode 100644 src/components/AuthLayout/input.tsx create mode 100644 src/components/AuthLayout/login.tsx create mode 100644 src/components/AuthLayout/type.d.ts diff --git a/src/app/login/page.tsx b/src/app/login/page.tsx new file mode 100644 index 0000000..269bb22 --- /dev/null +++ b/src/app/login/page.tsx @@ -0,0 +1,9 @@ +'use client'; + +import { AuthLayout } from '@/components'; + +const AuthPage = () => { + return ; +}; + +export default AuthPage; diff --git a/src/assets/images/arrow_back_auth.svg b/src/assets/images/arrow_back_auth.svg new file mode 100644 index 0000000..f0ed780 --- /dev/null +++ b/src/assets/images/arrow_back_auth.svg @@ -0,0 +1,11 @@ + + + + + + + + + + + diff --git a/src/assets/images/auth-bg-vert.svg b/src/assets/images/auth-bg-vert.svg new file mode 100644 index 0000000..fe8428f --- /dev/null +++ b/src/assets/images/auth-bg-vert.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/assets/images/auth-bg.svg b/src/assets/images/auth-bg.svg new file mode 100644 index 0000000..d84ce76 --- /dev/null +++ b/src/assets/images/auth-bg.svg @@ -0,0 +1,35 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/assets/images/button-bg.svg b/src/assets/images/button-bg.svg new file mode 100644 index 0000000..f002318 --- /dev/null +++ b/src/assets/images/button-bg.svg @@ -0,0 +1,4 @@ + + + + diff --git a/src/components/AuthLayout/AuthLayout.module.css b/src/components/AuthLayout/AuthLayout.module.css new file mode 100644 index 0000000..1c45489 --- /dev/null +++ b/src/components/AuthLayout/AuthLayout.module.css @@ -0,0 +1,162 @@ +.authWrapper { + height: 100vh; + width: 100vw; + background-color: #000000; + display: flex; + justify-content: center; + align-items: center; + font-family: 'ROG', sans-serif; +} + +.authContainer { + width: calc(100vw); + height: calc(100vh); + display: flex; + flex-direction: column; +} + +.authHeader { + flex-grow: 1; + display: flex; + flex-direction: row; + font-family: 'Orbitron'; + padding-left: 3rem; +} + +.authBody { + flex-grow: 30; + display: flex; + flex-direction: row; +} + +.homeNavLink { + color: #cdcdcd; + flex-grow: 1; + display: flex; + align-items: center; +} + +.homeNavLink:hover { + cursor: pointer; + text-decoration: underline; +} + +.homeNavLinkText { + margin-left: 0.5rem; +} + +.pragyanLogoSection { + display: flex; + justify-content: flex-end; + align-items: flex-end; + padding-right: 3rem; +} + +.pragyanLogo { + height: 60px; + width: auto; +} + +.pragyanLogo:hover { + cursor: pointer; +} + +.formWrapper { + flex-grow: 20; + display: flex; + flex-direction: column; + justify-content: center; + align-items: center; +} + +.formHeaderTextContainer { + display: flex; + flex-direction: column; + justify-content: center; + align-items: center; + width: 100%; + color: white; + font-size: 3rem; + font-weight: 700; + margin-top: 2rem; +} + +.formHeaderText { + border-left: 2px solid #397885; + border-right: 2px solid #397885; + padding: 0rem 1rem; + text-align: center; +} + +.formHeaderText::before { + content: ''; + display: block; + width: 10%; + position: relative; + left: -3.6%; + top: -2px; + height: 2px; + background-color: #397885; +} +.formHeaderText::after { + content: ''; + display: block; + width: 10%; + position: relative; + left: 93.6%; + bottom: -2px; + height: 2px; + background-color: #397885; +} + +.formHeaderTextHighLight { + padding: 5px; + background: linear-gradient(180deg, rgba(108, 186, 206, 0) 0%, #6cbace 100%); +} + +.formBodyContainer { + height: 100%; + width: 100%; + display: flex; + justify-content: center; +} + +.formArea { + width: 100%; + max-width: 80%; + height: 100%; + display: flex; + justify-content: center; + align-items: center; +} + +@media screen and (max-width: 850px) { + .formHeaderText { + font-size: 2.5rem; + } +} + +@media screen and (max-width: 500px) { + .formHeaderTextContainer { + margin-top: 3rem; + } + .pragyanLogo { + height: 40px; + padding: none; + } + .authHeader { + padding-left: 2rem; + } + .formHeaderText { + font-size: 2rem; + } +} + +@media screen and (max-width: 400px) { + .formArea { + max-width: 100%; + } + .formHeaderText { + font-size: 1.5rem; + } +} diff --git a/src/components/AuthLayout/AuthLayout.tsx b/src/components/AuthLayout/AuthLayout.tsx new file mode 100644 index 0000000..f6273fc --- /dev/null +++ b/src/components/AuthLayout/AuthLayout.tsx @@ -0,0 +1,50 @@ +import React from 'react'; +import Image from 'next/image'; +import Link from 'next/link'; +import { Login } from './login'; +import Arrow from '../../assets/images/arrow_back_auth.svg'; +import PragyanLogo from '../../assets/images/main-logo-22-white.svg'; +import styles from './AuthLayout.module.css'; + +const AuthLayout: React.FC = ({ formType }) => { + return ( +
+
+
+ + Arrow Left + Home + +
+ Pragyan Logo +
+
+
+
+
+
+ SYSTEM{' '} + + {formType === 'LOGIN' ? 'LOGIN' : 'SIGN UP'} + +
+
+
+
+ {formType === 'LOGIN' ? : <>} +
+
+
+
+
+
+ ); +}; + +export default AuthLayout; diff --git a/src/components/AuthLayout/form.module.css b/src/components/AuthLayout/form.module.css new file mode 100644 index 0000000..5b12b6d --- /dev/null +++ b/src/components/AuthLayout/form.module.css @@ -0,0 +1,246 @@ +.formContainer { + width: fit-content; + color: #cacac9; + width: 100%; + max-width: 50%; + display: flex; + justify-content: center; + flex-direction: column; + align-items: center; + background-image: url('../../assets/images/auth-bg.svg'); + background-size: 100% 100%; + padding: 6rem; + font-family: 'Orbitron', sans-serif; +} + +.signUpFormContainer { + padding-top: 2rem; + padding-bottom: 2rem; +} + +.formFields { + display: flex; + flex-direction: column; + width: 100%; + justify-content: center; + align-items: center; +} + +.formField { + width: 100%; + display: flex; + flex-direction: column; + justify-content: center; + margin-bottom: 8%; +} + +.formField:last-child { + margin-bottom: 3%; +} + +.formLabel { + font-size: 1rem; + margin-bottom: 0.5rem; +} + +.formInputBorder { + clip-path: polygon(0 0, 100% 0, 100% 70%, 98% 90%, 2% 90%, 0% 70%); + height: calc(2.5rem + 2px); + width: calc(100%); + background-color: #95f2ff; +} + +.formInput { + background-color: black; + background-image: linear-gradient(180deg, rgba(36, 49, 51, 0) 18%, #55797e 154%); + height: 2.5rem; + width: 100%; + color: #a3a4a3; + padding: 0rem 1rem; + clip-path: polygon(0 0, 100% 0, 100% 70%, 98% 90%, 2% 90%, 0% 70%); +} + +.formRadioInputs { + display: flex; + align-items: center; + justify-content: space-around; + margin-top: 0.5rem; +} + +.formRadioInput { + display: flex; + align-items: center; +} + +.formRadioInput > label { + margin-right: 0.5rem; +} + +div.formInput { + display: flex; + align-items: center; + font-size: 0.8rem; + height: 2rem; +} + +.formInput:focus { + outline: none; +} + +.disabledInput { + background: #4b4b4b; + border-left: 1rem solid #2a2a2a; + border-right: 1rem solid #2a2a2a; +} + +.formFieldExtras { + width: 100%; + display: flex; + flex-direction: row; + justify-content: space-between; + color: #eaeaea; + margin-bottom: 10%; +} + +.forgotPassword { + font-size: 0.8rem; + font-weight: 700; + display: inline; +} + +.forgotPassword > div { + margin-top: 5px; +} + +.forgotPassword > div:hover { + cursor: pointer; + text-decoration: underline; +} + +.formPageNav { + display: flex; + align-items: center; +} + +.formPageNavArrow:hover { + cursor: pointer; + filter: brightness(1.2); +} + +.formPageNavArrowLeft { + transform: rotate(180deg); + margin-right: 0.8rem; +} + +.formFieldSubmitContainer { + display: flex; + flex-direction: row; + justify-content: space-between; + width: 100%; +} + +.newHere { + color: #eaeaea; + font-size: 0.7rem; + margin-right: 5px; +} + +.authRedirect { + color: #6cbace; + font-weight: 600; + width: fit-content; + font-family: 'ROG'; +} + +.authRedirect:hover { + cursor: pointer; + text-decoration: underline; +} + +.buttonContainer { + width: 100%; + display: flex; + justify-content: space-between; +} + +.submitButton { + border: none; + background: url('../../assets/images/button-bg.svg'); + background-size: 100% 100%; + color: #cacac9; + font-size: 1rem; + font-weight: 600; + height: 2.2rem; + width: 50%; +} + +.submitButton:hover { + cursor: pointer; +} + +.signUpButton { + height: 2rem; +} + +.resetFormContainer { + display: flex; + flex-direction: column; + color: #cacac9; + padding: 2.5rem; + border: 1px solid #e8333b80; + margin: 1px; +} + +.registerButton { + background-color: transparent; + border: none; + background: url('../../assets/images/button-bg.svg'); + background-size: 100% 100%; + position: relative; + display: flex; + align-items: center; + justify-content: center; + cursor: pointer; + margin-top: 1rem; + color: #cacac9; + font-size: 1.1rem; + padding: 0.8rem 0.8rem; + font-weight: 600; +} + +@media screen and (max-width: 1300px) { + .formContainer { + max-width: 80%; + } +} + +@media screen and (max-width: 850px) { + .formContainer { + max-width: 100%; + } + .resetFormContainer { + padding: 1rem; + } +} + +@media screen and (max-width: 500px) { + .formContainer { + background: url('../../assets/images/auth-bg-vert.svg'); + background-size: 100% 100%; + padding: 4rem; + } + .formLabel { + font-size: 0.85rem; + } + .resetFormContainer { + margin: 1.5rem; + } + .buttonContainer { + flex-direction: column; + gap: 1rem; + align-items: center; + } + .submitButton { + width: 100%; + } +} diff --git a/src/components/AuthLayout/input.tsx b/src/components/AuthLayout/input.tsx new file mode 100644 index 0000000..987f4dd --- /dev/null +++ b/src/components/AuthLayout/input.tsx @@ -0,0 +1,24 @@ +import styles from './form.module.css'; + +export function FormInput({ + onChange, + name, + inputClassName, + wrapperClassName, + label, + inputType, +}: FormProps) { + return ( +
+
{label}
+
+ +
+
+ ); +} diff --git a/src/components/AuthLayout/login.tsx b/src/components/AuthLayout/login.tsx new file mode 100644 index 0000000..36e5173 --- /dev/null +++ b/src/components/AuthLayout/login.tsx @@ -0,0 +1,174 @@ +import React from 'react'; +// import Modal from "react-modal"; +import styles from './form.module.css'; +// import { Recaptcha } from "../../ReCaptcha"; +// import DAuthLogin from "../../DauthLogin/DauthLogin"; +import { FormInput } from './input'; +import Link from 'next/link'; + +export const Login: React.FC = () => { + const [loginForm, setLoginForm] = React.useState({ + userEmail: '', + userPass: '', + }); + // const [modalIsOpen, setIsOpen] = React.useState(false); + // const [forgotPasswordEmail, setForgotPasswordEmail] = React.useState(""); + // const [forgotPasswordRecaptcha, setForgotPasswordRecaptcha] = + // React.useState(""); + + // const handleRecaptcha = (_: string, value: string) => { + // setForgotPasswordRecaptcha(value); + // }; + + //const router = useRouter(); + + const handleFormChange = (field: string, value: string) => { + setLoginForm({ ...loginForm, [field]: value }); + }; + + // const handleForgotPassword = () => { + // if (!forgotPasswordRecaptcha) { + // // TODO: customErrorToast("Please verify that you are not a robot"); + // return; + // } + // fetch(`${BACKEND_URL}/auth/sendPasswordResetLink`, { + // method: "POST", + // headers: { + // "Content-Type": "application/json", + // }, + // body: JSON.stringify({ + // email: forgotPasswordEmail, + // recaptcha_code: forgotPasswordRecaptcha, + // }), + // }) + // .then((res) => res.json()) + // .then((data) => { + // if (data.status_code === 200) { + // customSuccessToast( + // "Password reset link sent to your email" + // ); + // setForgotPasswordEmail(""); + // } else customErrorToast(data.message); + // setIsOpen(false); + // }); + // }; + + const handleFormSubmit = () => { + if (!loginForm.userPass) + // TODO:customErrorToast("Please enter your password"); + ``; + else { + // fetch(`${BACKEND_URL}/auth/web`, { + // method: "POST", + // credentials: "include", + // headers: { + // "Content-Type": "application/json", + // }, + // body: JSON.stringify(loginForm), + // }) + // .then((res) => res.json()) + // .then((data) => { + // if (data.status_code === 200) { + // setIsLoggedIn(true); + // setuserID(data.message.user_id); + // customSuccessToast("Successfully logged in :)"); + // router.push("/"); + // } else customErrorToast(data.message); + // }) + // .catch(() => + // customErrorToast( + // "There was a problem logging you in :( Check your credentials and try again." + // ) + // ); + } + }; + + return ( + <> +
+
+ {/*
+
EMAIL *
+ { + handleFormChange("user_email", e.target.value); + }} + /> +
*/} + { + handleFormChange('user_email', e.target.value); + }} + /> + { + handleFormChange('user_pass', e.target.value); + }} + /> +
+
+
setIsOpen(true)} + > +
Forgot Password?
+
+
+ New Here? + + Sign Up + +
+
+
+
+ + +
+
+
{/* */}
+
+ {/* setIsOpen(false)} + ariaHideApp={false} + > +
+
+
+
Email*
+ { + setForgotPasswordEmail(e.target.value); + }} + /> +
+
+ +
+
+ +
+
*/} + + ); +}; diff --git a/src/components/AuthLayout/type.d.ts b/src/components/AuthLayout/type.d.ts new file mode 100644 index 0000000..a988f9c --- /dev/null +++ b/src/components/AuthLayout/type.d.ts @@ -0,0 +1,17 @@ +interface AuthLayoutProps { + formType: 'LOGIN' | 'SIGN UP'; +} + +interface FormProps { + onChange?: React.ChangeEventHandler; + name?: string; + inputClassName?: string; + wrapperClassName?: string; + label: string; + inputType?: string; +} + +interface LoginFormRequest { + userEmail: string; + userPass: string; +} diff --git a/src/components/index.ts b/src/components/index.ts index 59f87ce..b880807 100644 --- a/src/components/index.ts +++ b/src/components/index.ts @@ -12,3 +12,4 @@ export { default as LoadingWebView } from './Landing/LoadingWebView'; export { default as LoadingMobileView } from './Landing/LoadingMobileView'; export { default as ClusterCarousel } from './ClusterCarousel/ClusterCarousel'; export { default as Menu } from './Menu/Menu'; +export { default as AuthLayout } from './AuthLayout/AuthLayout'; From 1a1ebfeefc15855e44ccc35c7dffe0a35637e5f3 Mon Sep 17 00:00:00 2001 From: shubham-1806 Date: Sat, 9 Dec 2023 00:59:50 +0530 Subject: [PATCH 02/14] wip: signup --- src/assets/images/Keyboard arrow right.svg | 11 + src/components/AuthLayout/AuthLayout.tsx | 5 +- src/components/AuthLayout/form.module.css | 14 +- src/components/AuthLayout/input.tsx | 28 + src/components/AuthLayout/signup.tsx | 725 +++++++++++++++++++++ src/components/AuthLayout/type.d.ts | 47 +- src/config/config.example.ts | 1 + src/contexts/UserContext.tsx | 90 +++ src/contexts/types.d.ts | 29 + src/utils/dauthlogin.ts | 135 ++++ 10 files changed, 1082 insertions(+), 3 deletions(-) create mode 100644 src/assets/images/Keyboard arrow right.svg create mode 100644 src/components/AuthLayout/signup.tsx create mode 100644 src/contexts/UserContext.tsx create mode 100644 src/contexts/types.d.ts create mode 100644 src/utils/dauthlogin.ts diff --git a/src/assets/images/Keyboard arrow right.svg b/src/assets/images/Keyboard arrow right.svg new file mode 100644 index 0000000..131f66e --- /dev/null +++ b/src/assets/images/Keyboard arrow right.svg @@ -0,0 +1,11 @@ + + + + + + + + + + + diff --git a/src/components/AuthLayout/AuthLayout.tsx b/src/components/AuthLayout/AuthLayout.tsx index f6273fc..78b401c 100644 --- a/src/components/AuthLayout/AuthLayout.tsx +++ b/src/components/AuthLayout/AuthLayout.tsx @@ -2,11 +2,14 @@ import React from 'react'; import Image from 'next/image'; import Link from 'next/link'; import { Login } from './login'; +import { SignUp } from './signup'; import Arrow from '../../assets/images/arrow_back_auth.svg'; import PragyanLogo from '../../assets/images/main-logo-22-white.svg'; import styles from './AuthLayout.module.css'; +// import { userContext } from "../../contexts/UserContext"; const AuthLayout: React.FC = ({ formType }) => { + const [form, setForm] = React.useState("SIGN UP"); return (
@@ -37,7 +40,7 @@ const AuthLayout: React.FC = ({ formType }) => {
- {formType === 'LOGIN' ? : <>} + {formType === 'LOGIN' ? : }
diff --git a/src/components/AuthLayout/form.module.css b/src/components/AuthLayout/form.module.css index 5b12b6d..e4a9783 100644 --- a/src/components/AuthLayout/form.module.css +++ b/src/components/AuthLayout/form.module.css @@ -11,8 +11,14 @@ background-size: 100% 100%; padding: 6rem; font-family: 'Orbitron', sans-serif; + min-height: 70%; } +.signupFields { + margin-bottom: 0.5rem !important; +} + + .signUpFormContainer { padding-top: 2rem; padding-bottom: 2rem; @@ -227,7 +233,7 @@ div.formInput { .formContainer { background: url('../../assets/images/auth-bg-vert.svg'); background-size: 100% 100%; - padding: 4rem; + padding: 4.5rem; } .formLabel { font-size: 0.85rem; @@ -244,3 +250,9 @@ div.formInput { width: 100%; } } + +@media screen and (max-width: 500px) { + .registerFormContainer { + max-width: 80%; + } +} diff --git a/src/components/AuthLayout/input.tsx b/src/components/AuthLayout/input.tsx index 987f4dd..a2ee596 100644 --- a/src/components/AuthLayout/input.tsx +++ b/src/components/AuthLayout/input.tsx @@ -22,3 +22,31 @@ export function FormInput({ ); } + +export function FormSelect({ + onChange, + initialValue, + inputClassName, + wrapperClassName, + label, + options, +}: FormProps) { + return ( +
+
{label}
+
+ +
+
+ ); +} diff --git a/src/components/AuthLayout/signup.tsx b/src/components/AuthLayout/signup.tsx new file mode 100644 index 0000000..a2bb3d9 --- /dev/null +++ b/src/components/AuthLayout/signup.tsx @@ -0,0 +1,725 @@ +import React from 'react'; +import styles from './form.module.css'; +import Image from 'next/image'; +// import { customErrorToast, customSuccessToast } from "../../Toaster"; +import ArrowSolid from '@/assets/images/Keyboard arrow right.svg'; +// import { Recaptcha } from "../../ReCaptcha"; +import { API_URL } from '@/config/config'; +import { FormInput,FormSelect } from './input'; + +export const SignUp: React.FC = ({ setForm }) => { + const [formPage, setFormPage] = React.useState(7); + const [colleges, setColleges] = React.useState([]); + const [confirmPassword, setConfirmPassword] = React.useState(''); + const [registerForm, setRegisterForm] = React.useState({ + user_name: '', + user_email: '', + user_fullname: '', + user_password: '', + user_sex: 'Male', + user_nationality: '', + user_address: '', + user_pincode: '', + user_state: '', + user_phno: '', + user_degree: 'BTech', + user_year: '1st Year', + user_college: '', + user_othercollege: '', + user_city: '', + user_referral_code: '', + user_sponsor: 'no', + recaptcha_code: '', + is_app: 0, + }); + + React.useEffect(() => { + if (colleges && colleges.length > 0) return; + const fetchColleges = async () => { + const response = await fetch(`${API_URL}/colleges`); + if (response.ok) { + const data = await response.json(); + if (!Array.isArray(data.message)) { + // customErrorToast("Error fetching college list"); + } else { + data.message = [{ id: 0, college_name: 'Other' }].concat(data.message); + setColleges(data.message); + setRegisterForm(form => ({ + ...form, + user_college: data.message[0].college_name, + })); + } + } else { + // customErrorToast( + // "Error connecting to the backend :( Check your connection and refresh the page" + // ); + } + }; + fetchColleges(); + }, [colleges]); + + const handleFormFields = (field: string, value: string): void => { + setRegisterForm(form => ({ ...form, [field]: value })); + }; + + const handleFormSubmit = (): void => { + if (registerForm.recaptcha_code === '') { + // customErrorToast("Please verify that you are not a robot"); + return; + } + fetch(`${API_URL}/user/register`, { + method: 'POST', + headers: { + 'Content-Type': 'application/json', + }, + body: JSON.stringify(registerForm), + }) + .then(res => res.json()) + .then(data => { + if (data.status_code === 200) { + // customSuccessToast( + // "Successfully created an account. Login with the given credentials" + // ); + setForm('LOGIN'); + } else { + // customErrorToast(data.message); + } + }); + // .catch(() => + // customErrorToast( + // "Registration failed :( Check your network connection and try again" + // ) + // ); + }; + + const validateFormPage = (page: number): void => { + switch (page) { + case 1: + // if ( + // !registerForm.user_email || + // !registerForm.user_email.match( + // /^(([^<>()[\]\\.,;:\s@"]+(\.[^<>()[\]\\.,;:\s@"]+)*)|(".+"))@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))$/, + // ) + // ) { + // // customErrorToast("Please enter a valid email address"); + // break; + // } else if (!registerForm.user_password || registerForm.user_password.length < 8) { + // // customErrorToast( + // // "Password must be atleast 8 characters long" + // // ); + // break; + // } else if (registerForm.user_password !== confirmPassword) { + // // customErrorToast("Passwords do not match"); + // break; + // } + setFormPage(2); + break; + case 2: + // if (!registerForm.user_name) { + // // customErrorToast("Please enter a username"); + // break; + // } else if (!registerForm.user_fullname) { + // // customErrorToast("Please enter your full name"); + // break; + // } else if ( + // !registerForm.user_phno || + // !/^\+?(0|[1-9]\d*)$/.test(registerForm.user_phno) + // ) { + // // customErrorToast("Please enter a valid phone number"); + // break; + // } + setFormPage(3); + break; + case 3: + // if (registerForm.user_college === 'Other' && !registerForm.user_othercollege) { + // // customErrorToast("Please enter your college name"); + // break; + // } + // if (registerForm.user_college !== 'Other') { + // setRegisterForm(form => ({ + // ...form, + // user_othercollege: '', + // })); + // } + setFormPage(4); + break; + case 4: + // if (!registerForm.user_nationality) { + // // customErrorToast("Please enter your country"); + // break; + // } + setFormPage(5); + break; + case 5: + // if (!registerForm.user_state) { + // // customErrorToast("Please enter your state"); + // break; + // } else if (!registerForm.user_city) { + // // customErrorToast("Please enter your city"); + // break; + // } else if (!registerForm.user_address) { + // // customErrorToast("Please enter your address"); + // break; + // } + setFormPage(6); + break; + case 6: + // if ( + // !registerForm.user_pincode || + // !/^\+?(0|[1-9]\d*)$/.test(registerForm.user_pincode) + // ) { + // // customErrorToast("Please enter a valid pincode"); + // break; + // } + setFormPage(7); + default: + break; + } + }; + + // TODO:(P4) Change all form input to FormInput component + return ( +
+
+ {formPage === 1 && ( + <> + {/*
+
EMAIL *
+ { + handleFormFields( + "user_email", + e.target.value + ); + }} + value={registerForm.user_email} + /> +
+
+
PASSWORD *
+ { + handleFormFields( + "user_password", + e.target.value + ); + }} + value={registerForm.user_password} + /> +
+
+
+ CONFIRM PASSWORD * +
+ { + setConfirmPassword(e.target.value); + }} + value={confirmPassword} + /> +
*/} + { + handleFormFields('user_email', e.target.value); + }} + /> + { + handleFormFields('user_email', e.target.value); + }} + /> + { + handleFormFields('user_email', e.target.value); + }} + /> + + )} + {formPage === 2 && ( + <> + {/*
+
USERNAME *
+ { + handleFormFields('user_name', e.target.value); + }} + value={registerForm.user_name} + /> +
+
+
FULL NAME *
+ { + handleFormFields('user_fullname', e.target.value); + }} + value={registerForm.user_fullname} + /> +
+
+
PHONE NUMBER *
+ { + handleFormFields('user_phno', e.target.value); + }} + value={registerForm.user_phno} + /> +
*/} + { + handleFormFields('user_email', e.target.value); + }} + /> + { + handleFormFields('user_email', e.target.value); + }} + /> + { + handleFormFields('user_email', e.target.value); + }} + /> + + )} + {formPage === 3 && ( + <> + {/*
+
GENDER *
+ +
+
+
COLLEGE *
+ +
+
+
+ COLLEGE NAME ( if not in list ) +
+ { + handleFormFields('user_othercollege', e.target.value); + }} + disabled={registerForm.user_college !== 'Other'} + value={registerForm.user_othercollege} + /> +
*/} + { + handleFormFields('user_email', e.target.value); + }} + /> + college.college_name) : []} + wrapperClassName={styles.signupFields} + onChange={e => { + handleFormFields('user_email', e.target.value); + }} + /> + { + handleFormFields('user_email', e.target.value); + }} + /> + + )} + {formPage === 4 && ( + <> + {/*
+
DEGREE *
+ +
+
+
YEAR OF STUDY *
+ +
+
+
COUNTRY *
+ { + handleFormFields('user_nationality', e.target.value); + }} + value={registerForm.user_nationality} + /> +
*/} + { + handleFormFields('user_email', e.target.value); + }} + /> + { + handleFormFields('user_email', e.target.value); + }} + /> + { + handleFormFields('user_email', e.target.value); + }} + /> + + )} + {formPage === 5 && ( + <> + {/*
+
STATE *
+ { + handleFormFields('user_state', e.target.value); + }} + value={registerForm.user_state} + /> +
+
+
CITY *
+ { + handleFormFields('user_city', e.target.value); + }} + value={registerForm.user_city} + /> +
+
+
ADDRESS *
+ { + handleFormFields('user_address', e.target.value); + }} + value={registerForm.user_address} + /> +
*/} + { + handleFormFields('user_email', e.target.value); + }} + /> + { + handleFormFields('user_email', e.target.value); + }} + /> + { + handleFormFields('user_email', e.target.value); + }} + /> + + )} + {formPage === 6 && ( + <> + {/*
+
PINCODE *
+ { + handleFormFields('user_pincode', e.target.value); + }} + value={registerForm.user_pincode} + /> +
+
+
REFERRAL CODE
+ { + handleFormFields('user_referral_code', e.target.value); + }} + value={registerForm.user_referral_code} + /> +
+
+
+ WOULD YOU LIKE TO RECEIVE SPONSOR MESSAGES? +
+
+
+ + { + handleFormFields('user_sponsor', e.target.value); + }} + /> +
+
+ + { + handleFormFields('user_sponsor', e.target.value); + }} + defaultChecked + /> +
+
+
*/} + { + handleFormFields('user_email', e.target.value); + }} + /> + { + handleFormFields('user_email', e.target.value); + }} + /> + + )} + {formPage === 7 && ( + <> + {/*
+
VOUCHER NAME
+ { + handleFormFields('user_voucher_name', e.target.value); + }} + value={registerForm.user_voucher_name} + /> +
+
+
VERIFICATION *
+
Verify you're not a robot
+
*/} + { + handleFormFields('user_email', e.target.value); + }} + /> + {/* */} +
Bro Captch Bro
+ + )} +
+
+
+
+ {formPage === 1 && ( + <> + Already have an account? + setForm('LOGIN')}> + Sign In + + + )} + {formPage === 7 && ( +
+ +
+ )} +
+
+ {formPage > 1 && ( + Navigation Arrow Right setFormPage(formPage - 1)} + /> + )} + {formPage < 7 && ( + Navigation Arrow Right validateFormPage(formPage)} + /> + )} +
+
+
+ ); +}; diff --git a/src/components/AuthLayout/type.d.ts b/src/components/AuthLayout/type.d.ts index a988f9c..e38b25c 100644 --- a/src/components/AuthLayout/type.d.ts +++ b/src/components/AuthLayout/type.d.ts @@ -2,16 +2,61 @@ interface AuthLayoutProps { formType: 'LOGIN' | 'SIGN UP'; } +type FormType = 'LOGIN' | 'SIGN UP'; + +interface SignupFormProps { + setForm: (form: FormType) => void; +} + interface FormProps { - onChange?: React.ChangeEventHandler; + onChange?: React.ChangeEventHandler; name?: string; inputClassName?: string; wrapperClassName?: string; label: string; inputType?: string; + options?: string[]; + initialValue?: string; } interface LoginFormRequest { userEmail: string; userPass: string; } + +interface RegisterFormType { + user_name: string; + user_email: string; + user_fullname: string; + user_password: string; + user_sex: string; + user_nationality: string; + user_address: string; + user_pincode: string; + user_state: string; + user_phno: string; + user_degree: string; + user_year: string; + user_college: string; + user_othercollege?: string; + user_city: string; + user_sponsor?: string; + user_voucher_name?: string; + user_referral_code?: string; + recaptcha_code?: string; + is_app?: number; +} + +interface College { + id: number; + college_name: string; +} + +interface LoginFormType { + user_email: string; + user_pass: string; +} + +interface ReCaptchaState { + handleFormFields: (field: string, value: string) => void; +} \ No newline at end of file diff --git a/src/config/config.example.ts b/src/config/config.example.ts index d248af9..e108687 100644 --- a/src/config/config.example.ts +++ b/src/config/config.example.ts @@ -2,3 +2,4 @@ export const DAUTH_CLIENT_ID = ''; export const DAUTH_REDIRECT_URI = ''; export const API_URL = ''; export const BASEPATH = ''; + diff --git a/src/contexts/UserContext.tsx b/src/contexts/UserContext.tsx new file mode 100644 index 0000000..880d224 --- /dev/null +++ b/src/contexts/UserContext.tsx @@ -0,0 +1,90 @@ +import React, { createContext, useCallback, useState, useEffect, ReactNode } from "react"; +import { API_URL } from "../config/config"; + +export const userContext = createContext({ + isLoggedIn: false, + loadingAuth: true, + userID: undefined, + userName: undefined, + qrCode: undefined, + setUserName: () => undefined, + setIsLoggedIn: () => false, + setuserID: () => undefined, + userData: null, +}); + +const UserContextProvider = ({ children }: {children : ReactNode}) => { + const [userID, setuserID] = useState(); + const [userName, setUserName] = useState(); + const [qrCode, setQr] = useState(""); + const [isLoggedIn, setIsLoggedIn] = useState(false); + const [loadingAuth, setLoadingAuth] = useState(true); + const [userData, setUserData] = useState(null); + + const isAuth = useCallback(() => { + setLoadingAuth(true); + fetch(`${API_URL}/user/details`, { + method: "GET", + credentials: "include", + headers: { + "Content-Type": "application/json", + }, + }) + .then((res) => res.json()) + .then((response) => { + if (response.status_code === 401) { + setIsLoggedIn(false); + } else { + setIsLoggedIn(true); + } + setLoadingAuth(false); + setUserData(response.message); + }) + .catch((e) => { + console.log(e); + setLoadingAuth(false); + }); + }, [userID]); + + const getQr = useCallback(() => { + fetch(`${API_URL}/pr/qr`, { + method: "POST", + credentials: "include", + headers: { + "Content-Type": "application/json", + }, + }) + .then((res) => res.json()) + .then((data) => { + if (data.status_code === 200) { + setQr(data.message); + } else setQr(""); + }) + .catch((e) => console.log(e)); + }, [loadingAuth, isLoggedIn]); + + useEffect(() => { + isAuth(); + getQr(); + }, [isAuth]); + + return ( + + {children} + + ); +}; + +export default UserContextProvider; \ No newline at end of file diff --git a/src/contexts/types.d.ts b/src/contexts/types.d.ts new file mode 100644 index 0000000..f0affe1 --- /dev/null +++ b/src/contexts/types.d.ts @@ -0,0 +1,29 @@ +interface UserContext { + setIsLoggedIn: React.Dispatch>; + setuserID: React.Dispatch>; + setUserName: React.Dispatch>; + loadingAuth: boolean; + isLoggedIn: boolean; + userID: number | undefined; + userName: string | undefined; + qrCode: string | undefined; + userData: { + user_name: string; + user_email: string; + sex: string; + nationality: string; + address: string; + city: string; + state: string; + pincode: string; + phno: string; + degree: string; + yearOfStudy: string; + college: string; + othercollege: string; + sponsor: string; + voucher_name: string; + referral_code: string; + acco: string | null; + } | null; +} \ No newline at end of file diff --git a/src/utils/dauthlogin.ts b/src/utils/dauthlogin.ts new file mode 100644 index 0000000..9cec701 --- /dev/null +++ b/src/utils/dauthlogin.ts @@ -0,0 +1,135 @@ +import { useCallback, useContext, useState } from 'react'; +import { userContext } from '@/contexts/UserContext'; +import { useRouter } from 'next/router'; +import { API_URL, DAUTH_CLIENT_ID, DAUTH_REDIRECT_URI } from '@/config/config'; + +enum AuthStatusEnum { + PRE, + START, + WAITING, + ACCEPTED, + REJECTED, + AUTH, + ERROR, +} + +const DAuthLogin: any = ({ isMobile }: { isMobile: boolean }) => { + const [authStatus, setAuthStatus] = useState(AuthStatusEnum.PRE); + const { setuserID, setIsLoggedIn } = useContext(userContext); + + const router = useRouter(); + + const generateDauthAuthorizeUrl = () => { + const dauthAuthorizeURL = new URL('https://auth.delta.nitt.edu/authorize'); + + const dauthQueryParameters = { + client_id: DAUTH_CLIENT_ID, + redirect_uri: DAUTH_REDIRECT_URI, + response_type: 'code', + state: 'code', + grant_type: 'authorization_code', + scope: 'email+openid+profile+user', + nonce: '', + }; + + const appendQueryParametersToURL = (url: URL, queryParams: Object) => { + Object.keys(queryParams).forEach(query => { + url.searchParams.append(query, (queryParams as any)[query]); + }); + }; + + appendQueryParametersToURL(dauthAuthorizeURL, dauthQueryParameters); + + return dauthAuthorizeURL; + }; + + const sendAuthCodeToServer = useCallback(async (code: string) => { + try { + setAuthStatus(AuthStatusEnum.WAITING); + //backend url + fetch(API_URL + '/auth/dauth/web', { + method: 'POST', + credentials: 'include', + headers: { + 'Content-Type': 'application/json', + }, + body: JSON.stringify({ + code: code, + }), + }) + .then(async res => { + if (res.status != 200) { + setAuthStatus(AuthStatusEnum.ERROR); + } else { + return await res.json(); + } + }) + .then(data => { + if (data.status_code === 200) { + setIsLoggedIn(true); + setuserID(data.message.user_id); + router.push('/'); + } + }); + } catch (err) { + console.log(err); + setAuthStatus(AuthStatusEnum.ERROR); + } + }, []); + + const BASE_URL = typeof window !== 'undefined' ? window.location.origin : null; + + const receiveMessage = useCallback( + async (event: MessageEvent) => { + if (event.origin !== BASE_URL) { + return; + } + const { data } = event; + if (data.source === 'dauth-login-callback') { + if (!data.code) { + setAuthStatus(AuthStatusEnum.REJECTED); + } else { + setAuthStatus(AuthStatusEnum.ACCEPTED); + sendAuthCodeToServer(data.code); + } + } + }, + [BASE_URL, sendAuthCodeToServer], + ); + + const [windowObjectReference, setWindowObjectReference] = useState(null); + const [previousUrl, setPreviousUrl] = useState(null); + + const openSignInWindow = useCallback( + (url: string, name: string) => { + window.removeEventListener('message', receiveMessage); + + const strWindowFeatures = isMobile + ? '_blank' + : 'toolbar=no, menubar=no, width=600, height=700, top=100, left=100'; + + if (windowObjectReference === null || windowObjectReference.closed) { + setWindowObjectReference(window.open(url, name, strWindowFeatures)); + } else if (previousUrl !== url) { + setWindowObjectReference(window.open(url, name, strWindowFeatures)); + windowObjectReference?.focus(); + } else { + windowObjectReference.focus(); + } + setAuthStatus(AuthStatusEnum.WAITING); + + window.addEventListener('message', receiveMessage, false); + + setPreviousUrl(url); + }, + [previousUrl, receiveMessage, windowObjectReference], + ); + + const generateDauthStringAndOpenUrl = useCallback(() => { + const dauthURL = generateDauthAuthorizeUrl(); + openSignInWindow(dauthURL.toString(), 'dauthURL'); + setAuthStatus(AuthStatusEnum.START); + }, [openSignInWindow]); +}; + +export default DAuthLogin; From 91d88f1cccb49302815ba9a9d9263c4751176a22 Mon Sep 17 00:00:00 2001 From: mharrish7 Date: Sat, 9 Dec 2023 12:22:33 +0530 Subject: [PATCH 03/14] WIP(auth) --- fest-web-client | 2 +- package.json | 3 + public/auth-callback.html | 131 +++++ src/components/AuthLayout/AuthLayout.tsx | 12 +- src/components/AuthLayout/login.tsx | 185 +++++-- src/components/AuthLayout/signup.tsx | 584 ++++++----------------- src/components/AuthLayout/type.d.ts | 62 +-- src/config/config.example.ts | 3 +- src/utils/ApiConfig.ts | 69 +++ src/utils/dauthlogin.ts | 135 ------ 10 files changed, 557 insertions(+), 629 deletions(-) create mode 100644 public/auth-callback.html create mode 100644 src/utils/ApiConfig.ts diff --git a/fest-web-client b/fest-web-client index df5cc5c..5439d23 160000 --- a/fest-web-client +++ b/fest-web-client @@ -1 +1 @@ -Subproject commit df5cc5c73ca7e703da5583c2e844f19016f5be6d +Subproject commit 5439d23b31df32dc490e8166587d7eb6ccc77e4f diff --git a/package.json b/package.json index c87898d..3bda183 100644 --- a/package.json +++ b/package.json @@ -3,6 +3,9 @@ "version": "0.1.0", "private": true, "type": "module", + "workspaces" : [ + "fest-web-client/*" + ], "scripts": { "dev": "next dev", "build": "next build", diff --git a/public/auth-callback.html b/public/auth-callback.html new file mode 100644 index 0000000..a0db2e4 --- /dev/null +++ b/public/auth-callback.html @@ -0,0 +1,131 @@ + + + + + + + Delta Inductions | LOGIN REDIRECT + + + + + + + +
+
+ + + +

Redirecting back to the main site...

+
+
+ + + + \ No newline at end of file diff --git a/src/components/AuthLayout/AuthLayout.tsx b/src/components/AuthLayout/AuthLayout.tsx index 78b401c..71b7f2b 100644 --- a/src/components/AuthLayout/AuthLayout.tsx +++ b/src/components/AuthLayout/AuthLayout.tsx @@ -1,3 +1,5 @@ +'use client'; + import React from 'react'; import Image from 'next/image'; import Link from 'next/link'; @@ -9,7 +11,7 @@ import styles from './AuthLayout.module.css'; // import { userContext } from "../../contexts/UserContext"; const AuthLayout: React.FC = ({ formType }) => { - const [form, setForm] = React.useState("SIGN UP"); + const [form, setForm] = React.useState(formType); return (
@@ -34,13 +36,17 @@ const AuthLayout: React.FC = ({ formType }) => {
SYSTEM{' '} - {formType === 'LOGIN' ? 'LOGIN' : 'SIGN UP'} + {form === 'LOGIN' ? 'LOGIN' : 'SIGN UP'}
- {formType === 'LOGIN' ? : } + {form === 'LOGIN' ? ( + + ) : ( + + )}
diff --git a/src/components/AuthLayout/login.tsx b/src/components/AuthLayout/login.tsx index 36e5173..6ec902b 100644 --- a/src/components/AuthLayout/login.tsx +++ b/src/components/AuthLayout/login.tsx @@ -1,16 +1,151 @@ +/* eslint-disable */ +'use client'; + import React from 'react'; // import Modal from "react-modal"; import styles from './form.module.css'; +// import DAuthLogin from '@/utils/dauthlogin'; // import { Recaptcha } from "../../ReCaptcha"; // import DAuthLogin from "../../DauthLogin/DauthLogin"; import { FormInput } from './input'; import Link from 'next/link'; +import { useCallback, useState, useContext } from 'react'; +import { UserApi } from '../../../fest-web-client/client/src'; +import { apiConfig, authConfig } from '@/utils/ApiConfig'; +import { useRouter } from 'next/navigation'; +import { userContext } from '@/contexts/UserContext'; +import { DAUTH_CLIENT_ID, DAUTH_REDIRECT_URI, API_URL } from '@/config/config'; + +enum AuthStatusEnum { + PRE, + START, + WAITING, + ACCEPTED, + REJECTED, + AUTH, + ERROR, +} -export const Login: React.FC = () => { +export const Login: React.FC = ({ setForm }) => { const [loginForm, setLoginForm] = React.useState({ userEmail: '', - userPass: '', + userPassword: '', }); + + const isMobile = false; + + const userApi = new UserApi(authConfig); + const [authStatus, setAuthStatus] = useState(AuthStatusEnum.PRE); + const { setuserID, setIsLoggedIn } = useContext(userContext); + + const router = useRouter(); + + const generateDauthAuthorizeUrl = () => { + const dauthAuthorizeURL = new URL('https://auth.delta.nitt.edu/authorize'); + + const dauthQueryParameters = { + client_id: DAUTH_CLIENT_ID, + redirect_uri: DAUTH_REDIRECT_URI, + response_type: 'code', + state: 'code', + grant_type: 'authorization_code', + scope: 'email+openid+profile+user', + nonce: '', + }; + + const appendQueryParametersToURL = (url: URL, queryParams: Object) => { + Object.keys(queryParams).forEach(query => { + url.searchParams.append(query, (queryParams as any)[query]); + }); + }; + + appendQueryParametersToURL(dauthAuthorizeURL, dauthQueryParameters); + + return dauthAuthorizeURL; + }; + + const sendAuthCodeToServer = useCallback(async (code: string) => { + try { + setAuthStatus(AuthStatusEnum.WAITING); + //backend url + userApi + .dAuthUserLogin(code) + .then(res => console.log(res)) + .catch(e => console.log(e)); + } catch (err) { + console.log(err); + setAuthStatus(AuthStatusEnum.ERROR); + } + }, []); + + const handleLogin = useCallback(async () => { + try { + setAuthStatus(AuthStatusEnum.WAITING); + //backend url + userApi + .authUserLogin(loginForm) + .then(res => console.log(res)) + .catch(e => console.log(e)); + } catch (err) { + console.log(err); + setAuthStatus(AuthStatusEnum.ERROR); + } + }, []); + + const BASE_URL = typeof window !== 'undefined' ? window.location.origin : null; + + const receiveMessage = useCallback( + async (event: MessageEvent) => { + if (event.origin !== BASE_URL) { + return; + } + const { data } = event; + if (data.source === 'dauth-login-callback') { + if (!data.code) { + setAuthStatus(AuthStatusEnum.REJECTED); + } else { + setAuthStatus(AuthStatusEnum.ACCEPTED); + sendAuthCodeToServer(data.code); + } + } + }, + [BASE_URL, sendAuthCodeToServer], + ); + + const [windowObjectReference, setWindowObjectReference] = useState(null); + const [previousUrl, setPreviousUrl] = useState(null); + + const openSignInWindow = useCallback( + (url: string, name: string) => { + window.removeEventListener('message', receiveMessage); + + const strWindowFeatures = isMobile + ? '_blank' + : 'toolbar=no, menubar=no, width=600, height=700, top=100, left=100'; + + if (windowObjectReference === null || windowObjectReference.closed) { + setWindowObjectReference(window.open(url, name, strWindowFeatures)); + } else if (previousUrl !== url) { + setWindowObjectReference(window.open(url, name, strWindowFeatures)); + windowObjectReference?.focus(); + } else { + windowObjectReference.focus(); + } + setAuthStatus(AuthStatusEnum.WAITING); + + window.addEventListener('message', receiveMessage, false); + + setPreviousUrl(url); + }, + [previousUrl, receiveMessage, windowObjectReference], + ); + + const generateDauthStringAndOpenUrl = useCallback(() => { + const dauthURL = generateDauthAuthorizeUrl(); + openSignInWindow(dauthURL.toString(), 'dauthURL'); + setAuthStatus(AuthStatusEnum.START); + }, [openSignInWindow]); + // const [modalIsOpen, setIsOpen] = React.useState(false); // const [forgotPasswordEmail, setForgotPasswordEmail] = React.useState(""); // const [forgotPasswordRecaptcha, setForgotPasswordRecaptcha] = @@ -54,32 +189,10 @@ export const Login: React.FC = () => { // }; const handleFormSubmit = () => { - if (!loginForm.userPass) - // TODO:customErrorToast("Please enter your password"); - ``; + console.log(loginForm.userPassword); + if (!loginForm.userPassword) console.log('Please enter your password'); else { - // fetch(`${BACKEND_URL}/auth/web`, { - // method: "POST", - // credentials: "include", - // headers: { - // "Content-Type": "application/json", - // }, - // body: JSON.stringify(loginForm), - // }) - // .then((res) => res.json()) - // .then((data) => { - // if (data.status_code === 200) { - // setIsLoggedIn(true); - // setuserID(data.message.user_id); - // customSuccessToast("Successfully logged in :)"); - // router.push("/"); - // } else customErrorToast(data.message); - // }) - // .catch(() => - // customErrorToast( - // "There was a problem logging you in :( Check your credentials and try again." - // ) - // ); + handleLogin(); } }; @@ -101,7 +214,7 @@ export const Login: React.FC = () => { name="email" inputType="email" onChange={e => { - handleFormChange('user_email', e.target.value); + handleFormChange('userEmail', e.target.value); }} /> { name="password" inputType="password" onChange={e => { - handleFormChange('user_pass', e.target.value); + handleFormChange('userPassword', e.target.value); }} /> @@ -122,14 +235,22 @@ export const Login: React.FC = () => {
New Here? - +
{ + setForm('SIGN UP'); + }} + > Sign Up - +
-