feat(crm): Dediziertes Projektanfrage-Formular + Button in Vorgänge-Liste

- ProjectRequestFormModal: Eigenständiges Formular mit Projektdetails oben
  (Beschreibung, Auslastung/Start, Laufzeit/Vorort-Anteil, Stundensätze)
  und Standard-Vorgangsdaten darunter (Titel, Pipeline/Stage, Kontakt, etc.)
  Auto-Select bei genau einem isProjectType-Typ; Warnung wenn nicht konfiguriert
- DealsPage: Neuer outlined Button "Neue Projektanfrage" neben "Neuer Vorgang"

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
Thomas Reitz 2026-03-13 18:33:33 +01:00
parent 4c739945f0
commit b197660ac8
3 changed files with 655 additions and 0 deletions

View file

@ -6,6 +6,14 @@
--- ---
### Aenderungen 2026-03-13 (10): Dediziertes Projektanfrage-Formular + Button in Vorgänge-Liste
#### Frontend
- `crm/deals/ProjectRequestFormModal.tsx` — Neues dediziertes Modal "Neue Projektanfrage": Projektdetails-Sektion oben (Beschreibung, Auslastung/Start, Laufzeit/Vorort-Anteil, Stundensätze), darunter Vorgangsdaten (Titel, Pipeline/Stage, Kontakt, Unternehmen, Volumen/Abschluss, Notizen); Auto-Select bei genau einem isProjectType-Typ; Warnung wenn kein Typ konfiguriert; Submit-Button disabled bei fehlendem Typ
- `crm/deals/DealsPage.tsx` — Zweiter Button "Neue Projektanfrage" (outlined, primary) neben "Neuer Vorgang"; `isProjectRequestOpen` State; `ProjectRequestFormModal` eingebunden
---
### Aenderungen 2026-03-13 (9): Projektanfrage-Vorgangstyp (isProjectType + ProjectRequestDetails) ### Aenderungen 2026-03-13 (9): Projektanfrage-Vorgangstyp (isProjectType + ProjectRequestDetails)
#### Backend (crm-service) #### Backend (crm-service)

View file

@ -3,6 +3,7 @@ import { useNavigate } from 'react-router-dom';
import { Modal } from '../../components/Modal'; import { Modal } from '../../components/Modal';
import { useDeals, useDeleteDeal, usePipelines } from '../hooks'; import { useDeals, useDeleteDeal, usePipelines } from '../hooks';
import { DealFormModal } from './DealFormModal'; import { DealFormModal } from './DealFormModal';
import { ProjectRequestFormModal } from './ProjectRequestFormModal';
import type { Deal, DealStatus, DealsQueryParams } from '../types'; import type { Deal, DealStatus, DealsQueryParams } from '../types';
import styles from './DealsPage.module.css'; import styles from './DealsPage.module.css';
@ -47,6 +48,7 @@ export function DealsPage() {
const [pipelineFilter, setPipelineFilter] = useState(''); const [pipelineFilter, setPipelineFilter] = useState('');
const [stageFilter, setStageFilter] = useState(''); const [stageFilter, setStageFilter] = useState('');
const [isCreateOpen, setCreateOpen] = useState(false); const [isCreateOpen, setCreateOpen] = useState(false);
const [isProjectRequestOpen, setProjectRequestOpen] = useState(false);
const [editingDeal, setEditingDeal] = useState<Deal | null>(null); const [editingDeal, setEditingDeal] = useState<Deal | null>(null);
const [deletingDeal, setDeletingDeal] = useState<Deal | null>(null); const [deletingDeal, setDeletingDeal] = useState<Deal | null>(null);
@ -118,6 +120,21 @@ export function DealsPage() {
> >
{pagination?.total ?? 0} Vorgänge gesamt {pagination?.total ?? 0} Vorgänge gesamt
</span> </span>
<button
onClick={() => setProjectRequestOpen(true)}
style={{
padding: '0.5rem 1rem',
background: 'transparent',
color: 'var(--color-primary)',
border: '1px solid var(--color-primary)',
borderRadius: 'var(--radius-sm)',
fontSize: '0.875rem',
fontWeight: 600,
cursor: 'pointer',
}}
>
+ Neue Projektanfrage
</button>
<button <button
onClick={() => setCreateOpen(true)} onClick={() => setCreateOpen(true)}
style={{ style={{
@ -415,6 +432,13 @@ export function DealsPage() {
)} )}
</div> </div>
{/* Modal: Neue Projektanfrage */}
<ProjectRequestFormModal
isOpen={isProjectRequestOpen}
onClose={() => setProjectRequestOpen(false)}
onSuccess={() => { setProjectRequestOpen(false); }}
/>
{/* Modal: Neuen Vorgang anlegen */} {/* Modal: Neuen Vorgang anlegen */}
<DealFormModal <DealFormModal
isOpen={isCreateOpen} isOpen={isCreateOpen}

View file

@ -0,0 +1,623 @@
/**
* ProjectRequestFormModal
* Dediziertes Formular für Projektanfragen separates Modal neben "Neuer Vorgang".
* Zeigt projektspezifische Felder prominent am Anfang, gefolgt von Standard-Vorgangs-Feldern.
*/
import { useState, useEffect, useRef, useCallback } from 'react';
import { Modal } from '../../components/Modal';
import { useCreateDeal, usePipelines, useDealTypes } from '../hooks';
import { contactsApi, companiesApi } from '../api';
import type { Contact, Company } from '../types';
interface ProjectRequestFormModalProps {
isOpen: boolean;
onClose: () => void;
onSuccess: () => void;
}
// --------------------------------------------------------
// Styles (inline, konsistent mit DealFormModal)
// --------------------------------------------------------
const labelStyle: React.CSSProperties = {
fontSize: '0.875rem',
fontWeight: 500,
color: 'var(--color-text)',
marginBottom: '0.25rem',
display: 'block',
};
const inputStyle: React.CSSProperties = {
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-card)',
color: 'var(--color-text)',
};
const rowStyle: React.CSSProperties = {
display: 'grid',
gridTemplateColumns: '1fr 1fr',
gap: '0.75rem',
};
const dividerStyle: React.CSSProperties = {
borderTop: '1px solid var(--color-border)',
margin: '1.25rem 0',
};
const sectionLabelStyle: React.CSSProperties = {
fontSize: '0.75rem',
fontWeight: 700,
textTransform: 'uppercase',
letterSpacing: '0.06em',
color: 'var(--color-text-muted)',
marginBottom: '0.875rem',
};
// --------------------------------------------------------
export function ProjectRequestFormModal({
isOpen,
onClose,
onSuccess,
}: ProjectRequestFormModalProps) {
const createMutation = useCreateDeal();
const { data: pipelinesData } = usePipelines();
const pipelines = pipelinesData?.data ?? [];
const { data: dealTypesData } = useDealTypes();
const dealTypes = dealTypesData?.data ?? [];
// Nur isProjectType-Typen
const projectDealTypes = dealTypes.filter((dt) => dt.isProjectType);
// ---- Projektanfrage-Felder ----
const [prNotes, setPrNotes] = useState('');
const [prWorkload, setPrWorkload] = useState('');
const [prStartDate, setPrStartDate] = useState('');
const [prDuration, setPrDuration] = useState('');
const [prOnsitePercent, setPrOnsitePercent] = useState('');
const [prRateRemote, setPrRateRemote] = useState('');
const [prRateOnsite, setPrRateOnsite] = useState('');
// ---- Standard-Vorgang-Felder ----
const [title, setTitle] = useState('');
const [dealTypeId, setDealTypeId] = useState('');
const [pipelineId, setPipelineId] = useState('');
const [stageId, setStageId] = useState('');
const [value, setValue] = useState('');
const [currency, setCurrency] = useState('EUR');
const [expectedCloseDate, setExpectedCloseDate] = useState('');
const [notes, setNotes] = useState('');
const [error, setError] = useState('');
// Kontakt-Suche
const [contactSearch, setContactSearch] = useState('');
const [contactResults, setContactResults] = useState<Contact[]>([]);
const [selectedContact, setSelectedContact] = useState<{ id: string; name: string } | null>(null);
const [showContactDropdown, setShowContactDropdown] = useState(false);
const contactRef = useRef<HTMLDivElement>(null);
const contactTimeout = useRef<ReturnType<typeof setTimeout>>();
// Unternehmen-Suche
const [companySearch, setCompanySearch] = useState('');
const [companyResults, setCompanyResults] = useState<Company[]>([]);
const [selectedCompany, setSelectedCompany] = useState<{ id: string; name: string } | null>(null);
const [showCompanyDropdown, setShowCompanyDropdown] = useState(false);
const companyRef = useRef<HTMLDivElement>(null);
const companyTimeout = useRef<ReturnType<typeof setTimeout>>();
// Stages der gewählten Pipeline
const selectedPipeline = pipelines.find((p) => p.id === pipelineId);
const stages = selectedPipeline?.stages
? [...selectedPipeline.stages].sort((a, b) => a.sortOrder - b.sortOrder)
: [];
// ---- Reset beim Öffnen ----
const reset = useCallback(() => {
setPrNotes('');
setPrWorkload('');
setPrStartDate('');
setPrDuration('');
setPrOnsitePercent('');
setPrRateRemote('');
setPrRateOnsite('');
setTitle('');
setValue('');
setCurrency('EUR');
setExpectedCloseDate('');
setNotes('');
setError('');
setSelectedContact(null);
setContactSearch('');
setSelectedCompany(null);
setCompanySearch('');
setContactResults([]);
setCompanyResults([]);
setShowContactDropdown(false);
setShowCompanyDropdown(false);
}, []);
useEffect(() => {
if (isOpen) {
reset();
// DealType: auto-select falls genau einer vorhanden
setDealTypeId(projectDealTypes.length === 1 ? projectDealTypes[0].id : '');
// Pipeline: Standard-Pipeline vorauswählen
const defaultPl = pipelines.find((p) => p.isDefault) ?? pipelines[0];
setPipelineId(defaultPl?.id ?? '');
}
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [isOpen]);
// Wenn Pipeline wechselt → erste Stage
useEffect(() => {
if (stages.length > 0) setStageId(stages[0].id);
else setStageId('');
}, [pipelineId, stages.length]); // eslint-disable-line
// Click-outside
useEffect(() => {
function h(e: MouseEvent) {
if (contactRef.current && !contactRef.current.contains(e.target as Node))
setShowContactDropdown(false);
if (companyRef.current && !companyRef.current.contains(e.target as Node))
setShowCompanyDropdown(false);
}
document.addEventListener('mousedown', h);
return () => document.removeEventListener('mousedown', h);
}, []);
// Kontakt suchen (debounced)
useEffect(() => {
if (contactTimeout.current) clearTimeout(contactTimeout.current);
if (!contactSearch || contactSearch.length < 2) { setContactResults([]); return; }
contactTimeout.current = setTimeout(async () => {
try {
const res = await contactsApi.list({ search: contactSearch, pageSize: 8 });
setContactResults(res.data);
setShowContactDropdown(true);
} catch { setContactResults([]); }
}, 300);
return () => { if (contactTimeout.current) clearTimeout(contactTimeout.current); };
}, [contactSearch]);
// Unternehmen suchen (debounced)
useEffect(() => {
if (companyTimeout.current) clearTimeout(companyTimeout.current);
if (!companySearch || companySearch.length < 2) { setCompanyResults([]); return; }
companyTimeout.current = setTimeout(async () => {
try {
const res = await companiesApi.list({ search: companySearch, pageSize: 8 });
setCompanyResults(res.data);
setShowCompanyDropdown(true);
} catch { setCompanyResults([]); }
}, 300);
return () => { if (companyTimeout.current) clearTimeout(companyTimeout.current); };
}, [companySearch]);
// ---- Submit ----
const handleSubmit = (e: React.FormEvent) => {
e.preventDefault();
setError('');
if (!dealTypeId) {
setError('Bitte eine Vorgangsart (Projektanfrage-Typ) auswählen.');
return;
}
if (!title.trim()) {
setError('Titel ist ein Pflichtfeld.');
return;
}
if (!pipelineId) {
setError('Bitte eine Pipeline auswählen.');
return;
}
if (!stageId) {
setError('Bitte eine Stage auswählen.');
return;
}
const hasProjectData =
prNotes || prWorkload || prStartDate || prDuration ||
prOnsitePercent || prRateRemote || prRateOnsite;
const payload = {
title: title.trim(),
pipelineId,
stageId,
dealTypeId,
status: 'OPEN' as const,
currency,
...(selectedContact ? { contactId: selectedContact.id } : {}),
...(selectedCompany ? { companyId: selectedCompany.id } : {}),
...(value ? { value: parseFloat(value) } : {}),
...(expectedCloseDate ? { expectedCloseDate: new Date(expectedCloseDate).toISOString() } : {}),
...(notes ? { notes } : {}),
...(hasProjectData
? {
projectRequest: {
...(prNotes ? { notes: prNotes } : {}),
...(prWorkload ? { workload: parseFloat(prWorkload) } : {}),
...(prStartDate ? { startDate: new Date(prStartDate).toISOString() } : {}),
...(prDuration ? { duration: prDuration } : {}),
...(prOnsitePercent ? { onsitePercent: parseFloat(prOnsitePercent) } : {}),
...(prRateRemote ? { rateRemote: parseFloat(prRateRemote) } : {}),
...(prRateOnsite ? { rateOnsite: parseFloat(prRateOnsite) } : {}),
},
}
: {}),
};
createMutation.mutate(payload, {
onSuccess: () => {
reset();
onSuccess();
},
onError: (err: unknown) => {
const msg =
(err as { response?: { data?: { error?: { message?: string } } } })
?.response?.data?.error?.message ?? 'Fehler beim Anlegen';
setError(msg);
},
});
};
// ---- UI-Hilfskomponenten ----
const SearchDropdown = ({
results,
show,
onSelect,
renderLabel,
renderSub,
}: {
results: (Contact | Company)[];
show: boolean;
onSelect: (item: Contact | Company) => void;
renderLabel: (item: Contact | Company) => string;
renderSub?: (item: Contact | Company) => string | undefined;
}) => {
if (!show || results.length === 0) return null;
return (
<div
style={{
position: 'absolute',
top: '100%',
left: 0,
right: 0,
background: 'var(--color-bg-card)',
border: '1px solid var(--color-border)',
borderRadius: 'var(--radius-sm)',
boxShadow: 'var(--shadow-md)',
zIndex: 20,
maxHeight: 200,
overflowY: 'auto',
}}
>
{results.map((item) => {
const label = renderLabel(item);
const sub = renderSub?.(item);
return (
<div
key={item.id}
onClick={() => onSelect(item)}
style={{ padding: '0.5rem 0.75rem', cursor: 'pointer', fontSize: '0.875rem', borderBottom: '1px solid var(--color-border)' }}
onMouseEnter={(e) => ((e.currentTarget as HTMLDivElement).style.background = 'var(--color-bg)')}
onMouseLeave={(e) => ((e.currentTarget as HTMLDivElement).style.background = 'transparent')}
>
{label}
{sub && <span style={{ color: 'var(--color-text-muted)', marginLeft: '0.5rem', fontSize: '0.8125rem' }}>{sub}</span>}
</div>
);
})}
</div>
);
};
// ---- Render ----
return (
<Modal
isOpen={isOpen}
onClose={onClose}
title="Neue Projektanfrage"
maxWidth="620px"
>
<form onSubmit={handleSubmit}>
{/* Fehler-Banner */}
{error && (
<div style={{ padding: '0.75rem', background: '#fef2f2', border: '1px solid #fecaca', borderRadius: 'var(--radius-sm)', color: 'var(--color-error)', fontSize: '0.875rem', marginBottom: '1rem' }}>
{error}
</div>
)}
{/* Kein Projektanfrage-Typ konfiguriert Warnung */}
{projectDealTypes.length === 0 && (
<div style={{ padding: '0.875rem 1rem', background: 'var(--color-warning-bg, #fffbeb)', border: '1px solid var(--color-warning-border, #fde68a)', borderRadius: 'var(--radius-sm)', color: 'var(--color-warning, #92400e)', fontSize: '0.875rem', marginBottom: '1.25rem' }}>
Noch kein Projektanfrage-Typ konfiguriert. Bitte zuerst unter{' '}
<a href="/crm/settings" style={{ color: 'inherit', fontWeight: 600 }}>
CRM&nbsp;Einstellungen&nbsp;&nbsp;Vorgangsarten
</a>{' '}
eine Vorgangsart anlegen und "Projektanfrage" aktivieren.
</div>
)}
{/* Vorgangsart-Auswahl (nur wenn mehrere Projektanfrage-Typen) */}
{projectDealTypes.length > 1 && (
<div style={{ marginBottom: '1rem' }}>
<label style={labelStyle}>Vorgangsart *</label>
<select
value={dealTypeId}
onChange={(e) => setDealTypeId(e.target.value)}
style={{ ...inputStyle, cursor: 'pointer' }}
>
<option value=""> Typ wählen </option>
{projectDealTypes.map((dt) => (
<option key={dt.id} value={dt.id}>{dt.name}</option>
))}
</select>
</div>
)}
{/* ================================================ */}
{/* PROJEKTANFRAGE-FELDER */}
{/* ================================================ */}
<div style={sectionLabelStyle}>📋 Projektdetails</div>
{/* Beschreibung */}
<div style={{ marginBottom: '0.875rem' }}>
<label style={labelStyle}>Beschreibung</label>
<textarea
style={{ ...inputStyle, minHeight: 88, resize: 'vertical' }}
value={prNotes}
onChange={(e) => setPrNotes(e.target.value)}
placeholder="Projektbeschreibung, Anforderungen, Rahmenbedingungen..."
autoFocus={projectDealTypes.length > 0}
/>
</div>
{/* Auslastung + Start */}
<div style={{ ...rowStyle, marginBottom: '0.875rem' }}>
<div>
<label style={labelStyle}>Auslastung (%)</label>
<input
type="number"
step="5"
min="0"
max="100"
style={inputStyle}
value={prWorkload}
onChange={(e) => setPrWorkload(e.target.value)}
placeholder="z.B. 80"
/>
</div>
<div>
<label style={labelStyle}>Start</label>
<input
type="date"
style={inputStyle}
value={prStartDate}
onChange={(e) => setPrStartDate(e.target.value)}
/>
</div>
</div>
{/* Laufzeit + Vorort-Anteil */}
<div style={{ ...rowStyle, marginBottom: '0.875rem' }}>
<div>
<label style={labelStyle}>Laufzeit</label>
<input
style={inputStyle}
value={prDuration}
onChange={(e) => setPrDuration(e.target.value)}
placeholder="z.B. 6 Monate"
/>
</div>
<div>
<label style={labelStyle}>Vorort-Anteil (%)</label>
<input
type="number"
step="5"
min="0"
max="100"
style={inputStyle}
value={prOnsitePercent}
onChange={(e) => setPrOnsitePercent(e.target.value)}
placeholder="z.B. 20"
/>
</div>
</div>
{/* Stundensätze */}
<div style={{ ...rowStyle, marginBottom: '0.875rem' }}>
<div>
<label style={labelStyle}>Stundensatz Remote (/h)</label>
<input
type="number"
step="1"
min="0"
style={inputStyle}
value={prRateRemote}
onChange={(e) => setPrRateRemote(e.target.value)}
placeholder="0"
/>
</div>
<div>
<label style={labelStyle}>Stundensatz Vorort (/h)</label>
<input
type="number"
step="1"
min="0"
style={inputStyle}
value={prRateOnsite}
onChange={(e) => setPrRateOnsite(e.target.value)}
placeholder="0"
/>
</div>
</div>
<div style={dividerStyle} />
{/* ================================================ */}
{/* STANDARD-VORGANG-FELDER */}
{/* ================================================ */}
<div style={sectionLabelStyle}>📁 Vorgangsdaten</div>
{/* Titel */}
<div style={{ marginBottom: '0.875rem' }}>
<label style={labelStyle}>Titel *</label>
<input
style={inputStyle}
value={title}
onChange={(e) => setTitle(e.target.value)}
placeholder="z.B. Projektanfrage Kundenname / Thema"
required
/>
</div>
{/* Pipeline + Stage */}
{pipelines.length === 0 ? (
<div style={{ padding: '0.75rem', background: 'var(--color-warning-bg, #fffbeb)', border: '1px solid var(--color-warning-border, #fde68a)', borderRadius: 'var(--radius-sm)', color: 'var(--color-warning, #92400e)', fontSize: '0.875rem', marginBottom: '0.875rem' }}>
Keine Pipelines konfiguriert.{' '}
<a href="/crm/settings" style={{ color: 'inherit', fontWeight: 600 }}>CRM Einstellungen Pipelines</a>
</div>
) : (
<div style={{ ...rowStyle, marginBottom: '0.875rem' }}>
<div>
<label style={labelStyle}>Pipeline *</label>
<select value={pipelineId} onChange={(e) => setPipelineId(e.target.value)} style={{ ...inputStyle, cursor: 'pointer' }}>
<option value="">Pipeline wählen</option>
{pipelines.filter((p) => p.isActive).map((p) => (
<option key={p.id} value={p.id}>{p.name}{p.isDefault ? ' (Standard)' : ''}</option>
))}
</select>
</div>
<div>
<label style={labelStyle}>Stage *</label>
<select value={stageId} onChange={(e) => setStageId(e.target.value)} style={{ ...inputStyle, cursor: 'pointer' }} disabled={stages.length === 0}>
<option value="">Stage wählen</option>
{stages.map((s) => <option key={s.id} value={s.id}>{s.name}</option>)}
</select>
</div>
</div>
)}
{/* Kontakt-Suche */}
<div style={{ marginBottom: '0.875rem', position: 'relative' }} ref={contactRef}>
<label style={labelStyle}>Kontakt</label>
<input
style={inputStyle}
value={contactSearch}
onChange={(e) => { setContactSearch(e.target.value); if (selectedContact) setSelectedContact(null); }}
onFocus={() => { if (contactResults.length > 0) setShowContactDropdown(true); }}
placeholder="Kontakt suchen..."
/>
{selectedContact && (
<button type="button" onClick={() => { setSelectedContact(null); setContactSearch(''); }}
style={{ position: 'absolute', right: 8, top: 30, background: 'none', border: 'none', color: 'var(--color-text-muted)', cursor: 'pointer', fontSize: '1rem' }}>×</button>
)}
<SearchDropdown
results={contactResults}
show={showContactDropdown}
onSelect={(item) => {
const c = item as Contact;
const name = c.type === 'ORGANIZATION' ? c.companyName : [c.firstName, c.lastName].filter(Boolean).join(' ');
setSelectedContact({ id: c.id, name: name ?? '' });
setContactSearch(name ?? '');
setShowContactDropdown(false);
}}
renderLabel={(item) => {
const c = item as Contact;
return (c.type === 'ORGANIZATION' ? c.companyName : [c.firstName, c.lastName].filter(Boolean).join(' ')) ?? '';
}}
renderSub={(item) => (item as Contact).email ?? undefined}
/>
</div>
{/* Unternehmen-Suche */}
<div style={{ marginBottom: '0.875rem', position: 'relative' }} ref={companyRef}>
<label style={labelStyle}>Unternehmen</label>
<input
style={inputStyle}
value={companySearch}
onChange={(e) => { setCompanySearch(e.target.value); if (selectedCompany) setSelectedCompany(null); }}
onFocus={() => { if (companyResults.length > 0) setShowCompanyDropdown(true); }}
placeholder="Unternehmen suchen..."
/>
{selectedCompany && (
<button type="button" onClick={() => { setSelectedCompany(null); setCompanySearch(''); }}
style={{ position: 'absolute', right: 8, top: 30, background: 'none', border: 'none', color: 'var(--color-text-muted)', cursor: 'pointer', fontSize: '1rem' }}>×</button>
)}
<SearchDropdown
results={companyResults}
show={showCompanyDropdown}
onSelect={(item) => {
const comp = item as Company;
setSelectedCompany({ id: comp.id, name: comp.name });
setCompanySearch(comp.name);
setShowCompanyDropdown(false);
}}
renderLabel={(item) => (item as Company).name}
renderSub={(item) => (item as Company).industry ?? undefined}
/>
</div>
{/* Wert + Erwarteter Abschluss */}
<div style={{ ...rowStyle, marginBottom: '0.875rem' }}>
<div>
<label style={labelStyle}>Volumen ()</label>
<input
type="number"
step="500"
min="0"
style={inputStyle}
value={value}
onChange={(e) => setValue(e.target.value)}
placeholder="0"
/>
</div>
<div>
<label style={labelStyle}>Erw. Abschluss</label>
<input
type="date"
style={inputStyle}
value={expectedCloseDate}
onChange={(e) => setExpectedCloseDate(e.target.value)}
/>
</div>
</div>
{/* Interne Notizen */}
<div style={{ marginBottom: '1.25rem' }}>
<label style={labelStyle}>Interne Notizen</label>
<textarea
style={{ ...inputStyle, minHeight: 52, resize: 'vertical' }}
value={notes}
onChange={(e) => setNotes(e.target.value)}
placeholder="Interne Bemerkungen zum Vorgang..."
/>
</div>
{/* Buttons */}
<div style={{ display: 'flex', justifyContent: 'flex-end', gap: '0.75rem' }}>
<button
type="button"
onClick={onClose}
disabled={createMutation.isPending}
style={{ padding: '0.5rem 1rem', background: 'transparent', border: '1px solid var(--color-border)', borderRadius: 'var(--radius-sm)', fontSize: '0.875rem', cursor: 'pointer', color: 'var(--color-text-secondary)' }}
>
Abbrechen
</button>
<button
type="submit"
disabled={createMutation.isPending || projectDealTypes.length === 0}
style={{ padding: '0.5rem 1.25rem', background: 'var(--color-primary)', color: 'white', border: 'none', borderRadius: 'var(--radius-sm)', fontSize: '0.875rem', fontWeight: 600, cursor: createMutation.isPending ? 'wait' : 'pointer', opacity: (createMutation.isPending || projectDealTypes.length === 0) ? 0.6 : 1 }}
>
{createMutation.isPending ? 'Anlegen...' : 'Projektanfrage anlegen'}
</button>
</div>
</form>
</Modal>
);
}