Skip to content

Commit

Permalink
[feat] new success and verification pages with full functionality EXC… (
Browse files Browse the repository at this point in the history
#37)

* [feat] new success and verification pages with full functionality EXCEPT automatic rerouting from verification to success is not yet functional

* [fix] added automatic routing from verification to success page

* [fix] added automatic routing from verification to success page

* [fix] addressed comments: removed unused imports, reformatted import path directories, and removed local styles from global styles file

* feat: availability card component and display (#38)

* created querying functions

* created AvailabilityCard component and added to general page

* changed styling and updates colors file to include new pomegranate color

* resolved ESLint errors

* added message for no availabilities

* updated to reflect new design and implemented content moving when menubar is expanded

* changed design for date range on availability card

* fix: update continue button style

* [fix] addressed comments: removed unused imports, reformatted import path directories, and removed local styles from global styles file

* fix: redirection fixes and separate inserting user flow

* fix: linting issues

---------

Co-authored-by: Rohin Juneja <[email protected]>
Co-authored-by: aidenm1 <[email protected]>
Co-authored-by: Celine Ji-Won Choi <[email protected]>
Co-authored-by: Celine Choi <[email protected]>
  • Loading branch information
5 people authored Dec 30, 2024
1 parent 42f8ef1 commit 2763523
Show file tree
Hide file tree
Showing 11 changed files with 475 additions and 33 deletions.
120 changes: 94 additions & 26 deletions api/supabase/queries/auth.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,37 +5,20 @@ export async function handleSignUp(
password: string,
): Promise<{ success: boolean; message: string }> {
try {
const { data, error } = await supabase.auth.signUp({ email, password });
await ensureLoggedOutForNewUser(email);
const { error } = await supabase.auth.signUp({
email,
password,
options: {
emailRedirectTo: 'http://localhost:3000/verification',
},
});

if (error) {
return { success: false, message: `Sign-up failed: ${error.message}` };
}

const user = data.user;
if (!user) {
return {
success: false,
message: 'Sign-up failed: User was not created.',
};
}

const { error: insertError } = await supabase.from('volunteers').insert([
{
user_id: user.id,
email,
first_name: '',
last_name: '',
phone_number: '',
notifications_opt_in: true, // default value
},
]);

if (insertError) {
return {
success: false,
message: `Error storing user data: ${insertError.message}`,
};
}
localStorage.setItem('tempEmail', email);

return { success: true, message: 'Sign-up successful!' };
} catch (err) {
Expand All @@ -49,11 +32,44 @@ export async function handleSignUp(
}
}

export const insertVolunteer = async (user: { id: string; email: string }) => {
if (!user) {
return {
success: false,
message: 'User data is missing. Cannot insert into volunteers table.',
};
}

const { error: insertError } = await supabase.from('volunteers').insert([
{
user_id: user.id,
email: user.email,
first_name: '',
last_name: '',
phone_number: '',
notifications_opt_in: true,
},
]);

if (insertError) {
return {
success: false,
message: `Error storing user data: ${insertError.message}`,
};
}

return {
success: true,
message: 'User successfully added to volunteers table.',
};
};

export async function handleSignIn(
email: string,
password: string,
): Promise<{ success: boolean; message: string }> {
try {
await ensureLoggedOutForNewUser(email);
const { error: signInError } = await supabase.auth.signInWithPassword({
email,
password,
Expand Down Expand Up @@ -96,6 +112,37 @@ export async function handleSignIn(
}
}

export const handleSignOut = async () => {
const { error } = await supabase.auth.signOut();
if (error) {
console.error('Error during logout:', error.message);
return;
}
};

export async function ensureLoggedOutForNewUser(
newEmail: string,
): Promise<void> {
const { data, error } = await supabase.auth.getSession();

if (error) {
console.error('Error fetching session:', error.message);
throw new Error('Failed to fetch session.');
}

const session = data.session;

if (
session &&
session.user &&
session.user.email &&
session.user.email !== newEmail
) {
console.log(`Logging out current user: ${session.user.email}`);
await handleSignOut();
}
}

export async function checkUserExists(
userId: string,
userType: 'volunteer' | 'facility',
Expand All @@ -118,3 +165,24 @@ export async function checkUserExists(
return false;
}
}

export async function resendVerificationEmail(email: string): Promise<string> {
try {
const { error } = await supabase.auth.resend({
type: 'signup',
email: email,
});

if (error) {
return `Error: ${error.message}`;
}

return 'Verification email resent successfully!';
} catch (err) {
return 'Unexpected error while resending verification email.';
}
}

export function getTempEmail(): string | null {
return localStorage.getItem('tempEmail');
}
2 changes: 1 addition & 1 deletion app/(auth)/signup/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@ export default function SignUp() {
setIsError(!success);

if (success) {
router.push('/onboarding/role-selection');
router.push('/verification');
}
};

Expand Down
38 changes: 38 additions & 0 deletions app/(auth)/success/page.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
'use client';

import { useRouter } from 'next/navigation';
import Rose from '@/public/images/rose-greenbg.svg';
import {
Background,
Image,
InlineContainer,
ReviewContainer,
RoundedCornerButton,
Title,
} from '../../../styles/styles';

export default function Success() {
const router = useRouter(); // Initialize useRouter

const handleContinue = () => {
router.push('/onboarding/role-selection'); // Navigate to the onboarding/general page
};

return (
<Background>
<Image src={Rose} alt="Rose" />
<InlineContainer>
<ReviewContainer>
<Title>Successfully verified!</Title>
<text>
Your email has been verified. Please use this email address to login
in the future.
</text>
<RoundedCornerButton onClick={handleContinue}>
Continue
</RoundedCornerButton>
</ReviewContainer>
</InlineContainer>
</Background>
);
}
152 changes: 152 additions & 0 deletions app/(auth)/verification/page.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,152 @@
'use client';

import { useEffect, useState } from 'react';
import { useRouter } from 'next/navigation';
import {
getTempEmail,
insertVolunteer,
resendVerificationEmail,
} from '@/api/supabase/queries/auth';
import Bud from '@/public/images/bud.svg';
import EmailIcon from '@/public/images/email.svg';
import {
Background,
Footer,
Image,
InlineContainer,
Link,
ReviewContainer,
RoundedCornerButton,
Title,
} from '@/styles/styles';
import { P } from '@/styles/text';
import { useSession } from '@/utils/AuthProvider';
import {
EmailContainer,
EmailIconStyled,
EmailText,
ResendMessage,
} from './verification-styles';

export default function Verification() {
const router = useRouter();
const { session } = useSession();
const [tempEmail, setTempEmail] = useState<string | null>(null);
const [resendStatus, setResendStatus] = useState<string>('');
const [isError, setIsError] = useState<boolean>(false);
const [isEmailConfirmed, setIsEmailConfirmed] = useState<boolean>(false);

useEffect(() => {
const email = getTempEmail();
setTempEmail(email);
}, []);

// Using session to check if the email is confirmed
useEffect(() => {
if (session?.user) {
const isVerified = session.user.email_confirmed_at !== null;
setIsEmailConfirmed(isVerified);
}
}, [session]);

useEffect(() => {
if (isEmailConfirmed && session?.user) {
const addUserToVolunteers = async () => {
const email = session.user.email;

if (!email) {
setIsError(true);
setResendStatus('Email is undefined. Please try again.');
return;
}

try {
const result = await insertVolunteer({
id: session.user.id,
email,
});
if (result.success) {
router.push('/success');
} else {
setIsError(true);
setResendStatus(result.message);
}
} catch (error) {
console.error('Error adding user to volunteers:', error);
setIsError(true);
setResendStatus('An error occurred while processing your request.');
}
};

addUserToVolunteers();
}
}, [isEmailConfirmed, session, router]);

const handleResendLink = async () => {
if (tempEmail) {
const message = await resendVerificationEmail(tempEmail);
setIsError(message.includes('Error'));
setResendStatus(message);
} else {
setIsError(true);
}
};

const handleUseAnotherAccount = () => {
router.push('/signin');
localStorage.removeItem('tempEmail');
};

return (
<Background>
<Image src={Bud} alt="Bud" />
<InlineContainer>
<ReviewContainer>
<Title>Verification Needed</Title>
<P>Thanks for signing up!</P>
<P>
A verification link has been sent to the email you specified, please
check your inbox for next steps.
</P>

<EmailContainer>
<EmailIconStyled src={EmailIcon} alt="Email Icon" />
<EmailText>
{tempEmail ? tempEmail : 'Email address not found'}
</EmailText>
</EmailContainer>

<RoundedCornerButton
onClick={handleUseAnotherAccount}
width="70%"
bgColor="white"
textColor="black"
>
Use another account
</RoundedCornerButton>

<Footer>
Didn&apos;t receive it?{' '}
<Link href="#" onClick={handleResendLink}>
Resend link
</Link>
</Footer>

{isError && !tempEmail && (
<ResendMessage $isError={isError}>
Email address not found!{' '}
<Link href="#" onClick={() => router.push('/signup')}>
Return to the sign-up page
</Link>{' '}
to restart the sign-up process.
</ResendMessage>
)}

{resendStatus && tempEmail && (
<ResendMessage $isError={isError}>{resendStatus}</ResendMessage>
)}
</ReviewContainer>
</InlineContainer>
</Background>
);
}
33 changes: 33 additions & 0 deletions app/(auth)/verification/verification-styles.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
import NextImage from 'next/image';
import styled from 'styled-components';
import COLORS from '@/styles/colors';
import { SMALL } from '@/styles/text';

export const EmailContainer = styled.div`
display: flex;
align-items: center;
margin-top: -0.9rem;
gap: 8px;
`;

export const EmailIconStyled = styled(NextImage)`
width: 24px;
height: 24px;
margin-right: -4px;
`;

export const EmailText = styled.p`
font-size: 1rem;
color: ${COLORS.gray11};
line-height: 1.5;
display: inline-block;
`;

export const ResendMessage = styled(SMALL)<{ $isError: boolean }>`
color: ${({ $isError }) => ($isError ? 'red' : 'green')};
text-align: center;
font-weight: 400;
margin-top: -0.75rem;
font-size: 1rem;
display: block;
`;
Loading

0 comments on commit 2763523

Please sign in to comment.