import { useEffect } from 'react'; import { useNavigate } from 'react-router-dom'; import { X, Loader2 } from 'lucide-react'; // ---- Kleurmapping voor statussen ---- export const STATUS_COLORS = { // clients lead: 'yellow', active: 'green', paused: 'muted', archived: 'muted', // engagements planned: 'blue', in_progress: 'accent', completed: 'green', on_hold: 'yellow', // tasks todo: 'blue', done: 'green', cancelled: 'muted', // facturen draft: 'muted', sent: 'blue', paid: 'green', overdue: 'red', // overig critical: 'red', high: 'yellow', medium: 'blue', low: 'muted', planning: 'blue', inactive: 'muted', standby: 'yellow', decommissioned: 'muted', new: 'yellow', contacted: 'blue', qualified: 'accent', converted: 'green', lost: 'muted' }; const BADGE_STYLES = { accent: 'bg-accent-soft text-accent', green: 'bg-green/15 text-green', yellow: 'bg-yellow/15 text-yellow', red: 'bg-red/15 text-red', blue: 'bg-blue/15 text-blue', purple: 'bg-purple/15 text-purple', pink: 'bg-pink/15 text-pink', muted: 'bg-muted/15 text-muted' }; export function Badge({ color = 'muted', children, className = '' }) { return ( {children} ); } export function StatusBadge({ status, labels = {} }) { return {labels[status] || status}; } export function Card({ title, subtitle, actions, children, className = '', padding = true }) { return (
{(title || actions) && (

{title}

{subtitle &&
{subtitle}
}
{actions &&
{actions}
}
)}
{children}
); } export function KpiCard({ icon: Icon, label, value, sub, color = 'accent', to, iconColor: iconCol }) { const navigate = useNavigate(); const colors = { accent: 'text-accent bg-accent-soft', green: 'text-green bg-green/15', yellow: 'text-yellow bg-yellow/15', red: 'text-red bg-red/15', blue: 'text-blue bg-blue/15', purple: 'text-purple bg-purple/15' }; return (
navigate(to) : undefined} className={`bg-card border border-border-soft rounded-xl p-4 card-hover h-full ${to ? 'cursor-pointer' : ''}`} >
{label}
{value}
{sub &&
{sub}
}
{Icon && (
)}
); } const BTN_VARIANTS = { primary: 'bg-accent text-bg hover:bg-accent/90 font-semibold', secondary: 'bg-card border border-border text-text hover:bg-card-hover', danger: 'bg-red/15 text-red hover:bg-red/25', ghost: 'text-muted hover:text-text hover:bg-card' }; export function Btn({ variant = 'primary', size = 'md', loading, disabled, children, className = '', ...props }) { const sizes = { sm: 'px-2.5 py-1 text-[12px]', md: 'px-3.5 py-2 text-[13px]', lg: 'px-5 py-2.5 text-[14px]' }; return ( ); } export function Field({ label, error, children, className = '' }) { return ( ); } export const inputCls = 'w-full bg-bg-soft border border-border rounded-lg px-3 py-2 text-[13px] placeholder:text-muted focus:outline-none focus:border-accent/60 transition-colors'; export function Modal({ open, onClose, title, children, wide }) { useEffect(() => { if (!open) return; const handler = (e) => e.key === 'Escape' && onClose?.(); window.addEventListener('keydown', handler); return () => window.removeEventListener('keydown', handler); }, [open, onClose]); if (!open) return null; return (

{title}

{children}
); } export function Spinner({ text = 'Laden...' }) { return (
{text}
); } export function EmptyState({ icon: Icon, title, hint, action }) { return (
{Icon && (
)}
{title}
{hint &&
{hint}
} {action &&
{action}
}
); } export function ErrorBox({ error, onRetry }) { if (!error) return null; return (
{String(error.message || error)} {onRetry && ( )}
); } // Data-tabel met uniforme styling export function Table({ columns, rows, keyFn, onRowClick, empty }) { if (!rows || rows.length === 0) { return empty ||
Geen gegevens
; } return (
{columns.map((c, i) => ( ))} {rows.map((row, i) => ( onRowClick(row) : undefined} > {columns.map((c, j) => ( ))} ))}
{c.label}
{c.render ? c.render(row) : row[c.key]}
); }