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:
Benjamin Sutter
2026-05-17 13:42:30 +02:00
parent 30e840489d
commit 2da62a4861
14 changed files with 226 additions and 38 deletions
+52
View File
@@ -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>
)
}
+33
View File
@@ -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>
)
}
+2
View File
@@ -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'