feat: F025 button & action logic audit — global toast, confirm dialog, wired mutations
- Add ToastProvider (global MUI Snackbar stack) + toastStore (Zustand) - Add ConfirmDialog reusable component for destructive actions - Wire toast feedback to all async mutations: review status, AI output status, match approval, shortlist add/remove/title/finalize, signal review actions - Fix dead UserMenu buttons (Profil, Einstellungen) — show info toast - Migrate local Snackbar instances in AddToShortlistDialog and FutureSignalDetailPanel to global toast store Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,52 @@
|
||||
import { Button, Dialog, DialogActions, DialogContent, DialogTitle, Typography } from '@mui/material'
|
||||
import { AlertTriangle } from 'lucide-react'
|
||||
|
||||
interface Props {
|
||||
open: boolean
|
||||
title: string
|
||||
message: string
|
||||
confirmLabel?: string
|
||||
cancelLabel?: string
|
||||
destructive?: boolean
|
||||
onConfirm: () => void
|
||||
onCancel: () => void
|
||||
}
|
||||
|
||||
export function ConfirmDialog({
|
||||
open,
|
||||
title,
|
||||
message,
|
||||
confirmLabel = 'Bestätigen',
|
||||
cancelLabel = 'Abbrechen',
|
||||
destructive = false,
|
||||
onConfirm,
|
||||
onCancel,
|
||||
}: Props) {
|
||||
return (
|
||||
<Dialog open={open} onClose={onCancel} maxWidth="xs" fullWidth>
|
||||
<DialogTitle sx={{ display: 'flex', alignItems: 'center', gap: 1 }}>
|
||||
{destructive && <AlertTriangle size={18} color="#c0392b" />}
|
||||
{title}
|
||||
</DialogTitle>
|
||||
<DialogContent>
|
||||
<Typography variant="body2" color="text.secondary">
|
||||
{message}
|
||||
</Typography>
|
||||
</DialogContent>
|
||||
<DialogActions sx={{ px: 3, pb: 2 }}>
|
||||
<Button variant="outlined" size="small" onClick={onCancel}>
|
||||
{cancelLabel}
|
||||
</Button>
|
||||
<Button
|
||||
variant="contained"
|
||||
size="small"
|
||||
color={destructive ? 'error' : 'primary'}
|
||||
onClick={onConfirm}
|
||||
autoFocus
|
||||
>
|
||||
{confirmLabel}
|
||||
</Button>
|
||||
</DialogActions>
|
||||
</Dialog>
|
||||
)
|
||||
}
|
||||
@@ -0,0 +1,33 @@
|
||||
import { Alert, Snackbar, Stack } from '@mui/material'
|
||||
import { useToastStore } from '../../stores/toastStore'
|
||||
|
||||
export function ToastProvider() {
|
||||
const { toasts, dismissToast } = useToastStore()
|
||||
|
||||
return (
|
||||
<Stack
|
||||
spacing={1}
|
||||
sx={{ position: 'fixed', bottom: 24, left: '50%', transform: 'translateX(-50%)', zIndex: 9999, alignItems: 'center' }}
|
||||
>
|
||||
{toasts.map((toast) => (
|
||||
<Snackbar
|
||||
key={toast.id}
|
||||
open
|
||||
autoHideDuration={toast.duration ?? 4000}
|
||||
onClose={() => dismissToast(toast.id)}
|
||||
anchorOrigin={{ vertical: 'bottom', horizontal: 'center' }}
|
||||
sx={{ position: 'relative', transform: 'none', left: 'auto', bottom: 'auto' }}
|
||||
>
|
||||
<Alert
|
||||
onClose={() => dismissToast(toast.id)}
|
||||
severity={toast.severity}
|
||||
variant="filled"
|
||||
sx={{ minWidth: 320, boxShadow: 3 }}
|
||||
>
|
||||
{toast.message}
|
||||
</Alert>
|
||||
</Snackbar>
|
||||
))}
|
||||
</Stack>
|
||||
)
|
||||
}
|
||||
@@ -8,3 +8,5 @@ export { CardSkeleton } from './CardSkeleton'
|
||||
export { PanelLoadingState } from './PanelLoadingState'
|
||||
export { UnauthorizedState } from './UnauthorizedState'
|
||||
export { RestrictedState } from './RestrictedState'
|
||||
export { ToastProvider } from './ToastProvider'
|
||||
export { ConfirmDialog } from './ConfirmDialog'
|
||||
|
||||
Reference in New Issue
Block a user