import { useState } from 'react'; import { usePipelines, useCreatePipeline, useDeletePipeline, useAddStage, useUpdateStage, useRemoveStage, } from '../hooks'; import { Modal } from '../../components/Modal'; import type { Pipeline, PipelineStage } from '../types'; import styles from './PipelinesPage.module.css'; /* ------------------------------------------------------------------ */ /* Einzelne Stage-Zeile (View + Edit) */ /* ------------------------------------------------------------------ */ function StageRow({ stage, pipelineId, }: { stage: PipelineStage; pipelineId: string; }) { const [isEditing, setIsEditing] = useState(false); const [editName, setEditName] = useState(stage.name); const [editColor, setEditColor] = useState(stage.color); const [editProbability, setEditProbability] = useState( Math.round((stage.probability ?? 0) * 100), ); const updateStageMutation = useUpdateStage(); const removeStageMutation = useRemoveStage(); const handleSave = () => { const changes: Record = {}; if (editName.trim() !== stage.name) changes.name = editName.trim(); if (editColor !== stage.color) changes.color = editColor; const newProb = editProbability / 100; if (newProb !== (stage.probability ?? 0)) changes.probability = newProb; if (Object.keys(changes).length === 0) { setIsEditing(false); return; } updateStageMutation.mutate( { pipelineId, stageId: stage.id, data: changes }, { onSuccess: () => setIsEditing(false) }, ); }; const handleCancel = () => { setEditName(stage.name); setEditColor(stage.color); setEditProbability(Math.round((stage.probability ?? 0) * 100)); setIsEditing(false); }; if (isEditing) { return (
{stage.sortOrder + 1} setEditColor(e.target.value)} title="Farbe" /> setEditName(e.target.value)} onKeyDown={(e) => { if (e.key === 'Enter') { e.preventDefault(); handleSave(); } if (e.key === 'Escape') handleCancel(); }} autoFocus /> setEditProbability(Number(e.target.value))} onKeyDown={(e) => { if (e.key === 'Enter') { e.preventDefault(); handleSave(); } if (e.key === 'Escape') handleCancel(); }} style={{ width: '4rem', padding: '0.375rem 0.5rem', border: '1px solid var(--color-border)', borderRadius: 'var(--radius-sm)', fontSize: '0.8125rem', textAlign: 'right', background: 'var(--color-bg)', color: 'var(--color-text)', }} title="Wahrscheinlichkeit (%)" /> %
); } return (
{stage.sortOrder + 1} setIsEditing(true)} title="Doppelklick zum Bearbeiten" style={{ cursor: 'pointer' }} > {stage.name} {Math.round((stage.probability ?? 0) * 100)}%
); } /* ------------------------------------------------------------------ */ /* Pipeline Card (klappbar) */ /* ------------------------------------------------------------------ */ function PipelineCard({ pipeline }: { pipeline: Pipeline }) { const [isOpen, setIsOpen] = useState(true); const [newStageName, setNewStageName] = useState(''); const [newStageColor, setNewStageColor] = useState('#6B7280'); const [newStageProbability, setNewStageProbability] = useState(0); const [isDeleteOpen, setDeleteOpen] = useState(false); const addStageMutation = useAddStage(); const deletePipelineMutation = useDeletePipeline(); const stages = [...pipeline.stages].sort((a, b) => a.sortOrder - b.sortOrder); const handleAddStage = () => { if (!newStageName.trim()) return; addStageMutation.mutate( { pipelineId: pipeline.id, data: { name: newStageName.trim(), sortOrder: stages.length, color: newStageColor, probability: newStageProbability / 100, }, }, { onSuccess: () => { setNewStageName(''); setNewStageColor('#6B7280'); setNewStageProbability(0); }, }, ); }; return (
setIsOpen((p) => !p)} >
{pipeline.name} {pipeline.isDefault && ( Standard )} {stages.length} Stufen
e.stopPropagation()} >
{isOpen && (
{stages.length === 0 && (

Keine Stufen vorhanden

)} {stages.map((stage) => ( ))} {/* Neue Stufe hinzufügen */}
setNewStageName(e.target.value)} placeholder="Neue Stufe..." onKeyDown={(e) => { if (e.key === 'Enter') { e.preventDefault(); handleAddStage(); } }} /> setNewStageColor(e.target.value)} title="Farbe" /> setNewStageProbability(Number(e.target.value))} onKeyDown={(e) => { if (e.key === 'Enter') { e.preventDefault(); handleAddStage(); } }} placeholder="0" style={{ width: '3.5rem', padding: '0.375rem 0.5rem', border: '1px solid var(--color-border)', borderRadius: 'var(--radius-sm)', fontSize: '0.8125rem', textAlign: 'right', background: 'var(--color-bg)', color: 'var(--color-text)', }} title="Wahrscheinlichkeit (%)" /> %
)} {/* Löschen-Modal */} setDeleteOpen(false)} title="Pipeline löschen" maxWidth="420px" >

Soll die Pipeline {pipeline.name} wirklich gelöscht werden?

Alle Stufen und zugehörige Vorgänge werden ebenfalls gelöscht.

); } /* ------------------------------------------------------------------ */ /* Hauptseite */ /* ------------------------------------------------------------------ */ export function PipelinesPage() { const { data, isLoading, error } = usePipelines(); const createMutation = useCreatePipeline(); const [showNewForm, setShowNewForm] = useState(false); const [newName, setNewName] = useState(''); const [newIsDefault, setNewIsDefault] = useState(false); if (isLoading) return

Laden...

; if (error) return (

Fehler beim Laden der Pipelines

); const pipelines = data?.data ?? []; const handleCreate = () => { if (!newName.trim()) return; createMutation.mutate( { name: newName.trim(), isDefault: newIsDefault, }, { onSuccess: () => { setNewName(''); setNewIsDefault(false); setShowNewForm(false); }, }, ); }; return (
{/* Header */}

Pipelines

{pipelines.length} Pipelines
{/* Neue Pipeline Form */} {showNewForm && (
setNewName(e.target.value)} placeholder="z.B. Vertrieb" onKeyDown={(e) => { if (e.key === 'Enter') { e.preventDefault(); handleCreate(); } }} style={{ width: '100%', padding: '0.625rem 0.75rem', border: '1px solid var(--color-border)', borderRadius: 'var(--radius-sm)', fontSize: '0.9375rem', outline: 'none', boxSizing: 'border-box', background: 'var(--color-bg)', color: 'var(--color-text)', }} />
)} {/* Pipeline-Liste */} {pipelines.length === 0 && !showNewForm && (

Noch keine Pipelines vorhanden

Erstelle eine Pipeline, um Vorgänge zu verwalten.

)} {pipelines.map((pipeline) => ( ))}
); }