From 792c512a6eaa77bba79dd9224e69119243dd141f Mon Sep 17 00:00:00 2001 From: jojocoelho Date: Sat, 16 Aug 2025 16:06:29 +0100 Subject: [PATCH 1/4] feat: toastify notifications --- bun.lock | 3 ++ package.json | 3 +- src/app/layout.tsx | 23 ++++++++++++- src/contexts/ToastContext.tsx | 61 +++++++++++++++++++++++++++++++++++ 4 files changed, 88 insertions(+), 2 deletions(-) create mode 100644 src/contexts/ToastContext.tsx diff --git a/bun.lock b/bun.lock index 5d86e9e..664ea19 100644 --- a/bun.lock +++ b/bun.lock @@ -14,6 +14,7 @@ "react": "19.1.1", "react-dom": "19.1.1", "react-hook-form": "^7.60.0", + "react-toastify": "^11.0.5", "tailwind-merge": "^3.3.1", "zod": "^4.0.9", "zustand": "^5.0.6", @@ -1056,6 +1057,8 @@ "react-is": ["react-is@16.13.1", "", {}, "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ=="], + "react-toastify": ["react-toastify@11.0.5", "", { "dependencies": { "clsx": "^2.1.1" }, "peerDependencies": { "react": "^18 || ^19", "react-dom": "^18 || ^19" } }, "sha512-EpqHBGvnSTtHYhCPLxML05NLY2ZX0JURbAdNYa6BUkk+amz4wbKBQvoKQAB0ardvSarUBuY4Q4s1sluAzZwkmA=="], + "recast": ["recast@0.23.11", "", { "dependencies": { "ast-types": "^0.16.1", "esprima": "~4.0.0", "source-map": "~0.6.1", "tiny-invariant": "^1.3.3", "tslib": "^2.0.1" } }, "sha512-YTUo+Flmw4ZXiWfQKGcwwc11KnoRAYgzAE2E7mXKCjSviTKShtxBsN6YUUBB2gtaBzKzeKunxhUwNHQuRryhWA=="], "redent": ["redent@3.0.0", "", { "dependencies": { "indent-string": "^4.0.0", "strip-indent": "^3.0.0" } }, "sha512-6tDA8g98We0zd0GvVeMT9arEOnTw9qM03L9cJXaCjrip1OO764RDBLBfrB4cwzNGDj5OA5ioymC9GkizgWJDUg=="], diff --git a/package.json b/package.json index 682418f..d1ac440 100644 --- a/package.json +++ b/package.json @@ -14,15 +14,16 @@ }, "dependencies": { "@headlessui/react": "^2.2.4", - "motion": "^12.23.12", "@hookform/resolvers": "^5.1.1", "axios": "^1.10.0", "clsx": "^2.1.1", "jwt-decode": "^4.0.0", + "motion": "^12.23.12", "next": "15.4.5", "react": "19.1.1", "react-dom": "19.1.1", "react-hook-form": "^7.60.0", + "react-toastify": "^11.0.5", "tailwind-merge": "^3.3.1", "zod": "^4.0.9", "zustand": "^5.0.6" diff --git a/src/app/layout.tsx b/src/app/layout.tsx index 690bb89..8a6e9a8 100644 --- a/src/app/layout.tsx +++ b/src/app/layout.tsx @@ -1,6 +1,9 @@ import type { Metadata } from "next"; import { Bai_Jamjuree } from "next/font/google"; import "./globals.css"; +import "react-toastify/dist/ReactToastify.css"; +import { ToastContainer } from "react-toastify"; +import { ToastProvider } from "@/contexts/ToastContext"; const jamjuree = Bai_Jamjuree({ subsets: ["latin"], @@ -22,7 +25,25 @@ export default function RootLayout({ }>) { return ( - {children} + + + {children} + + + ); } diff --git a/src/contexts/ToastContext.tsx b/src/contexts/ToastContext.tsx new file mode 100644 index 0000000..de1d660 --- /dev/null +++ b/src/contexts/ToastContext.tsx @@ -0,0 +1,61 @@ +"use client"; + +import React, { createContext, useContext, ReactNode } from 'react'; +import { toast, ToastOptions } from 'react-toastify'; + +const defaultOptions: ToastOptions = { + position: "top-right", + autoClose: 5000, + hideProgressBar: false, + closeOnClick: true, + pauseOnHover: true, + draggable: true, +}; + +interface ToastContextType { + showSuccess: (message: string, options?: ToastOptions) => void; + showError: (message: string, options?: ToastOptions) => void; + showWarning: (message: string, options?: ToastOptions) => void; + showInfo: (message: string, options?: ToastOptions) => void; +} + +const ToastContext = createContext(undefined); + +interface ToastProviderProps { + children: ReactNode; +} + +export const ToastProvider: React.FC = ({ children }) => { + const showSuccess = (message: string, options?: ToastOptions) => { + toast.success(message, { ...defaultOptions, ...options }); + }; + + const showError = (message: string, options?: ToastOptions) => { + toast.error(message, { ...defaultOptions, ...options }); + }; + + const showWarning = (message: string, options?: ToastOptions) => { + toast.warning(message, { ...defaultOptions, ...options }); + }; + + const showInfo = (message: string, options?: ToastOptions) => { + toast.info(message, { ...defaultOptions, ...options }); + }; + + const value = { + showSuccess, + showError, + showWarning, + showInfo, + }; + + return {children}; +}; + +export const useToast = (): ToastContextType => { + const context = useContext(ToastContext); + if (context === undefined) { + throw new Error('useToast must be used within a ToastProvider'); + } + return context; +}; From fe540e95b60e714333d4b92c7f804361411e6f29 Mon Sep 17 00:00:00 2001 From: jojocoelho Date: Sat, 16 Aug 2025 16:08:30 +0100 Subject: [PATCH 2/4] fix: format --- src/contexts/ToastContext.tsx | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/src/contexts/ToastContext.tsx b/src/contexts/ToastContext.tsx index de1d660..fa0a585 100644 --- a/src/contexts/ToastContext.tsx +++ b/src/contexts/ToastContext.tsx @@ -1,7 +1,7 @@ "use client"; -import React, { createContext, useContext, ReactNode } from 'react'; -import { toast, ToastOptions } from 'react-toastify'; +import React, { createContext, useContext, ReactNode } from "react"; +import { toast, ToastOptions } from "react-toastify"; const defaultOptions: ToastOptions = { position: "top-right", @@ -49,13 +49,15 @@ export const ToastProvider: React.FC = ({ children }) => { showInfo, }; - return {children}; + return ( + {children} + ); }; export const useToast = (): ToastContextType => { const context = useContext(ToastContext); if (context === undefined) { - throw new Error('useToast must be used within a ToastProvider'); + throw new Error("useToast must be used within a ToastProvider"); } return context; }; From c3a83b0e41be63f6952375057ae01b591737d66d Mon Sep 17 00:00:00 2001 From: jojocoelho Date: Sat, 16 Aug 2025 16:12:42 +0100 Subject: [PATCH 3/4] feat: removed redundant code --- src/contexts/ToastContext.tsx | 17 ++++------------- 1 file changed, 4 insertions(+), 13 deletions(-) diff --git a/src/contexts/ToastContext.tsx b/src/contexts/ToastContext.tsx index fa0a585..53be004 100644 --- a/src/contexts/ToastContext.tsx +++ b/src/contexts/ToastContext.tsx @@ -3,15 +3,6 @@ import React, { createContext, useContext, ReactNode } from "react"; import { toast, ToastOptions } from "react-toastify"; -const defaultOptions: ToastOptions = { - position: "top-right", - autoClose: 5000, - hideProgressBar: false, - closeOnClick: true, - pauseOnHover: true, - draggable: true, -}; - interface ToastContextType { showSuccess: (message: string, options?: ToastOptions) => void; showError: (message: string, options?: ToastOptions) => void; @@ -27,19 +18,19 @@ interface ToastProviderProps { export const ToastProvider: React.FC = ({ children }) => { const showSuccess = (message: string, options?: ToastOptions) => { - toast.success(message, { ...defaultOptions, ...options }); + toast.success(message, options); }; const showError = (message: string, options?: ToastOptions) => { - toast.error(message, { ...defaultOptions, ...options }); + toast.error(message, options); }; const showWarning = (message: string, options?: ToastOptions) => { - toast.warning(message, { ...defaultOptions, ...options }); + toast.warning(message, options); }; const showInfo = (message: string, options?: ToastOptions) => { - toast.info(message, { ...defaultOptions, ...options }); + toast.info(message, options); }; const value = { From 6b7fbe746b9406c8e7d1f9a32db651619fbce6a7 Mon Sep 17 00:00:00 2001 From: jojocoelho Date: Fri, 22 Aug 2025 11:08:30 +0100 Subject: [PATCH 4/4] fix: layout was rendering children twice --- src/app/layout.tsx | 37 +++++++++++++++++++------------------ 1 file changed, 19 insertions(+), 18 deletions(-) diff --git a/src/app/layout.tsx b/src/app/layout.tsx index 8e050aa..52a9ef6 100644 --- a/src/app/layout.tsx +++ b/src/app/layout.tsx @@ -27,24 +27,25 @@ export default function RootLayout({ return ( - - {children} - - - {children} + + + {children} + + + );