Merge backend + frontend #1
@ -24,6 +24,10 @@ import { AuthContextProvider } from "./AuthContext";
|
|||||||
// React router
|
// React router
|
||||||
import Router from "./Router";
|
import Router from "./Router";
|
||||||
|
|
||||||
|
// Toasts
|
||||||
|
import Toasts from "./components/Toasts";
|
||||||
|
import { ToastContextProvider } from "./ToastContext";
|
||||||
|
|
||||||
const queryClient = new QueryClient();
|
const queryClient = new QueryClient();
|
||||||
|
|
||||||
const App = () => {
|
const App = () => {
|
||||||
@ -35,9 +39,12 @@ const App = () => {
|
|||||||
<title>Todo</title>
|
<title>Todo</title>
|
||||||
</Helmet>
|
</Helmet>
|
||||||
<ThemeProvider>
|
<ThemeProvider>
|
||||||
|
<ToastContextProvider>
|
||||||
<Container maxWidth="md">
|
<Container maxWidth="md">
|
||||||
|
<Toasts />
|
||||||
<Router />
|
<Router />
|
||||||
</Container>
|
</Container>
|
||||||
|
</ToastContextProvider>
|
||||||
</ThemeProvider>
|
</ThemeProvider>
|
||||||
</HelmetProvider>
|
</HelmetProvider>
|
||||||
</AuthContextProvider>
|
</AuthContextProvider>
|
||||||
|
49
frontend/src/ToastContext.tsx
Normal file
49
frontend/src/ToastContext.tsx
Normal file
@ -0,0 +1,49 @@
|
|||||||
|
import { AlertColor } from "@mui/material/Alert";
|
||||||
|
|
||||||
|
import React, { createContext, useState } from "react";
|
||||||
|
|
||||||
|
export interface IToast {
|
||||||
|
category?: AlertColor;
|
||||||
|
key: number;
|
||||||
|
message: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface IToastContext {
|
||||||
|
addToast: (message: string, category: AlertColor | undefined) => void;
|
||||||
|
setToasts: React.Dispatch<React.SetStateAction<IToast[]>>;
|
||||||
|
toasts: IToast[];
|
||||||
|
}
|
||||||
|
|
||||||
|
export const ToastContext = createContext<IToastContext>({
|
||||||
|
addToast: () => {},
|
||||||
|
setToasts: () => {},
|
||||||
|
toasts: [],
|
||||||
|
});
|
||||||
|
|
||||||
|
interface IProps {
|
||||||
|
children?: React.ReactNode;
|
||||||
|
}
|
||||||
|
|
||||||
|
export const ToastContextProvider = ({ children }: IProps) => {
|
||||||
|
const [toasts, setToasts] = useState<IToast[]>([]);
|
||||||
|
|
||||||
|
const addToast = (
|
||||||
|
message: string,
|
||||||
|
category: AlertColor | undefined = undefined,
|
||||||
|
) => {
|
||||||
|
setToasts((prev) => [
|
||||||
|
...prev,
|
||||||
|
{
|
||||||
|
category,
|
||||||
|
key: new Date().getTime(),
|
||||||
|
message,
|
||||||
|
},
|
||||||
|
]);
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<ToastContext.Provider value={{ addToast, setToasts, toasts }}>
|
||||||
|
{children}
|
||||||
|
</ToastContext.Provider>
|
||||||
|
);
|
||||||
|
};
|
@ -8,7 +8,7 @@ interface IProps {
|
|||||||
const Title = ({ title }: IProps) => (
|
const Title = ({ title }: IProps) => (
|
||||||
<>
|
<>
|
||||||
<Helmet>
|
<Helmet>
|
||||||
<title>Tozo | {title}</title>
|
<title>Todo | {title}</title>
|
||||||
</Helmet>
|
</Helmet>
|
||||||
<Typography component="h1" variant="h5">
|
<Typography component="h1" variant="h5">
|
||||||
{title}
|
{title}
|
||||||
|
47
frontend/src/components/Toasts.tsx
Normal file
47
frontend/src/components/Toasts.tsx
Normal file
@ -0,0 +1,47 @@
|
|||||||
|
import Alert from "@mui/material/Alert";
|
||||||
|
import Snackbar from "@mui/material/Snackbar";
|
||||||
|
import React, { useContext, useEffect, useState } from "react";
|
||||||
|
|
||||||
|
import { ToastContext, IToast } from "src/ToastContext";
|
||||||
|
|
||||||
|
const Toasts = () => {
|
||||||
|
const { toasts, setToasts } = useContext(ToastContext);
|
||||||
|
const [open, setOpen] = useState(false);
|
||||||
|
const [currentToast, setCurrentToast] = useState<IToast | undefined>();
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (!open && toasts.length) {
|
||||||
|
setCurrentToast(toasts[0]);
|
||||||
|
setToasts((prev) => prev.slice(1));
|
||||||
|
setOpen(true);
|
||||||
|
}
|
||||||
|
}, [open, setCurrentToast, setOpen, setToasts, toasts]);
|
||||||
|
|
||||||
|
const onClose = (event?: React.SyntheticEvent | Event, reason?: string) => {
|
||||||
|
if (reason !== "clickaway") {
|
||||||
|
setOpen(false);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Snackbar
|
||||||
|
anchorOrigin={{
|
||||||
|
horizontal: "center",
|
||||||
|
vertical: "top",
|
||||||
|
}}
|
||||||
|
autoHideDuration={6000}
|
||||||
|
key={currentToast?.key}
|
||||||
|
onClose={onClose}
|
||||||
|
open={open}
|
||||||
|
TransitionProps={{
|
||||||
|
onExited: () => setCurrentToast(undefined),
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<Alert onClose={onClose} severity={currentToast?.category}>
|
||||||
|
{currentToast?.message}
|
||||||
|
</Alert>
|
||||||
|
</Snackbar>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default Toasts;
|
Loading…
x
Reference in New Issue
Block a user