summaryrefslogtreecommitdiff
path: root/client
diff options
context:
space:
mode:
authorMichael Hunteman <michael@huntm.net>2024-09-02 16:27:12 -0700
committerMichael Hunteman <michael@huntm.net>2024-09-02 16:27:12 -0700
commit46f925663e595fa8dd87f7aa15a0061abcfae3d7 (patch)
treef753fec000afa643353db582587d524e77afb8f3 /client
parent4d920cc13f1a3fa0d3551e78a5a8f0ff8b05046e (diff)
Add ui error messages
Diffstat (limited to 'client')
-rw-r--r--client/src/components/AdminLogin.tsx41
-rw-r--r--client/src/components/GuestLogin.tsx41
-rw-r--r--client/src/components/RsvpForm.tsx27
-rw-r--r--client/src/error.ts11
-rw-r--r--client/src/models.ts9
5 files changed, 107 insertions, 22 deletions
diff --git a/client/src/components/AdminLogin.tsx b/client/src/components/AdminLogin.tsx
index 4f53566..4271d60 100644
--- a/client/src/components/AdminLogin.tsx
+++ b/client/src/components/AdminLogin.tsx
@@ -1,16 +1,38 @@
-import React from 'react';
+import React, { useState } from 'react';
import { useNavigate } from 'react-router-dom';
import { useDispatch } from 'react-redux';
-import { Button, Paper, TextField, Typography } from '@mui/material';
+import {
+ Alert,
+ Button,
+ Paper,
+ Snackbar,
+ TextField,
+ Typography,
+} from '@mui/material';
import { useForm } from 'react-hook-form';
import { setAdmin } from '../slices/auth/adminSlice';
import { useLoginAdminMutation } from '../slices/api/adminSlice';
-import type { Credentials } from '../models';
+import type { Credentials, StatusProps } from '../models';
+import { isFetchBaseQueryError } from '../error';
+import type { Data } from '../error';
+
+const Status = ({ error, setOpen }: StatusProps) => {
+ return isFetchBaseQueryError(error) ? (
+ <Alert severity="error" onClose={() => setOpen(false)}>
+ {(error.data as Data).message}
+ </Alert>
+ ) : (
+ <Alert severity="error" onClose={() => setOpen(false)}>
+ Admin login failed
+ </Alert>
+ );
+};
function GuestLogin() {
const dispatch = useDispatch();
const navigate = useNavigate();
- const [login] = useLoginAdminMutation();
+ const [login, { isLoading, error }] = useLoginAdminMutation();
+ const [open, setOpen] = useState<boolean>(false);
const {
register,
@@ -28,7 +50,7 @@ function GuestLogin() {
dispatch(setAdmin(await login(data).unwrap()));
navigate('/dashboard');
} catch (e) {
- console.log(e);
+ setOpen(true);
}
};
@@ -83,6 +105,15 @@ function GuestLogin() {
Log in
</Button>
</Paper>
+ <Snackbar
+ open={!isLoading && open}
+ onClose={() => setOpen(false)}
+ autoHideDuration={5000}
+ >
+ <div>
+ <Status {...({ error, setOpen } as StatusProps)} />
+ </div>
+ </Snackbar>
</form>
);
}
diff --git a/client/src/components/GuestLogin.tsx b/client/src/components/GuestLogin.tsx
index f42c1d6..acf229f 100644
--- a/client/src/components/GuestLogin.tsx
+++ b/client/src/components/GuestLogin.tsx
@@ -1,16 +1,38 @@
-import React from 'react';
+import React, { useState } from 'react';
import { useNavigate } from 'react-router-dom';
import { useDispatch } from 'react-redux';
-import { Button, Paper, TextField, Typography } from '@mui/material';
+import {
+ Alert,
+ Button,
+ Paper,
+ Snackbar,
+ TextField,
+ Typography,
+} from '@mui/material';
import { useForm } from 'react-hook-form';
import { setGuest } from '../slices/auth/guestSlice';
import { useLoginGuestMutation } from '../slices/api/guestSlice';
-import type { Name } from '../models';
+import type { Name, StatusProps } from '../models';
+import { isFetchBaseQueryError } from '../error';
+import type { Data } from '../error';
+
+const Status = ({ error, setOpen }: StatusProps) => {
+ return isFetchBaseQueryError(error) ? (
+ <Alert severity="error" onClose={() => setOpen(false)}>
+ {(error.data as Data).message}
+ </Alert>
+ ) : (
+ <Alert severity="error" onClose={() => setOpen(false)}>
+ Guest login failed
+ </Alert>
+ );
+};
function GuestLogin() {
const dispatch = useDispatch();
const navigate = useNavigate();
- const [login] = useLoginGuestMutation();
+ const [login, { isLoading, error }] = useLoginGuestMutation();
+ const [open, setOpen] = useState<boolean>(false);
const {
register,
@@ -28,7 +50,7 @@ function GuestLogin() {
dispatch(setGuest(await login(data).unwrap()));
navigate('/rsvp');
} catch (e) {
- console.log(e);
+ setOpen(true);
}
};
@@ -83,6 +105,15 @@ function GuestLogin() {
Log in
</Button>
</Paper>
+ <Snackbar
+ open={!isLoading && open}
+ onClose={() => setOpen(false)}
+ autoHideDuration={5000}
+ >
+ <div>
+ <Status {...({ error, setOpen } as StatusProps)} />
+ </div>
+ </Snackbar>
</form>
);
}
diff --git a/client/src/components/RsvpForm.tsx b/client/src/components/RsvpForm.tsx
index 33ca108..7b2892f 100644
--- a/client/src/components/RsvpForm.tsx
+++ b/client/src/components/RsvpForm.tsx
@@ -17,18 +17,21 @@ import {
import MailIcon from '@mui/icons-material/Mail';
import { useForm, Controller, useFieldArray } from 'react-hook-form';
import { useUpdateGuestMutation } from '../slices/api/guestSlice';
-import type { Guest } from '../models';
+import type { Guest, StatusProps } from '../models';
+import { isFetchBaseQueryError } from '../error';
+import type { Data } from '../error';
-interface StatusProps {
- isError: boolean;
- setOpen: (open: boolean) => void;
-}
-
-const Status = ({ isError, setOpen }: StatusProps) => {
+const Status = ({ isError, error, setOpen }: StatusProps) => {
return isError ? (
- <Alert severity="error" onClose={() => setOpen(false)}>
- RSVP failed
- </Alert>
+ isFetchBaseQueryError(error) ? (
+ <Alert severity="error" onClose={() => setOpen(false)}>
+ {(error.data as Data).message}
+ </Alert>
+ ) : (
+ <Alert severity="error" onClose={() => setOpen(false)}>
+ RSVP failed
+ </Alert>
+ )
) : (
<Alert severity="success" onClose={() => setOpen(false)}>
RSVP updated
@@ -37,7 +40,7 @@ const Status = ({ isError, setOpen }: StatusProps) => {
};
function RsvpForm() {
- const [updateGuest, { isLoading, isSuccess, isError }] =
+ const [updateGuest, { isLoading, isSuccess, isError, error }] =
useUpdateGuestMutation();
const guest: Guest = useOutletContext();
const previousPartySize = useRef((guest.partySize ?? 1) - 1);
@@ -278,7 +281,7 @@ function RsvpForm() {
autoHideDuration={5000}
>
<div>
- <Status isError={isError} setOpen={setOpen} />
+ <Status {...({ isError, error, setOpen } as StatusProps)} />
</div>
</Snackbar>
</Grid>
diff --git a/client/src/error.ts b/client/src/error.ts
new file mode 100644
index 0000000..8935e23
--- /dev/null
+++ b/client/src/error.ts
@@ -0,0 +1,11 @@
+import type { FetchBaseQueryError } from '@reduxjs/toolkit/query/react';
+
+export const isFetchBaseQueryError = (
+ error: any
+): error is FetchBaseQueryError => {
+ return 'data' in error && 'message' in error.data;
+};
+
+export interface Data {
+ message: string;
+}
diff --git a/client/src/models.ts b/client/src/models.ts
index 6840a46..5e53484 100644
--- a/client/src/models.ts
+++ b/client/src/models.ts
@@ -1,3 +1,6 @@
+import { SerializedError } from '@reduxjs/toolkit';
+import { FetchBaseQueryError } from '@reduxjs/toolkit/query';
+
export interface Guest {
id?: number;
firstName: string;
@@ -28,3 +31,9 @@ export interface AdminLogin {
guests: Guest[];
token: string;
}
+
+export interface StatusProps {
+ isError?: boolean;
+ error: FetchBaseQueryError | SerializedError | undefined;
+ setOpen: (open: boolean) => void;
+}