import { useEffect, useState } from 'react';
import { Link, useNavigate, useParams } from 'react-router-dom';
import {
ArrowLeft, Mail, Phone, User, Globe, Pencil, Trash2, X, AlertTriangle, Plus, Lightbulb
} from 'lucide-react';
import { api, fmt } from '../api';
import { Card, Btn, Field, inputCls, Modal, Spinner, ErrorBox, Table, StatusBadge, Badge } from '../components/ui';
import { EngagementFormModal, TYPE_LABELS, ENG_STATUS_LABELS } from './Engagements';
const STATUS_LABELS = { lead: 'Lead', active: 'Actief', paused: 'Gepauzeerd', archived: 'Gearchiveerd' };
const INV_STATUS_LABELS = { draft: 'Concept', sent: 'Verzonden', paid: 'Betaald', overdue: 'Verlopen', cancelled: 'Geannuleerd' };
const EMPTY_FORM = {
name: '', industry: '', website: '', contact_name: '', contact_email: '',
contact_phone: '', status: 'lead', source: '', notes: ''
};
// Zelfde formulier als in Clients.jsx (bewerk-modus)
function ClientFormModal({ open, onClose, client, onSaved, onDelete }) {
const [form, setForm] = useState(EMPTY_FORM);
const [saving, setSaving] = useState(false);
const [error, setError] = useState(null);
useEffect(() => {
if (!open) return;
setError(null);
setForm(client ? {
name: client.name || '',
industry: client.industry || '',
website: client.website || '',
contact_name: client.contact_name || '',
contact_email: client.contact_email || '',
contact_phone: client.contact_phone || '',
status: client.status || 'lead',
source: client.source || '',
notes: client.notes || ''
} : EMPTY_FORM);
}, [open, client]);
const set = (k) => (e) => setForm((f) => ({ ...f, [k]: e.target.value }));
const submit = async (e) => {
e.preventDefault();
setSaving(true);
setError(null);
try {
await api.put(`/clients/${client.id}`, form);
onSaved();
} catch (err) {
setError(err);
} finally {
setSaving(false);
}
};
return (
);
}
const ADVICE_COLORS = { critical: 'text-red', high: 'text-yellow', medium: 'text-blue', low: 'text-muted' };
export default function ClientDetail() {
const { id } = useParams();
const navigate = useNavigate();
const [data, setData] = useState(null);
const [error, setError] = useState(null);
const [tab, setTab] = useState('overzicht');
const [editOpen, setEditOpen] = useState(false);
const [engModalOpen, setEngModalOpen] = useState(false);
const [noteText, setNoteText] = useState('');
const [noteSaving, setNoteSaving] = useState(false);
const [noteError, setNoteError] = useState(null);
const load = () => {
setError(null);
api.get(`/clients/${id}`).then(setData).catch(setError);
};
useEffect(load, [id]);
if (!data) {
return error ? : ;
}
const { client, engagements, assessments, advice, diagrams, notes, timeEntries, invoices } = data;
const totalHours = timeEntries.reduce((s, t) => s + (t.hours || 0), 0);
const websiteHref = client.website
? (/^https?:\/\//i.test(client.website) ? client.website : `https://${client.website}`)
: null;
const dismissAdvice = async (adviceId) => {
try {
await api.post(`/clients/${id}/advice/${adviceId}/dismiss`);
load();
} catch (err) {
setError(err);
}
};
const addNote = async (e) => {
e.preventDefault();
if (!noteText.trim()) return;
setNoteSaving(true);
setNoteError(null);
try {
await api.post(`/clients/${id}/notes`, { content: noteText.trim() });
setNoteText('');
load();
} catch (err) {
setNoteError(err);
} finally {
setNoteSaving(false);
}
};
const removeNote = async (noteId) => {
if (!window.confirm('Deze notitie verwijderen?')) return;
try {
await api.del(`/clients/${id}/notes/${noteId}`);
load();
} catch (err) {
setError(err);
}
};
const removeClient = async () => {
if (!window.confirm(`Cliënt "${client.name}" verwijderen? Dit kan niet ongedaan worden gemaakt.`)) return;
try {
await api.del(`/clients/${client.id}`);
navigate('/clients');
} catch (err) {
setError(err);
setEditOpen(false);
}
};
const tabs = [
{ key: 'overzicht', label: 'Overzicht' },
{ key: 'engagements', label: `Engagements (${engagements.length})` },
{ key: 'notities', label: `Notities (${notes.length})` },
{ key: 'uren', label: `Uren (${timeEntries.length})` },
{ key: 'facturen', label: `Facturen (${invoices.length})` }
];
return (
{error &&
}
{/* Header */}
{client.name}
{client.contact_name &&
{client.contact_name}}
{client.contact_email &&
{client.contact_email}}
{client.contact_phone &&
{client.contact_phone}}
Terug
setEditOpen(true)}>
Bewerken
{/* Tabs */}
{tabs.map((t) => (
))}
{/* Tab: Overzicht */}
{tab === 'overzicht' && (
{advice.length === 0 ? (
Alles onder controle
) : (
{advice.map((a) => (
{a.advice_text}
{fmt.datetime(a.created_at)}
))}
)}
{client.notes
? {client.notes}
: Geen notities
}
Alles}>
{diagrams.length === 0 ? (
Nog geen diagrammen
) : (
{diagrams.map((d) => (
{d.name}
{d.diagram_type}
{fmt.date(d.updated_at)}
))}
)}
{assessments.length === 0 ? (
Nog geen assessments
) : (
{assessments.map((a) => (
Assessment #{a.id}
{fmt.datetime(a.created_at)}
))}
)}
)}
{/* Tab: Engagements */}
{tab === 'engagements' && (
setEngModalOpen(true)}> Nieuw engagement}
>
(
{e.title}
)
},
{ label: 'Type', render: (e) => TYPE_LABELS[e.type] || e.type },
{ label: 'Status', render: (e) => },
{ label: 'Start', render: (e) => {fmt.date(e.start_date)} }
]}
rows={engagements}
keyFn={(e) => e.id}
empty={Nog geen engagements
}
/>
)}
{/* Tab: Notities */}
{tab === 'notities' && (
{notes.length === 0 ? (
Nog geen notities
) : (
{notes.map((n) => (
{n.content}
{n.created_by} · {fmt.datetime(n.created_at)}
))}
)}
)}
{/* Tab: Uren */}
{tab === 'uren' && (
fmt.date(t.date) },
{ label: 'Gebruiker', render: (t) => t.username || - },
{ label: 'Uren', render: (t) => fmt.hours(t.hours) },
{ label: 'Omschrijving', render: (t) => t.description || - },
{ label: 'Facturabel', render: (t) => {t.billable ? 'Ja' : 'Nee'} },
{ label: 'Tarief', render: (t) => {fmt.euro(t.hourly_rate)} }
]}
rows={timeEntries}
keyFn={(t) => t.id}
empty={Nog geen uren geregistreerd
}
/>
{timeEntries.length > 0 && (
Totaal: {fmt.hours(totalHours)}
)}
)}
{/* Tab: Facturen */}
{tab === 'facturen' && (
(
{i.number}
)
},
{ label: 'Datum', render: (i) => {fmt.date(i.date)} },
{ label: 'Totaal', render: (i) => fmt.euro(i.total) },
{ label: 'Status', render: (i) => }
]}
rows={invoices}
keyFn={(i) => i.id}
empty={Nog geen facturen
}
/>
)}
setEditOpen(false)}
onSaved={() => { setEditOpen(false); load(); }}
onDelete={removeClient}
/>
setEngModalOpen(false)}
clientId={client.id}
onSaved={() => { setEngModalOpen(false); load(); }}
/>
);
}