From a6b1fa31e890b51b043c4211b14cd4be29ed6fba Mon Sep 17 00:00:00 2001 From: Michael Hunteman Date: Tue, 9 Jul 2024 18:24:15 -0700 Subject: Add success and error alerts --- client/src/apiSlice.ts | 67 ---------------------------- client/src/components/Admin.tsx | 2 +- client/src/components/GuestLogin.tsx | 79 +++++++++++++++++++++++++++++++++ client/src/components/Rsvp.tsx | 2 +- client/src/components/RsvpForm.tsx | 23 +++++++--- client/src/features/auth/GuestLogin.tsx | 79 --------------------------------- client/src/features/auth/authSlice.ts | 31 ------------- client/src/main.tsx | 2 +- client/src/slices/apiSlice.ts | 67 ++++++++++++++++++++++++++++ client/src/slices/authSlice.ts | 31 +++++++++++++ client/src/store.ts | 4 +- 11 files changed, 199 insertions(+), 188 deletions(-) delete mode 100644 client/src/apiSlice.ts create mode 100644 client/src/components/GuestLogin.tsx delete mode 100644 client/src/features/auth/GuestLogin.tsx delete mode 100644 client/src/features/auth/authSlice.ts create mode 100644 client/src/slices/apiSlice.ts create mode 100644 client/src/slices/authSlice.ts diff --git a/client/src/apiSlice.ts b/client/src/apiSlice.ts deleted file mode 100644 index 038615f..0000000 --- a/client/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: 'http://192.168.1.41:8080/', - prepareHeaders: (headers, { getState }) => { - const token = (getState() as RootState).auth.token; - if (token) { - headers.set('authorization', `${token}`); - } - return headers; - }, - }), - tagTypes: ['Guest'], - endpoints: (builder) => ({ - getGuests: builder.query({ - query: () => 'guest', - providesTags: ['Guest'], - }), - updateGuest: builder.mutation({ - query: (guest) => ({ - url: `guest/${guest?.id}`, - method: 'PUT', - body: guest, - providesTags: ['Guest'], - }), - }), - login: builder.mutation({ - query: (credentials) => ({ - url: 'guest/login', - method: 'POST', - body: credentials, - }), - }), - }), -}); - -export const { useGetGuestsQuery, useUpdateGuestMutation, useLoginMutation } = - apiSlice; diff --git a/client/src/components/Admin.tsx b/client/src/components/Admin.tsx index 1c941a5..f1845b4 100644 --- a/client/src/components/Admin.tsx +++ b/client/src/components/Admin.tsx @@ -1,5 +1,5 @@ import React from 'react'; -import { useGetGuestsQuery } from '../apiSlice'; +import { useGetGuestsQuery } from '../slices/apiSlice'; function Admin() { const { diff --git a/client/src/components/GuestLogin.tsx b/client/src/components/GuestLogin.tsx new file mode 100644 index 0000000..c06bc6e --- /dev/null +++ b/client/src/components/GuestLogin.tsx @@ -0,0 +1,79 @@ +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 '../slices/authSlice'; +import { useLoginMutation } from '../slices/apiSlice'; +import type { LoginRequest } from '../slices/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/client/src/components/Rsvp.tsx b/client/src/components/Rsvp.tsx index d908f75..d3d9677 100644 --- a/client/src/components/Rsvp.tsx +++ b/client/src/components/Rsvp.tsx @@ -4,7 +4,7 @@ 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'; +import { selectCurrentGuest } from '../slices/authSlice'; const authenticate = () => { const guest = useSelector(selectCurrentGuest); diff --git a/client/src/components/RsvpForm.tsx b/client/src/components/RsvpForm.tsx index f20d810..d981053 100644 --- a/client/src/components/RsvpForm.tsx +++ b/client/src/components/RsvpForm.tsx @@ -2,6 +2,7 @@ import React from 'react'; import { useRef } from 'react'; import { useNavigate, useOutletContext } from 'react-router-dom'; import { + Alert, Button, Container, FormControl, @@ -13,11 +14,12 @@ import { TextField, } from '@mui/material'; import { useForm, Controller, useFieldArray } from 'react-hook-form'; -import { useUpdateGuestMutation } from '../apiSlice'; -import type { Guest } from '../apiSlice'; +import { useUpdateGuestMutation } from '../slices/apiSlice'; +import type { Guest } from '../slices/apiSlice'; function RsvpForm() { - const [updateGuest] = useUpdateGuestMutation(); + const [updateGuest, { isSuccess: success, isError: error }] = + useUpdateGuestMutation(); const guest: Guest = useOutletContext(); const previousPartySize = useRef(guest?.partySize - 1); const navigate = useNavigate(); @@ -43,7 +45,6 @@ function RsvpForm() { const onSubmit = async (data: Guest) => { updateGuest({ ...data }); - navigate('/guest/login'); }; const { fields, append, remove } = useFieldArray({ @@ -120,7 +121,7 @@ function RsvpForm() { - + - + +
+
+ {success && RSVP updated} + {error && RSVP failed} +
+
diff --git a/client/src/features/auth/GuestLogin.tsx b/client/src/features/auth/GuestLogin.tsx deleted file mode 100644 index 4da7e45..0000000 --- a/client/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/client/src/features/auth/authSlice.ts b/client/src/features/auth/authSlice.ts deleted file mode 100644 index 878de0c..0000000 --- a/client/src/features/auth/authSlice.ts +++ /dev/null @@ -1,31 +0,0 @@ -import { createSlice } from '@reduxjs/toolkit'; -import type { PayloadAction } 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, - { - payload: { guest, token }, - }: PayloadAction<{ guest: Guest; token: string }> - ) => { - 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/client/src/main.tsx b/client/src/main.tsx index 45ff68b..2477c37 100644 --- a/client/src/main.tsx +++ b/client/src/main.tsx @@ -7,7 +7,7 @@ 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 GuestLogin from './components/GuestLogin'; import Rsvp from './components/Rsvp'; import RsvpForm from './components/RsvpForm'; import Admin from './components/Admin'; diff --git a/client/src/slices/apiSlice.ts b/client/src/slices/apiSlice.ts new file mode 100644 index 0000000..90cdc48 --- /dev/null +++ b/client/src/slices/apiSlice.ts @@ -0,0 +1,67 @@ +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: 'http://192.168.1.41:8080/', + prepareHeaders: (headers, { getState }) => { + const token = (getState() as RootState).auth.token; + if (token) { + headers.set('authorization', `${token}`); + } + return headers; + }, + }), + tagTypes: ['Guest'], + endpoints: (builder) => ({ + getGuests: builder.query({ + query: () => 'guest', + providesTags: ['Guest'], + }), + updateGuest: builder.mutation({ + query: (guest) => ({ + url: `guest/${guest?.id}`, + method: 'PUT', + body: guest, + providesTags: ['Guest'], + }), + }), + login: builder.mutation({ + query: (credentials) => ({ + url: 'guest/login', + method: 'POST', + body: credentials, + }), + }), + }), +}); + +export const { useGetGuestsQuery, useUpdateGuestMutation, useLoginMutation } = + apiSlice; diff --git a/client/src/slices/authSlice.ts b/client/src/slices/authSlice.ts new file mode 100644 index 0000000..e1fec78 --- /dev/null +++ b/client/src/slices/authSlice.ts @@ -0,0 +1,31 @@ +import { createSlice } from '@reduxjs/toolkit'; +import type { PayloadAction } 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, + { + payload: { guest, token }, + }: PayloadAction<{ guest: Guest; token: string }> + ) => { + 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/client/src/store.ts b/client/src/store.ts index 264639e..18b3461 100644 --- a/client/src/store.ts +++ b/client/src/store.ts @@ -1,6 +1,6 @@ import { configureStore } from '@reduxjs/toolkit'; -import { apiSlice } from './apiSlice'; -import authReducer from './features/auth/authSlice'; +import { apiSlice } from './slices/apiSlice'; +import authReducer from './slices/authSlice'; const store = configureStore({ reducer: { -- cgit v1.2.3