toast fix

This commit is contained in:
2025-09-17 11:58:23 +02:00
parent 3e55dc50c4
commit bd7b2348ce
3 changed files with 161 additions and 10 deletions

View File

@@ -2,10 +2,8 @@ name: Build All Docker Images
on:
push:
branches: [ main, develop ]
tags: [ 'v*' ]
pull_request:
branches: [ main ]
env:
REGISTRY: ghcr.io

View File

@@ -7,6 +7,7 @@ import { Toaster as HotToaster } from 'react-hot-toast'
import { AuthProvider } from '@/contexts/AuthContext'
import { ModulesProvider } from '@/contexts/ModulesContext'
import { PluginProvider } from '@/contexts/PluginContext'
import { ToastProvider } from '@/contexts/ToastContext'
import { Navigation } from '@/components/ui/navigation'
const inter = Inter({ subsets: ['latin'] })
@@ -68,13 +69,15 @@ export default function RootLayout({
<AuthProvider>
<ModulesProvider>
<PluginProvider>
<div className="min-h-screen bg-background">
<Navigation />
<main className="container mx-auto px-4 py-8">
{children}
</main>
</div>
<Toaster />
<ToastProvider>
<div className="min-h-screen bg-background">
<Navigation />
<main className="container mx-auto px-4 py-8">
{children}
</main>
</div>
<Toaster />
</ToastProvider>
<HotToaster />
</PluginProvider>
</ModulesProvider>

View File

@@ -0,0 +1,150 @@
"use client"
import React, { createContext, useContext, useState, useCallback, useRef } from 'react'
import { generateShortId } from '@/lib/id-utils'
export interface ToastProps {
id: string
title?: string
description?: string
variant?: 'default' | 'destructive' | 'success' | 'warning'
action?: React.ReactElement
duration?: number
}
export interface ToastOptions extends Omit<ToastProps, 'id'> {
duration?: number
}
interface ToastContextType {
toasts: ToastProps[]
toast: (options: ToastOptions) => () => void
success: (title: string, description?: string, options?: Partial<ToastOptions>) => () => void
error: (title: string, description?: string, options?: Partial<ToastOptions>) => () => void
warning: (title: string, description?: string, options?: Partial<ToastOptions>) => () => void
info: (title: string, description?: string, options?: Partial<ToastOptions>) => () => void
dismiss: (id: string) => void
clearAll: () => void
}
const ToastContext = createContext<ToastContextType | undefined>(undefined)
export function ToastProvider({ children }: { children: React.ReactNode }) {
const [toasts, setToasts] = useState<ToastProps[]>([])
const timeoutRefs = useRef<Map<string, NodeJS.Timeout>>(new Map())
const dismissToast = useCallback((id: string) => {
setToasts(prev => prev.filter(toast => toast.id !== id))
// Clear timeout if exists
const timeoutId = timeoutRefs.current.get(id)
if (timeoutId) {
clearTimeout(timeoutId)
timeoutRefs.current.delete(id)
}
}, [])
const toast = useCallback((options: ToastOptions) => {
const {
duration = 5000,
variant = 'default',
...props
} = options
// Generate unique ID using improved utility
const id = generateShortId('toast')
const toastWithId: ToastProps = {
...props,
id,
variant,
duration
}
// Add to toasts array
setToasts(prev => [...prev, toastWithId])
// Auto-remove after specified duration
if (duration > 0) {
const timeoutId = setTimeout(() => {
dismissToast(id)
}, duration)
timeoutRefs.current.set(id, timeoutId)
}
// Return dismiss function for manual control
return () => dismissToast(id)
}, [dismissToast])
// Convenience methods for common toast types
const success = useCallback((title: string, description?: string, options?: Partial<ToastOptions>) => {
return toast({
title,
description,
variant: 'success',
...options
})
}, [toast])
const error = useCallback((title: string, description?: string, options?: Partial<ToastOptions>) => {
return toast({
title,
description,
variant: 'destructive',
duration: 7000, // Errors should stay longer
...options
})
}, [toast])
const warning = useCallback((title: string, description?: string, options?: Partial<ToastOptions>) => {
return toast({
title,
description,
variant: 'warning',
...options
})
}, [toast])
const info = useCallback((title: string, description?: string, options?: Partial<ToastOptions>) => {
return toast({
title,
description,
variant: 'default',
...options
})
}, [toast])
// Clear all toasts
const clearAll = useCallback(() => {
// Clear all timeouts
timeoutRefs.current.forEach(timeoutId => clearTimeout(timeoutId))
timeoutRefs.current.clear()
setToasts([])
}, [])
const value: ToastContextType = {
toasts,
toast,
success,
error,
warning,
info,
dismiss: dismissToast,
clearAll,
}
return (
<ToastContext.Provider value={value}>
{children}
</ToastContext.Provider>
)
}
export function useToast() {
const context = useContext(ToastContext)
if (context === undefined) {
throw new Error('useToast must be used within a ToastProvider')
}
return context
}