summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/ThemeContextProvider.tsx12
-rw-r--r--src/apiSlice.ts51
-rw-r--r--src/components/Admin.tsx6
-rw-r--r--src/components/Desktop.tsx6
-rw-r--r--src/components/Home.tsx76
-rw-r--r--src/components/Mobile.tsx17
-rw-r--r--src/components/RsvpForm.tsx63
-rw-r--r--src/components/active.css6
-rw-r--r--src/features/auth/GuestLogin.tsx50
-rw-r--r--src/features/auth/authSlice.ts10
-rw-r--r--src/main.tsx28
-rw-r--r--src/mocks/handlers.ts44
-rw-r--r--src/pages.ts4
-rw-r--r--src/store.ts10
14 files changed, 244 insertions, 139 deletions
diff --git a/src/ThemeContextProvider.tsx b/src/ThemeContextProvider.tsx
index 63f4e81..dc3cb89 100644
--- a/src/ThemeContextProvider.tsx
+++ b/src/ThemeContextProvider.tsx
@@ -4,14 +4,14 @@ import useMediaQuery from '@mui/material/useMediaQuery';
type ThemeContextType = {
toggleColorMode: () => void;
-}
+};
type ThemeProviderProps = {
children: ReactNode;
-}
+};
export const ThemeContext = createContext<ThemeContextType>({
- toggleColorMode: () => {}
+ toggleColorMode: () => {},
});
function ThemeContextProvider({ children }: ThemeProviderProps) {
@@ -29,16 +29,14 @@ function ThemeContextProvider({ children }: ThemeProviderProps) {
createTheme({
palette: {
mode,
- }
+ },
}),
[mode]
);
return (
<ThemeContext.Provider value={{ toggleColorMode }}>
- <ThemeProvider theme={theme}>
- {children}
- </ThemeProvider>
+ <ThemeProvider theme={theme}>{children}</ThemeProvider>
</ThemeContext.Provider>
);
}
diff --git a/src/apiSlice.ts b/src/apiSlice.ts
index 6a1196b..f337fdd 100644
--- a/src/apiSlice.ts
+++ b/src/apiSlice.ts
@@ -1,23 +1,23 @@
import { createApi, fetchBaseQuery } from '@reduxjs/toolkit/query/react';
-import { RootState } from './store'
+import { RootState } from './store';
export interface LoginRequest {
- firstName: string
- lastName: string
+ firstName: string;
+ lastName: string;
}
export interface LoginResponse {
- guest: Guest
- token: string
+ guest: Guest;
+ token: string;
}
export interface Guest {
- id: number
- firstName: string
- lastName: string
- attendance: string
- email: string
- message: string
+ id: number;
+ firstName: string;
+ lastName: string;
+ attendance: string;
+ email: string;
+ message: string;
}
export const apiSlice = createApi({
@@ -30,34 +30,31 @@ export const apiSlice = createApi({
headers.set('authorization', `Bearer ${token}`);
}
return headers;
- }
+ },
}),
tagTypes: ['Guests'],
- endpoints: builder => ({
+ endpoints: (builder) => ({
getGuests: builder.query<void, void>({
query: () => '/guests',
- providesTags: ['Guests']
+ providesTags: ['Guests'],
}),
updateGuest: builder.mutation<Guest, Guest>({
- query: guest => ({
+ query: (guest) => ({
url: `/guests/${guest?.id}`,
method: 'PATCH',
body: guest,
- providesTags: ['Guests']
- })
+ providesTags: ['Guests'],
+ }),
}),
login: builder.mutation<LoginResponse, LoginRequest>({
- query: credentials => ({
+ query: (credentials) => ({
url: '/guest-login',
method: 'POST',
- body: credentials
- })
- })
- })
+ body: credentials,
+ }),
+ }),
+ }),
});
-export const {
- useGetGuestsQuery,
- useUpdateGuestMutation,
- useLoginMutation
-} = apiSlice;
+export const { useGetGuestsQuery, useUpdateGuestMutation, useLoginMutation } =
+ apiSlice;
diff --git a/src/components/Admin.tsx b/src/components/Admin.tsx
index bd1545d..a3da3fa 100644
--- a/src/components/Admin.tsx
+++ b/src/components/Admin.tsx
@@ -6,17 +6,17 @@ function Admin() {
isLoading,
isSuccess,
isError,
- error
+ error,
} = useGetGuestsQuery();
let content;
if (isLoading) {
- content = <p>Loading...</p>
+ content = <p>Loading...</p>;
} else if (isSuccess) {
content = JSON.stringify(guests);
} else if (isError) {
- content = <>{error.toString()}</>
+ content = <>{error.toString()}</>;
}
return (
diff --git a/src/components/Desktop.tsx b/src/components/Desktop.tsx
index 34a0621..983c929 100644
--- a/src/components/Desktop.tsx
+++ b/src/components/Desktop.tsx
@@ -1,6 +1,6 @@
import { useContext } from 'react';
-import { Link } from "react-router-dom";
-import { Button, IconButton } from "@mui/material";
+import { Link } from 'react-router-dom';
+import { Button, IconButton } from '@mui/material';
import DarkModeIcon from '@mui/icons-material/DarkMode';
import LightModeIcon from '@mui/icons-material/LightMode';
import { useTheme } from '@mui/material/styles';
@@ -13,7 +13,7 @@ function Desktop() {
return (
<div style={{ marginLeft: 'auto' }}>
- {pages.map(page => (
+ {pages.map((page) => (
<Button color="inherit" component={Link} to={page?.to} key={page?.name}>
{page?.name}
</Button>
diff --git a/src/components/Home.tsx b/src/components/Home.tsx
new file mode 100644
index 0000000..da9eb6b
--- /dev/null
+++ b/src/components/Home.tsx
@@ -0,0 +1,76 @@
+import { useEffect, useRef, useState } from 'react';
+import './active.css';
+
+function Home() {
+ const [index, setIndex] = useState(0);
+ const colors = ['#FF0000', '#00FF00', '#0000FF'];
+ const timeout = useRef(0);
+
+ useEffect(() => {
+ resetTimeout();
+ timeout.current = window.setTimeout(
+ () =>
+ setIndex((prevIndex) =>
+ prevIndex === colors.length - 1 ? 0 : prevIndex + 1
+ ),
+ 2500
+ );
+
+ return () => {
+ resetTimeout();
+ };
+ }, [index]);
+
+ const resetTimeout = () => {
+ if (timeout.current) {
+ clearTimeout(timeout.current);
+ }
+ };
+
+ return (
+ <>
+ <div style={{ margin: 'auto', overflow: 'hidden' }}>
+ <div
+ style={{
+ whiteSpace: 'nowrap',
+ transform: `translate3d(${-index * 100}%, 0, 0)`,
+ transition: 'ease 1000ms',
+ }}
+ >
+ {colors.map((backgroundColor, colorIndex) => (
+ <div
+ key={colorIndex}
+ style={{
+ display: 'inline-block',
+ backgroundColor,
+ height: '80vh',
+ width: '100%',
+ }}
+ />
+ ))}
+ </div>
+ <div style={{ textAlign: 'center' }}>
+ {colors.map((_, colorIndex) => (
+ <div
+ key={colorIndex}
+ style={{
+ display: 'inline-block',
+ height: '0.8rem',
+ width: '0.8rem',
+ borderRadius: '50%',
+ cursor: 'pointer',
+ margin: '0.7rem 0.7rem 0.7rem',
+ }}
+ className={colorIndex === index ? 'active' : 'inactive'}
+ onClick={() => {
+ setIndex(colorIndex);
+ }}
+ />
+ ))}
+ </div>
+ </div>
+ </>
+ );
+}
+
+export default Home;
diff --git a/src/components/Mobile.tsx b/src/components/Mobile.tsx
index f2ca255..f5502c0 100644
--- a/src/components/Mobile.tsx
+++ b/src/components/Mobile.tsx
@@ -1,7 +1,7 @@
import { useContext } from 'react';
-import { Link } from "react-router-dom";
+import { Link } from 'react-router-dom';
import { useState } from 'react';
-import { Button, IconButton, Menu, MenuItem } from "@mui/material";
+import { Button, IconButton, Menu, MenuItem } from '@mui/material';
import DarkModeIcon from '@mui/icons-material/DarkMode';
import LightModeIcon from '@mui/icons-material/LightMode';
import { useTheme } from '@mui/material/styles';
@@ -27,13 +27,22 @@ function Mobile() {
<IconButton color="inherit" onClick={toggleColorMode}>
{theme.palette.mode === 'dark' ? <DarkModeIcon /> : <LightModeIcon />}
</IconButton>
- <IconButton color="inherit" sx={{ ml: 'auto' }} onClick={handleOpenNavMenu}>
+ <IconButton
+ color="inherit"
+ sx={{ ml: 'auto' }}
+ onClick={handleOpenNavMenu}
+ >
<MenuIcon />
</IconButton>
<Menu anchorEl={anchorEl} open={!!anchorEl} onClose={handleCloseNavMenu}>
{pages.map((page) => (
<MenuItem key={page.name} onClick={handleCloseNavMenu}>
- <Button color="inherit" component={Link} to={page?.to} key={page?.name}>
+ <Button
+ color="inherit"
+ component={Link}
+ to={page?.to}
+ key={page?.name}
+ >
{page?.name}
</Button>
</MenuItem>
diff --git a/src/components/RsvpForm.tsx b/src/components/RsvpForm.tsx
index d7efecf..090987b 100644
--- a/src/components/RsvpForm.tsx
+++ b/src/components/RsvpForm.tsx
@@ -10,14 +10,19 @@ import {
TextField,
} from '@mui/material';
import { useForm, Controller } from 'react-hook-form';
-import { useOutletContext } from "react-router-dom";
+import { useOutletContext } from 'react-router-dom';
import { useUpdateGuestMutation, Guest } from '../apiSlice';
function RsvpForm() {
const [updateGuest] = useUpdateGuestMutation();
const guest: Guest = useOutletContext();
- const { register, handleSubmit, control, formState: { errors } } = useForm({
+ const {
+ register,
+ handleSubmit,
+ control,
+ formState: { errors },
+ } = useForm({
defaultValues: {
id: guest?.id,
firstName: guest?.firstName,
@@ -25,22 +30,27 @@ function RsvpForm() {
attendance: '',
email: '',
partySize: 1,
- message: ''
- }
+ message: '',
+ },
});
const onSubmit = async (data: Guest) => {
- updateGuest({...data});
+ updateGuest({ ...data });
};
return (
- <Container component="form" maxWidth="sm" noValidate onSubmit={handleSubmit(onSubmit)}>
+ <Container
+ component="form"
+ maxWidth="sm"
+ noValidate
+ onSubmit={handleSubmit(onSubmit)}
+ >
<Grid container spacing={2} sx={{ mt: 8 }}>
<Grid item xs={12}>
<p>
- Please RSVP for the wedding by March 10, 2025.
- The ceremony will commence at 3 pm on April 26 in ...
- The reception will follow at 5 pm in A Venue on the Ridge.
+ Please RSVP for the wedding by March 10, 2025. The ceremony will
+ commence at 3 pm on April 26 in ... The reception will follow at 5
+ pm in A Venue on the Ridge.
</p>
</Grid>
<Grid item xs={12}>
@@ -81,12 +91,13 @@ function RsvpForm() {
error={!!errors.email}
helperText={errors.email?.message}
required
- {...register('email',
- { required: 'This field is required',
- pattern: { value: /\S+@\S+\.\S+/,
- message: 'Please enter a valid email address' }
- }
- )}
+ {...register('email', {
+ required: 'This field is required',
+ pattern: {
+ value: /\S+@\S+\.\S+/,
+ message: 'Please enter a valid email address',
+ },
+ })}
/>
</Grid>
<Grid item xs={12} md={6} lg={6}>
@@ -98,12 +109,14 @@ function RsvpForm() {
error={!!errors.partySize}
helperText={errors.partySize?.message}
required
- {...register('partySize',
- { required: 'This field is required',
- min: { value: 1, message: 'Please enter a positive integer' },
- max: { value: 9, message: 'Please enter an integer less than 10' }
- }
- )}
+ {...register('partySize', {
+ required: 'This field is required',
+ min: { value: 1, message: 'Please enter a positive integer' },
+ max: {
+ value: 9,
+ message: 'Please enter an integer less than 10',
+ },
+ })}
/>
</Grid>
<Grid item xs={12}>
@@ -118,9 +131,11 @@ function RsvpForm() {
</Grid>
<Grid item xs={12}>
<div
- style={{ display: 'flex',
- flexDirection: 'column',
- alignItems: 'center' }}
+ style={{
+ display: 'flex',
+ flexDirection: 'column',
+ alignItems: 'center',
+ }}
>
<Button type="submit" variant="contained">
RSVP
diff --git a/src/components/active.css b/src/components/active.css
new file mode 100644
index 0000000..60feeea
--- /dev/null
+++ b/src/components/active.css
@@ -0,0 +1,6 @@
+.active {
+ background-color: #1976d2;
+}
+.inactive {
+ background-color: #c4c4c4;
+} \ No newline at end of file
diff --git a/src/features/auth/GuestLogin.tsx b/src/features/auth/GuestLogin.tsx
index 11f3d5b..94bad05 100644
--- a/src/features/auth/GuestLogin.tsx
+++ b/src/features/auth/GuestLogin.tsx
@@ -10,33 +10,42 @@ function GuestLogin() {
const navigate = useNavigate();
const [login] = useLoginMutation();
- const { register, handleSubmit, formState: { errors } } = useForm<LoginRequest>({
+ const {
+ register,
+ handleSubmit,
+ formState: { errors },
+ } = useForm<LoginRequest>({
defaultValues: {
firstName: '',
- lastName: ''
- }
+ lastName: '',
+ },
});
const onSubmit = async (data: LoginRequest) => {
- try {
- dispatch(setCredentials(await login(data).unwrap()));
- navigate('/rsvp');
- } catch (e) {
- console.log(e);
- }
+ try {
+ dispatch(setCredentials(await login(data).unwrap()));
+ navigate('/rsvp');
+ } catch (e) {
+ console.log(e);
+ }
};
return (
- <Container component="form" maxWidth="xs" noValidate onSubmit={handleSubmit(onSubmit)}>
+ <Container
+ component="form"
+ maxWidth="xs"
+ noValidate
+ onSubmit={handleSubmit(onSubmit)}
+ >
<div
- style={{ marginTop: 80,
- display: 'flex',
- flexDirection: 'column',
- alignItems: 'center' }}
+ style={{
+ marginTop: 80,
+ display: 'flex',
+ flexDirection: 'column',
+ alignItems: 'center',
+ }}
>
- <Typography variant="h6">
- Guest Login
- </Typography>
+ <Typography variant="h6">Guest Login</Typography>
<TextField
label="First Name"
variant="outlined"
@@ -57,12 +66,7 @@ function GuestLogin() {
required
{...register('lastName', { required: 'This field is required' })}
/>
- <Button
- type="submit"
- variant="contained"
- fullWidth
- sx={{ mt: 2 }}
- >
+ <Button type="submit" variant="contained" fullWidth sx={{ mt: 2 }}>
Log in
</Button>
</div>
diff --git a/src/features/auth/authSlice.ts b/src/features/auth/authSlice.ts
index d5b294c..34ede58 100644
--- a/src/features/auth/authSlice.ts
+++ b/src/features/auth/authSlice.ts
@@ -3,9 +3,9 @@ import type { RootState } from '../../store';
import { Guest } from '../../apiSlice';
type AuthState = {
- guest: Guest | null
- token: string | null
-}
+ guest: Guest | null;
+ token: string | null;
+};
const authSlice = createSlice({
name: 'auth',
@@ -15,8 +15,8 @@ const authSlice = createSlice({
const { guest, token } = action.payload;
state.guest = guest;
state.token = token;
- }
- }
+ },
+ },
});
export const { setCredentials } = authSlice.actions;
diff --git a/src/main.tsx b/src/main.tsx
index 3125ede..8aba432 100644
--- a/src/main.tsx
+++ b/src/main.tsx
@@ -4,35 +4,39 @@ import { createBrowserRouter, RouterProvider } from 'react-router-dom';
import { Provider } from 'react-redux';
import App from './App';
import store from './store';
-import ThemeContextProvider from './ThemeContextProvider'
+import ThemeContextProvider from './ThemeContextProvider';
import Schedule from './components/Schedule';
import Registry from './components/Registry';
import GuestLogin from './features/auth/GuestLogin';
import Rsvp from './components/Rsvp';
import RsvpForm from './components/RsvpForm';
import Admin from './components/Admin';
+import Home from './components/Home';
const router = createBrowserRouter([
{
- path: '/',
element: <App />,
children: [
{
+ path: '/',
+ element: <Home />,
+ },
+ {
path: 'schedule',
- element: <Schedule />
+ element: <Schedule />,
},
{
path: 'registry',
- element: <Registry />
+ element: <Registry />,
},
{
path: 'guest-login',
- element: <GuestLogin />
+ element: <GuestLogin />,
},
{
path: 'admin',
- element: <Admin />
- }
+ element: <Admin />,
+ },
],
},
{
@@ -40,11 +44,11 @@ const router = createBrowserRouter([
children: [
{
path: 'rsvp',
- element: <RsvpForm />
- }
- ]
- }
-], { basename: '/wedding' });
+ element: <RsvpForm />,
+ },
+ ],
+ },
+]);
const enableMocking = async () => {
const { worker } = await import('./mocks/browser');
diff --git a/src/mocks/handlers.ts b/src/mocks/handlers.ts
index 6bd17fa..e3569df 100644
--- a/src/mocks/handlers.ts
+++ b/src/mocks/handlers.ts
@@ -5,32 +5,28 @@ const token = nanoid();
export const handlers = [
http.post('/guest-login', () => {
- return HttpResponse.json(
- {
- guest: {
- id: 1,
- firstName: 'Michael',
- lastName: 'Hunteman',
- attendance: 'false',
- meal: '',
- email: '',
- message: ''
- },
- token,
- }
- )
- }),
- http.patch('/guests/1', () => {
- return HttpResponse.json(
- {
+ return HttpResponse.json({
+ guest: {
id: 1,
firstName: 'Michael',
lastName: 'Hunteman',
- attendance: 'true',
- meal: 'beef',
+ attendance: 'false',
+ meal: '',
email: '',
- message: ''
- }
- )
- })
+ message: '',
+ },
+ token,
+ });
+ }),
+ http.patch('/guests/1', () => {
+ return HttpResponse.json({
+ id: 1,
+ firstName: 'Michael',
+ lastName: 'Hunteman',
+ attendance: 'true',
+ meal: 'beef',
+ email: '',
+ message: '',
+ });
+ }),
];
diff --git a/src/pages.ts b/src/pages.ts
index ce1d29e..5b5d119 100644
--- a/src/pages.ts
+++ b/src/pages.ts
@@ -1,8 +1,8 @@
const pages = [
- { name: 'Schedule', to: '/schedule'},
+ { name: 'Schedule', to: '/schedule' },
{ name: 'RSVP', to: '/guest-login' },
{ name: 'Registry', to: '/registry' },
- { name: 'Admin', to: '/admin' }
+ { name: 'Admin', to: '/admin' },
];
export default pages;
diff --git a/src/store.ts b/src/store.ts
index d3e02d2..264639e 100644
--- a/src/store.ts
+++ b/src/store.ts
@@ -5,12 +5,12 @@ import authReducer from './features/auth/authSlice';
const store = configureStore({
reducer: {
[apiSlice.reducerPath]: apiSlice.reducer,
- auth: authReducer
+ auth: authReducer,
},
- middleware: getDefaultMiddleware =>
- getDefaultMiddleware().concat(apiSlice.middleware)
+ middleware: (getDefaultMiddleware) =>
+ getDefaultMiddleware().concat(apiSlice.middleware),
});
export default store;
-export type RootState = ReturnType<typeof store.getState>
-export type AppDispatch = typeof store.dispatch
+export type RootState = ReturnType<typeof store.getState>;
+export type AppDispatch = typeof store.dispatch;