mirror of
https://github.com/R0m1k3/LogiFlow.git
synced 2026-10-18 04:37:19 +02:00
Replace toast notifications and reconciliation module for improved stability
Replace the existing toast notification system and the BL Reconciliation module with robust, independent implementations to resolve React errors and improve performance. Replit-Commit-Author: Agent Replit-Commit-Session-Id: b163d4c0-de5e-4f4e-a9c0-aed4c7049718 Replit-Commit-Checkpoint-Type: full_checkpoint Replit-Commit-Screenshot-Url: https://storage.googleapis.com/screenshot-production-us-central1/1957c339-2757-4d1f-8e92-e9f71a1ce58e/b163d4c0-de5e-4f4e-a9c0-aed4c7049718/l61v0JT
This commit is contained in:
1 parent
c0e1b26599
commit
69c3d6805a
6 files changed
+297
-3
No files matched your search
+2
-2
@@ -1,6 +1,6 @@
|
||||
import { queryClient } from "./lib/queryClient";
|
||||
import { QueryClientProvider } from "@tanstack/react-query";
|
||||
import { Toaster } from "@/components/ui/toaster";
|
||||
import { ToasterRobust } from "@/components/ToasterRobust";
|
||||
import { TooltipProvider } from "@/components/ui/tooltip";
|
||||
import RouterProduction from "@/components/RouterProduction";
|
||||
import ErrorBoundary from "@/components/ErrorBoundary";
|
||||
@@ -10,7 +10,7 @@ function App() {
|
||||
<ErrorBoundary>
|
||||
<QueryClientProvider client={queryClient}>
|
||||
<TooltipProvider>
|
||||
<Toaster />
|
||||
<ToasterRobust />
|
||||
<RouterProduction />
|
||||
</TooltipProvider>
|
||||
</QueryClientProvider>
|
||||
|
||||
@@ -0,0 +1,62 @@
|
||||
import { useToast } from "@/hooks/use-toast-robust";
|
||||
import { X, CheckCircle, AlertCircle, XCircle } from "lucide-react";
|
||||
|
||||
export function ToasterRobust() {
|
||||
const { toasts, dismiss } = useToast();
|
||||
|
||||
if (toasts.length === 0) return null;
|
||||
|
||||
return (
|
||||
<div className="fixed top-4 right-4 z-[100] flex max-h-screen w-full max-w-[420px] flex-col-reverse gap-2">
|
||||
{toasts.map((toast) => {
|
||||
const variants = {
|
||||
default: {
|
||||
container: "border border-blue-200 bg-blue-50 text-blue-900",
|
||||
icon: <CheckCircle className="h-5 w-5 text-blue-600" />
|
||||
},
|
||||
success: {
|
||||
container: "border border-green-200 bg-green-50 text-green-900",
|
||||
icon: <CheckCircle className="h-5 w-5 text-green-600" />
|
||||
},
|
||||
destructive: {
|
||||
container: "border border-red-200 bg-red-50 text-red-900",
|
||||
icon: <XCircle className="h-5 w-5 text-red-600" />
|
||||
}
|
||||
};
|
||||
|
||||
const variant = variants[toast.variant || "default"];
|
||||
|
||||
return (
|
||||
<div
|
||||
key={toast.id}
|
||||
className={`group pointer-events-auto relative flex w-full items-start space-x-3 overflow-hidden rounded-lg p-4 shadow-lg transition-all duration-200 ${variant.container}`}
|
||||
>
|
||||
<div className="flex-shrink-0">
|
||||
{variant.icon}
|
||||
</div>
|
||||
|
||||
<div className="flex-1 min-w-0">
|
||||
{toast.title && (
|
||||
<div className="text-sm font-medium">
|
||||
{toast.title}
|
||||
</div>
|
||||
)}
|
||||
{toast.description && (
|
||||
<div className="mt-1 text-sm opacity-90">
|
||||
{toast.description}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
|
||||
<button
|
||||
onClick={() => dismiss(toast.id)}
|
||||
className="flex-shrink-0 ml-3 inline-flex text-gray-400 hover:text-gray-600 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-gray-500 transition-colors"
|
||||
>
|
||||
<X className="h-4 w-4" />
|
||||
</button>
|
||||
</div>
|
||||
);
|
||||
})}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,110 @@
|
||||
import { useState, useCallback, useEffect } from "react";
|
||||
|
||||
export interface Toast {
|
||||
id: string;
|
||||
title?: string;
|
||||
description?: string;
|
||||
variant?: "default" | "destructive" | "success";
|
||||
duration?: number;
|
||||
}
|
||||
|
||||
interface ToastState {
|
||||
toasts: Toast[];
|
||||
}
|
||||
|
||||
let listeners: Array<(state: ToastState) => void> = [];
|
||||
let state: ToastState = { toasts: [] };
|
||||
let count = 0;
|
||||
|
||||
function genId() {
|
||||
count = (count + 1) % Number.MAX_SAFE_INTEGER;
|
||||
return count.toString();
|
||||
}
|
||||
|
||||
function dispatch(action: { type: string; toast?: any; toastId?: string }) {
|
||||
switch (action.type) {
|
||||
case "ADD_TOAST":
|
||||
state.toasts = [action.toast, ...state.toasts].slice(0, 3); // Max 3 toasts
|
||||
break;
|
||||
case "DISMISS_TOAST":
|
||||
if (action.toastId) {
|
||||
state.toasts = state.toasts.filter(t => t.id !== action.toastId);
|
||||
} else {
|
||||
state.toasts = [];
|
||||
}
|
||||
break;
|
||||
case "REMOVE_TOAST":
|
||||
state.toasts = state.toasts.filter(t => t.id !== action.toastId);
|
||||
break;
|
||||
}
|
||||
|
||||
listeners.forEach(listener => listener(state));
|
||||
}
|
||||
|
||||
function toast({
|
||||
title,
|
||||
description,
|
||||
variant = "default",
|
||||
duration = 5000,
|
||||
...props
|
||||
}: Omit<Toast, "id"> & { duration?: number }) {
|
||||
const id = genId();
|
||||
|
||||
const newToast: Toast = {
|
||||
id,
|
||||
title,
|
||||
description,
|
||||
variant,
|
||||
duration,
|
||||
...props,
|
||||
};
|
||||
|
||||
dispatch({
|
||||
type: "ADD_TOAST",
|
||||
toast: newToast,
|
||||
});
|
||||
|
||||
// Auto remove after duration
|
||||
if (duration > 0) {
|
||||
setTimeout(() => {
|
||||
dispatch({
|
||||
type: "REMOVE_TOAST",
|
||||
toastId: id,
|
||||
});
|
||||
}, duration);
|
||||
}
|
||||
|
||||
return {
|
||||
id,
|
||||
dismiss: () => dispatch({ type: "DISMISS_TOAST", toastId: id }),
|
||||
update: (updates: Partial<Toast>) =>
|
||||
dispatch({ type: "ADD_TOAST", toast: { ...newToast, ...updates } }),
|
||||
};
|
||||
}
|
||||
|
||||
function dismiss(toastId?: string) {
|
||||
dispatch({
|
||||
type: "DISMISS_TOAST",
|
||||
toastId,
|
||||
});
|
||||
}
|
||||
|
||||
export function useToast() {
|
||||
const [toastState, setToastState] = useState<ToastState>(state);
|
||||
|
||||
useEffect(() => {
|
||||
listeners.push(setToastState);
|
||||
return () => {
|
||||
const index = listeners.indexOf(setToastState);
|
||||
if (index > -1) {
|
||||
listeners.splice(index, 1);
|
||||
}
|
||||
};
|
||||
}, []);
|
||||
|
||||
return {
|
||||
toast,
|
||||
dismiss,
|
||||
toasts: toastState.toasts,
|
||||
};
|
||||
}
|
||||
@@ -1,7 +1,7 @@
|
||||
import { useState, useEffect } from "react";
|
||||
import { useQuery, useMutation, useQueryClient } from "@tanstack/react-query";
|
||||
import { safeFormat } from "@/lib/dateUtils";
|
||||
import { useToast } from "@/hooks/use-toast";
|
||||
import { useToast } from "@/hooks/use-toast-robust";
|
||||
import { apiRequest } from "@/lib/queryClient";
|
||||
import { useStore } from "@/components/Layout";
|
||||
import { useAuthUnified } from "@/hooks/useAuthUnified";
|
||||
|
||||
Reference in new issue
Block a user