import React, { useEffect, useMemo, useState } from 'react'; import { Modal } from '../ui/Modal'; import { useToast } from '../Toast'; import { STAGE_LABELS } from './opportunityUi'; import { acquisitionOptionsApi, opportunitiesApi } from '../../lib/api'; import { OPPORTUNITY_STAGES, type Opportunity, type OpportunityStage } from '../../types'; interface OpportunityCopyDialogProps { opportunity: Opportunity; onClose: () => void; onCopied: () => void; } /** * Kopiert eine Opportunity fuer einen oder mehrere andere Berater. * * Eine Opportunity laeuft fuer genau einen Berater; passt das Projekt auch fuer * Kollegen, bekommt jeder eine eigene Kopie mit eigener Phase. Die Berater * kommen aus derselben Auswahlliste wie in der Akquise (Einstellungen -> * Akquise). Der Berater des Originals steht nicht zur Wahl - fuer ihn gibt es * die Opportunity schon. * * Die Phase startet bei "Neu": der Kollege hat den Vorgang noch nicht * durchlaufen. Wer es anders braucht, stellt sie hier um. */ export const OpportunityCopyDialog: React.FC = ({ opportunity, onClose, onCopied, }) => { const { addToast } = useToast(); const [options, setOptions] = useState(null); const [selected, setSelected] = useState([]); const [stage, setStage] = useState('Neu'); const [isSaving, setIsSaving] = useState(false); useEffect(() => { let cancelled = false; acquisitionOptionsApi .list() .then((list) => { if (!cancelled) setOptions(list.consultant.map((option) => option.value)); }) .catch((err) => { if (cancelled) return; setOptions([]); addToast(err instanceof Error ? err.message : 'Berater konnten nicht geladen werden.', 'error'); }); return () => { cancelled = true; }; }, [addToast]); const available = useMemo(() => { const own = (opportunity.consultant ?? '').trim().toLowerCase(); return (options ?? []).filter((name) => name.trim().toLowerCase() !== own); }, [options, opportunity.consultant]); const toggle = (name: string) => setSelected((prev) => prev.includes(name) ? prev.filter((entry) => entry !== name) : [...prev, name] ); const submit = async () => { if (selected.length === 0) return; setIsSaving(true); try { const result = await opportunitiesApi.copy(opportunity.id, { consultants: selected, stage }); addToast( result.opportunity_ids.length === 1 ? `„${opportunity.title}" für ${selected[0]} angelegt.` : `„${opportunity.title}" für ${result.opportunity_ids.length} Berater angelegt.`, 'success' ); onCopied(); onClose(); } catch (err) { addToast(err instanceof Error ? err.message : 'Kopieren fehlgeschlagen.', 'error'); } finally { setIsSaving(false); } }; return ( } >

Für jeden gewählten Berater entsteht eine eigene Opportunity mit denselben Projektdaten, Ansprechpartnern und Notizen - aber eigener Phase.

Berater

{options === null ? (
) : available.length === 0 ? (

Keine weiteren Berater in der Liste. Gepflegt wird sie unter Einstellungen → Akquise.

) : (
{available.map((name) => { const checked = selected.includes(name); return ( ); })}
)}
); };