Add ToastProvider component and useToast hook; update CabinetMaintenance for toast notifications; enhance Button styles and Tailwind CSS configuration
This commit is contained in:
parent
835a3cc7fd
commit
96784c02c7
15
src/App.jsx
15
src/App.jsx
@ -1,15 +1,20 @@
|
|||||||
import { Outlet } from "react-router-dom"
|
import { Outlet } from "react-router-dom";
|
||||||
import Header from "./components/Header"
|
import Header from "./components/Header";
|
||||||
import Footer from "./components/Footer"
|
import Footer from "./components/Footer";
|
||||||
|
import { ToastProvider } from "./components/Toast";
|
||||||
|
|
||||||
function App() {
|
function App() {
|
||||||
return <div className="flex flex-col min-h-screen">
|
return (
|
||||||
|
<ToastProvider>
|
||||||
|
<div className="flex flex-col min-h-screen">
|
||||||
<Header />
|
<Header />
|
||||||
<main className="flex flex-grow transition-color-mode md:p-7 2xl:p-12 bg-surface dark:bg-surface-dark">
|
<main className="flex flex-grow transition-color-mode md:p-7 2xl:p-12 bg-surface dark:bg-surface-dark">
|
||||||
<Outlet />
|
<Outlet />
|
||||||
</main>
|
</main>
|
||||||
<Footer />
|
<Footer />
|
||||||
</div>
|
</div>
|
||||||
|
</ToastProvider>
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
export default App
|
export default App;
|
||||||
|
@ -3,7 +3,7 @@ import PropTypes from 'prop-types';
|
|||||||
function Button({text, onClick}) {
|
function Button({text, onClick}) {
|
||||||
return (
|
return (
|
||||||
<button
|
<button
|
||||||
className="px-12 py-2 text-lg text-white rounded-full bg-primary dark:bg-primary-dark"
|
className="px-12 py-2 text-lg text-white dark:text-primary-dark rounded-full bg-primary dark:bg-secondary-dark"
|
||||||
onClick={onClick}
|
onClick={onClick}
|
||||||
>
|
>
|
||||||
{text}
|
{text}
|
||||||
|
56
src/components/Toast.jsx
Normal file
56
src/components/Toast.jsx
Normal file
@ -0,0 +1,56 @@
|
|||||||
|
import { createContext, useState } from "react";
|
||||||
|
import { CircleAlert, X, CircleX, Check } from "lucide-react";
|
||||||
|
import { toTitleCase } from "../util/util";
|
||||||
|
|
||||||
|
import PropTypes from "prop-types";
|
||||||
|
|
||||||
|
export const ToastContext = createContext();
|
||||||
|
|
||||||
|
export const ToastProvider = ({ children }) => {
|
||||||
|
const [toast, setToast] = useState({ show: false, message: "", type: "" });
|
||||||
|
const showToast = (message, type) => {
|
||||||
|
setToast({ show: true, message, type });
|
||||||
|
setTimeout(() => {
|
||||||
|
setToast({ show: false, message: "", type: "" });
|
||||||
|
}, 7000);
|
||||||
|
};
|
||||||
|
let toastIcon;
|
||||||
|
let surfaceColor;
|
||||||
|
let borderColor;
|
||||||
|
if(toast.type === "warning") {
|
||||||
|
toastIcon = <CircleAlert size={30} fill="#EA7000" stroke="#FDF1E5" />;
|
||||||
|
surfaceColor = "bg-warning-surface";
|
||||||
|
borderColor = "border-warning";
|
||||||
|
} else if(toast.type === "success") {
|
||||||
|
toastIcon = <Check size={30} color="green"/>;
|
||||||
|
surfaceColor = "bg-success-surface";
|
||||||
|
borderColor = "border-success";
|
||||||
|
} else if (toast.type === "error") {
|
||||||
|
toastIcon = <CircleX size={30} fill="#E5254B" stroke="#FCE9ED" />;
|
||||||
|
surfaceColor = "bg-error-surface";
|
||||||
|
borderColor = "border-error";
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<ToastContext.Provider value={{ showToast }}>
|
||||||
|
{children}
|
||||||
|
{toast.show && (
|
||||||
|
<div
|
||||||
|
className={`fixed bottom-10 right-5 px-5 py-2 ${surfaceColor} border-2 border-l-8 ${borderColor} rounded-xl font-medium text-onToast z-10 flex gap-5 items-center animate-[slideIn_0.5s]`}
|
||||||
|
role="alert"
|
||||||
|
>
|
||||||
|
{toastIcon}
|
||||||
|
<div>
|
||||||
|
<div className="text-lg text-onToast">{toTitleCase(toast.type)}</div>
|
||||||
|
<div className="text-sm font-body">{toast.message}</div>
|
||||||
|
</div>
|
||||||
|
<X onClick={() => setToast({ show: false, message: "", type: "" })}/>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
</ToastContext.Provider>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
ToastProvider.propTypes = {
|
||||||
|
children: PropTypes.node.isRequired,
|
||||||
|
};
|
4
src/hooks/useToast.js
Normal file
4
src/hooks/useToast.js
Normal file
@ -0,0 +1,4 @@
|
|||||||
|
import { useContext } from "react";
|
||||||
|
import { ToastContext } from "../components/Toast";
|
||||||
|
|
||||||
|
export const useToast = () => useContext(ToastContext);
|
@ -2,16 +2,19 @@ import { useState } from "react";
|
|||||||
import { useNavigate } from "react-router-dom";
|
import { useNavigate } from "react-router-dom";
|
||||||
import FormBox from "../components/FormBox";
|
import FormBox from "../components/FormBox";
|
||||||
import Button from "../components/Button";
|
import Button from "../components/Button";
|
||||||
|
import { useToast } from "../hooks/useToast";
|
||||||
|
|
||||||
function CabinetMaintenace() {
|
function CabinetMaintenace() {
|
||||||
const navigate = useNavigate();
|
const navigate = useNavigate();
|
||||||
|
const { showToast } = useToast();
|
||||||
const [operation, setOperation] = useState("");
|
const [operation, setOperation] = useState("");
|
||||||
|
|
||||||
const handleNext = () => {
|
const handleNext = () => {
|
||||||
if (operation === "") {
|
if (operation === "") {
|
||||||
alert("Please select an operation.");
|
showToast("Please select an operation", "error");
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
navigate(operation)
|
navigate(operation);
|
||||||
};
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
@ -19,9 +22,9 @@ function CabinetMaintenace() {
|
|||||||
<FormBox title="Cabinet Maintenance">
|
<FormBox title="Cabinet Maintenance">
|
||||||
<div className="p-2">
|
<div className="p-2">
|
||||||
<div className="my-5">
|
<div className="my-5">
|
||||||
<label className="mr-4 text-lg ">Operation</label>
|
<label className="mr-4 text-lg text-black dark:text-white">Operation</label>
|
||||||
<select
|
<select
|
||||||
className="w-1/5 h-10 px-2 rounded-full border-2 border-grey text-grey"
|
className="w-1/5 h-10 px-2 rounded-full dark:bg-grey dark:text-primary-dark border-2 border-grey text-grey focus:border-none focus:outline-none"
|
||||||
onChange={(e) => setOperation(e.target.value)}
|
onChange={(e) => setOperation(e.target.value)}
|
||||||
defaultValue={operation}
|
defaultValue={operation}
|
||||||
>
|
>
|
||||||
|
@ -7,4 +7,8 @@ const getUserInfoFromSession = () => {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export { getUserInfoFromSession };
|
const toTitleCase = (str) => {
|
||||||
|
return str.replace(/\w\S*/g, function(txt){return txt.charAt(0).toUpperCase() + txt.substr(1).toLowerCase();});
|
||||||
|
}
|
||||||
|
|
||||||
|
export { getUserInfoFromSession, toTitleCase };
|
@ -10,6 +10,25 @@ export default {
|
|||||||
white: '#FFFFFF',
|
white: '#FFFFFF',
|
||||||
black: '#000000',
|
black: '#000000',
|
||||||
grey: '#979797',
|
grey: '#979797',
|
||||||
|
error: {
|
||||||
|
DEFAULT: '#E5254B',
|
||||||
|
dark: '#E5254B',
|
||||||
|
surface: {DEFAULT: '#FCE9ED', dark: '#FCE9ED'}
|
||||||
|
},
|
||||||
|
onToast: {
|
||||||
|
DEFAULT: '#646564',
|
||||||
|
dark: '#646564',
|
||||||
|
},
|
||||||
|
warning: {
|
||||||
|
DEFAULT: '#EA7000',
|
||||||
|
dark: '#EA7000',
|
||||||
|
surface: {DEFAULT: '#FDF1E5', dark: '#FDF1E5'}
|
||||||
|
},
|
||||||
|
success: {
|
||||||
|
DEFAULT: '#038100',
|
||||||
|
dark: '#038100',
|
||||||
|
surface: {DEFAULT: '#E6F2E5', dark: '#E6F2E5'}
|
||||||
|
},
|
||||||
transparent: 'transparent',
|
transparent: 'transparent',
|
||||||
primary: {
|
primary: {
|
||||||
DEFAULT: '#008C46',
|
DEFAULT: '#008C46',
|
||||||
@ -42,6 +61,16 @@ export default {
|
|||||||
fontSize: {
|
fontSize: {
|
||||||
title: '40px',
|
title: '40px',
|
||||||
},
|
},
|
||||||
|
keyframes: {
|
||||||
|
slideIn: {
|
||||||
|
'0%': { transform: 'translateX(100%)', opacity: '0' },
|
||||||
|
'100%': { transform: 'translateX(0)', opacity: '1' },
|
||||||
|
},
|
||||||
|
fadeOut: {
|
||||||
|
'0%': { opacity: '1' },
|
||||||
|
'100%': { opacity: '0' },
|
||||||
|
},
|
||||||
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
plugins: [
|
plugins: [
|
||||||
|
Loading…
x
Reference in New Issue
Block a user