From 68de013805766dc5c25b71c1d0b19bec7a6190b5 Mon Sep 17 00:00:00 2001 From: rachaelch3n Date: Fri, 6 Dec 2024 19:17:21 -0800 Subject: [PATCH 01/16] created check email exists query --- api/supabase/queries/users.ts | 13 +++++++++++++ 1 file changed, 13 insertions(+) create mode 100644 api/supabase/queries/users.ts diff --git a/api/supabase/queries/users.ts b/api/supabase/queries/users.ts new file mode 100644 index 0000000..4cffd93 --- /dev/null +++ b/api/supabase/queries/users.ts @@ -0,0 +1,13 @@ +import supabase from '../createClient'; + +export async function checkEmailExists(email: string): Promise { + const { data, error } = await supabase.rpc('check_email_exists', { + p_email: email, + }); + + if (error) { + throw new Error(`Error checking email existence: ${error.message}`); + } + + return data; +} From c372a86fbbed35c591af38fc9d664f463ad126f8 Mon Sep 17 00:00:00 2001 From: rachaelch3n Date: Fri, 6 Dec 2024 19:20:22 -0800 Subject: [PATCH 02/16] finilized auth styling --- app/(auth)/login/page.tsx | 95 ++++++++---- app/(auth)/signup/page.tsx | 235 ++++++++++++++++++++---------- components/PasswordComplexity.tsx | 43 ++++-- components/PasswordInput.tsx | 6 +- components/TextInput/index.tsx | 51 +++++++ components/TextInput/styles.ts | 69 +++++++++ lib/icons.tsx | 83 +++++++++++ utils/AuthProvider.tsx | 8 +- 8 files changed, 467 insertions(+), 123 deletions(-) create mode 100644 components/TextInput/index.tsx create mode 100644 components/TextInput/styles.ts diff --git a/app/(auth)/login/page.tsx b/app/(auth)/login/page.tsx index 839f2ea..b7b63b9 100644 --- a/app/(auth)/login/page.tsx +++ b/app/(auth)/login/page.tsx @@ -1,47 +1,86 @@ 'use client'; import { useState } from 'react'; -import { useRouter } from 'next/navigation'; +// import { useRouter } from 'next/navigation'; +import TextInput from '@/components/TextInput'; +import { StyledButton, StyledForm } from '@/components/TextInput/styles'; +import COLORS from '@/styles/colors'; +import { H2, P3 } from '@/styles/text'; import { useAuth } from '../../../utils/AuthProvider'; export default function Login() { const { signIn } = useAuth(); // Use `signIn` function from AuthProvider const [email, setEmail] = useState(''); const [password, setPassword] = useState(''); - const router = useRouter(); + const [showPassword, setShowPassword] = useState(false); + const [invalidEmailError, setInvalidEmailError] = useState(''); + const [invalidPasswordError, setInvalidPasswordError] = useState(''); + const isFormValid = email && password; + + // const { push } = useRouter(); const handleLogin = async () => { - // Define handleLogin try { - await signIn(email, password); - router.push('/'); // Redirect to the home page on success - } catch (error) { - if (error instanceof Error) { - console.error('Login Error:', error.message); + const { error } = await signIn(email, password); + // push('/'); + + if (error) { + // Match error messages from Supabase + if (error.message.includes('Invalid login credentials')) { + setInvalidEmailError('Invalid email address'); + setInvalidPasswordError('Invalid password'); + } + return; } + + // Clear errors on success + setInvalidEmailError(''); + setInvalidPasswordError(''); + } catch (err) { + console.error('Login Error:', err); + setInvalidEmailError('An unexpected error occurred. Please try again.'); } }; return ( - <> - setEmail(e.target.value)} - value={email} - placeholder="Email" - /> - {/* Email input*/} - setPassword(e.target.value)} - value={password} - placeholder="Password" - /> - {' '} - {/* Sign in button */} - + +
+

Log In

+
+ + {/* Email input*/} + {invalidEmailError} +
+
+ setShowPassword(!showPassword)} + error={!!invalidPasswordError} + /> + {invalidPasswordError} + {/* Password input*/} +
+ + Log in + {' '} + {/* Sign in button */} +
+
); } diff --git a/app/(auth)/signup/page.tsx b/app/(auth)/signup/page.tsx index ad3a436..14ed135 100644 --- a/app/(auth)/signup/page.tsx +++ b/app/(auth)/signup/page.tsx @@ -2,7 +2,10 @@ import { useState } from 'react'; import PasswordComplexity from '@/components/PasswordComplexity'; -import PasswordInput from '@/components/PasswordInput'; +import TextInput from '@/components/TextInput'; +import { StyledButton, StyledForm } from '@/components/TextInput/styles'; +import COLORS from '@/styles/colors'; +import { H2, P3 } from '@/styles/text'; import { useAuth } from '@/utils/AuthProvider'; export default function SignUp() { @@ -10,114 +13,186 @@ export default function SignUp() { const [email, setEmail] = useState(''); const [password, setPassword] = useState(''); const [confirmPassword, setConfirmPassword] = useState(''); - const [passwordError, setPasswordError] = useState(''); - const [passwordComplexityError, setPasswordComplexityError] = useState< - string | null - >(null); - const [passwordComplexity, setPasswordComplexity] = useState(false); + const [samePasswordCheck, setSamePasswordCheck] = useState(''); + const [mismatchError, setMismatchError] = useState(''); + const [isPasswordComplexityMet, setIsPasswordComplexityMet] = + useState(false); const [showPassword, setShowPassword] = useState(false); const [showConfirmPassword, setShowConfirmPassword] = useState(false); + const [checkEmailExistsError] = useState(''); + const [checkValidEmailError, setCheckValidEmailError] = useState(''); + const [isSubmitted, setIsSubmitted] = useState(false); + const [isEmailValid, setIsEmailValid] = useState(true); + + const isFormValid = email && password && confirmPassword; + + const isValidEmail = (email: string): boolean => { + const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/; + return emailRegex.test(email); + }; + + const handleEmailChange = async (newEmail: string) => { + setEmail(newEmail); + + // Validate email format + if (!isValidEmail(newEmail)) { + setIsEmailValid(false); + setCheckValidEmailError('Please enter a valid email address'); + } else { + setIsEmailValid(true); + setCheckValidEmailError(''); + } + // Check if the email already exists + }; // Handles input to password - const handlePasswordChange = (e: React.ChangeEvent) => { - const newPassword = e.target.value; + const handlePasswordChange = (newPassword: string) => { setPassword(newPassword); - validatePasswords(newPassword, confirmPassword); - validatePasswordComplexity(newPassword); + + if (!newPassword || !confirmPassword) { + setMismatchError(''); + setSamePasswordCheck(''); + return; + } + + // Set mismatch error if passwords do not match + if (newPassword !== confirmPassword) { + setMismatchError('✗ Passwords do not match'); + setSamePasswordCheck(''); + } else { + setMismatchError(''); + setSamePasswordCheck('✓ Passwords match'); + } + + validateIsPasswordComplexityMet(newPassword); }; // Handles input to confirm password - const handleConfirmPasswordChange = ( - e: React.ChangeEvent, - ) => { - const newConfirmPassword = e.target.value; + const handleConfirmPasswordChange = (newConfirmPassword: string) => { setConfirmPassword(newConfirmPassword); - validatePasswords(password, newConfirmPassword); - }; - // Checks if passwords match and sets error - const validatePasswords = ( - password: string | null, - confirmPassword: string | null, - ) => { - if (password !== confirmPassword) { - setPasswordError('Passwords do not match.'); + // Clear mismatch error if either field is empty + if (!password || !newConfirmPassword) { + setMismatchError(''); + setSamePasswordCheck(''); + return; + } + + // Set mismatch error if passwords do not match + if (password !== newConfirmPassword) { + setMismatchError('✗ Passwords do not match'); + setSamePasswordCheck(''); } else { - setPasswordError(''); // Clear error when passwords match + setMismatchError(''); + setSamePasswordCheck('✓ Passwords match'); } }; // Set password complexity error if requirements are not met - const validatePasswordComplexity = (password: string | null) => { + const validateIsPasswordComplexityMet = (password: string | null) => { const hasLowerCase = /[a-z]/.test(password || ''); const hasNumber = /\d/.test(password || ''); const longEnough = (password || '').length >= 8; if (password && hasLowerCase && hasNumber && longEnough) { - setPasswordComplexity(true); - setPasswordComplexityError(null); // Clear error if all conditions are met - } else if (password) { - setPasswordComplexity(false); - setPasswordComplexityError('Password must meet complexity requirements'); + setIsPasswordComplexityMet(true); } else { - setPasswordComplexity(false); - setPasswordComplexityError(null); // Clear error if password is empty + setIsPasswordComplexityMet(false); } }; const handleSignUp = async () => { - if (password) { + setIsSubmitted(true); + + if (!isValidEmail(email)) { + setCheckValidEmailError('Please enter a valid email address'); + } else { + setCheckValidEmailError(''); // Clear email format error if valid + } + + try { await signUp(email, password); + } catch (error) { + console.error('Sign up failed:', error); + alert('There was an error during sign up. Please try again.'); } }; return ( - <> - setEmail(e.target.value)} - value={email} - placeholder="Email" - /> - {/* Email input*/} - setShowPassword(!showPassword)} - name="password" - /> - {/* Password input with toggle visibility */} - setShowConfirmPassword(!showConfirmPassword)} - name="confirmPassword" - /> - {/* Confirm password input with toggle visibility */} - {' '} - {/* Sign up button */} - {confirmPassword && passwordError && ( -

{passwordError}

- )} - {/* Conditional password validation error message */} - - {/* Password complexity requirements */} - {password && !passwordComplexity && passwordComplexityError && ( -

{passwordComplexityError}

- )} - {/* Password complexity error message */} - + +
+

Sign Up

+
+ + {/* Email input*/} + {checkEmailExistsError && ( + {checkEmailExistsError} + )} + {!isEmailValid && isSubmitted && ( + {checkValidEmailError} + )} +
+
+ setShowPassword(!showPassword)} + label="Password" + error={isSubmitted && !isPasswordComplexityMet} + /> + {/* Password input*/} + + + + {/* Password complexity requirements */} +
+
+ {password && ( + + setShowConfirmPassword(!showConfirmPassword) + } + label="Confirm Password" + error={isSubmitted && !samePasswordCheck} + /> + )} + {/* Confirm password input with toggle visibility */} + + {samePasswordCheck && ( + {samePasswordCheck} + )} + + {isSubmitted && !samePasswordCheck && ( + {mismatchError} + )} + {/* Conditional password validation error message */} +
+ + Sign up + {' '} + {/* Sign up button */} +
+
); } diff --git a/components/PasswordComplexity.tsx b/components/PasswordComplexity.tsx index e7b2ba1..ad4167d 100644 --- a/components/PasswordComplexity.tsx +++ b/components/PasswordComplexity.tsx @@ -1,14 +1,35 @@ +import COLORS from '@/styles/colors'; +import { P3 } from '@/styles/text'; + export default function PasswordComplexity({ password }: { password: string }) { - // Display requirements if there is input + // Define complexity rules with their check logic + const requirements = [ + { + met: /[a-z]/.test(password), + text: 'At least 1 lowercase character', + }, + { + met: /\d/.test(password), + text: 'At least 1 number', + }, + { + met: password.length >= 8, + text: 'At least 8 characters', + }, + ]; + + // Sort requirements: passed ones at the top + const sortedRequirements = requirements.sort((a, b) => { + return Number(b.met) - Number(a.met); + }); + + // Display sorted requirements only if there is input if (password.length > 0) { return (
- - - = 8} text="At least 8 characters" /> + {sortedRequirements.map((req, index) => ( + + ))}
); } @@ -19,8 +40,12 @@ export default function PasswordComplexity({ password }: { password: string }) { // Helper component to display each requirement with conditional styling function Requirement({ met, text }: { met: boolean; text: string }) { return ( -

+ {met ? '✓' : '✗'} {text} -

+ ); } diff --git a/components/PasswordInput.tsx b/components/PasswordInput.tsx index e10a3c9..ee96c30 100644 --- a/components/PasswordInput.tsx +++ b/components/PasswordInput.tsx @@ -1,7 +1,7 @@ import React from 'react'; interface PasswordInputProps { - value: string | null; + value: string; onChange: (e: React.ChangeEvent) => void; placeholder: string; isVisible: boolean; @@ -14,7 +14,6 @@ const PasswordInput: React.FC = ({ onChange, placeholder, isVisible, - toggleVisibility, name, }) => { return ( @@ -26,9 +25,6 @@ const PasswordInput: React.FC = ({ value={value || ''} placeholder={placeholder} /> - ); }; diff --git a/components/TextInput/index.tsx b/components/TextInput/index.tsx new file mode 100644 index 0000000..698bb23 --- /dev/null +++ b/components/TextInput/index.tsx @@ -0,0 +1,51 @@ +import React from 'react'; +import Icon from '../Icon'; +import { IconWrapper, InputWrapper, StyledInput, StyledLabel } from './styles'; + +interface TextInputProps { + label: string; + id: string; + type: string; + value: string; + onChange: (s: string) => void; + isVisible?: boolean; + toggleVisibility?: () => void; + error?: boolean; +} + +const TextInput: React.FC = ({ + label, + id, + type, + onChange, + isVisible, + value, + toggleVisibility, + error, +}) => { + const inputType = type === 'password' && isVisible ? 'text' : type; + + const handleChange = (event: React.ChangeEvent) => { + onChange(event.target.value); + }; + + return ( + + {label && {label}} + + {type === 'password' && toggleVisibility && ( + + + + )} + + ); +}; + +export default TextInput; diff --git a/components/TextInput/styles.ts b/components/TextInput/styles.ts new file mode 100644 index 0000000..7040b92 --- /dev/null +++ b/components/TextInput/styles.ts @@ -0,0 +1,69 @@ +import styled from 'styled-components'; +import COLORS from '@/styles/colors'; +import { P2 } from '@/styles/text'; + +export const InputWrapper = styled.div` + position: relative; + display: flex; + flex-direction: column; + // overflow: visible; +`; + +export const StyledLabel = styled(P2).attrs({ as: 'label' })` + margin-bottom: 0.25rem; +`; + +export const StyledInput = styled(P2).attrs({ as: 'input' })<{ + error?: boolean; +}>` + padding: 0.75rem; + border: 0.0625rem solid #ccc; + border: 1px solid ${({ error }) => (error ? COLORS.errorRed : '#ccc')}; + border-radius: 0.3125rem; + font-family: inherit; /* Inherit font-family from P2 */ + margin-bottom: 0.25rem; + transition: border-color 0.3s ease; + + &:focus { + border-color: ${({ error }) => (error ? COLORS.errorRed : COLORS.shrub)}; + outline: none; + } +`; + +export const IconWrapper = styled.span` + position: absolute; + right: 1rem; + cursor: pointer; + top: 50%; + display: flex; + align-items: center; + justify-content: center; + color: ${COLORS.darkgray}; +`; + +export const StyledButton = styled.button` + background-color: ${COLORS.shrub}; + color: white; + padding: 0.625rem 1.25rem; + border: none; + border-radius: 3.125rem; + justify-content: center; + width: 100%; + height: 2.625rem; + cursor: pointer; + + &:disabled { + background-color: ${COLORS.midgray}; // Change to a gray color to indicate disabled state + cursor: not-allowed; + } +`; + +export const TextSpacingWrapper = styled.div` + margin: 0; + marginbottom: 0.25rem; +`; + +export const StyledForm = styled.form` + minheight: 100; + padding: 1.25rem; +`; diff --git a/lib/icons.tsx b/lib/icons.tsx index a4b9cd6..736a6fe 100644 --- a/lib/icons.tsx +++ b/lib/icons.tsx @@ -452,6 +452,89 @@ export const IconSvgs = { /> ), + eye: ( + + + + + + + + + + ), + hide: ( + + + + + + + + + + ), + + check: ( + + + + ), + x: ( + + + + + ), }; export type IconType = keyof typeof IconSvgs; diff --git a/utils/AuthProvider.tsx b/utils/AuthProvider.tsx index b4b5a96..c8b42e0 100644 --- a/utils/AuthProvider.tsx +++ b/utils/AuthProvider.tsx @@ -10,7 +10,8 @@ import { useState, } from 'react'; import { UUID } from 'crypto'; -import { AuthResponse, Session } from '@supabase/supabase-js'; +import { AuthError, AuthResponse, Session } from '@supabase/supabase-js'; +import { checkEmailExists } from '@/api/supabase/queries/users'; import supabase from '../api/supabase/createClient'; interface AuthContextType { @@ -79,6 +80,11 @@ export function AuthProvider({ children }: { children: ReactNode }) { const value = await supabase.auth.signUp({ email, password }); // will trigger onAuthStateChange to update the session // check if email already exists + const emailExists = await checkEmailExists(email); + if (emailExists) { + const authError = new AuthError('Account already exists for this email'); + value.error = authError; + } return value; }, []); From 09622e00021c79d5d9333493fd3a040c1afbeac5 Mon Sep 17 00:00:00 2001 From: rachaelch3n Date: Sat, 7 Dec 2024 16:11:51 -0800 Subject: [PATCH 03/16] added svg for checks --- app/(auth)/login/page.tsx | 23 ++++++++---- app/(auth)/signup/page.tsx | 60 ++++++++++++++++++++----------- components/PasswordComplexity.tsx | 6 +++- 3 files changed, 62 insertions(+), 27 deletions(-) diff --git a/app/(auth)/login/page.tsx b/app/(auth)/login/page.tsx index b7b63b9..42148ff 100644 --- a/app/(auth)/login/page.tsx +++ b/app/(auth)/login/page.tsx @@ -1,7 +1,7 @@ 'use client'; import { useState } from 'react'; -// import { useRouter } from 'next/navigation'; +import { useRouter } from 'next/navigation'; import TextInput from '@/components/TextInput'; import { StyledButton, StyledForm } from '@/components/TextInput/styles'; import COLORS from '@/styles/colors'; @@ -18,11 +18,21 @@ export default function Login() { const isFormValid = email && password; - // const { push } = useRouter(); + const router = useRouter(); + + const handleEmailChange = async (newEmail: string) => { + setEmail(newEmail); + setInvalidEmailError(''); + setInvalidPasswordError(''); + }; + + const handlePasswordChange = (newPassword: string) => { + setPassword(newPassword); + }; + const handleLogin = async () => { try { const { error } = await signIn(email, password); - // push('/'); if (error) { // Match error messages from Supabase @@ -36,6 +46,7 @@ export default function Login() { // Clear errors on success setInvalidEmailError(''); setInvalidPasswordError(''); + router.push('/view-plants'); } catch (err) { console.error('Login Error:', err); setInvalidEmailError('An unexpected error occurred. Please try again.'); @@ -45,13 +56,13 @@ export default function Login() { return (
-

Log In

+

Log In

@@ -63,7 +74,7 @@ export default function Login() { id="password-input" label="Password" type="password" - onChange={setPassword} + onChange={handlePasswordChange} value={password} isVisible={showPassword} toggleVisibility={() => setShowPassword(!showPassword)} diff --git a/app/(auth)/signup/page.tsx b/app/(auth)/signup/page.tsx index 14ed135..bee3fe2 100644 --- a/app/(auth)/signup/page.tsx +++ b/app/(auth)/signup/page.tsx @@ -1,6 +1,8 @@ 'use client'; import { useState } from 'react'; +import { useRouter } from 'next/navigation'; +import Icon from '@/components/Icon'; import PasswordComplexity from '@/components/PasswordComplexity'; import TextInput from '@/components/TextInput'; import { StyledButton, StyledForm } from '@/components/TextInput/styles'; @@ -14,17 +16,17 @@ export default function SignUp() { const [password, setPassword] = useState(''); const [confirmPassword, setConfirmPassword] = useState(''); const [samePasswordCheck, setSamePasswordCheck] = useState(''); - const [mismatchError, setMismatchError] = useState(''); const [isPasswordComplexityMet, setIsPasswordComplexityMet] = useState(false); const [showPassword, setShowPassword] = useState(false); const [showConfirmPassword, setShowConfirmPassword] = useState(false); - const [checkEmailExistsError] = useState(''); + const [checkEmailExistsError, setCheckEmailExistsError] = useState(''); const [checkValidEmailError, setCheckValidEmailError] = useState(''); const [isSubmitted, setIsSubmitted] = useState(false); const [isEmailValid, setIsEmailValid] = useState(true); const isFormValid = email && password && confirmPassword; + const router = useRouter(); const isValidEmail = (email: string): boolean => { const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/; @@ -33,6 +35,7 @@ export default function SignUp() { const handleEmailChange = async (newEmail: string) => { setEmail(newEmail); + setCheckEmailExistsError(''); // Validate email format if (!isValidEmail(newEmail)) { @@ -49,22 +52,18 @@ export default function SignUp() { const handlePasswordChange = (newPassword: string) => { setPassword(newPassword); + validateIsPasswordComplexityMet(newPassword); if (!newPassword || !confirmPassword) { - setMismatchError(''); setSamePasswordCheck(''); return; } // Set mismatch error if passwords do not match if (newPassword !== confirmPassword) { - setMismatchError('✗ Passwords do not match'); setSamePasswordCheck(''); } else { - setMismatchError(''); setSamePasswordCheck('✓ Passwords match'); } - - validateIsPasswordComplexityMet(newPassword); }; // Handles input to confirm password @@ -73,17 +72,14 @@ export default function SignUp() { // Clear mismatch error if either field is empty if (!password || !newConfirmPassword) { - setMismatchError(''); setSamePasswordCheck(''); return; } // Set mismatch error if passwords do not match if (password !== newConfirmPassword) { - setMismatchError('✗ Passwords do not match'); setSamePasswordCheck(''); } else { - setMismatchError(''); setSamePasswordCheck('✓ Passwords match'); } }; @@ -94,11 +90,9 @@ export default function SignUp() { const hasNumber = /\d/.test(password || ''); const longEnough = (password || '').length >= 8; - if (password && hasLowerCase && hasNumber && longEnough) { - setIsPasswordComplexityMet(true); - } else { - setIsPasswordComplexityMet(false); - } + setIsPasswordComplexityMet( + !!password && hasLowerCase && hasNumber && longEnough, + ); }; const handleSignUp = async () => { @@ -111,7 +105,19 @@ export default function SignUp() { } try { - await signUp(email, password); + const result = await signUp(email, password); + if (result.error) { + // Handle the specific error (e.g., duplicate email) + if (result.error.message === 'Account already exists for this email') { + setCheckEmailExistsError(result.error.message); + } else if (checkValidEmailError == 'false') { + alert(result.error.message); // Show a generic alert for other errors + } + } else { + // Handle successful sign-up (e.g., navigate to another page) + setCheckEmailExistsError(''); + router.push('/onboarding'); + } } catch (error) { console.error('Sign up failed:', error); alert('There was an error during sign up. Please try again.'); @@ -121,7 +127,7 @@ export default function SignUp() { return (
-

Sign Up

+

Sign Up

{/* Email input*/} - {checkEmailExistsError && ( + {checkEmailExistsError && isSubmitted && ( {checkEmailExistsError} )} {!isEmailValid && isSubmitted && ( @@ -176,11 +182,25 @@ export default function SignUp() { {/* Confirm password input with toggle visibility */} {samePasswordCheck && ( - {samePasswordCheck} + +
+ + {'Passwords match'} +
+
)} {isSubmitted && !samePasswordCheck && ( - {mismatchError} + +
+ + {'Passwords do not match'} +
+
)} {/* Conditional password validation error message */}
diff --git a/components/PasswordComplexity.tsx b/components/PasswordComplexity.tsx index ad4167d..f61cf1d 100644 --- a/components/PasswordComplexity.tsx +++ b/components/PasswordComplexity.tsx @@ -1,5 +1,6 @@ import COLORS from '@/styles/colors'; import { P3 } from '@/styles/text'; +import Icon from './Icon'; export default function PasswordComplexity({ password }: { password: string }) { // Define complexity rules with their check logic @@ -45,7 +46,10 @@ function Requirement({ met, text }: { met: boolean; text: string }) { color: met ? '#0D8817' : COLORS.errorRed, }} > - {met ? '✓' : '✗'} {text} +
+ + {text} +
); } From 3aa513d9ff909e9004de9ef3d7833bd1e42fb599 Mon Sep 17 00:00:00 2001 From: rachaelch3n Date: Sun, 8 Dec 2024 23:46:27 -0800 Subject: [PATCH 04/16] fixed signup function to properly check for exisitng emails and allowing signup --- app/(auth)/signup/page.tsx | 2 -- utils/AuthProvider.tsx | 2 +- 2 files changed, 1 insertion(+), 3 deletions(-) diff --git a/app/(auth)/signup/page.tsx b/app/(auth)/signup/page.tsx index bee3fe2..8f26071 100644 --- a/app/(auth)/signup/page.tsx +++ b/app/(auth)/signup/page.tsx @@ -110,8 +110,6 @@ export default function SignUp() { // Handle the specific error (e.g., duplicate email) if (result.error.message === 'Account already exists for this email') { setCheckEmailExistsError(result.error.message); - } else if (checkValidEmailError == 'false') { - alert(result.error.message); // Show a generic alert for other errors } } else { // Handle successful sign-up (e.g., navigate to another page) diff --git a/utils/AuthProvider.tsx b/utils/AuthProvider.tsx index c8b42e0..fce10fb 100644 --- a/utils/AuthProvider.tsx +++ b/utils/AuthProvider.tsx @@ -77,10 +77,10 @@ export function AuthProvider({ children }: { children: ReactNode }) { // Sign Up function const signUp = useCallback(async (email: string, password: string) => { - const value = await supabase.auth.signUp({ email, password }); // will trigger onAuthStateChange to update the session // check if email already exists const emailExists = await checkEmailExists(email); + const value = await supabase.auth.signUp({ email, password }); if (emailExists) { const authError = new AuthError('Account already exists for this email'); value.error = authError; From 8422851870e1cc52c4056921fe832a1d86a9a544 Mon Sep 17 00:00:00 2001 From: rachaelch3n Date: Mon, 9 Dec 2024 01:01:54 -0800 Subject: [PATCH 05/16] added links to respective login and signup pages --- app/(auth)/login/page.tsx | 23 ++- app/(auth)/signup/page.tsx | 26 ++- components/Buttons.tsx | 16 ++ package.json | 3 +- pnpm-lock.yaml | 405 ++++++++++++++++++++++++++++++------- 5 files changed, 377 insertions(+), 96 deletions(-) diff --git a/app/(auth)/login/page.tsx b/app/(auth)/login/page.tsx index 42148ff..aaca290 100644 --- a/app/(auth)/login/page.tsx +++ b/app/(auth)/login/page.tsx @@ -2,8 +2,9 @@ import { useState } from 'react'; import { useRouter } from 'next/navigation'; +import { BigButton, StyledLinkButton } from '@/components/Buttons'; import TextInput from '@/components/TextInput'; -import { StyledButton, StyledForm } from '@/components/TextInput/styles'; +import { StyledForm } from '@/components/TextInput/styles'; import COLORS from '@/styles/colors'; import { H2, P3 } from '@/styles/text'; import { useAuth } from '../../../utils/AuthProvider'; @@ -55,8 +56,16 @@ export default function Login() { return ( +

+ Log In +

-

Log In

+ + Don’t have an account? + + Sign up + +
{invalidPasswordError} {/* Password input*/}
- - Log in - {' '} + + Log In + {' '} {/* Sign in button */}
diff --git a/app/(auth)/signup/page.tsx b/app/(auth)/signup/page.tsx index 8f26071..9bb796b 100644 --- a/app/(auth)/signup/page.tsx +++ b/app/(auth)/signup/page.tsx @@ -2,10 +2,11 @@ import { useState } from 'react'; import { useRouter } from 'next/navigation'; +import { BigButton, StyledLinkButton } from '@/components/Buttons'; import Icon from '@/components/Icon'; import PasswordComplexity from '@/components/PasswordComplexity'; import TextInput from '@/components/TextInput'; -import { StyledButton, StyledForm } from '@/components/TextInput/styles'; +import { StyledForm } from '@/components/TextInput/styles'; import COLORS from '@/styles/colors'; import { H2, P3 } from '@/styles/text'; import { useAuth } from '@/utils/AuthProvider'; @@ -124,8 +125,17 @@ export default function SignUp() { return ( -
-

Sign Up

+

+ Sign Up +

+ +
+ + Already have an account? + + Log in + +
- - Sign up - {' '} + + Sign Up + {' '} {/* Sign up button */}
diff --git a/components/Buttons.tsx b/components/Buttons.tsx index 4ba2ba4..37b1c9e 100644 --- a/components/Buttons.tsx +++ b/components/Buttons.tsx @@ -1,3 +1,4 @@ +import Link from 'next/link'; import styled from 'styled-components'; import COLORS from '@/styles/colors'; @@ -12,4 +13,19 @@ export const BigButton = styled.button<{ $color?: string }>` width: 100%; height: 3rem; max-height: 3rem; + + &:disabled { + background-color: ${COLORS.midgray}; // Change to a gray color to indicate disabled state + cursor: not-allowed; + } +`; + +export const StyledLinkButton = styled(Link)` + color: '#0769BF'; + border: none; + text-decoration: none; + text-align: center; + display: inline-block; + cursor: pointer; + text-decoration: underline; `; diff --git a/package.json b/package.json index 4b9357b..416419e 100644 --- a/package.json +++ b/package.json @@ -16,7 +16,7 @@ "dependencies": { "@supabase/supabase-js": "^2.45.4", "dotenv": "^16.4.5", - "next": "^14.2.10", + "next": "^15.0.4", "react": "^18", "react-dom": "^18", "react-multi-select-component": "^4.3.4", @@ -25,6 +25,7 @@ }, "devDependencies": { "@ianvs/prettier-plugin-sort-imports": "^4.3.1", + "@next/swc-darwin-arm64": "^15.0.4", "@types/node": "^20.16.5", "@types/react": "^18.3.5", "@types/react-dom": "^18.3.0", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 2b691b2..1459425 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -15,8 +15,8 @@ importers: specifier: ^16.4.5 version: 16.4.5 next: - specifier: ^14.2.10 - version: 14.2.18(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + specifier: ^15.0.4 + version: 15.0.4(react-dom@18.3.1(react@18.3.1))(react@18.3.1) react: specifier: ^18 version: 18.3.1 @@ -36,6 +36,9 @@ importers: '@ianvs/prettier-plugin-sort-imports': specifier: ^4.3.1 version: 4.4.0(prettier@3.3.3) + '@next/swc-darwin-arm64': + specifier: ^15.0.4 + version: 15.0.4 '@types/node': specifier: ^20.16.5 version: 20.17.6 @@ -102,6 +105,9 @@ packages: resolution: {integrity: sha512-Z/yiTPj+lDVnF7lWeKCIJzaIkI0vYO87dMpZ4bg4TDrFe4XXLFWL1TbXU27gBP3QccxV9mZICCrnjnYlJjXHOA==} engines: {node: '>=6.9.0'} + '@emnapi/runtime@1.3.1': + resolution: {integrity: sha512-kEBmG8KyqtxJZv+ygbEim+KCGtIq1fC22Ms3S4ziXmYKm8uyoLX0MHONVKwp+9opg390VaKRNt4a7A9NwmpNhw==} + '@emotion/is-prop-valid@1.2.2': resolution: {integrity: sha512-uNsoYd37AFmaCdXlg6EYD1KaPOaRWRByMCYzbKUX4+hhMfrxdVSelShywL4JVaAeM/eHUOSprYBQls+/neX3pw==} @@ -151,6 +157,111 @@ packages: '@vue/compiler-sfc': optional: true + '@img/sharp-darwin-arm64@0.33.5': + resolution: {integrity: sha512-UT4p+iz/2H4twwAoLCqfA9UH5pI6DggwKEGuaPy7nCVQ8ZsiY5PIcrRvD1DzuY3qYL07NtIQcWnBSY/heikIFQ==} + engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} + cpu: [arm64] + os: [darwin] + + '@img/sharp-darwin-x64@0.33.5': + resolution: {integrity: sha512-fyHac4jIc1ANYGRDxtiqelIbdWkIuQaI84Mv45KvGRRxSAa7o7d1ZKAOBaYbnepLC1WqxfpimdeWfvqqSGwR2Q==} + engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} + cpu: [x64] + os: [darwin] + + '@img/sharp-libvips-darwin-arm64@1.0.4': + resolution: {integrity: sha512-XblONe153h0O2zuFfTAbQYAX2JhYmDHeWikp1LM9Hul9gVPjFY427k6dFEcOL72O01QxQsWi761svJ/ev9xEDg==} + cpu: [arm64] + os: [darwin] + + '@img/sharp-libvips-darwin-x64@1.0.4': + resolution: {integrity: sha512-xnGR8YuZYfJGmWPvmlunFaWJsb9T/AO2ykoP3Fz/0X5XV2aoYBPkX6xqCQvUTKKiLddarLaxpzNe+b1hjeWHAQ==} + cpu: [x64] + os: [darwin] + + '@img/sharp-libvips-linux-arm64@1.0.4': + resolution: {integrity: sha512-9B+taZ8DlyyqzZQnoeIvDVR/2F4EbMepXMc/NdVbkzsJbzkUjhXv/70GQJ7tdLA4YJgNP25zukcxpX2/SueNrA==} + cpu: [arm64] + os: [linux] + + '@img/sharp-libvips-linux-arm@1.0.5': + resolution: {integrity: sha512-gvcC4ACAOPRNATg/ov8/MnbxFDJqf/pDePbBnuBDcjsI8PssmjoKMAz4LtLaVi+OnSb5FK/yIOamqDwGmXW32g==} + cpu: [arm] + os: [linux] + + '@img/sharp-libvips-linux-s390x@1.0.4': + resolution: {integrity: sha512-u7Wz6ntiSSgGSGcjZ55im6uvTrOxSIS8/dgoVMoiGE9I6JAfU50yH5BoDlYA1tcuGS7g/QNtetJnxA6QEsCVTA==} + cpu: [s390x] + os: [linux] + + '@img/sharp-libvips-linux-x64@1.0.4': + resolution: {integrity: sha512-MmWmQ3iPFZr0Iev+BAgVMb3ZyC4KeFc3jFxnNbEPas60e1cIfevbtuyf9nDGIzOaW9PdnDciJm+wFFaTlj5xYw==} + cpu: [x64] + os: [linux] + + '@img/sharp-libvips-linuxmusl-arm64@1.0.4': + resolution: {integrity: sha512-9Ti+BbTYDcsbp4wfYib8Ctm1ilkugkA/uscUn6UXK1ldpC1JjiXbLfFZtRlBhjPZ5o1NCLiDbg8fhUPKStHoTA==} + cpu: [arm64] + os: [linux] + + '@img/sharp-libvips-linuxmusl-x64@1.0.4': + resolution: {integrity: sha512-viYN1KX9m+/hGkJtvYYp+CCLgnJXwiQB39damAO7WMdKWlIhmYTfHjwSbQeUK/20vY154mwezd9HflVFM1wVSw==} + cpu: [x64] + os: [linux] + + '@img/sharp-linux-arm64@0.33.5': + resolution: {integrity: sha512-JMVv+AMRyGOHtO1RFBiJy/MBsgz0x4AWrT6QoEVVTyh1E39TrCUpTRI7mx9VksGX4awWASxqCYLCV4wBZHAYxA==} + engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} + cpu: [arm64] + os: [linux] + + '@img/sharp-linux-arm@0.33.5': + resolution: {integrity: sha512-JTS1eldqZbJxjvKaAkxhZmBqPRGmxgu+qFKSInv8moZ2AmT5Yib3EQ1c6gp493HvrvV8QgdOXdyaIBrhvFhBMQ==} + engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} + cpu: [arm] + os: [linux] + + '@img/sharp-linux-s390x@0.33.5': + resolution: {integrity: sha512-y/5PCd+mP4CA/sPDKl2961b+C9d+vPAveS33s6Z3zfASk2j5upL6fXVPZi7ztePZ5CuH+1kW8JtvxgbuXHRa4Q==} + engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} + cpu: [s390x] + os: [linux] + + '@img/sharp-linux-x64@0.33.5': + resolution: {integrity: sha512-opC+Ok5pRNAzuvq1AG0ar+1owsu842/Ab+4qvU879ippJBHvyY5n2mxF1izXqkPYlGuP/M556uh53jRLJmzTWA==} + engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} + cpu: [x64] + os: [linux] + + '@img/sharp-linuxmusl-arm64@0.33.5': + resolution: {integrity: sha512-XrHMZwGQGvJg2V/oRSUfSAfjfPxO+4DkiRh6p2AFjLQztWUuY/o8Mq0eMQVIY7HJ1CDQUJlxGGZRw1a5bqmd1g==} + engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} + cpu: [arm64] + os: [linux] + + '@img/sharp-linuxmusl-x64@0.33.5': + resolution: {integrity: sha512-WT+d/cgqKkkKySYmqoZ8y3pxx7lx9vVejxW/W4DOFMYVSkErR+w7mf2u8m/y4+xHe7yY9DAXQMWQhpnMuFfScw==} + engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} + cpu: [x64] + os: [linux] + + '@img/sharp-wasm32@0.33.5': + resolution: {integrity: sha512-ykUW4LVGaMcU9lu9thv85CbRMAwfeadCJHRsg2GmeRa/cJxsVY9Rbd57JcMxBkKHag5U/x7TSBpScF4U8ElVzg==} + engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} + cpu: [wasm32] + + '@img/sharp-win32-ia32@0.33.5': + resolution: {integrity: sha512-T36PblLaTwuVJ/zw/LaH0PdZkRz5rd3SmMHX8GSmR7vtNSP5Z6bQkExdSK7xGWyxLw4sUknBuugTelgw2faBbQ==} + engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} + cpu: [ia32] + os: [win32] + + '@img/sharp-win32-x64@0.33.5': + resolution: {integrity: sha512-MpY/o8/8kj+EcnxwvrP4aTJSWw/aZ7JIGR4aBeZkZw5B7/Jn+tY9/VNwtcoGmdT7GfggGIU4kygOMSbYnOrAbg==} + engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} + cpu: [x64] + os: [win32] + '@isaacs/cliui@8.0.2': resolution: {integrity: sha512-O8jcjabXaleOG9DQ0+ARXWZBTfnP4WNAqzuiJK7ll44AmxGKv/J2M4TPjxjY3znBCfvBXFzucm1twdyFybFqEA==} engines: {node: '>=12'} @@ -177,62 +288,55 @@ packages: '@jridgewell/trace-mapping@0.3.25': resolution: {integrity: sha512-vNk6aEwybGtawWmy/PzwnGDOjCkLWSD2wqvjGGAgOAwCGWySYXfYoxt00IJkTF+8Lb57DwOb3Aa0o9CApepiYQ==} - '@next/env@14.2.18': - resolution: {integrity: sha512-2vWLOUwIPgoqMJKG6dt35fVXVhgM09tw4tK3/Q34GFXDrfiHlG7iS33VA4ggnjWxjiz9KV5xzfsQzJX6vGAekA==} + '@next/env@15.0.4': + resolution: {integrity: sha512-WNRvtgnRVDD4oM8gbUcRc27IAhaL4eXQ/2ovGbgLnPGUvdyDr8UdXP4Q/IBDdAdojnD2eScryIDirv0YUCjUVw==} '@next/eslint-plugin-next@14.2.8': resolution: {integrity: sha512-ue5vcq9Fjk3asACRDrzYjcGMEN7pMMDQ5zUD+FenkqvlPCVUD1x7PxBNOLfPYDZOrk/Vnl4GHmjj2mZDqPW8TQ==} - '@next/swc-darwin-arm64@14.2.18': - resolution: {integrity: sha512-tOBlDHCjGdyLf0ube/rDUs6VtwNOajaWV+5FV/ajPgrvHeisllEdymY/oDgv2cx561+gJksfMUtqf8crug7sbA==} + '@next/swc-darwin-arm64@15.0.4': + resolution: {integrity: sha512-QecQXPD0yRHxSXWL5Ff80nD+A56sUXZG9koUsjWJwA2Z0ZgVQfuy7gd0/otjxoOovPVHR2eVEvPMHbtZP+pf9w==} engines: {node: '>= 10'} - cpu: [arm64] os: [darwin] - '@next/swc-darwin-x64@14.2.18': - resolution: {integrity: sha512-uJCEjutt5VeJ30jjrHV1VIHCsbMYnEqytQgvREx+DjURd/fmKy15NaVK4aR/u98S1LGTnjq35lRTnRyygglxoA==} + '@next/swc-darwin-x64@15.0.4': + resolution: {integrity: sha512-pb7Bye3y1Og3PlCtnz2oO4z+/b3pH2/HSYkLbL0hbVuTGil7fPen8/3pyyLjdiTLcFJ+ymeU3bck5hd4IPFFCA==} engines: {node: '>= 10'} cpu: [x64] os: [darwin] - '@next/swc-linux-arm64-gnu@14.2.18': - resolution: {integrity: sha512-IL6rU8vnBB+BAm6YSWZewc+qvdL1EaA+VhLQ6tlUc0xp+kkdxQrVqAnh8Zek1ccKHlTDFRyAft0e60gteYmQ4A==} + '@next/swc-linux-arm64-gnu@15.0.4': + resolution: {integrity: sha512-12oSaBFjGpB227VHzoXF3gJoK2SlVGmFJMaBJSu5rbpaoT5OjP5OuCLuR9/jnyBF1BAWMs/boa6mLMoJPRriMA==} engines: {node: '>= 10'} cpu: [arm64] os: [linux] - '@next/swc-linux-arm64-musl@14.2.18': - resolution: {integrity: sha512-RCaENbIZqKKqTlL8KNd+AZV/yAdCsovblOpYFp0OJ7ZxgLNbV5w23CUU1G5On+0fgafrsGcW+GdMKdFjaRwyYA==} + '@next/swc-linux-arm64-musl@15.0.4': + resolution: {integrity: sha512-QARO88fR/a+wg+OFC3dGytJVVviiYFEyjc/Zzkjn/HevUuJ7qGUUAUYy5PGVWY1YgTzeRYz78akQrVQ8r+sMjw==} engines: {node: '>= 10'} cpu: [arm64] os: [linux] - '@next/swc-linux-x64-gnu@14.2.18': - resolution: {integrity: sha512-3kmv8DlyhPRCEBM1Vavn8NjyXtMeQ49ID0Olr/Sut7pgzaQTo4h01S7Z8YNE0VtbowyuAL26ibcz0ka6xCTH5g==} + '@next/swc-linux-x64-gnu@15.0.4': + resolution: {integrity: sha512-Z50b0gvYiUU1vLzfAMiChV8Y+6u/T2mdfpXPHraqpypP7yIT2UV9YBBhcwYkxujmCvGEcRTVWOj3EP7XW/wUnw==} engines: {node: '>= 10'} cpu: [x64] os: [linux] - '@next/swc-linux-x64-musl@14.2.18': - resolution: {integrity: sha512-mliTfa8seVSpTbVEcKEXGjC18+TDII8ykW4a36au97spm9XMPqQTpdGPNBJ9RySSFw9/hLuaCMByluQIAnkzlw==} + '@next/swc-linux-x64-musl@15.0.4': + resolution: {integrity: sha512-7H9C4FAsrTAbA/ENzvFWsVytqRYhaJYKa2B3fyQcv96TkOGVMcvyS6s+sj4jZlacxxTcn7ygaMXUPkEk7b78zw==} engines: {node: '>= 10'} cpu: [x64] os: [linux] - '@next/swc-win32-arm64-msvc@14.2.18': - resolution: {integrity: sha512-J5g0UFPbAjKYmqS3Cy7l2fetFmWMY9Oao32eUsBPYohts26BdrMUyfCJnZFQkX9npYaHNDOWqZ6uV9hSDPw9NA==} + '@next/swc-win32-arm64-msvc@15.0.4': + resolution: {integrity: sha512-Z/v3WV5xRaeWlgJzN9r4PydWD8sXV35ywc28W63i37G2jnUgScA4OOgS8hQdiXLxE3gqfSuHTicUhr7931OXPQ==} engines: {node: '>= 10'} cpu: [arm64] os: [win32] - '@next/swc-win32-ia32-msvc@14.2.18': - resolution: {integrity: sha512-Ynxuk4ZgIpdcN7d16ivJdjsDG1+3hTvK24Pp8DiDmIa2+A4CfhJSEHHVndCHok6rnLUzAZD+/UOKESQgTsAZGg==} - engines: {node: '>= 10'} - cpu: [ia32] - os: [win32] - - '@next/swc-win32-x64-msvc@14.2.18': - resolution: {integrity: sha512-dtRGMhiU9TN5nyhwzce+7c/4CCeykYS+ipY/4mIrGzJ71+7zNo55ZxCB7cAVuNqdwtYniFNR2c9OFQ6UdFIMcg==} + '@next/swc-win32-x64-msvc@15.0.4': + resolution: {integrity: sha512-NGLchGruagh8lQpDr98bHLyWJXOBSmkEAfK980OiNBa7vNm6PsNoPvzTfstT78WyOeMRQphEQ455rggd7Eo+Dw==} engines: {node: '>= 10'} cpu: [x64] os: [win32] @@ -288,8 +392,8 @@ packages: '@swc/counter@0.1.3': resolution: {integrity: sha512-e2BR4lsJkkRlKZ/qCHPw9ZaSxc0MVUd7gtbtaB7aMvHeJVYe8sOB8DBZkP2DtISHGSku9sCK6T6cnY0CtXrOCQ==} - '@swc/helpers@0.5.5': - resolution: {integrity: sha512-KGYxvIOXcceOAbEk4bi/dVLEK9z8sZ0uBB3Il5b1rhfClSpcX0yfRO0KmTkqR2cnQDymwLB+25ZyMzICg/cm/A==} + '@swc/helpers@0.5.13': + resolution: {integrity: sha512-UoKGxQ3r5kYI9dALKJapMmuK+1zWM/H17Z1+iwnNmzcJRnfFuevZs375TA5rW31pu4BS4NoSy1fRsexDXfWn5w==} '@types/json-schema@7.0.15': resolution: {integrity: sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==} @@ -530,6 +634,13 @@ packages: color-name@1.1.4: resolution: {integrity: sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==} + color-string@1.9.1: + resolution: {integrity: sha512-shrVawQFojnZv6xM40anx4CkoDP+fZsw/ZerEMsW/pyzsRbElpsL/DBVW7q3ExxwusdNXI3lXpuhEZkzs8p5Eg==} + + color@4.2.3: + resolution: {integrity: sha512-1rXeuUUiGGrykh+CeBdu5Ie7OJwinCgQY0bc7GCRxy5xVHy+moaqkpL/jqQq0MtQOeYcrqEz4abc5f0KtU7W4A==} + engines: {node: '>=12.5.0'} + concat-map@0.0.1: resolution: {integrity: sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==} @@ -594,6 +705,10 @@ packages: resolution: {integrity: sha512-8QmQKqEASLd5nx0U1B1okLElbUuuttJ/AnYmRXbbbGDWh6uS208EjD4Xqq/I9wK7u0v6O08XhTWnt5XtEbR6Dg==} engines: {node: '>= 0.4'} + detect-libc@2.0.3: + resolution: {integrity: sha512-bwy0MGW55bG41VqxxypOsdSdGqLwXPI/focwgTYCFMbdUiBAxLg9CFzG08sz2aqzknwiX7Hkl0bQENjg8iLByw==} + engines: {node: '>=8'} + dir-glob@3.0.1: resolution: {integrity: sha512-WkrWp9GR4KXfKGYzOLmTuGVi1UWFfws377n9cc55/tb6DuqyF6pcQ5AbiHEshaDpY9v6oaSr2XCDidGmMwdzIA==} engines: {node: '>=8'} @@ -972,6 +1087,9 @@ packages: resolution: {integrity: sha512-wcjaerHw0ydZwfhiKbXJWLDY8A7yV7KhjQOpb83hGgGfId/aQa4TOvwyzn2PuswW2gPCYEL/nEAiSVpdOj1lXw==} engines: {node: '>= 0.4'} + is-arrayish@0.3.2: + resolution: {integrity: sha512-eVRqCvVlZbuw3GrM63ovNSNAeA1K16kaR/LRY/92w0zxQ5/1YzwblUX652i4Xs9RwAGjW9d9y6X88t8OaAJfWQ==} + is-async-function@2.0.0: resolution: {integrity: sha512-Y1JXKrfykRJGdlDwdKlLpLyMIiWqWvuSd17TvZk68PLAOGOoF4Xyav1z0Xhoi+gCYjZVeC5SI+hYFOfvXmGRCA==} engines: {node: '>= 0.4'} @@ -1207,21 +1325,24 @@ packages: natural-compare@1.4.0: resolution: {integrity: sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==} - next@14.2.18: - resolution: {integrity: sha512-H9qbjDuGivUDEnK6wa+p2XKO+iMzgVgyr9Zp/4Iv29lKa+DYaxJGjOeEA+5VOvJh/M7HLiskehInSa0cWxVXUw==} - engines: {node: '>=18.17.0'} + next@15.0.4: + resolution: {integrity: sha512-nuy8FH6M1FG0lktGotamQDCXhh5hZ19Vo0ht1AOIQWrYJLP598TIUagKtvJrfJ5AGwB/WmDqkKaKhMpVifvGPA==} + engines: {node: ^18.18.0 || ^19.8.0 || >= 20.0.0} hasBin: true peerDependencies: '@opentelemetry/api': ^1.1.0 '@playwright/test': ^1.41.2 - react: ^18.2.0 - react-dom: ^18.2.0 + babel-plugin-react-compiler: '*' + react: ^18.2.0 || 19.0.0-rc-66855b96-20241106 || ^19.0.0 + react-dom: ^18.2.0 || 19.0.0-rc-66855b96-20241106 || ^19.0.0 sass: ^1.3.0 peerDependenciesMeta: '@opentelemetry/api': optional: true '@playwright/test': optional: true + babel-plugin-react-compiler: + optional: true sass: optional: true @@ -1462,6 +1583,10 @@ packages: shallowequal@1.1.0: resolution: {integrity: sha512-y0m1JoUZSlPAjXVtPPW70aZWfIL/dSP7AFkRnniLCrK/8MDKog3TySTBmckD+RObVxH0v4Tox67+F14PdED2oQ==} + sharp@0.33.5: + resolution: {integrity: sha512-haPVm1EkS9pgvHrQ/F3Xy+hgcuMV0Wm9vfIBSiwZ05k+xgb0PkBQpGsAA/oWdDobNaZTH5ppvHtzCFbnSEwHVw==} + engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} + shebang-command@2.0.0: resolution: {integrity: sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==} engines: {node: '>=8'} @@ -1481,6 +1606,9 @@ packages: resolution: {integrity: sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==} engines: {node: '>=14'} + simple-swizzle@0.2.2: + resolution: {integrity: sha512-JA//kQgZtbuY83m+xT+tXJkmJncGMTFT+C+g2h2R9uxkYIrE2yy9sgmcLhCnw57/WSD+Eh3J97FPEDFnbXnDUg==} + slash@3.0.0: resolution: {integrity: sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==} engines: {node: '>=8'} @@ -1550,13 +1678,13 @@ packages: react: '>= 16.8.0' react-dom: '>= 16.8.0' - styled-jsx@5.1.1: - resolution: {integrity: sha512-pW7uC1l4mBZ8ugbiZrcIsiIvVx1UmTfw7UkC3Um2tmfUq9Bhk8IiyEIPl6F8agHgjzku6j0xQEZbfA5uSgSaCw==} + styled-jsx@5.1.6: + resolution: {integrity: sha512-qSVyDTeMotdvQYoHWLNGwRFJHC+i+ZvdBRYosOFgC+Wg1vx4frN2/RG/NA7SYqqvKNLf39P2LSRA2pu6n0XYZA==} engines: {node: '>= 12.0.0'} peerDependencies: '@babel/core': '*' babel-plugin-macros: '*' - react: '>= 16.8.0 || 17.x.x || ^18.0.0-0' + react: '>= 16.8.0 || 17.x.x || ^18.0.0-0 || ^19.0.0-0' peerDependenciesMeta: '@babel/core': optional: true @@ -1770,6 +1898,11 @@ snapshots: '@babel/helper-string-parser': 7.25.9 '@babel/helper-validator-identifier': 7.25.9 + '@emnapi/runtime@1.3.1': + dependencies: + tslib: 2.8.1 + optional: true + '@emotion/is-prop-valid@1.2.2': dependencies: '@emotion/memoize': 0.8.1 @@ -1824,6 +1957,81 @@ snapshots: transitivePeerDependencies: - supports-color + '@img/sharp-darwin-arm64@0.33.5': + optionalDependencies: + '@img/sharp-libvips-darwin-arm64': 1.0.4 + optional: true + + '@img/sharp-darwin-x64@0.33.5': + optionalDependencies: + '@img/sharp-libvips-darwin-x64': 1.0.4 + optional: true + + '@img/sharp-libvips-darwin-arm64@1.0.4': + optional: true + + '@img/sharp-libvips-darwin-x64@1.0.4': + optional: true + + '@img/sharp-libvips-linux-arm64@1.0.4': + optional: true + + '@img/sharp-libvips-linux-arm@1.0.5': + optional: true + + '@img/sharp-libvips-linux-s390x@1.0.4': + optional: true + + '@img/sharp-libvips-linux-x64@1.0.4': + optional: true + + '@img/sharp-libvips-linuxmusl-arm64@1.0.4': + optional: true + + '@img/sharp-libvips-linuxmusl-x64@1.0.4': + optional: true + + '@img/sharp-linux-arm64@0.33.5': + optionalDependencies: + '@img/sharp-libvips-linux-arm64': 1.0.4 + optional: true + + '@img/sharp-linux-arm@0.33.5': + optionalDependencies: + '@img/sharp-libvips-linux-arm': 1.0.5 + optional: true + + '@img/sharp-linux-s390x@0.33.5': + optionalDependencies: + '@img/sharp-libvips-linux-s390x': 1.0.4 + optional: true + + '@img/sharp-linux-x64@0.33.5': + optionalDependencies: + '@img/sharp-libvips-linux-x64': 1.0.4 + optional: true + + '@img/sharp-linuxmusl-arm64@0.33.5': + optionalDependencies: + '@img/sharp-libvips-linuxmusl-arm64': 1.0.4 + optional: true + + '@img/sharp-linuxmusl-x64@0.33.5': + optionalDependencies: + '@img/sharp-libvips-linuxmusl-x64': 1.0.4 + optional: true + + '@img/sharp-wasm32@0.33.5': + dependencies: + '@emnapi/runtime': 1.3.1 + optional: true + + '@img/sharp-win32-ia32@0.33.5': + optional: true + + '@img/sharp-win32-x64@0.33.5': + optional: true + '@isaacs/cliui@8.0.2': dependencies: string-width: 5.1.2 @@ -1854,37 +2062,33 @@ snapshots: '@jridgewell/resolve-uri': 3.1.2 '@jridgewell/sourcemap-codec': 1.5.0 - '@next/env@14.2.18': {} + '@next/env@15.0.4': {} '@next/eslint-plugin-next@14.2.8': dependencies: glob: 10.3.10 - '@next/swc-darwin-arm64@14.2.18': - optional: true - - '@next/swc-darwin-x64@14.2.18': - optional: true + '@next/swc-darwin-arm64@15.0.4': {} - '@next/swc-linux-arm64-gnu@14.2.18': + '@next/swc-darwin-x64@15.0.4': optional: true - '@next/swc-linux-arm64-musl@14.2.18': + '@next/swc-linux-arm64-gnu@15.0.4': optional: true - '@next/swc-linux-x64-gnu@14.2.18': + '@next/swc-linux-arm64-musl@15.0.4': optional: true - '@next/swc-linux-x64-musl@14.2.18': + '@next/swc-linux-x64-gnu@15.0.4': optional: true - '@next/swc-win32-arm64-msvc@14.2.18': + '@next/swc-linux-x64-musl@15.0.4': optional: true - '@next/swc-win32-ia32-msvc@14.2.18': + '@next/swc-win32-arm64-msvc@15.0.4': optional: true - '@next/swc-win32-x64-msvc@14.2.18': + '@next/swc-win32-x64-msvc@15.0.4': optional: true '@nodelib/fs.scandir@2.1.5': @@ -1952,9 +2156,8 @@ snapshots: '@swc/counter@0.1.3': {} - '@swc/helpers@0.5.5': + '@swc/helpers@0.5.13': dependencies: - '@swc/counter': 0.1.3 tslib: 2.8.1 '@types/json-schema@7.0.15': {} @@ -2242,6 +2445,18 @@ snapshots: color-name@1.1.4: {} + color-string@1.9.1: + dependencies: + color-name: 1.1.4 + simple-swizzle: 0.2.2 + optional: true + + color@4.2.3: + dependencies: + color-convert: 2.0.1 + color-string: 1.9.1 + optional: true + concat-map@0.0.1: {} cross-spawn@7.0.5: @@ -2304,6 +2519,9 @@ snapshots: has-property-descriptors: 1.0.2 object-keys: 1.1.1 + detect-libc@2.0.3: + optional: true + dir-glob@3.0.1: dependencies: path-type: 4.0.0 @@ -2436,8 +2654,8 @@ snapshots: '@typescript-eslint/parser': 7.2.0(eslint@8.57.1)(typescript@5.4.5) eslint: 8.57.1 eslint-import-resolver-node: 0.3.9 - eslint-import-resolver-typescript: 3.6.3(@typescript-eslint/parser@7.2.0(eslint@8.57.1)(typescript@5.4.5))(eslint-import-resolver-node@0.3.9)(eslint-plugin-import@2.31.0)(eslint@8.57.1) - eslint-plugin-import: 2.31.0(@typescript-eslint/parser@7.2.0(eslint@8.57.1)(typescript@5.4.5))(eslint-import-resolver-typescript@3.6.3)(eslint@8.57.1) + eslint-import-resolver-typescript: 3.6.3(@typescript-eslint/parser@7.2.0(eslint@8.57.1)(typescript@5.4.5))(eslint-import-resolver-node@0.3.9)(eslint-plugin-import@2.31.0(@typescript-eslint/parser@7.2.0(eslint@8.57.1)(typescript@5.4.5))(eslint@8.57.1))(eslint@8.57.1) + eslint-plugin-import: 2.31.0(@typescript-eslint/parser@7.2.0(eslint@8.57.1)(typescript@5.4.5))(eslint-import-resolver-typescript@3.6.3(@typescript-eslint/parser@7.2.0(eslint@8.57.1)(typescript@5.4.5))(eslint-import-resolver-node@0.3.9)(eslint-plugin-import@2.31.0(@typescript-eslint/parser@7.2.0(eslint@8.57.1)(typescript@5.4.5))(eslint@8.57.1))(eslint@8.57.1))(eslint@8.57.1) eslint-plugin-jsx-a11y: 6.10.2(eslint@8.57.1) eslint-plugin-react: 7.37.2(eslint@8.57.1) eslint-plugin-react-hooks: 5.0.0-canary-7118f5dd7-20230705(eslint@8.57.1) @@ -2460,37 +2678,37 @@ snapshots: transitivePeerDependencies: - supports-color - eslint-import-resolver-typescript@3.6.3(@typescript-eslint/parser@7.2.0(eslint@8.57.1)(typescript@5.4.5))(eslint-import-resolver-node@0.3.9)(eslint-plugin-import@2.31.0)(eslint@8.57.1): + eslint-import-resolver-typescript@3.6.3(@typescript-eslint/parser@7.2.0(eslint@8.57.1)(typescript@5.4.5))(eslint-import-resolver-node@0.3.9)(eslint-plugin-import@2.31.0(@typescript-eslint/parser@7.2.0(eslint@8.57.1)(typescript@5.4.5))(eslint@8.57.1))(eslint@8.57.1): dependencies: '@nolyfill/is-core-module': 1.0.39 debug: 4.3.7 enhanced-resolve: 5.17.1 eslint: 8.57.1 - eslint-module-utils: 2.12.0(@typescript-eslint/parser@7.2.0(eslint@8.57.1)(typescript@5.4.5))(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@3.6.3)(eslint@8.57.1) + eslint-module-utils: 2.12.0(@typescript-eslint/parser@7.2.0(eslint@8.57.1)(typescript@5.4.5))(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@3.6.3(@typescript-eslint/parser@7.2.0(eslint@8.57.1)(typescript@5.4.5))(eslint-import-resolver-node@0.3.9)(eslint-plugin-import@2.31.0(@typescript-eslint/parser@7.2.0(eslint@8.57.1)(typescript@5.4.5))(eslint@8.57.1))(eslint@8.57.1))(eslint@8.57.1) fast-glob: 3.3.2 get-tsconfig: 4.8.1 is-bun-module: 1.2.1 is-glob: 4.0.3 optionalDependencies: - eslint-plugin-import: 2.31.0(@typescript-eslint/parser@7.2.0(eslint@8.57.1)(typescript@5.4.5))(eslint-import-resolver-typescript@3.6.3)(eslint@8.57.1) + eslint-plugin-import: 2.31.0(@typescript-eslint/parser@7.2.0(eslint@8.57.1)(typescript@5.4.5))(eslint-import-resolver-typescript@3.6.3(@typescript-eslint/parser@7.2.0(eslint@8.57.1)(typescript@5.4.5))(eslint-import-resolver-node@0.3.9)(eslint-plugin-import@2.31.0(@typescript-eslint/parser@7.2.0(eslint@8.57.1)(typescript@5.4.5))(eslint@8.57.1))(eslint@8.57.1))(eslint@8.57.1) transitivePeerDependencies: - '@typescript-eslint/parser' - eslint-import-resolver-node - eslint-import-resolver-webpack - supports-color - eslint-module-utils@2.12.0(@typescript-eslint/parser@7.2.0(eslint@8.57.1)(typescript@5.4.5))(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@3.6.3)(eslint@8.57.1): + eslint-module-utils@2.12.0(@typescript-eslint/parser@7.2.0(eslint@8.57.1)(typescript@5.4.5))(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@3.6.3(@typescript-eslint/parser@7.2.0(eslint@8.57.1)(typescript@5.4.5))(eslint-import-resolver-node@0.3.9)(eslint-plugin-import@2.31.0(@typescript-eslint/parser@7.2.0(eslint@8.57.1)(typescript@5.4.5))(eslint@8.57.1))(eslint@8.57.1))(eslint@8.57.1): dependencies: debug: 3.2.7 optionalDependencies: '@typescript-eslint/parser': 7.2.0(eslint@8.57.1)(typescript@5.4.5) eslint: 8.57.1 eslint-import-resolver-node: 0.3.9 - eslint-import-resolver-typescript: 3.6.3(@typescript-eslint/parser@7.2.0(eslint@8.57.1)(typescript@5.4.5))(eslint-import-resolver-node@0.3.9)(eslint-plugin-import@2.31.0)(eslint@8.57.1) + eslint-import-resolver-typescript: 3.6.3(@typescript-eslint/parser@7.2.0(eslint@8.57.1)(typescript@5.4.5))(eslint-import-resolver-node@0.3.9)(eslint-plugin-import@2.31.0(@typescript-eslint/parser@7.2.0(eslint@8.57.1)(typescript@5.4.5))(eslint@8.57.1))(eslint@8.57.1) transitivePeerDependencies: - supports-color - eslint-plugin-import@2.31.0(@typescript-eslint/parser@7.2.0(eslint@8.57.1)(typescript@5.4.5))(eslint-import-resolver-typescript@3.6.3)(eslint@8.57.1): + eslint-plugin-import@2.31.0(@typescript-eslint/parser@7.2.0(eslint@8.57.1)(typescript@5.4.5))(eslint-import-resolver-typescript@3.6.3(@typescript-eslint/parser@7.2.0(eslint@8.57.1)(typescript@5.4.5))(eslint-import-resolver-node@0.3.9)(eslint-plugin-import@2.31.0(@typescript-eslint/parser@7.2.0(eslint@8.57.1)(typescript@5.4.5))(eslint@8.57.1))(eslint@8.57.1))(eslint@8.57.1): dependencies: '@rtsao/scc': 1.1.0 array-includes: 3.1.8 @@ -2501,7 +2719,7 @@ snapshots: doctrine: 2.1.0 eslint: 8.57.1 eslint-import-resolver-node: 0.3.9 - eslint-module-utils: 2.12.0(@typescript-eslint/parser@7.2.0(eslint@8.57.1)(typescript@5.4.5))(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@3.6.3)(eslint@8.57.1) + eslint-module-utils: 2.12.0(@typescript-eslint/parser@7.2.0(eslint@8.57.1)(typescript@5.4.5))(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@3.6.3(@typescript-eslint/parser@7.2.0(eslint@8.57.1)(typescript@5.4.5))(eslint-import-resolver-node@0.3.9)(eslint-plugin-import@2.31.0(@typescript-eslint/parser@7.2.0(eslint@8.57.1)(typescript@5.4.5))(eslint@8.57.1))(eslint@8.57.1))(eslint@8.57.1) hasown: 2.0.2 is-core-module: 2.15.1 is-glob: 4.0.3 @@ -2858,6 +3076,9 @@ snapshots: call-bind: 1.0.7 get-intrinsic: 1.2.4 + is-arrayish@0.3.2: + optional: true + is-async-function@2.0.0: dependencies: has-tostringtag: 1.0.2 @@ -3069,27 +3290,27 @@ snapshots: natural-compare@1.4.0: {} - next@14.2.18(react-dom@18.3.1(react@18.3.1))(react@18.3.1): + next@15.0.4(react-dom@18.3.1(react@18.3.1))(react@18.3.1): dependencies: - '@next/env': 14.2.18 - '@swc/helpers': 0.5.5 + '@next/env': 15.0.4 + '@swc/counter': 0.1.3 + '@swc/helpers': 0.5.13 busboy: 1.6.0 caniuse-lite: 1.0.30001680 - graceful-fs: 4.2.11 postcss: 8.4.31 react: 18.3.1 react-dom: 18.3.1(react@18.3.1) - styled-jsx: 5.1.1(react@18.3.1) + styled-jsx: 5.1.6(react@18.3.1) optionalDependencies: - '@next/swc-darwin-arm64': 14.2.18 - '@next/swc-darwin-x64': 14.2.18 - '@next/swc-linux-arm64-gnu': 14.2.18 - '@next/swc-linux-arm64-musl': 14.2.18 - '@next/swc-linux-x64-gnu': 14.2.18 - '@next/swc-linux-x64-musl': 14.2.18 - '@next/swc-win32-arm64-msvc': 14.2.18 - '@next/swc-win32-ia32-msvc': 14.2.18 - '@next/swc-win32-x64-msvc': 14.2.18 + '@next/swc-darwin-arm64': 15.0.4 + '@next/swc-darwin-x64': 15.0.4 + '@next/swc-linux-arm64-gnu': 15.0.4 + '@next/swc-linux-arm64-musl': 15.0.4 + '@next/swc-linux-x64-gnu': 15.0.4 + '@next/swc-linux-x64-musl': 15.0.4 + '@next/swc-win32-arm64-msvc': 15.0.4 + '@next/swc-win32-x64-msvc': 15.0.4 + sharp: 0.33.5 transitivePeerDependencies: - '@babel/core' - babel-plugin-macros @@ -3338,6 +3559,33 @@ snapshots: shallowequal@1.1.0: {} + sharp@0.33.5: + dependencies: + color: 4.2.3 + detect-libc: 2.0.3 + semver: 7.6.3 + optionalDependencies: + '@img/sharp-darwin-arm64': 0.33.5 + '@img/sharp-darwin-x64': 0.33.5 + '@img/sharp-libvips-darwin-arm64': 1.0.4 + '@img/sharp-libvips-darwin-x64': 1.0.4 + '@img/sharp-libvips-linux-arm': 1.0.5 + '@img/sharp-libvips-linux-arm64': 1.0.4 + '@img/sharp-libvips-linux-s390x': 1.0.4 + '@img/sharp-libvips-linux-x64': 1.0.4 + '@img/sharp-libvips-linuxmusl-arm64': 1.0.4 + '@img/sharp-libvips-linuxmusl-x64': 1.0.4 + '@img/sharp-linux-arm': 0.33.5 + '@img/sharp-linux-arm64': 0.33.5 + '@img/sharp-linux-s390x': 0.33.5 + '@img/sharp-linux-x64': 0.33.5 + '@img/sharp-linuxmusl-arm64': 0.33.5 + '@img/sharp-linuxmusl-x64': 0.33.5 + '@img/sharp-wasm32': 0.33.5 + '@img/sharp-win32-ia32': 0.33.5 + '@img/sharp-win32-x64': 0.33.5 + optional: true + shebang-command@2.0.0: dependencies: shebang-regex: 3.0.0 @@ -3355,6 +3603,11 @@ snapshots: signal-exit@4.1.0: {} + simple-swizzle@0.2.2: + dependencies: + is-arrayish: 0.3.2 + optional: true + slash@3.0.0: {} source-map-js@1.2.1: {} @@ -3446,7 +3699,7 @@ snapshots: stylis: 4.3.2 tslib: 2.6.2 - styled-jsx@5.1.1(react@18.3.1): + styled-jsx@5.1.6(react@18.3.1): dependencies: client-only: 0.0.1 react: 18.3.1 From 5cbf52dd0067c7286e74981a4fd5d0621eaf6d71 Mon Sep 17 00:00:00 2001 From: rachaelch3n Date: Tue, 10 Dec 2024 17:29:49 -0800 Subject: [PATCH 06/16] fixed another edge case so mismath error disappears if passwrod input is empty --- app/(auth)/signup/page.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/(auth)/signup/page.tsx b/app/(auth)/signup/page.tsx index 9bb796b..b844e60 100644 --- a/app/(auth)/signup/page.tsx +++ b/app/(auth)/signup/page.tsx @@ -200,7 +200,7 @@ export default function SignUp() { )} - {isSubmitted && !samePasswordCheck && ( + {isSubmitted && !samePasswordCheck && !!password && (
Date: Thu, 12 Dec 2024 21:02:25 -0800 Subject: [PATCH 07/16] checkEmail before calling supabase signup --- utils/AuthProvider.tsx | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/utils/AuthProvider.tsx b/utils/AuthProvider.tsx index fce10fb..62d1731 100644 --- a/utils/AuthProvider.tsx +++ b/utils/AuthProvider.tsx @@ -80,11 +80,12 @@ export function AuthProvider({ children }: { children: ReactNode }) { // will trigger onAuthStateChange to update the session // check if email already exists const emailExists = await checkEmailExists(email); - const value = await supabase.auth.signUp({ email, password }); if (emailExists) { const authError = new AuthError('Account already exists for this email'); - value.error = authError; + // return the authError in an AuthResponse object + return { data: { user: null, session: null }, error: authError }; } + const value = await supabase.auth.signUp({ email, password }); return value; }, []); From c3d59a1ae4b443e6f6a451b199e1070894311802 Mon Sep 17 00:00:00 2001 From: Catherine Tan Date: Thu, 12 Dec 2024 21:02:54 -0800 Subject: [PATCH 08/16] fix lint errors --- app/(auth)/signup/page.tsx | 2 +- components/TextInput/index.tsx | 10 ++++------ components/TextInput/styles.ts | 6 +++--- 3 files changed, 8 insertions(+), 10 deletions(-) diff --git a/app/(auth)/signup/page.tsx b/app/(auth)/signup/page.tsx index b844e60..3702d68 100644 --- a/app/(auth)/signup/page.tsx +++ b/app/(auth)/signup/page.tsx @@ -130,7 +130,7 @@ export default function SignUp() {
- + Already have an account? Log in diff --git a/components/TextInput/index.tsx b/components/TextInput/index.tsx index 698bb23..9769f62 100644 --- a/components/TextInput/index.tsx +++ b/components/TextInput/index.tsx @@ -13,7 +13,7 @@ interface TextInputProps { error?: boolean; } -const TextInput: React.FC = ({ +export default function TextInput({ label, id, type, @@ -22,7 +22,7 @@ const TextInput: React.FC = ({ value, toggleVisibility, error, -}) => { +}: TextInputProps) { const inputType = type === 'password' && isVisible ? 'text' : type; const handleChange = (event: React.ChangeEvent) => { @@ -37,7 +37,7 @@ const TextInput: React.FC = ({ type={inputType} value={value} onChange={handleChange} - error={error} + $error={error} /> {type === 'password' && toggleVisibility && ( @@ -46,6 +46,4 @@ const TextInput: React.FC = ({ )} ); -}; - -export default TextInput; +} diff --git a/components/TextInput/styles.ts b/components/TextInput/styles.ts index 7040b92..119df9b 100644 --- a/components/TextInput/styles.ts +++ b/components/TextInput/styles.ts @@ -14,18 +14,18 @@ export const StyledLabel = styled(P2).attrs({ as: 'label' })` `; export const StyledInput = styled(P2).attrs({ as: 'input' })<{ - error?: boolean; + $error?: boolean; }>` padding: 0.75rem; border: 0.0625rem solid #ccc; - border: 1px solid ${({ error }) => (error ? COLORS.errorRed : '#ccc')}; + border: 1px solid ${({ $error }) => ($error ? COLORS.errorRed : '#ccc')}; border-radius: 0.3125rem; font-family: inherit; /* Inherit font-family from P2 */ margin-bottom: 0.25rem; transition: border-color 0.3s ease; &:focus { - border-color: ${({ error }) => (error ? COLORS.errorRed : COLORS.shrub)}; + border-color: ${({ $error }) => ($error ? COLORS.errorRed : COLORS.shrub)}; outline: none; } `; From 7cd0335e9de9b6e337f306b1c891b1d946a243f7 Mon Sep 17 00:00:00 2001 From: Catherine Tan Date: Thu, 12 Dec 2024 21:26:58 -0800 Subject: [PATCH 09/16] move STyledForm to (auth)/styles --- app/(auth)/login/page.tsx | 2 +- app/(auth)/signup/page.tsx | 14 ++++++-------- app/(auth)/styles.ts | 6 ++++++ components/TextInput/styles.ts | 8 +------- utils/AuthProvider.tsx | 12 ++++++------ 5 files changed, 20 insertions(+), 22 deletions(-) create mode 100644 app/(auth)/styles.ts diff --git a/app/(auth)/login/page.tsx b/app/(auth)/login/page.tsx index aaca290..13dfba8 100644 --- a/app/(auth)/login/page.tsx +++ b/app/(auth)/login/page.tsx @@ -4,10 +4,10 @@ import { useState } from 'react'; import { useRouter } from 'next/navigation'; import { BigButton, StyledLinkButton } from '@/components/Buttons'; import TextInput from '@/components/TextInput'; -import { StyledForm } from '@/components/TextInput/styles'; import COLORS from '@/styles/colors'; import { H2, P3 } from '@/styles/text'; import { useAuth } from '../../../utils/AuthProvider'; +import { StyledForm } from '../styles'; export default function Login() { const { signIn } = useAuth(); // Use `signIn` function from AuthProvider diff --git a/app/(auth)/signup/page.tsx b/app/(auth)/signup/page.tsx index 3702d68..4614c75 100644 --- a/app/(auth)/signup/page.tsx +++ b/app/(auth)/signup/page.tsx @@ -6,10 +6,10 @@ import { BigButton, StyledLinkButton } from '@/components/Buttons'; import Icon from '@/components/Icon'; import PasswordComplexity from '@/components/PasswordComplexity'; import TextInput from '@/components/TextInput'; -import { StyledForm } from '@/components/TextInput/styles'; import COLORS from '@/styles/colors'; import { H2, P3 } from '@/styles/text'; import { useAuth } from '@/utils/AuthProvider'; +import { StyledForm } from '../styles'; export default function SignUp() { const { signUp } = useAuth(); @@ -109,9 +109,7 @@ export default function SignUp() { const result = await signUp(email, password); if (result.error) { // Handle the specific error (e.g., duplicate email) - if (result.error.message === 'Account already exists for this email') { - setCheckEmailExistsError(result.error.message); - } + setCheckEmailExistsError(result.error.message); } else { // Handle successful sign-up (e.g., navigate to another page) setCheckEmailExistsError(''); @@ -190,23 +188,23 @@ export default function SignUp() { {/* Confirm password input with toggle visibility */} {samePasswordCheck && ( - +
- {'Passwords match'} + Passwords match
)} {isSubmitted && !samePasswordCheck && !!password && ( - +
- {'Passwords do not match'} + Passwords do not match
)} diff --git a/app/(auth)/styles.ts b/app/(auth)/styles.ts new file mode 100644 index 0000000..1bfedd6 --- /dev/null +++ b/app/(auth)/styles.ts @@ -0,0 +1,6 @@ +import styled from 'styled-components'; + +export const StyledForm = styled.form` + padding: 24px; + padding-top: 48px; +`; diff --git a/components/TextInput/styles.ts b/components/TextInput/styles.ts index 119df9b..fdcd078 100644 --- a/components/TextInput/styles.ts +++ b/components/TextInput/styles.ts @@ -6,7 +6,6 @@ export const InputWrapper = styled.div` position: relative; display: flex; flex-direction: column; - // overflow: visible; `; export const StyledLabel = styled(P2).attrs({ as: 'label' })` @@ -60,10 +59,5 @@ export const StyledButton = styled.button` export const TextSpacingWrapper = styled.div` margin: 0; - marginbottom: 0.25rem; -`; - -export const StyledForm = styled.form` - minheight: 100; - padding: 1.25rem; + margin-bottom: 0.25rem; `; diff --git a/utils/AuthProvider.tsx b/utils/AuthProvider.tsx index 62d1731..395c34c 100644 --- a/utils/AuthProvider.tsx +++ b/utils/AuthProvider.tsx @@ -79,12 +79,12 @@ export function AuthProvider({ children }: { children: ReactNode }) { const signUp = useCallback(async (email: string, password: string) => { // will trigger onAuthStateChange to update the session // check if email already exists - const emailExists = await checkEmailExists(email); - if (emailExists) { - const authError = new AuthError('Account already exists for this email'); - // return the authError in an AuthResponse object - return { data: { user: null, session: null }, error: authError }; - } + // const emailExists = await checkEmailExists(email); + // if (emailExists) { + // const authError = new AuthError('Account already exists for this email'); + // // return the authError in an AuthResponse object + // return { data: { user: null, session: null }, error: authError }; + // } const value = await supabase.auth.signUp({ email, password }); return value; }, []); From 5a7d1b8f1beb077b4f87a7515ed37a24250462c3 Mon Sep 17 00:00:00 2001 From: Catherine Tan Date: Thu, 12 Dec 2024 21:47:22 -0800 Subject: [PATCH 10/16] clean up PasswordComplexity --- components/PasswordComplexity.tsx | 17 +++++++---------- styles/colors.ts | 1 + 2 files changed, 8 insertions(+), 10 deletions(-) diff --git a/components/PasswordComplexity.tsx b/components/PasswordComplexity.tsx index f61cf1d..1744d5e 100644 --- a/components/PasswordComplexity.tsx +++ b/components/PasswordComplexity.tsx @@ -1,4 +1,5 @@ import COLORS from '@/styles/colors'; +import { Flex } from '@/styles/containers'; import { P3 } from '@/styles/text'; import Icon from './Icon'; @@ -20,9 +21,9 @@ export default function PasswordComplexity({ password }: { password: string }) { ]; // Sort requirements: passed ones at the top - const sortedRequirements = requirements.sort((a, b) => { - return Number(b.met) - Number(a.met); - }); + const sortedRequirements = requirements.sort( + (a, b) => Number(b.met) - Number(a.met), + ); // Display sorted requirements only if there is input if (password.length > 0) { @@ -41,15 +42,11 @@ export default function PasswordComplexity({ password }: { password: string }) { // Helper component to display each requirement with conditional styling function Requirement({ met, text }: { met: boolean; text: string }) { return ( - -
+ + {text} -
+
); } diff --git a/styles/colors.ts b/styles/colors.ts index 08e23d6..40c2642 100644 --- a/styles/colors.ts +++ b/styles/colors.ts @@ -17,6 +17,7 @@ const COLORS = { // error errorRed: '#D94E4E', + successGreen: '#0D8817', //seasonality chart colors indoors: '#F5B868', From 5b273166fd53dbffc7b81aca261a86aa87a493d8 Mon Sep 17 00:00:00 2001 From: Catherine Tan Date: Thu, 12 Dec 2024 22:41:20 -0800 Subject: [PATCH 11/16] remove unnecessary states in signpu --- app/(auth)/signup/page.tsx | 90 ++++++++++--------------------- components/Buttons.tsx | 1 + components/PasswordComplexity.tsx | 20 +++++-- 3 files changed, 47 insertions(+), 64 deletions(-) diff --git a/app/(auth)/signup/page.tsx b/app/(auth)/signup/page.tsx index 4614c75..b94250b 100644 --- a/app/(auth)/signup/page.tsx +++ b/app/(auth)/signup/page.tsx @@ -13,21 +13,21 @@ import { StyledForm } from '../styles'; export default function SignUp() { const { signUp } = useAuth(); - const [email, setEmail] = useState(''); + const [email, setEmail] = useState(''); const [password, setPassword] = useState(''); const [confirmPassword, setConfirmPassword] = useState(''); - const [samePasswordCheck, setSamePasswordCheck] = useState(''); const [isPasswordComplexityMet, setIsPasswordComplexityMet] = useState(false); - const [showPassword, setShowPassword] = useState(false); - const [showConfirmPassword, setShowConfirmPassword] = useState(false); + const [showPassword, setShowPassword] = useState(false); + const [showConfirmPassword, setShowConfirmPassword] = + useState(false); const [checkEmailExistsError, setCheckEmailExistsError] = useState(''); const [checkValidEmailError, setCheckValidEmailError] = useState(''); - const [isSubmitted, setIsSubmitted] = useState(false); - const [isEmailValid, setIsEmailValid] = useState(true); + const [isSubmitted, setIsSubmitted] = useState(false); - const isFormValid = email && password && confirmPassword; const router = useRouter(); + const passwordsMatch = password === confirmPassword; + const canSubmitForm = email && password && confirmPassword && passwordsMatch; const isValidEmail = (email: string): boolean => { const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/; @@ -36,64 +36,26 @@ export default function SignUp() { const handleEmailChange = async (newEmail: string) => { setEmail(newEmail); + // Clear out the email errors when user starts typing again setCheckEmailExistsError(''); + setCheckValidEmailError(''); - // Validate email format - if (!isValidEmail(newEmail)) { - setIsEmailValid(false); - setCheckValidEmailError('Please enter a valid email address'); - } else { - setIsEmailValid(true); - setCheckValidEmailError(''); + // If not first try, validate email format as user types + if (isSubmitted) { + setCheckValidEmailError( + !isValidEmail(newEmail) ? 'Please enter a valid email address' : '', + ); } - // Check if the email already exists }; // Handles input to password const handlePasswordChange = (newPassword: string) => { setPassword(newPassword); - - validateIsPasswordComplexityMet(newPassword); - if (!newPassword || !confirmPassword) { - setSamePasswordCheck(''); - return; - } - - // Set mismatch error if passwords do not match - if (newPassword !== confirmPassword) { - setSamePasswordCheck(''); - } else { - setSamePasswordCheck('✓ Passwords match'); - } }; // Handles input to confirm password const handleConfirmPasswordChange = (newConfirmPassword: string) => { setConfirmPassword(newConfirmPassword); - - // Clear mismatch error if either field is empty - if (!password || !newConfirmPassword) { - setSamePasswordCheck(''); - return; - } - - // Set mismatch error if passwords do not match - if (password !== newConfirmPassword) { - setSamePasswordCheck(''); - } else { - setSamePasswordCheck('✓ Passwords match'); - } - }; - - // Set password complexity error if requirements are not met - const validateIsPasswordComplexityMet = (password: string | null) => { - const hasLowerCase = /[a-z]/.test(password || ''); - const hasNumber = /\d/.test(password || ''); - const longEnough = (password || '').length >= 8; - - setIsPasswordComplexityMet( - !!password && hasLowerCase && hasNumber && longEnough, - ); }; const handleSignUp = async () => { @@ -101,6 +63,7 @@ export default function SignUp() { if (!isValidEmail(email)) { setCheckValidEmailError('Please enter a valid email address'); + return; } else { setCheckValidEmailError(''); // Clear email format error if valid } @@ -141,14 +104,14 @@ export default function SignUp() { type="email" onChange={handleEmailChange} value={email} - error={!!checkEmailExistsError || (!isEmailValid && isSubmitted)} + error={!!checkEmailExistsError} /> {/* Email input*/} {checkEmailExistsError && isSubmitted && ( - {checkEmailExistsError} + {checkEmailExistsError} )} - {!isEmailValid && isSubmitted && ( - {checkValidEmailError} + {checkValidEmailError && isSubmitted && ( + {checkValidEmailError} )}
@@ -166,6 +129,7 @@ export default function SignUp() { {/* Password complexity requirements */} @@ -182,12 +146,12 @@ export default function SignUp() { setShowConfirmPassword(!showConfirmPassword) } label="Confirm Password" - error={isSubmitted && !samePasswordCheck} + error={isSubmitted && !passwordsMatch} /> )} {/* Confirm password input with toggle visibility */} - {samePasswordCheck && ( + {password && passwordsMatch && (
)} - {isSubmitted && !samePasswordCheck && !!password && ( + {isSubmitted && !passwordsMatch && !!password && (
- - Sign Up + + Sign Up {' '} {/* Sign up button */}
diff --git a/components/Buttons.tsx b/components/Buttons.tsx index 37b1c9e..85ae1ec 100644 --- a/components/Buttons.tsx +++ b/components/Buttons.tsx @@ -13,6 +13,7 @@ export const BigButton = styled.button<{ $color?: string }>` width: 100%; height: 3rem; max-height: 3rem; + font-family: inherit; &:disabled { background-color: ${COLORS.midgray}; // Change to a gray color to indicate disabled state diff --git a/components/PasswordComplexity.tsx b/components/PasswordComplexity.tsx index 1744d5e..3dafa73 100644 --- a/components/PasswordComplexity.tsx +++ b/components/PasswordComplexity.tsx @@ -1,10 +1,24 @@ +import { useEffect } from 'react'; import COLORS from '@/styles/colors'; import { Flex } from '@/styles/containers'; import { P3 } from '@/styles/text'; import Icon from './Icon'; -export default function PasswordComplexity({ password }: { password: string }) { - // Define complexity rules with their check logic +export default function PasswordComplexity({ + password, + setPasswordComplexityMet, +}: { + password: string; + setPasswordComplexityMet: (met: boolean) => void; +}) { + useEffect(() => { + // didn't use requirements in the dependency array as that would + // require wrapping requirements in useMemo + const allRequirementsMet = + /[a-z]/.test(password) && /\d/.test(password) && password.length >= 8; + setPasswordComplexityMet(allRequirementsMet); + }, [password, setPasswordComplexityMet]); + const requirements = [ { met: /[a-z]/.test(password), @@ -40,7 +54,7 @@ export default function PasswordComplexity({ password }: { password: string }) { } // Helper component to display each requirement with conditional styling -function Requirement({ met, text }: { met: boolean; text: string }) { +export function Requirement({ met, text }: { met: boolean; text: string }) { return ( From 6aa0a4033cef9b2eee0333ba755a53ee60f6b493 Mon Sep 17 00:00:00 2001 From: Catherine Tan Date: Thu, 12 Dec 2024 22:51:41 -0800 Subject: [PATCH 12/16] use Requirement component in signup --- app/(auth)/signup/page.tsx | 52 +++++++++++++------------------------- 1 file changed, 18 insertions(+), 34 deletions(-) diff --git a/app/(auth)/signup/page.tsx b/app/(auth)/signup/page.tsx index b94250b..d21caea 100644 --- a/app/(auth)/signup/page.tsx +++ b/app/(auth)/signup/page.tsx @@ -3,8 +3,9 @@ import { useState } from 'react'; import { useRouter } from 'next/navigation'; import { BigButton, StyledLinkButton } from '@/components/Buttons'; -import Icon from '@/components/Icon'; -import PasswordComplexity from '@/components/PasswordComplexity'; +import PasswordComplexity, { + Requirement, +} from '@/components/PasswordComplexity'; import TextInput from '@/components/TextInput'; import COLORS from '@/styles/colors'; import { H2, P3 } from '@/styles/text'; @@ -41,11 +42,11 @@ export default function SignUp() { setCheckValidEmailError(''); // If not first try, validate email format as user types - if (isSubmitted) { - setCheckValidEmailError( - !isValidEmail(newEmail) ? 'Please enter a valid email address' : '', - ); - } + // if (isSubmitted) { + // setCheckValidEmailError( + // !isValidEmail(newEmail) ? 'Please enter a valid email address' : '', + // ); + // } }; // Handles input to password @@ -115,6 +116,7 @@ export default function SignUp() { )}
+ {/* Password input*/} - {/* Password input*/} + {/* Password complexity requirements */} - - {/* Password complexity requirements */}
+ {/* Confirm password input with toggle visibility */} {password && ( )} - {/* Confirm password input with toggle visibility */} - - {password && passwordsMatch && ( - -
- - Passwords match -
-
- )} - - {isSubmitted && !passwordsMatch && !!password && ( - -
- - Passwords do not match -
-
- )} {/* Conditional password validation error message */} + {password && ( + + )}
+ {/* Sign up button */} Sign Up {' '} - {/* Sign up button */}
); From d698220bf137a90612f36aea3c7517745ff3c3e9 Mon Sep 17 00:00:00 2001 From: Catherine Tan Date: Thu, 12 Dec 2024 23:02:24 -0800 Subject: [PATCH 13/16] simplify error state to signupError --- app/(auth)/signup/page.tsx | 70 +++++++++++++++++--------------------- 1 file changed, 32 insertions(+), 38 deletions(-) diff --git a/app/(auth)/signup/page.tsx b/app/(auth)/signup/page.tsx index d21caea..25f0c81 100644 --- a/app/(auth)/signup/page.tsx +++ b/app/(auth)/signup/page.tsx @@ -22,13 +22,13 @@ export default function SignUp() { const [showPassword, setShowPassword] = useState(false); const [showConfirmPassword, setShowConfirmPassword] = useState(false); - const [checkEmailExistsError, setCheckEmailExistsError] = useState(''); - const [checkValidEmailError, setCheckValidEmailError] = useState(''); + const [signupError, setSignupError] = useState(''); const [isSubmitted, setIsSubmitted] = useState(false); const router = useRouter(); const passwordsMatch = password === confirmPassword; - const canSubmitForm = email && password && confirmPassword && passwordsMatch; + const canSubmitForm = + email && password && passwordsMatch && isPasswordComplexityMet; const isValidEmail = (email: string): boolean => { const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/; @@ -38,10 +38,9 @@ export default function SignUp() { const handleEmailChange = async (newEmail: string) => { setEmail(newEmail); // Clear out the email errors when user starts typing again - setCheckEmailExistsError(''); - setCheckValidEmailError(''); + setSignupError(''); - // If not first try, validate email format as user types + // TODO: decide if we want to validate email as user is typing // if (isSubmitted) { // setCheckValidEmailError( // !isValidEmail(newEmail) ? 'Please enter a valid email address' : '', @@ -63,20 +62,17 @@ export default function SignUp() { setIsSubmitted(true); if (!isValidEmail(email)) { - setCheckValidEmailError('Please enter a valid email address'); + setSignupError('Please enter a valid email address'); return; - } else { - setCheckValidEmailError(''); // Clear email format error if valid } try { const result = await signUp(email, password); if (result.error) { - // Handle the specific error (e.g., duplicate email) - setCheckEmailExistsError(result.error.message); + setSignupError(result.error.message); } else { // Handle successful sign-up (e.g., navigate to another page) - setCheckEmailExistsError(''); + setSignupError(''); router.push('/onboarding'); } } catch (error) { @@ -99,20 +95,17 @@ export default function SignUp() {
+ {/* Email input*/} - {/* Email input*/} - {checkEmailExistsError && isSubmitted && ( - {checkEmailExistsError} - )} - {checkValidEmailError && isSubmitted && ( - {checkValidEmailError} + {signupError && isSubmitted && ( + {signupError} )}
@@ -137,25 +130,26 @@ export default function SignUp() {
{/* Confirm password input with toggle visibility */} {password && ( - - setShowConfirmPassword(!showConfirmPassword) - } - label="Confirm Password" - error={isSubmitted && !passwordsMatch} - /> - )} - {/* Conditional password validation error message */} - {password && ( - + <> + + setShowConfirmPassword(!showConfirmPassword) + } + label="Confirm Password" + error={isSubmitted && !passwordsMatch} + /> + {confirmPassword && ( + + )} + )}
{/* Sign up button */} From 2c884d0b7f91cb82ac49f2d2427417f03ba40f38 Mon Sep 17 00:00:00 2001 From: Catherine Tan Date: Thu, 12 Dec 2024 23:18:18 -0800 Subject: [PATCH 14/16] modify handling of invalid login credientials error --- app/(auth)/login/page.tsx | 24 ++++++++++++++---------- 1 file changed, 14 insertions(+), 10 deletions(-) diff --git a/app/(auth)/login/page.tsx b/app/(auth)/login/page.tsx index 13dfba8..ad10866 100644 --- a/app/(auth)/login/page.tsx +++ b/app/(auth)/login/page.tsx @@ -17,7 +17,7 @@ export default function Login() { const [invalidEmailError, setInvalidEmailError] = useState(''); const [invalidPasswordError, setInvalidPasswordError] = useState(''); - const isFormValid = email && password; + const canSubmitForm = email && password; const router = useRouter(); @@ -36,14 +36,14 @@ export default function Login() { const { error } = await signIn(email, password); if (error) { - // Match error messages from Supabase - if (error.message.includes('Invalid login credentials')) { - setInvalidEmailError('Invalid email address'); - setInvalidPasswordError('Invalid password'); - } + setInvalidEmailError(error.message); + // TODO: use error.code rather than error.messsage + // if (error.message.includes('Invalid login credentials')) { + // setInvalidEmailError('Invalid email address'); + // setInvalidPasswordError('Invalid password'); + // } return; } - // Clear errors on success setInvalidEmailError(''); setInvalidPasswordError(''); @@ -60,7 +60,7 @@ export default function Login() { Log In
- + Don’t have an account? Sign up @@ -92,9 +92,13 @@ export default function Login() { {invalidPasswordError} {/* Password input*/}
- + Log In - {' '} + {/* Sign in button */}
From 8e2f7e29d650db54c1105dfe9a1d54d1aef4b315 Mon Sep 17 00:00:00 2001 From: Catherine Tan Date: Thu, 12 Dec 2024 23:22:58 -0800 Subject: [PATCH 15/16] fix lint errors in AuthProvider --- utils/AuthProvider.tsx | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/utils/AuthProvider.tsx b/utils/AuthProvider.tsx index 395c34c..ff68ee5 100644 --- a/utils/AuthProvider.tsx +++ b/utils/AuthProvider.tsx @@ -10,8 +10,8 @@ import { useState, } from 'react'; import { UUID } from 'crypto'; -import { AuthError, AuthResponse, Session } from '@supabase/supabase-js'; -import { checkEmailExists } from '@/api/supabase/queries/users'; +import { AuthResponse, Session } from '@supabase/supabase-js'; +// import { checkEmailExists } from '@/api/supabase/queries/users'; import supabase from '../api/supabase/createClient'; interface AuthContextType { @@ -78,7 +78,8 @@ export function AuthProvider({ children }: { children: ReactNode }) { // Sign Up function const signUp = useCallback(async (email: string, password: string) => { // will trigger onAuthStateChange to update the session - // check if email already exists + + // TODO: decide if we need to check if email already exists // const emailExists = await checkEmailExists(email); // if (emailExists) { // const authError = new AuthError('Account already exists for this email'); From 44464910728a1a50ef3b28945bc2e97c2c9f3c45 Mon Sep 17 00:00:00 2001 From: Catherine Tan Date: Thu, 12 Dec 2024 23:51:28 -0800 Subject: [PATCH 16/16] change single to maybeSingle in fetchProfileById --- api/supabase/queries/profiles.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/api/supabase/queries/profiles.ts b/api/supabase/queries/profiles.ts index f4ce965..5791a27 100644 --- a/api/supabase/queries/profiles.ts +++ b/api/supabase/queries/profiles.ts @@ -19,7 +19,7 @@ export async function fetchProfileById(userId: UUID) { .from('profiles') .select('*') .eq('user_id', userId) - .single(); + .maybeSingle(); if (error) throw new Error(`Error fetching profile id ${userId}: ${error.message}`);