From 5c1f79815067c65b4310398efe76b30898f763a7 Mon Sep 17 00:00:00 2001 From: Jayden Carvajal <147960616+jaydencarvajal511@users.noreply.github.com> Date: Mon, 10 Nov 2025 19:17:18 -0500 Subject: [PATCH 1/6] Update user.tsx --- .../features/applicant/components/ApplicantView/user.tsx | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/apps/frontend/src/features/applicant/components/ApplicantView/user.tsx b/apps/frontend/src/features/applicant/components/ApplicantView/user.tsx index 090aa7eb..4ff51368 100644 --- a/apps/frontend/src/features/applicant/components/ApplicantView/user.tsx +++ b/apps/frontend/src/features/applicant/components/ApplicantView/user.tsx @@ -15,10 +15,7 @@ import { User } from '@sharedTypes/types/user.types'; import { useApplication } from '@shared/hooks/useApplication'; import { useFullName } from '@shared/hooks/useUserData'; import FileUploadBox from '../FileUploadBox'; -import { - Application, - ApplicationStage, -} from '@sharedTypes/types/application.types'; +import { Application } from '@sharedTypes/types/application.types'; interface ApplicantViewProps { user: User; @@ -116,8 +113,7 @@ export const ApplicantView = ({ user }: ApplicantViewProps) => { {!isLoading && selectedApplication && - String(selectedApplication.stage) === - ApplicationStage.PM_CHALLENGE && ( + String(selectedApplication.stage) === 'PM_CHALLENGE' && ( Date: Mon, 10 Nov 2025 19:19:28 -0500 Subject: [PATCH 2/6] Update app.tsx --- apps/frontend/src/app.tsx | 46 +++++++++++++++++++++++++++++++++------ 1 file changed, 39 insertions(+), 7 deletions(-) diff --git a/apps/frontend/src/app.tsx b/apps/frontend/src/app.tsx index ce4e7fea..f6c597b6 100644 --- a/apps/frontend/src/app.tsx +++ b/apps/frontend/src/app.tsx @@ -3,30 +3,62 @@ import { useState } from 'react'; import ApplicantHomePage from '@features/applicant/pages/ApplicantHomePage'; import NotFoundPage from '@shared/pages/NotFoundPage'; +import DashboardPage from '@shared/pages/DashboardPage'; import ApplicationsPage from '@features/applications/pages/ApplicationsPage'; import ApplicationDetailPage from '@features/applications/pages/ApplicationDetailPage'; +import SettingsPage from '@shared/pages/SettingsPage'; import LoginContext from '@features/auth/components/LoginPage/LoginContext'; import ProtectedRoutes from '@features/auth/components/ProtectedRoutes'; -import LoginPage from '@features/auth/components/LoginPage'; +import Navigation from '@shared/components/Navigation'; import AdminRoutes from '@features/auth/components/AdminRoutes'; import HomePage from '@shared/pages/HomePage'; export const App: React.FC = () => { - const [token, setToken] = useState(''); + const [token, setToken] = useState(() => { + const storedToken = localStorage.getItem('token'); + return storedToken ? JSON.parse(storedToken) : ''; + }); + return ( - } /> + } /> } /> - }> + }> }> - } /> - } /> + + + + } + /> + + + + } + /> } + element={ + + + + } + /> + + + + } /> From a4b424192ae15d08788164eb677bf7e9bfdd1703 Mon Sep 17 00:00:00 2001 From: Jayden Carvajal <147960616+jaydencarvajal511@users.noreply.github.com> Date: Mon, 10 Nov 2025 19:20:42 -0500 Subject: [PATCH 3/6] Update index.tsx --- .../auth/components/LoginPage/index.tsx | 81 +++++++++---------- 1 file changed, 38 insertions(+), 43 deletions(-) diff --git a/apps/frontend/src/features/auth/components/LoginPage/index.tsx b/apps/frontend/src/features/auth/components/LoginPage/index.tsx index b9093a3d..5e18acbb 100644 --- a/apps/frontend/src/features/auth/components/LoginPage/index.tsx +++ b/apps/frontend/src/features/auth/components/LoginPage/index.tsx @@ -2,19 +2,15 @@ import { useEffect } from 'react'; import apiClient from '@api/apiClient'; import useLoginContext from './useLoginContext'; import { useNavigate } from 'react-router-dom'; -import { Box, CircularProgress, Typography } from '@mui/material'; +import { Button, Stack } from '@mui/material'; +import { CognitoJwtVerifier } from 'aws-jwt-verify'; + +const verifier = CognitoJwtVerifier.create({ + userPoolId: import.meta.env.VITE_COGNITO_USER_POOL_ID as string, + tokenUse: 'access', + clientId: import.meta.env.VITE_COGNITO_CLIENT_ID as string, +}); -/** - * LoginPage - Handles Cognito OAuth callback only - * - * This page is responsible for: - * 1. Receiving the auth code from Cognito redirect - * 2. Exchanging it for access/refresh tokens - * 3. Storing tokens and redirecting to dashboard - * - * This is NOT a landing page - users should not visit this directly. - * They arrive here only after authenticating with Cognito. - */ export default function LoginPage() { const { setToken } = useLoginContext(); const navigate = useNavigate(); @@ -23,12 +19,23 @@ export default function LoginPage() { const urlParams = new URLSearchParams(window.location.search); const authCode = urlParams.get('code'); - async function handleCognitoCallback() { - if (authCode) { + async function getToken() { + const localToken = localStorage.getItem('token'); + + if (localToken) { + try { + const token = JSON.parse(localToken); + await verifier.verify(token); + setToken(token); + navigate('/'); + } catch (error) { + console.log('Error verifying token:', error); + localStorage.removeItem('token'); + } + } else if (authCode) { try { const tokenResponse = await apiClient.getToken(authCode); - // Store both tokens in localStorage for persistence localStorage.setItem( 'auth_tokens', JSON.stringify({ @@ -37,47 +44,35 @@ export default function LoginPage() { }), ); - // Keep backward compatibility - store access token for existing code - sessionStorage.setItem( + localStorage.setItem( 'token', JSON.stringify(tokenResponse.access_token), ); setToken(tokenResponse.access_token); - - // Redirect to dashboard after successful login - navigate('/'); + window.location.href = '/'; } catch (error) { console.error('Error fetching token:', error); - // Redirect to home page on error - navigate('/home'); } - } else { - // No auth code - redirect to home page - navigate('/home'); } } - - handleCognitoCallback(); + getToken(); }, [navigate, setToken]); return ( - - - - Logging you in... - - + + ); } From 6b877c31a2da7650d47663aa8365aededc28c07f Mon Sep 17 00:00:00 2001 From: Jayden Carvajal <147960616+jaydencarvajal511@users.noreply.github.com> Date: Mon, 10 Nov 2025 19:21:16 -0500 Subject: [PATCH 4/6] Update index.tsx --- .../src/features/auth/components/ProtectedRoutes/index.tsx | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/apps/frontend/src/features/auth/components/ProtectedRoutes/index.tsx b/apps/frontend/src/features/auth/components/ProtectedRoutes/index.tsx index 07e64b0d..9ab01015 100644 --- a/apps/frontend/src/features/auth/components/ProtectedRoutes/index.tsx +++ b/apps/frontend/src/features/auth/components/ProtectedRoutes/index.tsx @@ -5,7 +5,10 @@ import { Navigate, Outlet } from 'react-router-dom'; * if the user is authenticated (i.e if an access token exists). * If the user is not authenticated, it redirects to the login page. */ -function ProtectedRoutes({ token }: { token: string }) { +function ProtectedRoutes() { + const storedToken = localStorage.getItem('token'); + const token = storedToken ? JSON.parse(storedToken) : ''; + return token ? : ; } From cf392dd15c28b5c2187d42adca794c151296fb15 Mon Sep 17 00:00:00 2001 From: Jayden Carvajal <147960616+jaydencarvajal511@users.noreply.github.com> Date: Mon, 10 Nov 2025 19:25:42 -0500 Subject: [PATCH 5/6] Update HomePage.tsx --- apps/frontend/src/shared/pages/HomePage.tsx | 426 ++++++++++++++++++-- 1 file changed, 398 insertions(+), 28 deletions(-) diff --git a/apps/frontend/src/shared/pages/HomePage.tsx b/apps/frontend/src/shared/pages/HomePage.tsx index 6eac0cfa..a680a9c5 100644 --- a/apps/frontend/src/shared/pages/HomePage.tsx +++ b/apps/frontend/src/shared/pages/HomePage.tsx @@ -1,29 +1,127 @@ -import { Box, Container, Stack } from '@mui/material'; -import { useAuth } from '@shared/hooks/useAuth'; -import { - Header, - WelcomeBanner, - RoleSelector, - DeadlineCountdown, -} from '@features/homepage/components'; - -/** - * HomePage - Application landing page - * - * Clean, presentational component that composes smaller, reusable components. - * Authentication logic is delegated to the useAuth hook. - * - * Responsibilities: - * - Display welcome banner - * - Show role selection options - * - Display application deadline countdown - * - Show login/logout based on authentication state - */ +import React, { useState, useEffect } from 'react'; +import { Box, Button, Container, Stack, Typography } from '@mui/material'; +import { useNavigate } from 'react-router-dom'; +import { CognitoJwtVerifier } from 'aws-jwt-verify'; +import apiClient from '@api/apiClient'; +import useLoginContext from '@features/auth/components/LoginPage/useLoginContext'; + +const verifier = CognitoJwtVerifier.create({ + userPoolId: import.meta.env.VITE_COGNITO_USER_POOL_ID as string, + tokenUse: 'access', + clientId: import.meta.env.VITE_COGNITO_CLIENT_ID as string, +}); + const HomePage = () => { - const { isAuthenticated, signOut } = useAuth(); + const navigate = useNavigate(); + const { setToken, token } = useLoginContext(); + const [isAuthenticated, setIsAuthenticated] = useState(false); + const [timeLeft, setTimeLeft] = useState({ + days: 0, + hours: 0, + minutes: 0, + seconds: 0, + }); + + // Hardcoded deadline: October 31, 2025 + const deadline = new Date('2025-10-31T23:59:59').getTime(); + + // Authentication logic - handle auth code and token verification + useEffect(() => { + const urlParams = new URLSearchParams(window.location.search); + const authCode = urlParams.get('code'); + + async function handleAuth() { + const localToken = localStorage.getItem('token'); + + if (localToken) { + try { + const token = JSON.parse(localToken); + await verifier.verify(token); + setToken(token); + setIsAuthenticated(true); + // Don't navigate away - let them stay on the home page + } catch (error) { + console.log('Error verifying token:', error); + localStorage.removeItem('token'); + setIsAuthenticated(false); + } + } else if (authCode) { + try { + const tokenResponse = await apiClient.getToken(authCode); + + // Store both tokens in localStorage for persistence + localStorage.setItem( + 'auth_tokens', + JSON.stringify({ + accessToken: tokenResponse.access_token, + refreshToken: tokenResponse.refresh_token, + }), + ); + + // Keep backward compatibility - store access token for existing code + localStorage.setItem( + 'token', + JSON.stringify(tokenResponse.access_token), + ); + setToken(tokenResponse.access_token); + setIsAuthenticated(true); + + // Redirect to dashboard after successful login from Cognito + navigate('/'); + } catch (error) { + console.error('Error fetching token:', error); + setIsAuthenticated(false); + } + } else { + // No token and no auth code - user is not authenticated + setIsAuthenticated(false); + } + } + handleAuth(); + }, [navigate, setToken]); + + // Countdown timer logic + useEffect(() => { + const timer = setInterval(() => { + const now = new Date().getTime(); + const distance = deadline - now; - // Application deadline - const deadline = new Date('2025-10-31T23:59:59'); + if (distance < 0) { + setTimeLeft({ days: 0, hours: 0, minutes: 0, seconds: 0 }); + clearInterval(timer); + } else { + const days = Math.floor(distance / (1000 * 60 * 60 * 24)); + const hours = Math.floor( + (distance % (1000 * 60 * 60 * 24)) / (1000 * 60 * 60), + ); + const minutes = Math.floor((distance % (1000 * 60 * 60)) / (1000 * 60)); + const seconds = Math.floor((distance % (1000 * 60)) / 1000); + + setTimeLeft({ days, hours, minutes, seconds }); + } + }, 1000); + + return () => clearInterval(timer); + }, [deadline]); + + const formatTime = (value: number) => String(value).padStart(2, '0'); + + const handleLearnMore = (role: string) => { + const urls: { [key: string]: string } = { + designer: 'https://www.c4cneu.com/apply/Product-Designer', + developer: 'https://www.c4cneu.com/apply/Software-Developer', + pm: 'https://www.c4cneu.com/apply/Product-Manager', + }; + window.open(urls[role], '_blank'); + }; + + const handleSignOut = () => { + // Clear all authentication data + localStorage.removeItem('token'); + localStorage.removeItem('auth_tokens'); + setToken(''); + setIsAuthenticated(false); + }; return ( { flexDirection: 'column', }} > -
+ {/* Header */} + + + C4C Logo + + 2025 Application + + + {isAuthenticated ? ( + + ) : ( + + )} + + {/* Main Content */} - - - + {/* Welcome Banner */} + + + Welcome to the C4C Application page! + + + Apply as a Developer, Designer, or Project Manager here. + + + + {/* Role Buttons */} + + + {/* Designer Row */} + + + + + + {/* Software Developer Row */} + + + + + + {/* Product Manager Row */} + + + + + + + + {/* Deadline Section */} + + + Deadline: October 31, 2025 + + + {/* Countdown Timer */} + + + {formatTime(timeLeft.days)} days, {formatTime(timeLeft.hours)}{' '} + hours, {formatTime(timeLeft.minutes)} minutes, and{' '} + {formatTime(timeLeft.seconds)} seconds left! + + + From d03e6b07bf637d02e2c6b22621d93e04baa33402 Mon Sep 17 00:00:00 2001 From: Jayden Carvajal Date: Tue, 18 Nov 2025 13:26:46 -0500 Subject: [PATCH 6/6] trying to fix conflicts --- apps/frontend/src/app.tsx | 3 --- 1 file changed, 3 deletions(-) diff --git a/apps/frontend/src/app.tsx b/apps/frontend/src/app.tsx index f6c597b6..fbf70c86 100644 --- a/apps/frontend/src/app.tsx +++ b/apps/frontend/src/app.tsx @@ -3,13 +3,10 @@ import { useState } from 'react'; import ApplicantHomePage from '@features/applicant/pages/ApplicantHomePage'; import NotFoundPage from '@shared/pages/NotFoundPage'; -import DashboardPage from '@shared/pages/DashboardPage'; import ApplicationsPage from '@features/applications/pages/ApplicationsPage'; import ApplicationDetailPage from '@features/applications/pages/ApplicationDetailPage'; -import SettingsPage from '@shared/pages/SettingsPage'; import LoginContext from '@features/auth/components/LoginPage/LoginContext'; import ProtectedRoutes from '@features/auth/components/ProtectedRoutes'; -import Navigation from '@shared/components/Navigation'; import AdminRoutes from '@features/auth/components/AdminRoutes'; import HomePage from '@shared/pages/HomePage';