aboutsummaryrefslogtreecommitdiff
path: root/web/src/context/ToastContext.tsx
diff options
context:
space:
mode:
authorzwlucas <lucas.fariamo08@gmail.com>2026-07-25 21:23:29 +0000
committerzwlucas <lucas.fariamo08@gmail.com>2026-07-25 21:23:29 +0000
commitef55d9bb24430610b60c1b77821307cd72c08c55 (patch)
treee6935a810b34370b0a4c821e55f6397aac50c832 /web/src/context/ToastContext.tsx
downloadhelpdesk-ef55d9bb24430610b60c1b77821307cd72c08c55.tar.gz
helpdesk-ef55d9bb24430610b60c1b77821307cd72c08c55.zip
feat: upload of the projectHEADmaster
Signed-off-by: zwlucas <lucas.fariamo08@gmail.com>
Diffstat (limited to 'web/src/context/ToastContext.tsx')
-rw-r--r--web/src/context/ToastContext.tsx71
1 files changed, 71 insertions, 0 deletions
diff --git a/web/src/context/ToastContext.tsx b/web/src/context/ToastContext.tsx
new file mode 100644
index 0000000..87b3894
--- /dev/null
+++ b/web/src/context/ToastContext.tsx
@@ -0,0 +1,71 @@
+/**
+ * ToastContext.tsx
+ * Sistema minimalista de Toast Notifications.
+ */
+
+import { createContext, useContext, useState, useCallback, type ReactNode } from 'react';
+import { createPortal } from 'react-dom';
+import styles from './Toast.module.css';
+
+type ToastType = 'info' | 'success' | 'error';
+
+interface ToastData {
+ id: string;
+ message: string;
+ type: ToastType;
+}
+
+interface ToastContextValue {
+ addToast: (message: string, type?: ToastType) => void;
+}
+
+const ToastContext = createContext<ToastContextValue | null>(null);
+
+export function ToastProvider({ children }: { children: ReactNode }) {
+ const [toasts, setToasts] = useState<ToastData[]>([]);
+
+ const addToast = useCallback((message: string, type: ToastType = 'info') => {
+ const id = Math.random().toString(36).substring(2, 9);
+ setToasts((prev) => [...prev, { id, message, type }]);
+
+ // Remove automaticamente após 4 segundos
+ setTimeout(() => {
+ setToasts((prev) => prev.filter((t) => t.id !== id));
+ }, 4000);
+ }, []);
+
+ const removeToast = (id: string) => {
+ setToasts((prev) => prev.filter((t) => t.id !== id));
+ };
+
+ return (
+ <ToastContext.Provider value={{ addToast }}>
+ {children}
+
+ {/* Portal renderiza os toasts no final do <body> */}
+ {createPortal(
+ <div className={styles.toastContainer}>
+ {toasts.map((toast) => (
+ <div key={toast.id} className={`${styles.toast} ${styles[toast.type]}`}>
+ <span>{toast.message}</span>
+ <button
+ className={styles.closeBtn}
+ onClick={() => removeToast(toast.id)}
+ aria-label="Fechar"
+ >
+ &times;
+ </button>
+ </div>
+ ))}
+ </div>,
+ document.body
+ )}
+ </ToastContext.Provider>
+ );
+}
+
+export function useToast() {
+ const ctx = useContext(ToastContext);
+ if (!ctx) throw new Error('useToast deve ser usado dentro do ToastProvider');
+ return ctx;
+}