import React, { useState } from 'react'; import { KanbanColumn, Tender } from '../types'; import { supabase, TABLES } from '../lib/supabase'; const columns: KanbanColumn[] = ['Ausschreibung', 'Experten sichten', 'Interview', 'Kunden Interview', 'Vertragsgestaltung', 'Abschluss', 'Geschlossen']; interface KanbanBoardProps { tenders: Tender[]; setTenders: React.Dispatch>; refreshData: () => Promise; } export const KanbanBoard: React.FC = ({ tenders, setTenders, refreshData }) => { const [draggedTenderId, setDraggedTenderId] = useState(null); const [dragOverColumn, setDragOverColumn] = useState(null); const [isProcessing, setIsProcessing] = useState(null); const activePipelineTenders = tenders.filter(t => t.status === 'Aktiv'); const updateTenderColumn = async (tenderId: string, newCol: KanbanColumn) => { setTenders(prev => prev.map(t => t.id === tenderId ? { ...t, kanbanStatus: newCol } : t)); await supabase.from(TABLES.TENDERS).update({ kanban_status: newCol }).eq('id', tenderId); }; const moveTender = (tenderId: string, direction: 'next' | 'prev') => { const tender = tenders.find(t => t.id === tenderId); if (!tender) return; const currentIndex = columns.indexOf(tender.kanbanStatus); let nextIndex = currentIndex + (direction === 'next' ? 1 : -1); nextIndex = Math.max(0, Math.min(columns.length - 1, nextIndex)); updateTenderColumn(tenderId, columns[nextIndex]); }; const handleDeactivateTender = async (tender: Tender) => { if (isProcessing) return; setIsProcessing(tender.id); try { const { error } = await supabase .from(TABLES.TENDERS) .update({ status: 'Inaktiv' }) .eq('id', tender.id); if (error) { if (error.code === '23514') { throw new Error('Die Datenbank erlaubt den Status "Inaktiv" noch nicht. Bitte führen Sie das SQL-Update für die Constraints aus.'); } throw error; } await refreshData(); } catch (error: any) { console.error('Deactivation Error:', error); alert('Lösch-Fehler: ' + error.message); } finally { setIsProcessing(null); } }; const handleCompleteTender = async (tender: Tender) => { if (isProcessing) return; setIsProcessing(tender.id); try { const isoDate = new Date().toISOString().split('T')[0]; const customerName = tender.selectedCustomer?.company || 'Nicht angegeben'; const contactPerson = tender.selectedCustomer?.name || 'Unbekannt'; // Multi-Expert Support: Map all selected experts const expertNames = tender.selectedExperts && tender.selectedExperts.length > 0 ? tender.selectedExperts.map(e => e.name).join(', ') : 'Kein Experte zugewiesen'; const structuredNotes = [ `KUNDE: ${customerName}`, `KONTAKT: ${contactPerson}`, `EXPERTE: ${expertNames}`, `START: ${isoDate}`, `ENDE: TBD`, `---`, tender.notes || '' ].join('\n'); const { error: insertError } = await supabase.from(TABLES.PROJECTS).insert([{ title: tender.title, status: 'Aktiv', notes: structuredNotes }]); if (insertError) throw insertError; const { error: updateError } = await supabase .from(TABLES.TENDERS) .update({ status: 'Archiviert', kanban_status: 'Geschlossen' }) .eq('id', tender.id); if (updateError) throw updateError; await refreshData(); } catch (error: any) { console.error('Full Process Error:', error); alert('Fehler beim Projektabschluss: ' + error.message); } finally { setIsProcessing(null); } }; const handleDragStart = (e: React.DragEvent, tenderId: string) => { setDraggedTenderId(tenderId); e.dataTransfer.setData('tenderId', tenderId); }; const handleDragEnd = () => { setDraggedTenderId(null); setDragOverColumn(null); }; const handleDragOver = (e: React.DragEvent, col: string) => { e.preventDefault(); setDragOverColumn(col); }; const handleDrop = (e: React.DragEvent, targetCol: KanbanColumn) => { e.preventDefault(); const tenderId = e.dataTransfer.getData('tenderId'); if (tenderId) updateTenderColumn(tenderId, targetCol); setDragOverColumn(null); setDraggedTenderId(null); }; return (

Vermittlungspipeline

Aktive Leads in der Akquise

{columns.map((col) => (
handleDragOver(e, col)} onDrop={(e) => handleDrop(e, col as KanbanColumn)} className={`w-80 flex flex-col rounded-[2.5rem] p-5 border transition-all ${ dragOverColumn === col ? 'bg-brand-50/70 dark:bg-brand-500/10 border-brand-500 ring-2 ring-brand-500/20' : 'bg-slate-200/40 dark:bg-slate-900/40 border-slate-300/50 dark:border-slate-800' }`} >

{col}

{activePipelineTenders.filter(t => t.kanbanStatus === col).length}
{activePipelineTenders.filter(t => t.kanbanStatus === col).map((tender) => (
handleDragStart(e, tender.id)} onDragEnd={handleDragEnd} className="bg-white dark:bg-slate-800 p-5 rounded-[1.5rem] shadow-heavy border border-slate-300 dark:border-slate-700 hover:border-brand-500 hover:shadow-2xl transition-all group relative cursor-grab active:cursor-grabbing scale-100 hover:scale-[1.02]" >

{tender.title}

{tender.selectedCustomer?.company || 'Lead'} • {tender.selectedExperts?.[0]?.name || 'Planung'}
{tender.status} {tender.source}
{col === 'Abschluss' && (
)} {col === 'Geschlossen' && (
)}
))}
))}
); };