From d4a3d9855824fd14f4fa7e225352b6b347459f17 Mon Sep 17 00:00:00 2001 From: Jessica Mulein Date: Wed, 27 Nov 2024 22:28:11 +0000 Subject: [PATCH] theme incremental improvement --- chili-and-cilantro-react/src/app/app.tsx | 9 + .../src/app/components/api-access.tsx | 90 ++++-- .../src/app/components/auth.scss | 7 +- .../app/components/change-password-page.tsx | 207 +++++++------ .../src/app/components/dashboard-page.scss | 2 +- .../app/components/forgot-password-page.tsx | 232 ++++++++------ .../src/app/components/login-page.tsx | 285 +++++++++--------- .../src/app/components/register-page.tsx | 169 ++++++----- .../src/app/components/splash-page.scss | 76 +++-- .../src/app/components/splash-page.tsx | 4 +- .../src/app/components/top-menu.scss | 18 +- .../src/app/components/top-menu.tsx | 2 +- .../src/app/components/verify-email-page.scss | 2 +- .../src/app/components/verify-email-page.tsx | 85 ++++-- chili-and-cilantro-react/src/main.tsx | 15 +- chili-and-cilantro-react/src/styles.scss | 50 ++- chili-and-cilantro-react/src/theme.tsx | 95 ++++-- 17 files changed, 807 insertions(+), 541 deletions(-) diff --git a/chili-and-cilantro-react/src/app/app.tsx b/chili-and-cilantro-react/src/app/app.tsx index 5b438e2..b6aaf4a 100644 --- a/chili-and-cilantro-react/src/app/app.tsx +++ b/chili-and-cilantro-react/src/app/app.tsx @@ -1,5 +1,6 @@ import { Route, Routes } from 'react-router-dom'; import '../styles.scss'; +import ApiAccess from './components/api-access'; import ChangePasswordPage from './components/change-password-page'; import DashboardPage from './components/dashboard-page'; import ForgotPasswordPage from './components/forgot-password-page'; @@ -19,6 +20,14 @@ function App() { } /> + + + + } + /> ({ + display: 'flex', + flexDirection: 'column', + alignItems: 'center', + justifyContent: 'center', + minHeight: '100vh', + backgroundColor: theme.palette.background.default, + padding: theme.spacing(3), +})); + +const ApiAccessContent = styled(Box)(({ theme }) => ({ + maxWidth: '600px', + width: '100%', + backgroundColor: theme.palette.background.paper, + borderRadius: theme.shape.borderRadius, + padding: theme.spacing(4), + boxShadow: theme.shadows[3], +})); + +const ApiAccessTitle = styled(Typography)(({ theme }) => ({ + marginBottom: theme.spacing(3), + color: theme.palette.primary.main, +})); + function ApiAccess() { - const isLoading = false; + const [isLoading, setIsLoading] = useState(true); const [token, setToken] = useState(null); useEffect(() => { - if (!isLoading) { - const token = localStorage.getItem('authToken'); - if (token) { - setToken(token); - } else { - console.error('Error getting the access token'); - } + const storedToken = localStorage.getItem('authToken'); + if (storedToken) { + setToken(storedToken); + } else { + console.error('Error getting the access token'); } - }, [isLoading]); + setIsLoading(false); + }, []); const copyToClipboard = async () => { if (token) { @@ -27,23 +52,42 @@ function ApiAccess() { }; if (isLoading) { - return
Loading...
; + return ( + + Loading... + + ); } - // if (error !== null) { - // return
Error: {error.message}
; - // } - return ( -
-

Your Access Token:

- - -
+ + + + Your Access Token + + + + + ); } diff --git a/chili-and-cilantro-react/src/app/components/auth.scss b/chili-and-cilantro-react/src/app/components/auth.scss index 1d1666a..83cf4f6 100644 --- a/chili-and-cilantro-react/src/app/components/auth.scss +++ b/chili-and-cilantro-react/src/app/components/auth.scss @@ -1,18 +1,19 @@ -@import '../../styles.scss'; +@use '../../styles' as *; // Auth container (for login and registration) .auth-container { max-width: 400px; margin: 2rem auto; padding: 2rem; - background-color: $light-color; + background-color: $new-dark; border-radius: 8px; box-shadow: 0 0 10px rgba(0, 0, 0, 0.1); + color: $new-gray; .auth-title { text-align: center; margin-bottom: 2rem; - color: $primary-color; + color: $new-lightGreen; } .auth-form { diff --git a/chili-and-cilantro-react/src/app/components/change-password-page.tsx b/chili-and-cilantro-react/src/app/components/change-password-page.tsx index 1f39af2..be34dd9 100644 --- a/chili-and-cilantro-react/src/app/components/change-password-page.tsx +++ b/chili-and-cilantro-react/src/app/components/change-password-page.tsx @@ -1,135 +1,158 @@ import { constants } from '@chili-and-cilantro/chili-and-cilantro-lib'; +import { Box, Button, Container, TextField, Typography } from '@mui/material'; import { useFormik } from 'formik'; -import React, { useContext, useState } from 'react'; -import { Navigate } from 'react-router-dom'; +import React, { useState } from 'react'; import * as Yup from 'yup'; -import { AuthContext } from '../auth-provider'; -import './auth.scss'; +import api from '../services/api'; -const ChangePasswordPage: React.FC = () => { - const { isAuthenticated, user, loading, error, changePassword } = - useContext(AuthContext); - const [successMessage, setSuccessMessage] = useState(null); - const [errorMessage, setErrorMessage] = useState(null); +interface FormValues { + currentPassword: string; + newPassword: string; + confirmNewPassword: string; +} - const validationSchema = Yup.object({ - currentPassword: Yup.string() - .matches(constants.PASSWORD_REGEX, constants.PASSWORD_REGEX_ERROR) - .required('Current password is required'), - newPassword: Yup.string() - .matches(constants.PASSWORD_REGEX, constants.PASSWORD_REGEX_ERROR) - .notOneOf( - [Yup.ref('currentPassword')], - 'New password must be different from the current password', - ) - .required('New password is required'), - confirmNewPassword: Yup.string() - .oneOf([Yup.ref('newPassword')], 'Passwords must match') - .required('Please confirm your new password'), - }); +const ChangePasswordPage: React.FC = () => { + const [successMessage, setSuccessMessage] = useState(''); + const [errorMessage, setErrorMessage] = useState(''); - const formik = useFormik({ + const formik = useFormik({ initialValues: { currentPassword: '', newPassword: '', confirmNewPassword: '', }, - validationSchema, + validationSchema: Yup.object({ + currentPassword: Yup.string().required('Required'), + newPassword: Yup.string() + .matches(constants.PASSWORD_REGEX, constants.PASSWORD_REGEX_ERROR) + .required('Required'), + confirmNewPassword: Yup.string() + .oneOf([Yup.ref('newPassword')], 'Passwords must match') + .required('Required'), + }), onSubmit: async (values, { setSubmitting, resetForm }) => { try { - const result = await changePassword( - values.currentPassword, - values.newPassword, - ); - if (result.success) { - setSuccessMessage(result.message); - setErrorMessage(null); - resetForm(); - } - } catch (err) { - console.error('Error changing password:', err); - if (err instanceof Error) { - setErrorMessage(err.message); + const response = await api.post('/user/change-password', { + currentPassword: values.currentPassword, + newPassword: values.newPassword, + }); + setSuccessMessage('Password changed successfully'); + setErrorMessage(''); + resetForm(); + } catch (error) { + if (error instanceof Error) { + setErrorMessage( + error.message || 'An error occurred while changing the password', + ); } else { setErrorMessage('An unexpected error occurred'); } - setSuccessMessage(null); + setSuccessMessage(''); } finally { setSubmitting(false); } }, }); - if (loading) { - return
Loading...
; - } - - if (!isAuthenticated || !user) { - return ; - } - return ( -
-

Change Password

-
-
- - + + + Change Password + + + - {formik.touched.currentPassword && formik.errors.currentPassword ? ( -
{formik.errors.currentPassword}
- ) : null} -
- -
- - - {formik.touched.newPassword && formik.errors.newPassword ? ( -
{formik.errors.newPassword}
- ) : null} -
- -
- - - {formik.touched.confirmNewPassword && - formik.errors.confirmNewPassword ? ( -
{formik.errors.confirmNewPassword}
- ) : null} -
- - {(error || errorMessage) && ( -
{error || errorMessage}
- )} + + {successMessage && ( -
{successMessage}
+ + {successMessage} + )} - - -
-
+ {errorMessage && ( + + {errorMessage} + + )} + + ); }; diff --git a/chili-and-cilantro-react/src/app/components/dashboard-page.scss b/chili-and-cilantro-react/src/app/components/dashboard-page.scss index c1b713d..8b49403 100644 --- a/chili-and-cilantro-react/src/app/components/dashboard-page.scss +++ b/chili-and-cilantro-react/src/app/components/dashboard-page.scss @@ -1,4 +1,4 @@ -@import '../../styles.scss'; +@use '../../styles' as *; // Dashboard styles .dashboard-container { diff --git a/chili-and-cilantro-react/src/app/components/forgot-password-page.tsx b/chili-and-cilantro-react/src/app/components/forgot-password-page.tsx index acb9cbd..60f9174 100644 --- a/chili-and-cilantro-react/src/app/components/forgot-password-page.tsx +++ b/chili-and-cilantro-react/src/app/components/forgot-password-page.tsx @@ -1,11 +1,11 @@ import { constants } from '@chili-and-cilantro/chili-and-cilantro-lib'; +import { Box, Button, Container, TextField, Typography } from '@mui/material'; import { isAxiosError } from 'axios'; import { useFormik } from 'formik'; import React, { useEffect, useState } from 'react'; import { useLocation, useNavigate } from 'react-router-dom'; import * as Yup from 'yup'; import api from '../services/api'; -import './auth.scss'; type FormValues = { email?: string; @@ -60,115 +60,161 @@ const ForgotPasswordPage: React.FC = () => { email: Yup.string().email('Invalid email address').required('Required'), }); - const handleSubmit = async (values: FormValues) => { - try { - if (values.email) { - // Handle forgot password - const response = await api.post('/user/forgot-password', { - email: values.email, - }); - if (response.status === 200) { - setSuccessMessage(response.data.message); - setErrorMessage(''); - } else { - setErrorMessage(response.data.message); - setSuccessMessage(''); + const formik = useFormik({ + initialValues, + validationSchema, + onSubmit: async (values) => { + try { + if (values.email) { + // Handle forgot password + const response = await api.post('/user/forgot-password', { + email: values.email, + }); + if (response.status === 200) { + setSuccessMessage(response.data.message); + setErrorMessage(''); + } else { + setErrorMessage(response.data.message); + setSuccessMessage(''); + } + } else if (values.password && values.confirmPassword) { + // Handle password reset + const params = new URLSearchParams(location.search); + const token = params.get('token'); + if (!token) { + setErrorMessage( + 'Invalid token. Please try the password reset process again.', + ); + return; + } + const response = await api.post('/user/reset-password', { + token, + password: values.password, + }); + if (response.status === 200) { + setSuccessMessage( + 'Your password has been successfully reset. You can now log in with your new password.', + ); + setErrorMessage(''); + setTimeout(() => navigate('/login'), 3000); + } else { + setErrorMessage(response.data.message); + setSuccessMessage(''); + } } - } else if (values.password && values.confirmPassword) { - // Handle password reset - const params = new URLSearchParams(location.search); - const token = params.get('token'); - if (!token) { + } catch (error) { + if (isAxiosError(error) && error.response) { setErrorMessage( - 'Invalid token. Please try the password reset process again.', - ); - return; - } - const response = await api.post('/user/reset-password', { - token, - password: values.password, - }); - if (response.status === 200) { - setSuccessMessage( - 'Your password has been successfully reset. You can now log in with your new password.', + error.response.data.message || + 'An error occurred while processing your request.', ); - setErrorMessage(''); - setTimeout(() => navigate('/login'), 3000); + setSuccessMessage(''); } else { - setErrorMessage(response.data.message); + setErrorMessage('An unexpected error occurred'); setSuccessMessage(''); } } - } catch (error) { - if (isAxiosError(error) && error.response) { - setErrorMessage( - error.response.data.message || - 'An error occurred while processing your request.', - ); - setSuccessMessage(''); - } else { - setErrorMessage('An unexpected error occurred'); - setSuccessMessage(''); - } - } - }; - - const formik = useFormik({ - initialValues, - validationSchema, - onSubmit: handleSubmit, + }, }); return ( -
-

- {isTokenValid ? 'Reset Password' : 'Forgot Password'} -

-
- {isTokenValid ? ( - <> -
- - + + + {isTokenValid ? 'Reset Password' : 'Forgot Password'} + + + {isTokenValid ? ( + <> + - {formik.errors.password && ( -
{formik.errors.password}
- )} -
-
- - - {formik.errors.confirmPassword && ( -
{formik.errors.confirmPassword}
- )} -
- - ) : ( -
- - - {formik.errors.email && ( -
{formik.errors.email}
- )} -
+ + ) : ( + + )} + + + {successMessage && ( + + {successMessage} + )} - - - - {successMessage && ( -
{successMessage}
- )} - {errorMessage &&
{errorMessage}
} -
+ {errorMessage && ( + + {errorMessage} + + )} + + ); }; diff --git a/chili-and-cilantro-react/src/app/components/login-page.tsx b/chili-and-cilantro-react/src/app/components/login-page.tsx index 18ad0d7..bdde1c6 100644 --- a/chili-and-cilantro-react/src/app/components/login-page.tsx +++ b/chili-and-cilantro-react/src/app/components/login-page.tsx @@ -1,6 +1,14 @@ import { constants } from '@chili-and-cilantro/chili-and-cilantro-lib'; +import { + Box, + Button, + Container, + Link as MuiLink, + TextField, + Typography, +} from '@mui/material'; import axios from 'axios'; -import { ErrorMessage, Field, Form, Formik, FormikProps } from 'formik'; +import { useFormik } from 'formik'; import { useEffect, useState } from 'react'; import { Link, useNavigate } from 'react-router-dom'; import * as Yup from 'yup'; @@ -20,71 +28,48 @@ const LoginPage = () => { const navigate = useNavigate(); const { login } = useAuth(); - const getInitialValues = (): FormValues => ({ - email: '', - username: '', - password: '', - }); - - const validationSchema = Yup.object({ - [loginType]: - loginType === 'email' - ? Yup.string().email('Invalid email address').required('Required') - : Yup.string() - .matches(constants.USERNAME_REGEX, constants.USERNAME_REGEX_ERROR) - .required('Required'), - password: Yup.string() - .matches(constants.PASSWORD_REGEX, constants.PASSWORD_REGEX_ERROR) - .required('Required'), - }); - - const handleSubmit = async ( - values: FormValues, - { - setSubmitting, - resetForm, - }: { - setSubmitting: (isSubmitting: boolean) => void; - resetForm: () => void; - }, - ) => { - try { - const loginResult = await login( - loginType === 'email' ? values.email : values.username, - values.password, - loginType === 'email', - ); - if ('error' in loginResult) { - setLoginError(loginResult.error); - return; - } - - // Wait for a short time to ensure state is updated - await new Promise((resolve) => setTimeout(resolve, 100)); - - resetForm(); - navigate('/dashboard'); - } catch (error) { - if (axios.isAxiosError(error) && error.response) { - setLoginError(error.response.data.message); - if ( - error.response.data.message === - 'Account status is PendingEmailVerification' - ) { - setResendStatus(null); + const formik = useFormik({ + initialValues: { + email: '', + username: '', + password: '', + } as FormValues, + validationSchema: Yup.object({ + [loginType]: + loginType === 'email' + ? Yup.string().email('Invalid email address').required('Required') + : Yup.string() + .matches(constants.USERNAME_REGEX, constants.USERNAME_REGEX_ERROR) + .required('Required'), + password: Yup.string() + .matches(constants.PASSWORD_REGEX, constants.PASSWORD_REGEX_ERROR) + .required('Required'), + }), + onSubmit: async (values, { setSubmitting, resetForm }) => { + try { + const loginResult = await login( + loginType === 'email' ? values.email : values.username, + values.password, + loginType === 'email', + ); + if ('error' in loginResult) { + setLoginError(loginResult.error); + return; } - } else { + resetForm(); + navigate('/dashboard'); + } catch (error) { setLoginError('An unexpected error occurred'); + } finally { + setSubmitting(false); } - } finally { - setSubmitting(false); - } - }; + }, + }); - const handleResendVerification = async (identifier: string) => { + const handleResendVerification = async () => { try { await axios.post('/user/resend-verification', { - [loginType]: identifier, + [loginType]: formik.values[loginType], }); setResendStatus('Verification email sent successfully'); } catch (error) { @@ -101,92 +86,104 @@ const LoginPage = () => { }, [loginType]); return ( -
-

Login

- - key={formKey} - initialValues={getInitialValues()} - validationSchema={validationSchema} - onSubmit={handleSubmit} - enableReinitialize + + - {({ - isSubmitting, - values, - errors, - touched, - }: FormikProps) => ( -
-
- - - -
- -
- - - - {loginError &&
{loginError}
} -
- - {resendStatus &&
{resendStatus}
} - - {loginError === 'Account status is PendingEmailVerification' && ( - - )} - - -
- )} - -
-
- { - e.preventDefault(); - setLoginType(loginType === 'email' ? 'username' : 'email'); - }} + + Login + + + + + {loginError && ( + + {loginError} + + )} + {resendStatus && ( + + {resendStatus} + + )} +
-
- Forgot Password? - - Don't have an account? Register - -
-
-
+ {formik.isSubmitting ? 'Logging in...' : 'Login'} + + {loginError === 'Account status is PendingEmailVerification' && ( + + )} + + { + e.preventDefault(); + setLoginType(loginType === 'email' ? 'username' : 'email'); + }} + > + {`Use ${loginType === 'email' ? 'Username' : 'Email'}`} + + + {"Don't have an account? Sign Up"} + + + + + ); }; diff --git a/chili-and-cilantro-react/src/app/components/register-page.tsx b/chili-and-cilantro-react/src/app/components/register-page.tsx index cc1de6a..8c232ab 100644 --- a/chili-and-cilantro-react/src/app/components/register-page.tsx +++ b/chili-and-cilantro-react/src/app/components/register-page.tsx @@ -1,44 +1,37 @@ import { constants } from '@chili-and-cilantro/chili-and-cilantro-lib'; -import { isAxiosError } from 'axios'; -import { FormikHelpers, useFormik } from 'formik'; -import React, { useEffect, useState } from 'react'; -import { Navigate, useNavigate } from 'react-router-dom'; +import { + Box, + Button, + Container, + Link as MuiLink, + TextField, + Typography, +} from '@mui/material'; +import { useFormik } from 'formik'; +import React, { useState } from 'react'; +import { Link, useNavigate } from 'react-router-dom'; import * as Yup from 'yup'; -import { AuthContext } from '../auth-provider'; import authService from '../services/auth-service'; -import './auth.scss'; interface FormValues { username: string; email: string; password: string; - timezone: string; } const RegisterPage: React.FC = () => { - const navigate = useNavigate(); - const { isAuthenticated } = React.useContext(AuthContext); const [registrationError, setRegistrationError] = useState( null, ); - const [registrationSuccess, setRegistrationSuccess] = - useState(false); - const [userTimezone, setUserTimezone] = useState(''); - - useEffect(() => { - // Get the user's timezone - const timezone = Intl.DateTimeFormat().resolvedOptions().timeZone; - setUserTimezone(timezone); - }, []); + const [registrationSuccess, setRegistrationSuccess] = useState(false); + const navigate = useNavigate(); const formik = useFormik({ initialValues: { username: '', email: '', password: '', - timezone: userTimezone, }, - enableReinitialize: true, validationSchema: Yup.object({ username: Yup.string() .matches(constants.USERNAME_REGEX, constants.USERNAME_REGEX_ERROR) @@ -47,15 +40,15 @@ const RegisterPage: React.FC = () => { password: Yup.string() .matches(constants.PASSWORD_REGEX, constants.PASSWORD_REGEX_ERROR) .required('Required'), - timezone: Yup.string().required('Timezone is required'), }), - onSubmit: async (values, { setSubmitting }: FormikHelpers) => { + onSubmit: async (values, { setSubmitting }) => { try { + const timezone = Intl.DateTimeFormat().resolvedOptions().timeZone; await authService.register( values.username, values.email, values.password, - values.timezone, + timezone, ); setRegistrationError(null); setRegistrationSuccess(true); @@ -64,9 +57,9 @@ const RegisterPage: React.FC = () => { }, 3000); } catch (error: unknown) { console.error(error); - if (isAxiosError(error) && error.response) { + if (error instanceof Error) { setRegistrationError( - error.response.data?.message || + error.message || 'An error occurred during registration. Please try again.', ); } else { @@ -81,74 +74,96 @@ const RegisterPage: React.FC = () => { }, }); - if (isAuthenticated) { - return ; - } - return ( -
-

Register

- {registrationSuccess && ( -
- Registration successful! You will be redirected to the login page - shortly. -
- )} -
-
- - + + + Register + + + - {formik.touched.username && formik.errors.username ? ( -
{formik.errors.username}
- ) : null} -
- -
- - - {formik.touched.email && formik.errors.email ? ( -
{formik.errors.email}
- ) : null} -
- -
- - - {formik.touched.password && formik.errors.password ? ( -
{formik.errors.password}
- ) : null} -
- - {registrationError && ( -
{registrationError}
- )} - - -
-
+ {registrationError && ( + + {registrationError} + + )} + {registrationSuccess && ( + + Registration successful! Redirecting to login page... + + )} + + + + Already have an account? Sign in + + + + + ); }; diff --git a/chili-and-cilantro-react/src/app/components/splash-page.scss b/chili-and-cilantro-react/src/app/components/splash-page.scss index 3576c57..8c98e67 100644 --- a/chili-and-cilantro-react/src/app/components/splash-page.scss +++ b/chili-and-cilantro-react/src/app/components/splash-page.scss @@ -1,36 +1,36 @@ -@import '../../styles.scss'; +@use 'sass:color'; +@use '../../styles' as *; // Splash Page styles .splash-container { max-width: 800px; margin: 2rem auto; padding: 2rem; - background-color: $primary-dark; // Using dark theme background - border-radius: 8px; - box-shadow: 0 0 10px rgba(0, 0, 0, 0.5); // Darker shadow for depth + background-color: $background-color; + border-radius: 10px; + box-shadow: 0 4px 10px rgba(0, 0, 0, 0.7); // Strong shadow for depth + color: $text-color-light; // Light text for readability text-align: center; - color: $text-color; // Use shared text color .splash-logo { - max-width: 60%; - margin-bottom: 1rem; + max-width: 70%; + margin-bottom: 1.5rem; } .splash-subdescription { - font-size: 1.5rem; - color: $highlight-color; // Use highlight color - font-family: $font-family; // Shared primary font + font-size: 1.6rem; + color: $highlight-color; // Bright green for emphasis + font-family: $font-family; margin-bottom: 2rem; } .feature-list { - margin-bottom: 2rem; text-align: left; .feature-title { - color: $accent-color; // Use accent color for headers + color: $highlight-color; + font-family: $font-secondary; font-size: 1.8rem; - font-family: $font-secondary; // Use secondary font for contrast margin-bottom: 1rem; text-align: center; } @@ -40,37 +40,47 @@ padding-left: 2rem; li { - margin-bottom: 0.5rem; - font-size: 1.1rem; - color: $text-muted; // Muted text color for bullets - font-family: $font-family; - } + font-size: 1.2rem; + color: $text-color-muted; + margin-bottom: 0.75rem; - li:hover { - color: $text-color; // Highlight bullets on hover - transition: color 0.3s ease-in-out; + &:hover { + color: $text-color-light; + transition: color 0.3s ease; + } } } } .game-description { - background-color: $secondary-dark; // Use lighter dark for the card + background-color: color.adjust( + $new-dark, + $lightness: 10% + ); // Slightly lighter dark background padding: 1.5rem; border-radius: 8px; box-shadow: 0 2px 10px rgba(0, 0, 0, 0.5); // Subtle shadow for depth margin-bottom: 2rem; h3 { - color: $highlight-color; // Use highlight color for headers font-family: $font-family; + color: $highlight-color; // Bright green for headers font-size: 1.5rem; margin-bottom: 1rem; } p { - color: $text-muted; // Muted text for description + color: $text-color-light; // Light text for better contrast font-family: $font-family; + line-height: 1.8; // Improve readability with increased line spacing + font-size: 1.2rem; // Slightly larger font size margin: 0; + + // Optional: Highlight keywords + span { + color: $highlight-color; // Use bright green for emphasis + font-weight: bold; + } } } @@ -80,21 +90,29 @@ gap: 1rem; .btn { - @extend .btn; // Extend shared button styles + display: inline-block; + padding: 0.75rem 1.5rem; + font-size: 1rem; + border-radius: 8px; text-decoration: none; text-align: center; + transition: background-color 0.3s ease; &-primary { - @extend .btn-primary; // Use primary button style + background-color: $new-gray; + color: $new-dark; + &:hover { - background-color: $highlight-hover; // Shared hover color + background-color: $new-red; } } &-secondary { - @extend .btn-secondary; // Use secondary button style + background-color: $new-gray; + color: $new-dark; + &:hover { - background-color: $accent-hover; // Shared hover color + background-color: color.scale($new-lightGreen, $lightness: -10%); } } } diff --git a/chili-and-cilantro-react/src/app/components/splash-page.tsx b/chili-and-cilantro-react/src/app/components/splash-page.tsx index 137f8e8..cb3bad8 100644 --- a/chili-and-cilantro-react/src/app/components/splash-page.tsx +++ b/chili-and-cilantro-react/src/app/components/splash-page.tsx @@ -14,7 +14,9 @@ const SplashPage: React.FC = () => { />

A Spicy Bluffing Game

-

Key Features:

+

+ Key Features: +

  • Exciting bluffing gameplay with a culinary twist
  • Strategic bidding and card placement
  • diff --git a/chili-and-cilantro-react/src/app/components/top-menu.scss b/chili-and-cilantro-react/src/app/components/top-menu.scss index 0ece32a..b51ae0c 100644 --- a/chili-and-cilantro-react/src/app/components/top-menu.scss +++ b/chili-and-cilantro-react/src/app/components/top-menu.scss @@ -1,8 +1,8 @@ -@import '../../styles.scss'; +@use '../../styles' as *; // TopMenu styles .top-menu { - background-color: $background-color; + background-color: $logo-background-color; padding: 0.5rem 2rem; position: fixed; top: 0; @@ -15,7 +15,7 @@ border: none; cursor: pointer; font-size: 1.5rem; - color: $light-color; + color: $new-dark; padding: 0.5rem; display: flex; align-items: center; @@ -36,7 +36,7 @@ position: absolute; right: 0; top: 100%; - background-color: $background-color; + background-color: $new-gray; border-radius: 4px; box-shadow: 0 2px 10px rgba(0, 0, 0, 0.1); display: flex; @@ -54,7 +54,7 @@ transition: background-color 0.3s ease; &:hover { - background-color: rgba($light-color, 0.1); + background-color: rgba($new-dark, 0.1); } } } @@ -80,7 +80,7 @@ position: absolute; right: 0; top: 100%; - background-color: $background-color; + background-color: $new-gray; border-radius: 4px; box-shadow: 0 2px 10px rgba(0, 0, 0, 0.1); display: flex; @@ -94,7 +94,7 @@ transition: background-color 0.3s ease; &:hover { - background-color: rgba($light-color, 0.1); + background-color: rgba($new-dark, 0.1); } } } @@ -111,7 +111,7 @@ .logo-container { display: flex; align-items: center; - background-color: $background-color; + background-color: $logo-background-color; .logo-symbol { height: 30px; @@ -119,7 +119,7 @@ .logo-text { font-size: 1.2rem; - color: $primary-color; + color: $new-lightGreen; } } diff --git a/chili-and-cilantro-react/src/app/components/top-menu.tsx b/chili-and-cilantro-react/src/app/components/top-menu.tsx index e72239d..ce907d9 100644 --- a/chili-and-cilantro-react/src/app/components/top-menu.tsx +++ b/chili-and-cilantro-react/src/app/components/top-menu.tsx @@ -56,7 +56,7 @@ const TopMenu: React.FC = () => { alt="CurseFund" className="logo-symbol" /> - + Chili and Cilantro
diff --git a/chili-and-cilantro-react/src/app/components/verify-email-page.scss b/chili-and-cilantro-react/src/app/components/verify-email-page.scss index 5200012..2508ea4 100644 --- a/chili-and-cilantro-react/src/app/components/verify-email-page.scss +++ b/chili-and-cilantro-react/src/app/components/verify-email-page.scss @@ -1,4 +1,4 @@ -@import '../../styles.scss'; +@use '../../styles' as *; .verification-container { max-width: 400px; diff --git a/chili-and-cilantro-react/src/app/components/verify-email-page.tsx b/chili-and-cilantro-react/src/app/components/verify-email-page.tsx index 4518514..e5f417e 100644 --- a/chili-and-cilantro-react/src/app/components/verify-email-page.tsx +++ b/chili-and-cilantro-react/src/app/components/verify-email-page.tsx @@ -1,7 +1,13 @@ +import { + Box, + Button, + CircularProgress, + Container, + Typography, +} from '@mui/material'; import React, { useEffect, useState } from 'react'; -import { Link, useLocation } from 'react-router-dom'; +import { Link as RouterLink, useLocation } from 'react-router-dom'; import api from '../services/api'; -import './verify-email-page.scss'; const VerifyEmailPage: React.FC = () => { const [message, setMessage] = useState(''); @@ -17,9 +23,7 @@ const VerifyEmailPage: React.FC = () => { const tokenFromQuery = query.get('token'); if (tokenFromQuery) { - (async () => { - await verifyEmail(tokenFromQuery); - })(); + verifyEmail(tokenFromQuery); } else { setLoading(false); setMessage('No verification token provided.'); @@ -37,7 +41,7 @@ const VerifyEmailPage: React.FC = () => { setMessage('Email verification failed. Please try again.'); setVerificationStatus('error'); } - } catch { + } catch (error) { setMessage( 'An error occurred during email verification. Please try again.', ); @@ -47,25 +51,58 @@ const VerifyEmailPage: React.FC = () => { } }; - if (loading) { - return
Verifying email...
; - } - return ( -
-

Email Verification

-

{message}

- {verificationStatus === 'success' && ( -
- Proceed to Login -
- )} - {verificationStatus === 'error' && ( -
- Return to Home -
- )} -
+ + + + Email Verification + + {loading ? ( + + ) : ( + <> + + {message} + + {verificationStatus === 'success' && ( + + )} + {verificationStatus === 'error' && ( + + )} + + )} + + ); }; diff --git a/chili-and-cilantro-react/src/main.tsx b/chili-and-cilantro-react/src/main.tsx index fd56ad9..ab88c6d 100644 --- a/chili-and-cilantro-react/src/main.tsx +++ b/chili-and-cilantro-react/src/main.tsx @@ -7,6 +7,8 @@ import App from './app/app'; import { library } from '@fortawesome/fontawesome-svg-core'; import '@fortawesome/fontawesome-svg-core/styles.css'; import { fas } from '@fortawesome/free-solid-svg-icons'; +import { CssBaseline, ThemeProvider } from '@mui/material'; +import theme from './theme'; library.add(fas); @@ -28,9 +30,12 @@ const root = ReactDOM.createRoot( document.getElementById('root') as HTMLElement, ); root.render( - - - - - , + + + + + + + + , ); diff --git a/chili-and-cilantro-react/src/styles.scss b/chili-and-cilantro-react/src/styles.scss index 3ae8203..d993781 100644 --- a/chili-and-cilantro-react/src/styles.scss +++ b/chili-and-cilantro-react/src/styles.scss @@ -1,33 +1,61 @@ @use 'sass:color'; -// Variables +// Color Base $logo-background-color: #fffdd0; $green-chili: #7edb0e; $green-chili-shadow: #0e683c; $red-chili: #ff081f; $red-chili-shadow: #9c0020; +$cilantro-light: #e3fe2d; +$cilantro-dark: #24b215; + +$new-dark: #05070b; +$new-green: #375c4b; +$new-lightGreen: #78bf33; +$new-red: #cc2129; +$new-gray: #cfcdc8; // Theme colors -$primary-dark: #333333; // Darker shade for text -$secondary-dark: #666666; // Medium gray for secondary text -$highlight-color: #7edb0e; // Green chili as highlight -$accent-color: #ff081f; // Red chili as accent +$primary-dark: $new-dark; // Darker shade for text +$secondary-dark: $new-gray; // Medium gray for secondary text +$highlight-color: $new-lightGreen; // Green chili as highlight +$accent-color: $new-red; // Red chili as accent $text-color: #000000; // Black text for readability +$text-color-light: #ffffff; // White text for contrast +$text-color-muted: $new-gray; // Muted gray for secondary text $text-muted: #666666; // Muted gray for secondary text -$danger-color: #ff081f; // Red chili for danger/alerts -$success-color: #7edb0e; // Green chili for success feedback +$danger-color: $new-red; // Red chili for danger/alerts +$success-color: $new-lightGreen; // Green chili for success feedback $border-color: #e0e0e0; // Light gray for borders -$primary-color: $green-chili; +$primary-color: $new-green; $light-color: #ffffff; // White background color -$background-color: #ffffff; // White background color +$background-color: $new-dark; // White background color // Button hover effects -$highlight-hover: lighten($highlight-color, 10%); -$accent-hover: lighten($accent-color, 10%); +$highlight-hover: color.adjust($highlight-color, $lightness: 10%); +$accent-hover: color.adjust($accent-color, $lightness: 10%); $font-family: 'Playfair Display', serif; $font-secondary: 'McFoodPoisoning', sans-serif; +.btn-primary { + background-color: $new-gray; + color: $new-dark; + + &:hover { + background-color: $new-red; + } +} + +.btn-secondary { + background-color: $new-gray; + color: $new-dark; + + &:hover { + background-color: $new-green; + } +} + // Reset and base styles * { box-sizing: border-box; diff --git a/chili-and-cilantro-react/src/theme.tsx b/chili-and-cilantro-react/src/theme.tsx index afde261..9e86d85 100644 --- a/chili-and-cilantro-react/src/theme.tsx +++ b/chili-and-cilantro-react/src/theme.tsx @@ -2,57 +2,98 @@ import { createTheme } from '@mui/material/styles'; const theme = createTheme({ palette: { - mode: 'light', + mode: 'dark', primary: { - main: '#7EDB0E', // green-chili - dark: '#0E683C', // green-chili-shadow - contrastText: '#FFFFFF', // white for better contrast + main: '#78bf33', // new-lightGreen + dark: '#375c4b', // new-green + light: '#7edb0e', // green-chili + contrastText: '#ffffff', }, secondary: { - main: '#FF081F', // red-chili - dark: '#9C0020', // red-chili-shadow - contrastText: '#FFFFFF', // white for better contrast + main: '#cc2129', // new-red + dark: '#9c0020', // red-chili-shadow + light: '#ff081f', // red-chili + contrastText: '#ffffff', }, background: { - default: '#FFFFFF', // white background - paper: '#FFFFFF', + default: '#05070b', // new-dark + paper: '#1a1e24', // slightly lighter than new-dark for contrast }, text: { - primary: '#000000', - secondary: '#333333', + primary: '#ffffff', + secondary: '#cfcdc8', // new-gray + }, + error: { + main: '#cc2129', // new-red + }, + warning: { + main: '#e3fe2d', // cilantro-light + }, + info: { + main: '#24b215', // cilantro-dark + }, + success: { + main: '#78bf33', // new-lightGreen }, }, - components: { - MuiAppBar: { - styleOverrides: { - root: { - backgroundColor: '#7EDB0E', // green-chili for AppBar - color: '#FFFFFF', // white text for contrast - }, - }, + typography: { + fontFamily: '"Playfair Display", serif', + h1: { + fontFamily: '"McFoodPoisoning", sans-serif', + fontWeight: 600, + }, + h2: { + fontFamily: '"McFoodPoisoning", sans-serif', + fontWeight: 500, }, + h3: { + fontWeight: 400, + }, + button: { + fontWeight: 700, + }, + }, + components: { MuiButton: { styleOverrides: { root: { - backgroundColor: '#7EDB0E', // green-chili for buttons - color: '#FFFFFF', // white text for contrast + borderRadius: 8, + textTransform: 'none', + }, + containedPrimary: { + backgroundColor: '#cfcdc8', // new-gray + color: '#05070b', // new-dark + '&:hover': { + backgroundColor: '#cc2129', // new-red + }, + }, + containedSecondary: { + backgroundColor: '#cfcdc8', // new-gray + color: '#05070b', // new-dark '&:hover': { - backgroundColor: '#0E683C', // darker green on hover + backgroundColor: '#375c4b', // new-green }, }, }, }, - MuiInputLabel: { + MuiTextField: { styleOverrides: { root: { - color: '#333333', // darker text for better readability + '& .MuiOutlinedInput-root': { + '& fieldset': { + borderColor: '#cfcdc8', // new-gray + }, + '&:hover fieldset': { + borderColor: '#78bf33', // new-lightGreen + }, + '&.Mui-focused fieldset': { + borderColor: '#78bf33', // new-lightGreen + }, + }, }, }, }, }, - typography: { - fontFamily: '"Playfair Display", serif', - }, }); export default theme;