fix: P0 stabilisation — score modifiers, logout cache clear, provider isolation, mutation error feedback
- scoreCalculator: apply dataQuality/confidence modifiers to finalScore (were computed but hardcoded to 0) - authService: call queryClient.clear() on logout to prevent cross-session data leakage - queryClient: extract to src/lib/queryClient.ts singleton so services can access it without circular imports - matchSyncService: new service layer owns match-generation logic; MockupNeedProvider no longer imports other providers directly - hooks (11 files): add onError + German toast feedback to every useMutation Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -2,6 +2,7 @@ import { useQuery, useMutation, useQueryClient } from '@tanstack/react-query'
|
||||
import { aiMonitoringService } from '../services/aiMonitoringService'
|
||||
import type { AIMonitoringFilters } from '../provider/IAIMonitoringProvider'
|
||||
import type { ReviewStatus } from '../domain/enums'
|
||||
import { useToastStore } from '../stores/toastStore'
|
||||
|
||||
const QK = 'aiOutputs'
|
||||
const STALE = 30_000
|
||||
@@ -33,5 +34,8 @@ export function useUpdateAIOutputReviewStatus() {
|
||||
onSuccess: () => {
|
||||
qc.invalidateQueries({ queryKey: [QK] })
|
||||
},
|
||||
onError: () => {
|
||||
useToastStore.getState().showToast('Status konnte nicht aktualisiert werden.', 'error')
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import { useQuery, useMutation, useQueryClient } from '@tanstack/react-query'
|
||||
import { sourceService } from '../services/sourceService'
|
||||
import type { SourceFilters, SourceStatus, TermsStatus } from '../domain/dataSource'
|
||||
import { useToastStore } from '../stores/toastStore'
|
||||
|
||||
const STALE_SOURCES = 30_000
|
||||
|
||||
@@ -42,6 +43,9 @@ export function useTriggerMockRun() {
|
||||
queryClient.invalidateQueries({ queryKey: ['data-source', sourceId] })
|
||||
queryClient.invalidateQueries({ queryKey: ['connector-runs', sourceId] })
|
||||
},
|
||||
onError: () => {
|
||||
useToastStore.getState().showToast('Verbindungstest fehlgeschlagen.', 'error')
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
@@ -54,6 +58,9 @@ export function useUpdateSourceStatus() {
|
||||
queryClient.invalidateQueries({ queryKey: ['data-sources'] })
|
||||
queryClient.invalidateQueries({ queryKey: ['data-source'] })
|
||||
},
|
||||
onError: () => {
|
||||
useToastStore.getState().showToast('Quellstatus konnte nicht aktualisiert werden.', 'error')
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
@@ -66,5 +73,8 @@ export function useMarkTermsStatus() {
|
||||
queryClient.invalidateQueries({ queryKey: ['data-sources'] })
|
||||
queryClient.invalidateQueries({ queryKey: ['data-source'] })
|
||||
},
|
||||
onError: () => {
|
||||
useToastStore.getState().showToast('AGB-Status konnte nicht gespeichert werden.', 'error')
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
@@ -2,6 +2,7 @@ import { useQuery, useMutation, useQueryClient } from '@tanstack/react-query'
|
||||
import { futureSignalService } from '../services/futureSignalService'
|
||||
import type { ReviewStatus } from '../domain/enums'
|
||||
import { STALE_SIGNALS } from '../lib/constants'
|
||||
import { useToastStore } from '../stores/toastStore'
|
||||
|
||||
export function useFutureSignals() {
|
||||
return useQuery({
|
||||
@@ -39,6 +40,9 @@ export function useVerifySignal() {
|
||||
onSuccess: () => {
|
||||
queryClient.invalidateQueries({ queryKey: ['futureSignals'] })
|
||||
},
|
||||
onError: () => {
|
||||
useToastStore.getState().showToast('Signal konnte nicht verifiziert werden.', 'error')
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
@@ -51,5 +55,8 @@ export function useUpdateSignalReviewStatus() {
|
||||
queryClient.invalidateQueries({ queryKey: ['futureSignals'] })
|
||||
queryClient.invalidateQueries({ queryKey: ['futureSignal'] })
|
||||
},
|
||||
onError: () => {
|
||||
useToastStore.getState().showToast('Status konnte nicht aktualisiert werden.', 'error')
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
@@ -2,6 +2,7 @@ import { useQuery, useMutation, useQueryClient } from '@tanstack/react-query'
|
||||
import { inquiryService } from '../services/inquiryService'
|
||||
import type { InquiryFilters } from '../provider/IInquiryProvider'
|
||||
import type { Attachment } from '../domain/inquiry'
|
||||
import { useToastStore } from '../stores/toastStore'
|
||||
|
||||
export function useActiveInquiries(filters?: InquiryFilters) {
|
||||
return useQuery({
|
||||
@@ -34,6 +35,9 @@ export function useSendInquiryReply() {
|
||||
qc.invalidateQueries({ queryKey: ['inquiry', inquiryId] })
|
||||
qc.invalidateQueries({ queryKey: ['inquiries'] })
|
||||
},
|
||||
onError: () => {
|
||||
useToastStore.getState().showToast('Antwort konnte nicht gesendet werden.', 'error')
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
@@ -2,6 +2,7 @@ import { useQuery, useMutation, useQueryClient } from '@tanstack/react-query'
|
||||
import { marketIntelligenceService } from '../services/marketIntelligenceService'
|
||||
import { reviewService } from '../services/reviewService'
|
||||
import type { MarketSignalFilters, SignalProcessingStatus } from '../domain/marketSignal'
|
||||
import { useToastStore } from '../stores/toastStore'
|
||||
|
||||
const STALE_SIGNALS = 30_000
|
||||
|
||||
@@ -33,6 +34,9 @@ export function useUpdateSignalStatus() {
|
||||
queryClient.invalidateQueries({ queryKey: ['market-signals'] })
|
||||
queryClient.invalidateQueries({ queryKey: ['market-signal'] })
|
||||
},
|
||||
onError: () => {
|
||||
useToastStore.getState().showToast('Signal-Status konnte nicht aktualisiert werden.', 'error')
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
@@ -44,6 +48,9 @@ export function useConvertToFutureSignal() {
|
||||
queryClient.invalidateQueries({ queryKey: ['market-signals'] })
|
||||
queryClient.invalidateQueries({ queryKey: ['market-signal'] })
|
||||
},
|
||||
onError: () => {
|
||||
useToastStore.getState().showToast('Konvertierung zu Future Signal fehlgeschlagen.', 'error')
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
@@ -63,11 +70,17 @@ export function useLinkSignalToEntity() {
|
||||
queryClient.invalidateQueries({ queryKey: ['market-signals'] })
|
||||
queryClient.invalidateQueries({ queryKey: ['market-signal'] })
|
||||
},
|
||||
onError: () => {
|
||||
useToastStore.getState().showToast('Signal konnte nicht verknüpft werden.', 'error')
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
export function useCreateReviewTask() {
|
||||
return useMutation({
|
||||
mutationFn: (signalId: string) => reviewService.createReviewTask(signalId),
|
||||
onError: () => {
|
||||
useToastStore.getState().showToast('Überprüfungsaufgabe konnte nicht erstellt werden.', 'error')
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import { useQuery, useMutation, useQueryClient } from '@tanstack/react-query'
|
||||
import { matchService } from '../services/matchService'
|
||||
import { STALE_MATCHES } from '../lib/constants'
|
||||
import { useToastStore } from '../stores/toastStore'
|
||||
|
||||
export function useMatches() {
|
||||
return useQuery({
|
||||
@@ -38,6 +39,9 @@ export function useApproveMatch() {
|
||||
onSuccess: () => {
|
||||
queryClient.invalidateQueries({ queryKey: ['matches'] })
|
||||
},
|
||||
onError: () => {
|
||||
useToastStore.getState().showToast('Match konnte nicht genehmigt werden.', 'error')
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import { useMutation } from '@tanstack/react-query'
|
||||
import { offerService } from '../services/offerService'
|
||||
import type { AssetType } from '../domain/enums'
|
||||
import { useToastStore } from '../stores/toastStore'
|
||||
|
||||
export function useCreateOfferDraft() {
|
||||
return useMutation({
|
||||
@@ -20,6 +21,9 @@ export function useCreateOfferDraft() {
|
||||
input.assetType,
|
||||
input.sizeRange,
|
||||
),
|
||||
onError: () => {
|
||||
useToastStore.getState().showToast('Angebot konnte nicht erstellt werden.', 'error')
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
@@ -27,23 +31,35 @@ export function useUpdateOfferField() {
|
||||
return useMutation({
|
||||
mutationFn: (input: { offerDraftId: string; fieldId: string; value: string }) =>
|
||||
offerService.updateOfferField(input.offerDraftId, input.fieldId, input.value),
|
||||
onError: () => {
|
||||
useToastStore.getState().showToast('Feld konnte nicht gespeichert werden.', 'error')
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
export function useMarkOfferChecked() {
|
||||
return useMutation({
|
||||
mutationFn: (offerDraftId: string) => offerService.markOfferChecked(offerDraftId),
|
||||
onError: () => {
|
||||
useToastStore.getState().showToast('Prüfung konnte nicht gespeichert werden.', 'error')
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
export function useGeneratePdfPreview() {
|
||||
return useMutation({
|
||||
mutationFn: (offerDraftId: string) => offerService.generatePdfPreview(offerDraftId),
|
||||
onError: () => {
|
||||
useToastStore.getState().showToast('PDF-Vorschau konnte nicht generiert werden.', 'error')
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
export function useSendOffer() {
|
||||
return useMutation({
|
||||
mutationFn: (offerDraftId: string) => offerService.sendOffer(offerDraftId),
|
||||
onError: () => {
|
||||
useToastStore.getState().showToast('Angebot konnte nicht gesendet werden. Bitte versuchen Sie es erneut.', 'error')
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import { useQuery, useMutation, useQueryClient } from '@tanstack/react-query'
|
||||
import { reminderService } from '../services/reminderService'
|
||||
import { useToastStore } from '../stores/toastStore'
|
||||
|
||||
export function useReminders() {
|
||||
return useQuery({
|
||||
@@ -32,6 +33,9 @@ export function useCompleteReminder() {
|
||||
queryClient.invalidateQueries({ queryKey: ['reminders'] })
|
||||
queryClient.invalidateQueries({ queryKey: ['reminder-insights'] })
|
||||
},
|
||||
onError: () => {
|
||||
useToastStore.getState().showToast('Erinnerung konnte nicht abgeschlossen werden.', 'error')
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
@@ -44,6 +48,9 @@ export function useDismissReminder() {
|
||||
queryClient.invalidateQueries({ queryKey: ['reminders'] })
|
||||
queryClient.invalidateQueries({ queryKey: ['reminder-insights'] })
|
||||
},
|
||||
onError: () => {
|
||||
useToastStore.getState().showToast('Erinnerung konnte nicht verworfen werden.', 'error')
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
@@ -56,6 +63,9 @@ export function useSnoozeReminder() {
|
||||
queryClient.invalidateQueries({ queryKey: ['reminders'] })
|
||||
queryClient.invalidateQueries({ queryKey: ['reminder-insights'] })
|
||||
},
|
||||
onError: () => {
|
||||
useToastStore.getState().showToast('Erinnerung konnte nicht verschoben werden.', 'error')
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
@@ -68,5 +78,8 @@ export function useUpdateReminder() {
|
||||
queryClient.invalidateQueries({ queryKey: ['reminders'] })
|
||||
queryClient.invalidateQueries({ queryKey: ['reminder', id] })
|
||||
},
|
||||
onError: () => {
|
||||
useToastStore.getState().showToast('Erinnerung konnte nicht aktualisiert werden.', 'error')
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
@@ -3,6 +3,7 @@ import { reviewService } from '../services/reviewService'
|
||||
import { useSessionStore } from '../stores/sessionStore'
|
||||
import type { ReviewFilters } from '../provider/IReviewProvider'
|
||||
import type { ReviewTaskStatus } from '../domain/review'
|
||||
import { useToastStore } from '../stores/toastStore'
|
||||
|
||||
const STALE_REVIEW = 30_000
|
||||
const QK = 'reviewQueue'
|
||||
@@ -37,6 +38,9 @@ export function useUpdateReviewStatus() {
|
||||
onSuccess: () => {
|
||||
queryClient.invalidateQueries({ queryKey: [QK] })
|
||||
},
|
||||
onError: () => {
|
||||
useToastStore.getState().showToast('Status konnte nicht aktualisiert werden.', 'error')
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
@@ -48,6 +52,9 @@ export function useAssignReviewTask() {
|
||||
onSuccess: () => {
|
||||
queryClient.invalidateQueries({ queryKey: [QK] })
|
||||
},
|
||||
onError: () => {
|
||||
useToastStore.getState().showToast('Aufgabe konnte nicht zugewiesen werden.', 'error')
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
@@ -62,6 +69,9 @@ export function useAddReviewNote() {
|
||||
onSuccess: () => {
|
||||
queryClient.invalidateQueries({ queryKey: [QK] })
|
||||
},
|
||||
onError: () => {
|
||||
useToastStore.getState().showToast('Notiz konnte nicht hinzugefügt werden.', 'error')
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
@@ -72,6 +82,9 @@ export function useApproveReviewItem() {
|
||||
mutationFn: ({ id, notes }: { id: string; notes?: string }) =>
|
||||
reviewService.approve(id, 'current-user', notes),
|
||||
onSuccess: () => queryClient.invalidateQueries({ queryKey: [QK] }),
|
||||
onError: () => {
|
||||
useToastStore.getState().showToast('Genehmigung fehlgeschlagen.', 'error')
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
@@ -81,5 +94,8 @@ export function useRejectReviewItem() {
|
||||
mutationFn: ({ id, notes }: { id: string; notes?: string }) =>
|
||||
reviewService.reject(id, 'current-user', notes),
|
||||
onSuccess: () => queryClient.invalidateQueries({ queryKey: [QK] }),
|
||||
onError: () => {
|
||||
useToastStore.getState().showToast('Ablehnung fehlgeschlagen.', 'error')
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import { useQuery, useMutation, useQueryClient } from '@tanstack/react-query'
|
||||
import { shortlistService } from '../services/shortlistService'
|
||||
import type { CreateShortlistInput, UpdateShortlistInput, ShortlistItemInput } from '../domain/shortlist'
|
||||
import { useToastStore } from '../stores/toastStore'
|
||||
|
||||
export function useShortlists() {
|
||||
return useQuery({
|
||||
@@ -26,6 +27,9 @@ export function useCreateShortlist() {
|
||||
onSuccess: () => {
|
||||
queryClient.invalidateQueries({ queryKey: ['shortlists'] })
|
||||
},
|
||||
onError: () => {
|
||||
useToastStore.getState().showToast('Merkliste konnte nicht erstellt werden.', 'error')
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
@@ -38,6 +42,9 @@ export function useAddToShortlist() {
|
||||
queryClient.invalidateQueries({ queryKey: ['shortlists'] })
|
||||
queryClient.invalidateQueries({ queryKey: ['shortlist', shortlistId] })
|
||||
},
|
||||
onError: () => {
|
||||
useToastStore.getState().showToast('Eintrag konnte nicht zur Merkliste hinzugefügt werden.', 'error')
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
@@ -50,6 +57,9 @@ export function useRemoveFromShortlist() {
|
||||
queryClient.invalidateQueries({ queryKey: ['shortlists'] })
|
||||
queryClient.invalidateQueries({ queryKey: ['shortlist', shortlistId] })
|
||||
},
|
||||
onError: () => {
|
||||
useToastStore.getState().showToast('Eintrag konnte nicht von der Merkliste entfernt werden.', 'error')
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
@@ -62,5 +72,8 @@ export function useUpdateShortlist() {
|
||||
queryClient.invalidateQueries({ queryKey: ['shortlists'] })
|
||||
queryClient.invalidateQueries({ queryKey: ['shortlist', id] })
|
||||
},
|
||||
onError: () => {
|
||||
useToastStore.getState().showToast('Merkliste konnte nicht aktualisiert werden.', 'error')
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import { useQuery, useMutation, useQueryClient } from '@tanstack/react-query'
|
||||
import { signalPipelineService } from '../services/signalPipelineService'
|
||||
import type { GateType } from '../domain/signalPipeline'
|
||||
import { useToastStore } from '../stores/toastStore'
|
||||
|
||||
const STALE_PIPELINE = 15_000
|
||||
|
||||
@@ -32,6 +33,9 @@ export function useEvaluateGate() {
|
||||
onSuccess: (_data, { signalId }) => {
|
||||
queryClient.invalidateQueries({ queryKey: ['signal-pipeline', signalId] })
|
||||
},
|
||||
onError: () => {
|
||||
useToastStore.getState().showToast('Gate-Evaluierung fehlgeschlagen.', 'error')
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
@@ -44,5 +48,8 @@ export function usePublishToFutureAvailability() {
|
||||
queryClient.invalidateQueries({ queryKey: ['signal-audit-trail', signalId] })
|
||||
queryClient.invalidateQueries({ queryKey: ['market-signals'] })
|
||||
},
|
||||
onError: () => {
|
||||
useToastStore.getState().showToast('Veröffentlichung fehlgeschlagen. Bitte versuchen Sie es erneut.', 'error')
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user