diff options
author | Michael Hunteman <michael@huntm.net> | 2024-02-21 19:43:07 -0600 |
---|---|---|
committer | Michael Hunteman <michael@huntm.net> | 2024-02-21 19:43:07 -0600 |
commit | 589e53f152d7363074049dfd1bd5a34286ae74d6 (patch) | |
tree | 187b98b25f7f3420ad2642171fd7d91a68713211 /src | |
parent | 32e0cacde5a468d8982d5c0d1fd7a242820cb60f (diff) |
Update example guests.json with RTK query
Diffstat (limited to 'src')
-rw-r--r-- | src/App.tsx | 8 | ||||
-rw-r--r-- | src/ThemeContextProvider.tsx | 22 | ||||
-rw-r--r-- | src/apiSlice.ts | 26 | ||||
-rw-r--r-- | src/components/Admin.tsx | 35 | ||||
-rw-r--r-- | src/components/GuestLogin.tsx | 18 | ||||
-rw-r--r-- | src/components/NavBar.tsx | 13 | ||||
-rw-r--r-- | src/components/Registry.tsx | 2 | ||||
-rw-r--r-- | src/components/Rsvp.tsx | 118 | ||||
-rw-r--r-- | src/components/RsvpForm.tsx | 109 | ||||
-rw-r--r-- | src/main.tsx | 21 |
10 files changed, 246 insertions, 126 deletions
diff --git a/src/App.tsx b/src/App.tsx index 073b9d6..c0ea612 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -1,18 +1,16 @@ -import { createContext, useState, useMemo } from 'react'; import { Outlet } from 'react-router-dom'; import CssBaseline from '@mui/material/CssBaseline'; import NavBar from './components/NavBar'; -import ThemeContextProvider from './ThemeContextProvider' function App() { return ( - <ThemeContextProvider> + <> <CssBaseline /> <NavBar /> <Outlet /> - </ThemeContextProvider> - ); + </> + ) } export default App; diff --git a/src/ThemeContextProvider.tsx b/src/ThemeContextProvider.tsx index 970f9d8..e1e928c 100644 --- a/src/ThemeContextProvider.tsx +++ b/src/ThemeContextProvider.tsx @@ -1,8 +1,8 @@ import { ReactNode, createContext, useMemo, useState } from 'react'; -import { StyledEngineProvider, ThemeProvider, createTheme } from '@mui/material/styles'; +import { ThemeProvider, createTheme } from '@mui/material/styles'; type ThemeContextType = { - switchColorMode: () => void; + toggleColorMode: () => void; }; type ThemeProviderProps = { @@ -10,13 +10,13 @@ type ThemeProviderProps = { }; export const ThemeContext = createContext<ThemeContextType>({ - switchColorMode: () => {} + toggleColorMode: () => {} }); function ThemeContextProvider({ children }: ThemeProviderProps) { const [mode, setMode] = useState<'light' | 'dark'>('light'); - const switchColorMode = () => { + const toggleColorMode = () => { setMode((prevMode) => (prevMode === 'light' ? 'dark' : 'light')); }; @@ -31,14 +31,12 @@ function ThemeContextProvider({ children }: ThemeProviderProps) { ); return ( - <StyledEngineProvider injectFirst> - <ThemeContext.Provider value={{ switchColorMode }}> - <ThemeProvider theme={theme}> - {children} - </ThemeProvider> - </ThemeContext.Provider> - </StyledEngineProvider> + <ThemeContext.Provider value={{ toggleColorMode }}> + <ThemeProvider theme={theme}> + {children} + </ThemeProvider> + </ThemeContext.Provider> ); -} +}; export default ThemeContextProvider; diff --git a/src/apiSlice.ts b/src/apiSlice.ts new file mode 100644 index 0000000..6d779ed --- /dev/null +++ b/src/apiSlice.ts @@ -0,0 +1,26 @@ +import { createApi, fetchBaseQuery } from '@reduxjs/toolkit/query/react'; + +export const apiSlice = createApi({ + reducerPath: 'api', + baseQuery: fetchBaseQuery({ baseUrl: 'http://localhost:3000' }), + 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'] + }) + }) + }) +}); + +export const { + useGetGuestsQuery, + useUpdateGuestMutation +} = apiSlice; diff --git a/src/components/Admin.tsx b/src/components/Admin.tsx new file mode 100644 index 0000000..8f6ce12 --- /dev/null +++ b/src/components/Admin.tsx @@ -0,0 +1,35 @@ +import Paper from '@mui/material/Paper'; +import Typography from '@mui/material/Typography'; + +import { useGetGuestsQuery } from '../apiSlice' + +function Admin() { + const { + data: guests, + isLoading, + isSuccess, + isError, + error + } = useGetGuestsQuery() + + let content + + if (isLoading) { + content = <Typography variant="h4">Loading...</Typography> + } else if (isSuccess) { + content = JSON.stringify(guests) + } else if (isError) { + content = <>{error.toString()}</> + } + + return ( + <Paper> + <Typography variant="h4" component="h4"> + Admin + </Typography> + {content} + </Paper> + ) +} + +export default Admin; diff --git a/src/components/GuestLogin.tsx b/src/components/GuestLogin.tsx new file mode 100644 index 0000000..5637276 --- /dev/null +++ b/src/components/GuestLogin.tsx @@ -0,0 +1,18 @@ +import Button from '@mui/material/Button'; +import Paper from '@mui/material/Paper'; +import Typography from '@mui/material/Typography'; + +function GuestLogin({ loggedIn, setLoggedIn }) { + return ( + <Paper> + <Typography variant="h6"> + Enter your name to RSVP + </Typography> + <Button onClick={() => setLoggedIn(!loggedIn)} variant="contained"> + Login + </Button> + </Paper> + ) +} + +export default GuestLogin; diff --git a/src/components/NavBar.tsx b/src/components/NavBar.tsx index a4b46c8..2cf1b31 100644 --- a/src/components/NavBar.tsx +++ b/src/components/NavBar.tsx @@ -1,4 +1,4 @@ -import { useContext, useMemo } from 'react'; +import { useContext } from 'react'; import { Link } from 'react-router-dom'; import AppBar from '@mui/material/AppBar'; import Box from '@mui/material/Box'; @@ -15,12 +15,13 @@ import { ThemeContext } from '../ThemeContextProvider'; function NavBar({ mode }) { const theme = useTheme(); - const { switchColorMode } = useContext(ThemeContext); + const { toggleColorMode } = useContext(ThemeContext); const pages = [ { name: 'Schedule', to: '/schedule'}, { name: 'RSVP', to: '/rsvp' }, - { name: 'Registry', to: '/registry' } + { name: 'Registry', to: '/registry' }, + { name: 'Admin', to: '/admin' } ]; return ( @@ -36,18 +37,18 @@ function NavBar({ mode }) { Madison and Michael's Wedding </Typography> <Stack direction="row" sx={{ marginLeft: 'auto' }}> - {pages.map((page) => ( + {pages.map(page => ( <Button color="inherit" component={Link} to={page.to} key={page.name}> {page.name} </Button> ))} - <IconButton color="inherit" onClick={switchColorMode}> + <IconButton color="inherit" onClick={toggleColorMode}> {theme.palette.mode === 'dark' ? <DarkModeIcon /> : <LightModeIcon />} </IconButton> </Stack> </Toolbar> </AppBar> ); -} +}; export default NavBar; diff --git a/src/components/Registry.tsx b/src/components/Registry.tsx index b07d680..5856909 100644 --- a/src/components/Registry.tsx +++ b/src/components/Registry.tsx @@ -8,7 +8,7 @@ function Registry() { Registry </Typography> </Paper> - ); + ) } export default Registry; diff --git a/src/components/Rsvp.tsx b/src/components/Rsvp.tsx index 81f37fc..858ca71 100644 --- a/src/components/Rsvp.tsx +++ b/src/components/Rsvp.tsx @@ -1,107 +1,29 @@ import { useState } from 'react'; -import Button from '@mui/material/Button'; -import Paper from '@mui/material/Paper'; -import Grid from '@mui/material/Grid'; -import TextField from '@mui/material/TextField'; -import Typography from '@mui/material/Typography'; -import Radio from '@mui/material/Radio'; -import RadioGroup from '@mui/material/RadioGroup'; -import FormControlLabel from '@mui/material/FormControlLabel'; -import FormControl from '@mui/material/FormControl'; -import FormLabel from '@mui/material/FormLabel'; +import RsvpForm from './RsvpForm'; +import GuestLogin from './GuestLogin'; +import { useGetGuestsQuery } from '../apiSlice' function Rsvp() { - const [guestList, setGuestList] = useState([]); + // Enter your name to RSVP; query the database + const [loggedIn, setLoggedIn] = useState(false); - const onAddBtnClick = event => { - setGuestList(guestList.concat( - <Grid container spacing={2}> - <Grid item xs={6} md={6} lg={6}> - <TextField key={guestList.length} label="Name" variant="outlined" /> - </Grid> - <Grid item xs={6} md={6} lg={6}> - <FormControl> - <FormLabel>Meal Preference</FormLabel> - <RadioGroup> - <FormControlLabel - value="Beef" - control={<Radio />} - label="Beef" - /> - <FormControlLabel - value="Chicken" - control={<Radio />} - label="Chicken" - /> - <FormControlLabel - value="Fish" - control={<Radio />} - label="Fish" - /> - <FormControlLabel - value="Vegetarian" - control={<Radio />} - label="Vegetarian" - /> - </RadioGroup> - </FormControl> - </Grid> - </Grid> - )); - } + const { + data: guests, + isLoading, + isSuccess, + isError, + error + } = useGetGuestsQuery() return ( - <Paper> - <Grid container spacing={2}> - <Grid item xs={4} md={4} lg={4}> - <Typography>Date: April 14, 2025</Typography> - </Grid> - <Grid item xs={4} md={4} lg={4}> - <Typography>Location: </Typography> - </Grid> - <Grid item xs={4} md={4} lg={4}> - <Typography>RSVP Deadline: </Typography> - </Grid> - <Grid item xs={4} md={4} lg={4}> - <TextField required label="Name" variant="outlined" /> - </Grid> - <Grid item xs={4} md={4} lg={4}> - <FormControl> - <FormLabel>Are you attending?</FormLabel> - <RadioGroup> - <FormControlLabel value="Yes" control={<Radio />} label="Yes" /> - <FormControlLabel value="No" control={<Radio />} label="No" /> - </RadioGroup> - </FormControl> - </Grid> - <Grid item xs={4} md={4} lg={4}> - <Button - onClick={onAddBtnClick} - sx={{ maxWidth: 240 }} - variant="contained"> - Add Additional Guests - {/* TODO: only allow guests we've selected; allow kids? */} - </Button> - </Grid> - {guestList} - <Grid item xs={6} md={6} lg={6}> - <TextField - label="Dietary Restrictions" - variant="outlined" - /> - </Grid> - <Grid item xs={6} md={6} lg={6}> - <TextField - label="Song Request" - variant="outlined" - /> - </Grid> - <Grid item> - <Button sx={{ maxWidth: 80 }} variant="contained">Submit</Button> - </Grid> - </Grid> - </Paper> - ); + <> + {loggedIn ? ( + <RsvpForm /> + ) : ( + <GuestLogin loggedIn={loggedIn} setLoggedIn={setLoggedIn} /> + )} + </> + ) } export default Rsvp; diff --git a/src/components/RsvpForm.tsx b/src/components/RsvpForm.tsx new file mode 100644 index 0000000..9dbc54c --- /dev/null +++ b/src/components/RsvpForm.tsx @@ -0,0 +1,109 @@ +import { useState } from 'react'; +import Button from '@mui/material/Button'; +import Paper from '@mui/material/Paper'; +import Grid from '@mui/material/Grid'; +import TextField from '@mui/material/TextField'; +import Typography from '@mui/material/Typography'; +import Radio from '@mui/material/Radio'; +import RadioGroup from '@mui/material/RadioGroup'; +import FormControlLabel from '@mui/material/FormControlLabel'; +import FormControl from '@mui/material/FormControl'; +import FormLabel from '@mui/material/FormLabel'; + +import { useGetGuestsQuery, useUpdateGuestMutation } from '../apiSlice'; + +function RsvpForm() { + const { + data: guests, + isLoading, + isSuccess, + isError, + error + } = useGetGuestsQuery() + + const [updateGuest] = useUpdateGuestMutation() + + const handleSubmit = (e) => { + e.preventDefault() + console.log('handle') + let guest = guests[0] + if (guest.attendance === 'true') { + updateGuest({...guest, attendance: 'false'}) + } else { + updateGuest({...guest, attendance: 'true'}) + } + } + + return ( + <Paper> + <Grid container spacing={2}> + <Grid item xs={12} md={4} lg={4}> + <Typography>Date: April 14, 2025</Typography> + </Grid> + <Grid item xs={12} md={4} lg={4}> + <Typography>Location: </Typography> + </Grid> + <Grid item xs={12} md={4} lg={4}> + <Typography>RSVP Deadline: </Typography> + </Grid> + <Grid item xs={12} md={4} lg={4}> + <TextField required label="Name" variant="outlined" /> + </Grid> + <Grid item xs={12} md={4} lg={4}> + <FormControl> + <FormLabel>Will you attend our wedding?</FormLabel> + <RadioGroup> + <FormControlLabel value="Yes" control={<Radio />} label="Yes" /> + <FormControlLabel value="No" control={<Radio />} label="No" /> + </RadioGroup> + </FormControl> + </Grid> + <Grid item xs={12} md={4} lg={4}> + <TextField required label="Plus-One" variant="outlined" /> + </Grid> + <Grid item xs={12} md={4} lg={4}> + <FormControl> + <FormLabel>Meal Preference</FormLabel> + <FormControlLabel + value="Beef" + control={<Radio />} + label="Beef" + /> + <FormControlLabel + value="Chicken" + control={<Radio />} + label="Chicken" + /> + <FormControlLabel + value="Fish" + control={<Radio />} + label="Fish" + /> + <FormControlLabel + value="Vegetarian" + control={<Radio />} + label="Vegetarian" + /> + </FormControl> + </Grid> + <Grid item xs={12} md={4} lg={4}> + <TextField + label="Dietary Restrictions" + variant="outlined" + /> + </Grid> + <Grid item xs={12} md={4} lg={4}> + <TextField + label="Advice" + variant="outlined" + /> + </Grid> + <Grid item> + <Button onClick={handleSubmit} variant="contained">Submit</Button> + </Grid> + </Grid> + </Paper> + ) +} + +export default RsvpForm; diff --git a/src/main.tsx b/src/main.tsx index 182761d..ecc4803 100644 --- a/src/main.tsx +++ b/src/main.tsx @@ -1,11 +1,16 @@ import React from 'react'; import ReactDOM from 'react-dom/client'; +import { render } from 'react-dom'; import { createBrowserRouter, RouterProvider } from 'react-router-dom'; +import { ApiProvider } from '@reduxjs/toolkit/query/react'; import App from './App'; +import { apiSlice } from './apiSlice'; +import ThemeContextProvider from './ThemeContextProvider' import Schedule from './components/Schedule'; import Registry from './components/Registry'; import Rsvp from './components/Rsvp'; +import Admin from './components/Admin'; const router = createBrowserRouter([ { @@ -23,13 +28,21 @@ const router = createBrowserRouter([ { path: "rsvp", element: <Rsvp /> + }, + { + path: "admin", + element: <Admin /> } ] } -]); +]) -ReactDOM.createRoot(document.getElementById('root')).render( +ReactDOM.createRoot(document.getElementById('root')!).render( <React.StrictMode> - <RouterProvider router={router} /> + <ApiProvider api={apiSlice}> + <ThemeContextProvider> + <RouterProvider router={router} /> + </ThemeContextProvider> + </ApiProvider> </React.StrictMode> -); +) |