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 --- client/src/components/Admin.tsx | 31 +++++ client/src/components/Desktop.tsx | 29 +++++ client/src/components/Home.tsx | 73 +++++++++++ client/src/components/Mobile.tsx | 55 +++++++++ client/src/components/NavBar.tsx | 29 +++++ client/src/components/Registry.tsx | 11 ++ client/src/components/Rsvp.tsx | 29 +++++ client/src/components/RsvpForm.tsx | 242 +++++++++++++++++++++++++++++++++++++ client/src/components/Schedule.tsx | 107 ++++++++++++++++ client/src/components/active.css | 7 ++ 10 files changed, 613 insertions(+) create mode 100644 client/src/components/Admin.tsx create mode 100644 client/src/components/Desktop.tsx create mode 100644 client/src/components/Home.tsx create mode 100644 client/src/components/Mobile.tsx create mode 100644 client/src/components/NavBar.tsx create mode 100644 client/src/components/Registry.tsx create mode 100644 client/src/components/Rsvp.tsx create mode 100644 client/src/components/RsvpForm.tsx create mode 100644 client/src/components/Schedule.tsx create mode 100644 client/src/components/active.css (limited to 'client/src/components') diff --git a/client/src/components/Admin.tsx b/client/src/components/Admin.tsx new file mode 100644 index 0000000..1c941a5 --- /dev/null +++ b/client/src/components/Admin.tsx @@ -0,0 +1,31 @@ +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/client/src/components/Desktop.tsx b/client/src/components/Desktop.tsx new file mode 100644 index 0000000..13de4ee --- /dev/null +++ b/client/src/components/Desktop.tsx @@ -0,0 +1,29 @@ +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/client/src/components/Home.tsx b/client/src/components/Home.tsx new file mode 100644 index 0000000..839667a --- /dev/null +++ b/client/src/components/Home.tsx @@ -0,0 +1,73 @@ +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/client/src/components/Mobile.tsx b/client/src/components/Mobile.tsx new file mode 100644 index 0000000..2e7b15c --- /dev/null +++ b/client/src/components/Mobile.tsx @@ -0,0 +1,55 @@ +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/client/src/components/NavBar.tsx b/client/src/components/NavBar.tsx new file mode 100644 index 0000000..52e8e6c --- /dev/null +++ b/client/src/components/NavBar.tsx @@ -0,0 +1,29 @@ +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/client/src/components/Registry.tsx b/client/src/components/Registry.tsx new file mode 100644 index 0000000..60a73f9 --- /dev/null +++ b/client/src/components/Registry.tsx @@ -0,0 +1,11 @@ +import React from 'react'; + +function Registry() { + return ( + <> +

Registry

+ + ); +} + +export default Registry; diff --git a/client/src/components/Rsvp.tsx b/client/src/components/Rsvp.tsx new file mode 100644 index 0000000..dad7213 --- /dev/null +++ b/client/src/components/Rsvp.tsx @@ -0,0 +1,29 @@ +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/client/src/components/RsvpForm.tsx b/client/src/components/RsvpForm.tsx new file mode 100644 index 0000000..71db0d8 --- /dev/null +++ b/client/src/components/RsvpForm.tsx @@ -0,0 +1,242 @@ +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/client/src/components/Schedule.tsx b/client/src/components/Schedule.tsx new file mode 100644 index 0000000..808499c --- /dev/null +++ b/client/src/components/Schedule.tsx @@ -0,0 +1,107 @@ +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/client/src/components/active.css b/client/src/components/active.css new file mode 100644 index 0000000..e8b5f60 --- /dev/null +++ b/client/src/components/active.css @@ -0,0 +1,7 @@ +.active { + background-color: #1976d2 +} + +.inactive { + background-color: #c4c4c4 +} -- cgit v1.2.3