From 91917289e21f859903ef8edc251223a47a262853 Mon Sep 17 00:00:00 2001
From: david
Date: Tue, 24 Feb 2026 13:45:55 +0300
Subject: [PATCH] add toast
---
frontend/src/App.tsx | 37 ++++----
frontend/src/context/toasts/ToastExample.tsx | 92 ++++++++++++++++++++
frontend/src/context/toasts/ToastRoot.tsx | 71 +++++++++++++++
frontend/src/context/toasts/index.ts | 3 +
frontend/src/context/toasts/useToast.tsx | 62 +++++++++++++
frontend/src/pages/Home.tsx | 9 ++
6 files changed, 258 insertions(+), 16 deletions(-)
create mode 100644 frontend/src/context/toasts/ToastExample.tsx
create mode 100644 frontend/src/context/toasts/ToastRoot.tsx
create mode 100644 frontend/src/context/toasts/index.ts
create mode 100644 frontend/src/context/toasts/useToast.tsx
diff --git a/frontend/src/App.tsx b/frontend/src/App.tsx
index cd9b4a0..5d0428d 100644
--- a/frontend/src/App.tsx
+++ b/frontend/src/App.tsx
@@ -1,6 +1,8 @@
import { Routes, Route } from 'react-router-dom'
import { ModalProvider } from './context/modals/useModal'
import { ModalRoot } from './context/modals/ModalRoot'
+import { ToastProvider } from './context/toasts/useToast'
+import { ToastRoot } from './context/toasts/ToastRoot'
import Cart from './pages/Cart'
import { Navbar } from './components/Navbar'
import { Home } from './pages/Home'
@@ -11,22 +13,25 @@ import { Orders } from './pages/Orders'
const App = () => {
return (
-
-
-
-
-
- } />
- } />
- } />
- } />
- } />
- } />
-
-
-
-
-
+
+
+
+
+
+
+ } />
+ } />
+ } />
+ } />
+ } />
+ } />
+
+
+
+
+
+
+
)
}
diff --git a/frontend/src/context/toasts/ToastExample.tsx b/frontend/src/context/toasts/ToastExample.tsx
new file mode 100644
index 0000000..dbc52f0
--- /dev/null
+++ b/frontend/src/context/toasts/ToastExample.tsx
@@ -0,0 +1,92 @@
+import { useToast } from './useToast';
+
+export const ToastExample = () => {
+ const { addNotification } = useToast();
+
+ const showSuccess = () => {
+ addNotification({
+ type: 'success',
+ title: 'Success!',
+ message: 'Operation completed successfully.',
+ duration: 3000,
+ });
+ };
+
+ const showError = () => {
+ addNotification({
+ type: 'error',
+ title: 'Error!',
+ message: 'Something went wrong. Please try again.',
+ duration: 5000,
+ });
+ };
+
+ const showWarning = () => {
+ addNotification({
+ type: 'warning',
+ title: 'Warning',
+ message: 'Please review your input before proceeding.',
+ duration: 4000,
+ });
+ };
+
+ const showInfo = () => {
+ addNotification({
+ type: 'info',
+ title: 'Information',
+ message: 'This is an informational message.',
+ duration: 3000,
+ });
+ };
+
+ const showSticky = () => {
+ addNotification({
+ type: 'success',
+ title: 'Sticky Notification',
+ message: 'This will stay until you close it!',
+ duration: 0, // 0 means no auto-dismiss
+ });
+ };
+
+ return (
+
+
Toast System Examples
+
+ Click the buttons below to see different toast notifications in action. The toast uses React Context for state management.
+
+
+
+
+
+
+
+
+
+
+ );
+};
\ No newline at end of file
diff --git a/frontend/src/context/toasts/ToastRoot.tsx b/frontend/src/context/toasts/ToastRoot.tsx
new file mode 100644
index 0000000..3d92fc2
--- /dev/null
+++ b/frontend/src/context/toasts/ToastRoot.tsx
@@ -0,0 +1,71 @@
+import { createPortal } from 'react-dom';
+import { useToast, NotificationType } from './useToast';
+
+// Simple Icons (No external lib)
+const Icons = {
+ success: (
+
+ ),
+ error: (
+
+ ),
+ warning: (
+
+ ),
+ info: (
+
+ ),
+};
+
+const getColors = (type: NotificationType) => {
+ switch (type) {
+ case 'success': return 'bg-white border-l-4 border-green-500';
+ case 'error': return 'bg-white border-l-4 border-red-500';
+ case 'warning': return 'bg-white border-l-4 border-yellow-500';
+ case 'info': return 'bg-white border-l-4 border-blue-500';
+ }
+};
+
+export const ToastRoot = () => {
+ const { toasts, removeNotification } = useToast();
+
+ if (toasts.length === 0) return null;
+
+ return createPortal(
+
+ {toasts.map((toast) => (
+
+
{Icons[toast.type]}
+
+
{toast.title}
+ {toast.message && (
+
{toast.message}
+ )}
+
+
+
+ ))}
+
,
+ document.body
+ );
+};
\ No newline at end of file
diff --git a/frontend/src/context/toasts/index.ts b/frontend/src/context/toasts/index.ts
new file mode 100644
index 0000000..486e6ab
--- /dev/null
+++ b/frontend/src/context/toasts/index.ts
@@ -0,0 +1,3 @@
+export { ToastProvider, useToast } from './useToast';
+export type { NotificationType, ToastNotification } from './useToast';
+export { ToastRoot } from './ToastRoot';
diff --git a/frontend/src/context/toasts/useToast.tsx b/frontend/src/context/toasts/useToast.tsx
new file mode 100644
index 0000000..75b9e2d
--- /dev/null
+++ b/frontend/src/context/toasts/useToast.tsx
@@ -0,0 +1,62 @@
+import { createContext, useContext, useState, useCallback, ReactNode, FC } from 'react';
+
+export type NotificationType = 'success' | 'error' | 'warning' | 'info';
+
+export interface ToastNotification {
+ id: string;
+ type: NotificationType;
+ title: string;
+ message?: string;
+ duration?: number; // ms, default 5000
+}
+
+interface ToastContextType {
+ toasts: ToastNotification[];
+ addNotification: (toast: Omit) => void;
+ removeNotification: (id: string) => void;
+}
+
+const ToastContext = createContext(undefined);
+
+interface ToastProviderProps {
+ children: ReactNode;
+}
+
+export const ToastProvider: FC = ({ children }) => {
+ const [toasts, setToasts] = useState([]);
+
+ const addNotification = useCallback((toast: Omit) => {
+ const id = Math.random().toString(36).substr(2, 9);
+ const newToast: ToastNotification = {
+ ...toast,
+ id,
+ duration: toast.duration ?? 5000, // Default 5s
+ };
+
+ setToasts((prev) => [...prev, newToast]);
+
+ // Auto-remove after duration
+ const duration = newToast.duration ?? 5000;
+ if (duration > 0) {
+ setTimeout(() => {
+ setToasts((prev) => prev.filter((t) => t.id !== id));
+ }, duration);
+ }
+ }, []);
+
+ const removeNotification = useCallback((id: string) => {
+ setToasts((prev) => prev.filter((t) => t.id !== id));
+ }, []);
+
+ return (
+
+ {children}
+
+ );
+};
+
+export const useToast = () => {
+ const context = useContext(ToastContext);
+ if (!context) throw new Error('useToast must be used within a ToastProvider');
+ return context;
+};
\ No newline at end of file
diff --git a/frontend/src/pages/Home.tsx b/frontend/src/pages/Home.tsx
index 73e56dd..507c24c 100644
--- a/frontend/src/pages/Home.tsx
+++ b/frontend/src/pages/Home.tsx
@@ -1,5 +1,6 @@
import { Link } from 'react-router-dom'
import { ModalExample } from '../context/modals/ModalExample'
+import { ToastExample } from '../context/toasts/ToastExample'
export function Home() {
return (
@@ -41,6 +42,14 @@ export function Home() {
+
+
+
Toast System Demo
+
+ Test our toast notification system with this interactive example. The toast uses React Context for state management.
+
+
+
)
}