diff --git a/package-lock.json b/package-lock.json index b75a06c..338edac 100644 --- a/package-lock.json +++ b/package-lock.json @@ -11,7 +11,8 @@ "@prisma/client": "^5.9.1", "@sendgrid/mail": "^8.1.0", "bcrypt": "^5.1.1", - "eclipse-components": "^0.0.141", + "eclipse-components": "^0.0.149", + "framer-motion": "^11.0.5", "next": "14.1.0", "next-auth": "^4.24.5", "prisma": "^5.9.1", @@ -63,6 +64,21 @@ "node": ">=6.9.0" } }, + "node_modules/@emotion/is-prop-valid": { + "version": "0.8.8", + "resolved": "https://registry.npmjs.org/@emotion/is-prop-valid/-/is-prop-valid-0.8.8.tgz", + "integrity": "sha512-u5WtneEAr5IDG2Wv65yhunPSMLIpuKsbuOktRojfrEiEvRyC85LgPMZI63cr7NUqT8ZIGdSVg8ZKGxIug4lXcA==", + "optional": true, + "dependencies": { + "@emotion/memoize": "0.7.4" + } + }, + "node_modules/@emotion/memoize": { + "version": "0.7.4", + "resolved": "https://registry.npmjs.org/@emotion/memoize/-/memoize-0.7.4.tgz", + "integrity": "sha512-Ja/Vfqe3HpuzRsG1oBtWTHk2PGZ7GR+2Vz5iYGelAw8dx32K0y7PjVuxK6z1nMpZOqAFsRUPCkK1YjJ56qJlgw==", + "optional": true + }, "node_modules/@eslint-community/eslint-utils": { "version": "4.4.0", "resolved": "https://registry.npmjs.org/@eslint-community/eslint-utils/-/eslint-utils-4.4.0.tgz", @@ -1589,9 +1605,9 @@ "dev": true }, "node_modules/eclipse-components": { - "version": "0.0.141", - "resolved": "https://registry.npmjs.org/eclipse-components/-/eclipse-components-0.0.141.tgz", - "integrity": "sha512-1uBiEY/gYK8SBp3lY6bv3Ab+cuo5qe0YvvMkOwsbnqCVuTQu19UoFrhiXHwPbBgQEGrGQxLR2wrHmy418SR4qw==", + "version": "0.0.149", + "resolved": "https://registry.npmjs.org/eclipse-components/-/eclipse-components-0.0.149.tgz", + "integrity": "sha512-0d3gxDuoKl3X99ETLJ9JFgpe8jy52MInqGounJoF3hPRcwwpRrlwMLohtDl7msztnkjjfvuFUqRJ+ib27kY2Ew==", "peerDependencies": { "react": "^18.2.0", "react-dom": "^18.2.0" @@ -2378,6 +2394,29 @@ "url": "https://github.com/sponsors/rawify" } }, + "node_modules/framer-motion": { + "version": "11.0.5", + "resolved": "https://registry.npmjs.org/framer-motion/-/framer-motion-11.0.5.tgz", + "integrity": "sha512-Lb0EYbQcSK/pgyQUJm+KzsQrKrJRX9sFRyzl9hSr9gFG4Mk8yP7BjhuxvRXzblOM/+JxycrJdCDVmOQBsjpYlw==", + "dependencies": { + "tslib": "^2.4.0" + }, + "optionalDependencies": { + "@emotion/is-prop-valid": "^0.8.2" + }, + "peerDependencies": { + "react": "^18.0.0", + "react-dom": "^18.0.0" + }, + "peerDependenciesMeta": { + "react": { + "optional": true + }, + "react-dom": { + "optional": true + } + } + }, "node_modules/fs-minipass": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/fs-minipass/-/fs-minipass-2.1.0.tgz", diff --git a/package.json b/package.json index 3adace8..75b932a 100644 --- a/package.json +++ b/package.json @@ -12,7 +12,8 @@ "@prisma/client": "^5.9.1", "@sendgrid/mail": "^8.1.0", "bcrypt": "^5.1.1", - "eclipse-components": "^0.0.141", + "eclipse-components": "^0.0.149", + "framer-motion": "^11.0.5", "next": "14.1.0", "next-auth": "^4.24.5", "prisma": "^5.9.1", @@ -31,4 +32,4 @@ "tailwindcss": "^3.3.0", "typescript": "^5" } -} \ No newline at end of file +} diff --git a/src/app/login/LoginForm.tsx b/src/app/login/LoginForm.tsx index c15505f..5e50d54 100644 --- a/src/app/login/LoginForm.tsx +++ b/src/app/login/LoginForm.tsx @@ -1,6 +1,6 @@ "use client"; -import { TextField, Button, EclipseLogoLong } from "eclipse-components"; +import { TextField, Button, EclipseLogoTextOrbGlow } from "eclipse-components"; import { signIn } from "next-auth/react"; import Link from "next/link"; import { useSearchParams } from "next/navigation"; @@ -14,7 +14,7 @@ export default function LoginForm() { return ( <> - +
{ e.preventDefault(); diff --git a/src/app/signup/SignupForm.tsx b/src/app/signup/SignupForm.tsx index 8ec8b0a..9c4311c 100644 --- a/src/app/signup/SignupForm.tsx +++ b/src/app/signup/SignupForm.tsx @@ -1,112 +1,339 @@ "use client"; -import { - TextField, - Button, - EclipseLogoLong, - Notification, -} from "eclipse-components"; +import { TextField, Button, EclipseLogoLong } from "eclipse-components"; +import { useState, useEffect } from "react"; import { signIn } from "next-auth/react"; import Link from "next/link"; import { useSearchParams } from "next/navigation"; +import { motion, AnimatePresence } from "framer-motion"; +import { + EclipseLogoTextOrbGlow, + StarBackground, + MainWrapper, +} from "eclipse-components"; /** * Signup Form Component * @returns JSX.Element */ -export default function SignupForm() { +type FormData = { + firstName: string; + lastName: string; + email: string; + password: string; + confirmPassword: string; +}; - const searchParams = useSearchParams(); +export default function SignupForm() { return ( - <> - - { - e.preventDefault(); +
+ + + + + +
+ ); +} + +/** + * Form Components + * + * @returns JSX.Element + */ +function Components(): JSX.Element { + const [formData, setFormData] = useState({ + firstName: "", + lastName: "", + email: "", + password: "", + confirmPassword: "", + }); + const [currentStep, setCurrentStep] = useState(0); + const [visible, setVisible] = useState(true); - const firstName = e.currentTarget.firstName.value; - const lastName = e.currentTarget.lastName.value; - const email = e.currentTarget.username.value; - const password = e.currentTarget.password.value; - const confirmPassword = e.currentTarget.confirmPassword.value; + /* + *Array of questions + */ + const questions = [ + { + text: "What's your name?", + key: "firstName", + type: "text", + placeholder: "First Name", + }, + { + text: "What's your last name?", + key: "lastName", + type: "text", + placeholder: "Last Name", + }, + { + text: "What's your email?", + key: "email", + type: "email", + placeholder: "Email", + }, + { + text: "Awesome, just a few more things left:", + key: "password", + type: "password", + placeholder: "Password", + }, + ]; + /* + *updates formData with the user's inputs + */ + const handleInputChange = (e: React.ChangeEvent) => { + const { name, value } = e.target; - // Validate password - if (password !== confirmPassword) { - console.log("Passwords do not match"); - return; - } + setFormData((prevFormData) => ({ + ...prevFormData, + [name]: value, + })); + }; - // Send request to create user - const response = await fetch("api/auth/signup", { - method: "POST", - headers: { - "Content-Type": "application/json", - }, - body: JSON.stringify({ - email: email, - password: password, - firstName: firstName, - lastName: lastName, - }), - }); - - const data = await response.json(); - - if (response.ok) { - await signIn("credentials", { - username: email, - password: password, - callbackUrl: searchParams.get("callbackUrl") || "/", - }); - } - }} - className="flex flex-col items-center gap-4 w-[18rem] md:w-[30rem]" - > -
- {" "} + /* + * Components for each question / step + */ + const formSteps = [ + { + id: 0, + component: ( + <> {" "} + autoFocus + onChange={handleInputChange} + className="w-full" + /> + + + ), + }, + { + id: 1, + component: ( + <> + + + ), + }, + { + id: 2, + component: ( + <> + + + + ), + }, + { + id: 3, + component: ( + <> + + + + + ), + }, + ]; + + useEffect(() => { + console.log("formData updated", formData); + }, [formData]); + + /* + * Proceed to the next question + */ + const goToNextStep = (e) => { + if (currentStep < questions.length - 1) { + setFormData((prev) => ({ + ...prev, + [questions[currentStep].key]: formData[questions[currentStep].key], + })); + setCurrentStep((currentStep) => currentStep + 1); + } + // Add any additional actions for the last step, like submitting the form + }; + + /* + * Go to previous step + */ + const goToPreviousStep = () => { + if (currentStep > 0) { + setCurrentStep((currentStep) => currentStep - 1); + } + }; + + /* + * Submit signup data to backend and sign in + */ + const handleSubmit = async (e) => { + setFormData((prev) => ({ + ...prev, + [questions[currentStep].key]: formData[questions[currentStep].key], + })); + + e.preventDefault(); + + // Validate password + if (formData.password !== formData.confirmPassword) { + console.log("Passwords do not match"); + return; + } + + // Send request to create user + const response = await fetch("/api/auth/signup", { + method: "POST", + headers: { "Content-Type": "application/json" }, + body: JSON.stringify({ + email: formData.email, + password: formData.password, + firstName: formData.firstName, + lastName: formData.lastName, + }), + }); + + const data = await response.json(); + + if (response.ok) { + // Proceed with sign-in or navigate to another page + // if (response.ok) { + await signIn("credentials", { + username: email, + password: password, + callbackUrl: searchParams.get("callbackUrl") || "/", + }); + } + }; + + /* + * Form return JSX + */ + const searchParams = useSearchParams(); + return ( + <> + { + e.preventDefault(); + if (currentStep === 3) { + handleSubmit(e); + } else { + goToNextStep(e); + } + }} + > +
+ + {formSteps.map((step) => { + if (step.id === currentStep) { + return ( + <> + +

+ {questions[currentStep].text}{" "} + +

+ {step.component} +
+ +

+ or press  + Enter +

+
+ Already have an account?{" "} + + Login + +
+ + ); + } + return null; + })} +
- - - - -

- Already have an account?{" "} - - Login - -

);