refactor: move PipelineItems from Zustand to Provider→Service→React Query

PipelineItems are domain data and must not live in Zustand. Moves the
full stack to the correct layer: MockupPipelineProvider (localStorage
persistence + seed fallback) → pipelineService → usePipeline hooks
(useQuery for reads, useMutation for writes with cache invalidation).

pipelineStore is now UI-only: dialogOpen, pendingItem, openSavedDialog,
closeSavedDialog. All consumers updated to use the new hooks.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
Benjamin Sutter
2026-05-24 12:32:57 +02:00
parent 0582031930
commit 723f553939
12 changed files with 303 additions and 115 deletions
+1
View File
@@ -1,3 +1,4 @@
export { usePipelineItems, useAddToPipeline, useMoveStage, useUpdateNotes, useLoseItem, useLinkInquiry } from './usePipeline'
export { useProperties, useProperty, usePropertyDetail } from './useProperties'
export { useMatches, useMatchesByNeed, useMatchesByProperty, useApproveMatch, useMatchDetail } from './useMatches'
export { useFutureSignals, useFutureSignalsByProperty, useVerifySignal } from './useFutureSignals'
+91
View File
@@ -0,0 +1,91 @@
import { useQuery, useMutation, useQueryClient } from '@tanstack/react-query'
import { pipelineService } from '../services/pipelineService'
import { usePipelineStore } from '../stores/pipelineStore'
import { useToastStore } from '../stores/toastStore'
import type { PipelineStage, PendingPipelineItem } from '../domain/pipeline'
export const PIPELINE_QUERY_KEY = ['pipeline', 'items'] as const
export function usePipelineItems() {
return useQuery({
queryKey: PIPELINE_QUERY_KEY,
queryFn: () => pipelineService.getAll(),
select: (res) => res.data ?? [],
staleTime: Infinity,
})
}
export function useAddToPipeline() {
const queryClient = useQueryClient()
const { closeSavedDialog } = usePipelineStore()
return useMutation({
mutationFn: ({ pending, notes }: { pending: PendingPipelineItem; notes?: string }) =>
pipelineService.confirmSaved(pending, notes),
onSuccess: (res) => {
queryClient.invalidateQueries({ queryKey: PIPELINE_QUERY_KEY })
closeSavedDialog()
if (res.data === 'duplicate') {
useToastStore.getState().showToast('Bereits in der Pipeline vorhanden.', 'warning')
} else {
useToastStore.getState().showToast('Zur Pipeline (Gemerkt) hinzugefügt.')
}
},
onError: () => {
useToastStore.getState().showToast('Fehler beim Hinzufügen zur Pipeline.', 'error')
},
})
}
export function useMoveStage() {
const queryClient = useQueryClient()
return useMutation({
mutationFn: ({ id, stage }: { id: string; stage: PipelineStage }) =>
pipelineService.moveStage(id, stage),
onSuccess: () => {
queryClient.invalidateQueries({ queryKey: PIPELINE_QUERY_KEY })
},
onError: () => {
useToastStore.getState().showToast('Fehler beim Verschieben.', 'error')
},
})
}
export function useUpdateNotes() {
const queryClient = useQueryClient()
return useMutation({
mutationFn: ({ id, notes }: { id: string; notes: string }) =>
pipelineService.updateNotes(id, notes),
onSuccess: () => {
queryClient.invalidateQueries({ queryKey: PIPELINE_QUERY_KEY })
},
})
}
export function useLoseItem() {
const queryClient = useQueryClient()
return useMutation({
mutationFn: (id: string) => pipelineService.loseItem(id),
onSuccess: () => {
queryClient.invalidateQueries({ queryKey: PIPELINE_QUERY_KEY })
},
onError: () => {
useToastStore.getState().showToast('Fehler beim Ablehnen.', 'error')
},
})
}
export function useLinkInquiry() {
const queryClient = useQueryClient()
return useMutation({
mutationFn: ({ id, inquiryId }: { id: string; inquiryId: string }) =>
pipelineService.linkInquiry(id, inquiryId),
onSuccess: () => {
queryClient.invalidateQueries({ queryKey: PIPELINE_QUERY_KEY })
},
})
}