From 7103019890960e793deefb64987a09b33be60b42 Mon Sep 17 00:00:00 2001 From: Michael Hunteman Date: Fri, 17 May 2024 15:20:30 -0700 Subject: Add golang server --- src/App.tsx | 16 --- src/ThemeContextProvider.tsx | 57 --------- src/apiSlice.ts | 67 ----------- src/components/Admin.tsx | 31 ----- src/components/Desktop.tsx | 29 ----- src/components/Home.tsx | 73 ------------ src/components/Mobile.tsx | 55 --------- src/components/NavBar.tsx | 29 ----- src/components/Registry.tsx | 11 -- src/components/Rsvp.tsx | 29 ----- src/components/RsvpForm.tsx | 242 --------------------------------------- src/components/Schedule.tsx | 107 ----------------- src/components/active.css | 7 -- src/features/auth/GuestLogin.tsx | 79 ------------- src/features/auth/authSlice.ts | 26 ----- src/main.css | 4 - src/main.tsx | 69 ----------- src/mocks/browser.ts | 4 - src/mocks/handlers.ts | 32 ------ src/pages.ts | 8 -- src/store.ts | 16 --- src/vite-env.d.ts | 1 - 22 files changed, 992 deletions(-) delete mode 100644 src/App.tsx delete mode 100644 src/ThemeContextProvider.tsx delete mode 100644 src/apiSlice.ts delete mode 100644 src/components/Admin.tsx delete mode 100644 src/components/Desktop.tsx delete mode 100644 src/components/Home.tsx delete mode 100644 src/components/Mobile.tsx delete mode 100644 src/components/NavBar.tsx delete mode 100644 src/components/Registry.tsx delete mode 100644 src/components/Rsvp.tsx delete mode 100644 src/components/RsvpForm.tsx delete mode 100644 src/components/Schedule.tsx delete mode 100644 src/components/active.css delete mode 100644 src/features/auth/GuestLogin.tsx delete mode 100644 src/features/auth/authSlice.ts delete mode 100644 src/main.css delete mode 100644 src/main.tsx delete mode 100644 src/mocks/browser.ts delete mode 100644 src/mocks/handlers.ts delete mode 100644 src/pages.ts delete mode 100644 src/store.ts delete mode 100644 src/vite-env.d.ts (limited to 'src') diff --git a/src/App.tsx b/src/App.tsx deleted file mode 100644 index 27fc180..0000000 --- a/src/App.tsx +++ /dev/null @@ -1,16 +0,0 @@ -import React from 'react'; -import { Outlet } from 'react-router-dom'; -import CssBaseline from '@mui/material/CssBaseline'; -import NavBar from './components/NavBar'; - -function App() { - return ( - <> - - - - - ); -} - -export default App; diff --git a/src/ThemeContextProvider.tsx b/src/ThemeContextProvider.tsx deleted file mode 100644 index 6ae1430..0000000 --- a/src/ThemeContextProvider.tsx +++ /dev/null @@ -1,57 +0,0 @@ -import React from 'react'; -import { ReactNode, createContext, useMemo, useState } from 'react'; -import { ThemeProvider, createTheme } from '@mui/material/styles'; -import useMediaQuery from '@mui/material/useMediaQuery'; -import type { PaletteMode } from '@mui/material'; - -type ThemeContextType = { - toggleColorMode: () => void; -}; - -type ThemeProviderProps = { - children: ReactNode; -}; - -export const ThemeContext = createContext({ - toggleColorMode: () => {}, -}); - -function ThemeContextProvider({ children }: ThemeProviderProps) { - const prefersDarkMode = useMediaQuery('(prefers-color-scheme: dark)'); - const [mode, setMode] = useState<'light' | 'dark'>( - prefersDarkMode ? 'dark' : 'light' - ); - - const toggleColorMode = () => { - setMode((prevMode: PaletteMode) => - prevMode === 'light' ? 'dark' : 'light' - ); - }; - - const getDesignTokens = (mode: PaletteMode) => ({ - palette: { - mode, - ...(mode === 'light' - ? { - primary: { - main: '#007bff', - }, - } - : { - primary: { - main: '#78bef8', - }, - }), - }, - }); - - const theme = useMemo(() => createTheme(getDesignTokens(mode)), [mode]); - - return ( - - {children} - - ); -} - -export default ThemeContextProvider; diff --git a/src/apiSlice.ts b/src/apiSlice.ts deleted file mode 100644 index 5d987f9..0000000 --- a/src/apiSlice.ts +++ /dev/null @@ -1,67 +0,0 @@ -import { createApi, fetchBaseQuery } from '@reduxjs/toolkit/query/react'; -import type { RootState } from './store'; - -export interface LoginRequest { - firstName: string; - lastName: string; -} - -export interface LoginResponse { - guest: Guest; - token: string; -} - -export interface Guest { - id: number; - firstName: string; - lastName: string; - attendance: string; - email: string; - message: string; - partySize: number; - partyList: Array; -} - -export interface PartyGuest { - firstName: string; - lastName: string; -} - -export const apiSlice = createApi({ - reducerPath: 'api', - baseQuery: fetchBaseQuery({ - baseUrl: '/', - prepareHeaders: (headers, { getState }) => { - const token = (getState() as RootState).auth.token; - if (token) { - headers.set('authorization', `Bearer ${token}`); - } - return headers; - }, - }), - tagTypes: ['Guests'], - endpoints: (builder) => ({ - getGuests: builder.query({ - query: () => '/guests', - providesTags: ['Guests'], - }), - updateGuest: builder.mutation({ - query: (guest) => ({ - url: `/guests/${guest?.id}`, - method: 'PATCH', - body: guest, - providesTags: ['Guests'], - }), - }), - login: builder.mutation({ - query: (credentials) => ({ - url: '/guest-login', - method: 'POST', - body: credentials, - }), - }), - }), -}); - -export const { useGetGuestsQuery, useUpdateGuestMutation, useLoginMutation } = - apiSlice; diff --git a/src/components/Admin.tsx b/src/components/Admin.tsx deleted file mode 100644 index 1c941a5..0000000 --- a/src/components/Admin.tsx +++ /dev/null @@ -1,31 +0,0 @@ -import React from 'react'; -import { useGetGuestsQuery } from '../apiSlice'; - -function Admin() { - const { - data: guests, - isLoading, - isSuccess, - isError, - error, - } = useGetGuestsQuery(); - - let content; - - if (isLoading) { - content =

Loading...

; - } else if (isSuccess) { - content = JSON.stringify(guests); - } else if (isError) { - content = <>{error.toString()}; - } - - return ( - <> -

Admin

- {content} - - ); -} - -export default Admin; diff --git a/src/components/Desktop.tsx b/src/components/Desktop.tsx deleted file mode 100644 index 13de4ee..0000000 --- a/src/components/Desktop.tsx +++ /dev/null @@ -1,29 +0,0 @@ -import React from 'react'; -import { useContext } from 'react'; -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'; -import { ThemeContext } from '../ThemeContextProvider'; -import pages from '../pages'; - -function Desktop() { - const theme = useTheme(); - const { toggleColorMode } = useContext(ThemeContext); - - return ( -
- {pages.map((page) => ( - - ))} - - {theme.palette.mode === 'dark' ? : } - -
- ); -} - -export default Desktop; diff --git a/src/components/Home.tsx b/src/components/Home.tsx deleted file mode 100644 index 839667a..0000000 --- a/src/components/Home.tsx +++ /dev/null @@ -1,73 +0,0 @@ -import React from 'react'; -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 deleted file mode 100644 index 2e7b15c..0000000 --- a/src/components/Mobile.tsx +++ /dev/null @@ -1,55 +0,0 @@ -import React from 'react'; -import { useContext, useState } from 'react'; -import { Link } from 'react-router-dom'; -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'; -import { ThemeContext } from '../ThemeContextProvider'; -import MenuIcon from '@mui/icons-material/Menu'; -import pages from '../pages'; - -function Mobile() { - const [anchorEl, setAnchorEl] = useState(null); - const theme = useTheme(); - const { toggleColorMode } = useContext(ThemeContext); - - const handleOpenNavMenu = (event: React.MouseEvent) => { - setAnchorEl(event.currentTarget); - }; - - const handleCloseNavMenu = () => { - setAnchorEl(null); - }; - - return ( - <> - - {theme.palette.mode === 'dark' ? : } - - - - - - {pages.map((page) => ( - - - - ))} - - - ); -} - -export default Mobile; diff --git a/src/components/NavBar.tsx b/src/components/NavBar.tsx deleted file mode 100644 index 52e8e6c..0000000 --- a/src/components/NavBar.tsx +++ /dev/null @@ -1,29 +0,0 @@ -import React from 'react'; -import { Link } from 'react-router-dom'; -import { AppBar, Toolbar, Typography } from '@mui/material'; -import useMediaQuery from '@mui/material/useMediaQuery'; -import Desktop from './Desktop'; -import Mobile from './Mobile'; - -function NavBar() { - const isMobile = useMediaQuery('(max-width: 768px)'); - - return ( - - - - Madison and Michael's Wedding - - {isMobile ? : } - - - ); -} - -export default NavBar; diff --git a/src/components/Registry.tsx b/src/components/Registry.tsx deleted file mode 100644 index 60a73f9..0000000 --- a/src/components/Registry.tsx +++ /dev/null @@ -1,11 +0,0 @@ -import React from 'react'; - -function Registry() { - return ( - <> -

Registry

- - ); -} - -export default Registry; diff --git a/src/components/Rsvp.tsx b/src/components/Rsvp.tsx deleted file mode 100644 index dad7213..0000000 --- a/src/components/Rsvp.tsx +++ /dev/null @@ -1,29 +0,0 @@ -import React from 'react'; -import { useMemo } from 'react'; -import { useLocation, Navigate, Outlet } from 'react-router-dom'; -import { useSelector } from 'react-redux'; -import CssBaseline from '@mui/material/CssBaseline'; -import NavBar from './NavBar'; -import { selectCurrentGuest } from '../features/auth/authSlice'; - -const authenticate = () => { - const guest = useSelector(selectCurrentGuest); - return useMemo(() => ({ guest }), [guest]); -}; - -function Rsvp() { - const auth = authenticate(); - const location = useLocation(); - - return auth?.guest ? ( - <> - - - - - ) : ( - - ); -} - -export default Rsvp; diff --git a/src/components/RsvpForm.tsx b/src/components/RsvpForm.tsx deleted file mode 100644 index 71db0d8..0000000 --- a/src/components/RsvpForm.tsx +++ /dev/null @@ -1,242 +0,0 @@ -import React from 'react'; -import { useRef } from 'react'; -import { useOutletContext } from 'react-router-dom'; -import { - Button, - Container, - FormControl, - FormControlLabel, - FormLabel, - Grid, - Radio, - RadioGroup, - TextField, -} from '@mui/material'; -import { useForm, Controller, useFieldArray } from 'react-hook-form'; -import { useUpdateGuestMutation } from '../apiSlice'; -import type { Guest } from '../apiSlice'; - -type FormValues = { - id: number; - firstName: string; - lastName: string; - attendance: string; - email: string; - partySize: number; - message: string; - partyList: { - firstName: string; - lastName: string; - }[]; -}; - -function RsvpForm() { - const [updateGuest] = useUpdateGuestMutation(); - const guest: Guest = useOutletContext(); - const previousPartySize = useRef(0); - - const { - register, - handleSubmit, - control, - watch, - formState: { errors }, - } = useForm({ - defaultValues: { - id: guest?.id, - firstName: guest?.firstName, - lastName: guest?.lastName, - attendance: '', - email: '', - message: '', - partySize: 1, - partyList: [], - }, - }); - - const onSubmit = async (data: Guest) => { - updateGuest({ ...data }); - }; - - const { fields, append, remove } = useFieldArray({ - control, - name: 'partyList', - }); - - const handleParty = () => { - const partySize = Number(watch('partySize')) - 1; - if ( - partySize > previousPartySize.current && - partySize > 0 && - partySize < 10 - ) { - append( - new Array(partySize - previousPartySize.current).fill({ - firstName: '', - lastName: '', - }) - ); - previousPartySize.current = partySize; - } else if (partySize < previousPartySize.current && partySize >= 0) { - remove( - [...Array(previousPartySize.current - partySize).keys()].map( - (_, i) => partySize - 1 + i - ) - ); - previousPartySize.current = partySize; - } - }; - - return ( - - - -

- Please RSVP for the wedding by March 10, 2025. The ceremony will - commence at 3 PM on April 26 in Divine Shepherd. The reception will - follow at 5 PM in A Venue on the Ridge. -

-
- -
- -
- - Will you attend? - - ( - - } - label="Yes" - /> - } - label="No" - /> - - )} - /> -
-
-
-
- - - - - { - event.currentTarget.blur(); - }} - error={!!errors.partySize} - helperText={errors.partySize?.message} - required - {...register('partySize', { - onChange: handleParty, - 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', - }, - })} - /> - - - - - {fields.map((field, index) => { - return ( - - - - - - - - - ); - })} - -
- -
-
-
-
- ); -} - -export default RsvpForm; diff --git a/src/components/Schedule.tsx b/src/components/Schedule.tsx deleted file mode 100644 index 3ebe446..0000000 --- a/src/components/Schedule.tsx +++ /dev/null @@ -1,107 +0,0 @@ -import React from 'react'; -import { Container, Paper, Typography, useTheme } from '@mui/material'; - -function Schedule() { - const theme = useTheme(); - return ( - - -
-
-
-

April 26, 2025

-
-
- Wedding Schedule -
-
-
-
-
-

2:00 PM

-
-
- Ceremony -

- Divine Shepherd -
- - 15005 Q St, Omaha, NE 68137 - -

-
-
-
-
-
-

4:00 PM

-
-
- Reception -

- A Venue on the Ridge -
- - 20033 Elkhorn Ridge Dr, Elkhorn, NE 68022 - -

-
-
-
-
-
- ); -} - -export default Schedule; diff --git a/src/components/active.css b/src/components/active.css deleted file mode 100644 index e8b5f60..0000000 --- a/src/components/active.css +++ /dev/null @@ -1,7 +0,0 @@ -.active { - background-color: #1976d2 -} - -.inactive { - background-color: #c4c4c4 -} diff --git a/src/features/auth/GuestLogin.tsx b/src/features/auth/GuestLogin.tsx deleted file mode 100644 index 4da7e45..0000000 --- a/src/features/auth/GuestLogin.tsx +++ /dev/null @@ -1,79 +0,0 @@ -import React from 'react'; -import { useNavigate } from 'react-router-dom'; -import { useDispatch } from 'react-redux'; -import { Button, Container, TextField, Typography } from '@mui/material'; -import { useForm } from 'react-hook-form'; -import { setCredentials } from './authSlice'; -import { useLoginMutation } from '../../apiSlice'; -import type { LoginRequest } from '../../apiSlice'; - -function GuestLogin() { - const dispatch = useDispatch(); - const navigate = useNavigate(); - const [login] = useLoginMutation(); - - const { - register, - handleSubmit, - formState: { errors }, - } = useForm({ - defaultValues: { - firstName: '', - lastName: '', - }, - }); - - const onSubmit = async (data: LoginRequest) => { - try { - dispatch(setCredentials(await login(data).unwrap())); - navigate('/rsvp'); - } catch (e) { - console.log(e); - } - }; - - return ( - -
- Guest Login - - - -
-
- ); -} - -export default GuestLogin; diff --git a/src/features/auth/authSlice.ts b/src/features/auth/authSlice.ts deleted file mode 100644 index bff2bdd..0000000 --- a/src/features/auth/authSlice.ts +++ /dev/null @@ -1,26 +0,0 @@ -import { createSlice } from '@reduxjs/toolkit'; -import type { RootState } from '../../store'; -import type { Guest } from '../../apiSlice'; - -type AuthState = { - guest?: Guest; - token?: string; -}; - -const authSlice = createSlice({ - name: 'auth', - initialState: { guest: undefined, token: undefined } as AuthState, - reducers: { - setCredentials: (state, action) => { - const { guest, token } = action.payload; - state.guest = guest; - state.token = token; - }, - }, -}); - -export const { setCredentials } = authSlice.actions; - -export default authSlice.reducer; - -export const selectCurrentGuest = (state: RootState) => state.auth.guest; diff --git a/src/main.css b/src/main.css deleted file mode 100644 index 5cb7cd6..0000000 --- a/src/main.css +++ /dev/null @@ -1,4 +0,0 @@ -#root, body { - height: 100vh; - margin: 0 -} diff --git a/src/main.tsx b/src/main.tsx deleted file mode 100644 index 8ada188..0000000 --- a/src/main.tsx +++ /dev/null @@ -1,69 +0,0 @@ -import React from 'react'; -import ReactDOM from 'react-dom/client'; -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 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'; -import './main.css'; - -const router = createBrowserRouter([ - { - element: , - children: [ - { - path: '/', - element: , - }, - { - path: 'schedule', - element: , - }, - { - path: 'registry', - element: , - }, - { - path: 'guest-login', - element: , - }, - { - path: 'admin', - element: , - }, - ], - }, - { - element: , - children: [ - { - path: 'rsvp', - element: , - }, - ], - }, -]); - -const enableMocking = async () => { - const { worker } = await import('./mocks/browser'); - return worker.start(); -}; - -enableMocking().then(() => { - ReactDOM.createRoot(document.getElementById('root')!).render( - - - - - - - - ); -}); diff --git a/src/mocks/browser.ts b/src/mocks/browser.ts deleted file mode 100644 index 0a56427..0000000 --- a/src/mocks/browser.ts +++ /dev/null @@ -1,4 +0,0 @@ -import { setupWorker } from 'msw/browser'; -import { handlers } from './handlers'; - -export const worker = setupWorker(...handlers); diff --git a/src/mocks/handlers.ts b/src/mocks/handlers.ts deleted file mode 100644 index e3569df..0000000 --- a/src/mocks/handlers.ts +++ /dev/null @@ -1,32 +0,0 @@ -import { http, HttpResponse } from 'msw'; -import { nanoid } from '@reduxjs/toolkit'; - -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({ - id: 1, - firstName: 'Michael', - lastName: 'Hunteman', - attendance: 'true', - meal: 'beef', - email: '', - message: '', - }); - }), -]; diff --git a/src/pages.ts b/src/pages.ts deleted file mode 100644 index bad57f6..0000000 --- a/src/pages.ts +++ /dev/null @@ -1,8 +0,0 @@ -const pages = [ - { name: 'Home', to: '/' }, - { name: 'Schedule', to: '/schedule' }, - { name: 'RSVP', to: '/guest-login' }, - { name: 'Registry', to: '/registry' }, -]; - -export default pages; diff --git a/src/store.ts b/src/store.ts deleted file mode 100644 index 264639e..0000000 --- a/src/store.ts +++ /dev/null @@ -1,16 +0,0 @@ -import { configureStore } from '@reduxjs/toolkit'; -import { apiSlice } from './apiSlice'; -import authReducer from './features/auth/authSlice'; - -const store = configureStore({ - reducer: { - [apiSlice.reducerPath]: apiSlice.reducer, - auth: authReducer, - }, - middleware: (getDefaultMiddleware) => - getDefaultMiddleware().concat(apiSlice.middleware), -}); - -export default store; -export type RootState = ReturnType; -export type AppDispatch = typeof store.dispatch; diff --git a/src/vite-env.d.ts b/src/vite-env.d.ts deleted file mode 100644 index 11f02fe..0000000 --- a/src/vite-env.d.ts +++ /dev/null @@ -1 +0,0 @@ -/// -- cgit v1.2.3