Merge backend + frontend #1
@@ -24,6 +24,10 @@ import { AuthContextProvider } from "./AuthContext";
 | 
			
		||||
// React router
 | 
			
		||||
import Router from "./Router";
 | 
			
		||||
 | 
			
		||||
// Toasts
 | 
			
		||||
import Toasts from "./components/Toasts";
 | 
			
		||||
import { ToastContextProvider } from "./ToastContext";
 | 
			
		||||
 | 
			
		||||
const queryClient = new QueryClient();
 | 
			
		||||
 | 
			
		||||
const App = () => {
 | 
			
		||||
@@ -35,9 +39,12 @@ const App = () => {
 | 
			
		||||
            <title>Todo</title>
 | 
			
		||||
          </Helmet>
 | 
			
		||||
          <ThemeProvider>
 | 
			
		||||
            <ToastContextProvider>
 | 
			
		||||
              <Container maxWidth="md">
 | 
			
		||||
                <Toasts />
 | 
			
		||||
                <Router />
 | 
			
		||||
              </Container>
 | 
			
		||||
            </ToastContextProvider>
 | 
			
		||||
          </ThemeProvider>
 | 
			
		||||
        </HelmetProvider>
 | 
			
		||||
      </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) => (
 | 
			
		||||
  <>
 | 
			
		||||
    <Helmet>
 | 
			
		||||
      <title>Tozo | {title}</title>
 | 
			
		||||
      <title>Todo | {title}</title>
 | 
			
		||||
    </Helmet>
 | 
			
		||||
    <Typography component="h1" variant="h5">
 | 
			
		||||
      {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;
 | 
			
		||||
		Reference in New Issue
	
	Block a user