From 5e5560b558ff818546c94135bebf334be770c920 Mon Sep 17 00:00:00 2001 From: Michael Hunteman Date: Sat, 9 Mar 2024 15:13:45 -0800 Subject: Add carousel --- .editorconfig | 14 +++----- .prettierrc | 5 +++ index.html | 4 +-- package-lock.json | 15 ++++++++ package.json | 1 + src/ThemeContextProvider.tsx | 12 +++---- src/apiSlice.ts | 51 +++++++++++++-------------- src/components/Admin.tsx | 6 ++-- src/components/Desktop.tsx | 6 ++-- src/components/Home.tsx | 76 ++++++++++++++++++++++++++++++++++++++++ src/components/Mobile.tsx | 17 ++++++--- src/components/RsvpForm.tsx | 63 ++++++++++++++++++++------------- src/components/active.css | 6 ++++ src/features/auth/GuestLogin.tsx | 50 ++++++++++++++------------ src/features/auth/authSlice.ts | 10 +++--- src/main.tsx | 28 ++++++++------- src/mocks/handlers.ts | 44 +++++++++++------------ src/pages.ts | 4 +-- src/store.ts | 10 +++--- 19 files changed, 271 insertions(+), 151 deletions(-) create mode 100644 .prettierrc create mode 100644 src/components/Home.tsx create mode 100644 src/components/active.css diff --git a/.editorconfig b/.editorconfig index 135ea6f..41c2aaf 100644 --- a/.editorconfig +++ b/.editorconfig @@ -1,15 +1,9 @@ root = true -[*.{ts,tsx,json}] +[*] +indent_size = 2 +indent_style = space end_of_line = lf -insert_final_newline = true charset = utf-8 trim_trailing_whitespace = true -indent_style = space -indent_size = 2 -# max_line_length = 80 - -[*.md] -indent_style = space -indent_size = 2 -trim_trailing_whitespace = false +insert_final_newline = true \ No newline at end of file diff --git a/.prettierrc b/.prettierrc new file mode 100644 index 0000000..e537c8a --- /dev/null +++ b/.prettierrc @@ -0,0 +1,5 @@ +{ + "singleQuote": true, + "trailingComma": "es5", + "arrowParens": "always" +} diff --git a/index.html b/index.html index dfcfa2d..a24cf1f 100644 --- a/index.html +++ b/index.html @@ -1,8 +1,8 @@ - + - + Wedding diff --git a/package-lock.json b/package-lock.json index 35bab87..9487398 100644 --- a/package-lock.json +++ b/package-lock.json @@ -15,6 +15,7 @@ "@mui/lab": "^5.0.0-alpha.165", "@mui/material": "^5.15.9", "@reduxjs/toolkit": "^2.2.1", + "prettier": "^3.2.5", "react": "^18.2.0", "react-dom": "^18.2.0", "react-hook-form": "^7.50.1", @@ -4065,6 +4066,20 @@ "node": ">= 0.8.0" } }, + "node_modules/prettier": { + "version": "3.2.5", + "resolved": "https://registry.npmjs.org/prettier/-/prettier-3.2.5.tgz", + "integrity": "sha512-3/GWa9aOC0YeD7LUfvOG2NiDyhOWRvt1k+rcKhOuYnMY24iiCphgneUfJDyFXd6rZCAnuLBv6UeAULtrhT/F4A==", + "bin": { + "prettier": "bin/prettier.cjs" + }, + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/prettier/prettier?sponsor=1" + } + }, "node_modules/prop-types": { "version": "15.8.1", "resolved": "https://registry.npmjs.org/prop-types/-/prop-types-15.8.1.tgz", diff --git a/package.json b/package.json index 0156564..3edb6db 100644 --- a/package.json +++ b/package.json @@ -17,6 +17,7 @@ "@mui/lab": "^5.0.0-alpha.165", "@mui/material": "^5.15.9", "@reduxjs/toolkit": "^2.2.1", + "prettier": "^3.2.5", "react": "^18.2.0", "react-dom": "^18.2.0", "react-hook-form": "^7.50.1", 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({ - toggleColorMode: () => {} + toggleColorMode: () => {}, }); function ThemeContextProvider({ children }: ThemeProviderProps) { @@ -29,16 +29,14 @@ function ThemeContextProvider({ children }: ThemeProviderProps) { createTheme({ palette: { mode, - } + }, }), [mode] ); return ( - - {children} - + {children} ); } 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({ query: () => '/guests', - providesTags: ['Guests'] + providesTags: ['Guests'], }), updateGuest: builder.mutation({ - query: guest => ({ + query: (guest) => ({ url: `/guests/${guest?.id}`, method: 'PATCH', body: guest, - providesTags: ['Guests'] - }) + providesTags: ['Guests'], + }), }), login: builder.mutation({ - 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 =

Loading...

+ content =

Loading...

; } 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 (
- {pages.map(page => ( + {pages.map((page) => ( 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 ( + <> +
+
+ {colors.map((backgroundColor, colorIndex) => ( +
+ ))} +
+
+ {colors.map((_, colorIndex) => ( +
{ + setIndex(colorIndex); + }} + /> + ))} +
+
+ + ); +} + +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() { {theme.palette.mode === 'dark' ? : } - + {pages.map((page) => ( - 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 ( - +

- 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.

@@ -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', + }, + })} /> @@ -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', + }, + })} /> @@ -118,9 +131,11 @@ function RsvpForm() {
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: , children: [ + { + path: '/', + element: , + }, { path: 'schedule', - element: + element: , }, { path: 'registry', - element: + element: , }, { path: 'guest-login', - element: + element: , }, { path: 'admin', - element: - } + element: , + }, ], }, { @@ -40,11 +44,11 @@ const router = createBrowserRouter([ children: [ { path: 'rsvp', - element: - } - ] - } -], { basename: '/wedding' }); + element: , + }, + ], + }, +]); 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 -export type AppDispatch = typeof store.dispatch +export type RootState = ReturnType; +export type AppDispatch = typeof store.dispatch; -- cgit v1.2.3