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
+17 -1
View File
@@ -8,6 +8,7 @@ import {
AIMonitoringEmptyState,
} from '../../components/ai-monitoring'
import { useAIOutputs, useUpdateAIOutputReviewStatus } from '../../hooks/useAIMonitoring'
import { useToastStore } from '../../stores/toastStore'
import type { AIOutput, AIOutputType } from '../../domain/aiOutput'
import type { ReviewStatus } from '../../domain/enums'
@@ -58,6 +59,7 @@ export default function AIMonitoring() {
const [filters, setFilters] = useState<Filters>({ type: '', reviewStatus: '', hasError: null })
const [selectedOutput, setSelectedOutput] = useState<AIOutput | null>(null)
const showToast = useToastStore((s) => s.showToast)
const { data: allOutputs = [], isLoading } = useAIOutputs()
const updateStatus = useUpdateAIOutputReviewStatus()
@@ -66,11 +68,25 @@ export default function AIMonitoring() {
const failedCount = allOutputs.filter(o => !!o.error).length
const pendingCount = allOutputs.filter(o => o.reviewStatus === 'UNREVIEWED' || o.reviewStatus === 'FLAGGED').length
const STATUS_TOAST: Record<ReviewStatus, string> = {
UNREVIEWED: 'Status zurückgesetzt.',
IN_REVIEW: 'Output zur Prüfung markiert.',
APPROVED: 'Output genehmigt.',
REJECTED: 'Output abgelehnt.',
FLAGGED: 'Output markiert.',
}
const handleUpdateStatus = (status: ReviewStatus) => {
if (!selectedOutput) return
updateStatus.mutate(
{ id: selectedOutput.id, status },
{ onSuccess: (res) => setSelectedOutput(res.data) }
{
onSuccess: (res) => {
setSelectedOutput(res.data)
showToast(STATUS_TOAST[status] ?? 'Status aktualisiert.')
},
onError: () => showToast('Statusänderung fehlgeschlagen.', 'error'),
}
)
}
+15
View File
@@ -9,6 +9,7 @@ import {
} from '../../components/review'
import { useReviewQueue, useUpdateReviewStatus, useAddReviewNote } from '../../hooks/useReviewQueue'
import { useSessionStore } from '../../stores/sessionStore'
import { useToastStore } from '../../stores/toastStore'
import type { ReviewTask, ReviewTaskStatus } from '../../domain/review'
import type { ReviewFilters } from '../../provider/IReviewProvider'
@@ -24,6 +25,7 @@ function filterTasks(tasks: ReviewTask[], filters: ReviewFilters): ReviewTask[]
export default function ReviewQueue() {
const { currentUser } = useSessionStore()
const showToast = useToastStore((s) => s.showToast)
const userRole = currentUser?.role ?? 'REVIEWER'
const [filters, setFilters] = useState<ReviewFilters>({})
@@ -40,6 +42,15 @@ export default function ReviewQueue() {
const escalatedCount = allTasks.filter(t => t.status === 'ESCALATED').length
const criticalCount = allTasks.filter(t => t.priority === 'CRITICAL' && (t.status === 'PENDING' || t.status === 'IN_REVIEW')).length
const STATUS_TOAST: Record<ReviewTaskStatus, string> = {
PENDING: 'Status auf "Ausstehend" gesetzt.',
IN_REVIEW: 'Aufgabe zur Prüfung übernommen.',
APPROVED: 'Aufgabe genehmigt.',
REJECTED: 'Aufgabe abgelehnt.',
ESCALATED: 'Aufgabe eskaliert.',
NEEDS_MORE_DATA: 'Weitere Daten angefordert.',
}
const handleAction = (status: ReviewTaskStatus) => {
if (!selectedTask) return
updateStatus.mutate(
@@ -47,7 +58,9 @@ export default function ReviewQueue() {
{
onSuccess: (res) => {
setSelectedTask(res.data)
showToast(STATUS_TOAST[status] ?? 'Status aktualisiert.')
},
onError: () => showToast('Statusänderung fehlgeschlagen.', 'error'),
}
)
}
@@ -59,7 +72,9 @@ export default function ReviewQueue() {
{
onSuccess: (res) => {
setSelectedTask(res.data)
showToast('Notiz hinzugefügt.')
},
onError: () => showToast('Notiz konnte nicht gespeichert werden.', 'error'),
}
)
}