From 723f5539397487c29a12b20c28b6d05b7f9503af Mon Sep 17 00:00:00 2001 From: Benjamin Sutter Date: Sun, 24 May 2026 12:32:57 +0200 Subject: [PATCH] =?UTF-8?q?refactor:=20move=20PipelineItems=20from=20Zusta?= =?UTF-8?q?nd=20to=20Provider=E2=86=92Service=E2=86=92React=20Query?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 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 --- .../compare/CompareColumnHeader.tsx | 4 +- .../pipeline/PipelineDetailPanel.tsx | 10 +- .../shortlist/AddToPipelineDialog.tsx | 25 ++--- src/domain/pipeline.ts | 14 +++ src/hooks/index.ts | 1 + src/hooks/usePipeline.ts | 91 +++++++++++++++++++ src/pages/demand/Anfragen.tsx | 17 ++-- src/pages/demand/Pipeline.tsx | 7 +- src/provider/IPipelineProvider.ts | 10 ++ src/provider/MockupPipelineProvider.ts | 67 ++++++++++++++ src/services/pipelineService.ts | 85 +++++++++++++++++ src/stores/pipelineStore.ts | 87 +----------------- 12 files changed, 303 insertions(+), 115 deletions(-) create mode 100644 src/hooks/usePipeline.ts create mode 100644 src/provider/IPipelineProvider.ts create mode 100644 src/provider/MockupPipelineProvider.ts create mode 100644 src/services/pipelineService.ts diff --git a/src/components/compare/CompareColumnHeader.tsx b/src/components/compare/CompareColumnHeader.tsx index 3c1168a..a2b0427 100644 --- a/src/components/compare/CompareColumnHeader.tsx +++ b/src/components/compare/CompareColumnHeader.tsx @@ -1,6 +1,7 @@ import { Box, Button, Chip, IconButton, Tooltip, Typography } from '@mui/material' import { X, AlertTriangle, BookmarkCheck, ExternalLink, Kanban } from 'lucide-react' import { useNavigate } from 'react-router' +import { usePipelineItems } from '../../hooks/usePipeline' import { usePipelineStore } from '../../stores/pipelineStore' import type { UnifiedMatchResult } from '../../domain/unifiedResult' @@ -20,7 +21,8 @@ interface Props { export function CompareColumnHeader({ item, onRemove }: Props) { const navigate = useNavigate() - const { items: pipelineItems, openSavedDialog } = usePipelineStore() + const { data: pipelineItems = [] } = usePipelineItems() + const { openSavedDialog } = usePipelineStore() const meta = TYPE_META[item.resultType] ?? { label: item.resultType, color: '#64748b' } const prop = item.resultType !== 'FUTURE_AVAILABILITY' ? (item as any).property : null diff --git a/src/components/pipeline/PipelineDetailPanel.tsx b/src/components/pipeline/PipelineDetailPanel.tsx index abc2794..21746fd 100644 --- a/src/components/pipeline/PipelineDetailPanel.tsx +++ b/src/components/pipeline/PipelineDetailPanel.tsx @@ -7,7 +7,7 @@ import { AlertTriangle, ChevronRight, CheckCircle, ExternalLink, FileText, MapPin, MessageSquare, Sparkles, StickyNote, X, } from 'lucide-react' -import { usePipelineStore } from '../../stores/pipelineStore' +import { useMoveStage, useUpdateNotes, useLoseItem } from '../../hooks/usePipeline' import type { PipelineItem } from '../../domain/pipeline' import { STAGES, NEXT_STAGE, MOCK_DOCS } from './pipelineConstants' import { scoreColor, detailPath, getKiInsight } from './pipelineUtils' @@ -16,7 +16,9 @@ import { scoreColor, detailPath, getKiInsight } from './pipelineUtils' export function DetailPanel({ item, onClose }: { item: PipelineItem; onClose: () => void }) { const navigate = useNavigate() - const { moveStage, updateNotes, loseItem } = usePipelineStore() + const { mutate: moveStage } = useMoveStage() + const { mutate: updateNotes } = useUpdateNotes() + const { mutate: loseItem } = useLoseItem() const path = detailPath(item) const [notes, setNotes] = useState(item.notes ?? '') const stageConfig = STAGES.find(s => s.key === item.stage)! @@ -128,7 +130,7 @@ export function DetailPanel({ item, onClose }: { item: PipelineItem; onClose: () {nextStage && ( +