import { useCallback, useEffect, useRef, useState } from 'react';
import { NavLink, Outlet, useNavigate } from 'react-router-dom';
import { api } from '../api.js';
import { useInvestisseur } from '../context/InvestisseurContext.jsx';
import { useUi } from '../context/UiContext.jsx';
import Logo from './Logo.jsx';
import UserMenu from './UserMenu.jsx';
/* ── Icônes nav ─────────────────────────────────────────────── */
const ICONS_BASE = '/api/icons-files/';
const I = ({ children }) => (
);
const IconDashboard = () => ;
const IconDeposits = () => ;
const IconInvestments = () => ;
const IconRepayments = () => ;
const IconFlatTax = () => ;
/* Icône nav hybride : bibliothèque si dispo, sinon fallback SVG inline */
function NavIcon({ libFilename, Fallback }) {
if (libFilename) {
return (
);
}
return ;
}
/* Bouton « réduire » (visible dans la sidebar étendue) */
const IconPanelCollapse = () => (
);
/* Bouton « étendre » (apparaît au hover du logo en mode réduit) */
const IconPanelExpand = () => (
);
/* ── Recherche rapide de projet ─────────────────────────────── */
function ProjectSearch() {
const navigate = useNavigate();
const { activeId, activeView } = useInvestisseur();
const [query, setQuery] = useState('');
const [allInv, setAllInv] = useState([]);
const [open, setOpen] = useState(false);
const [activeIdx, setActiveIdx] = useState(-1);
const inputRef = useRef(null);
const wrapRef = useRef(null);
/* Chargement (ou rechargement) des investissements */
const loadInv = useCallback(async () => {
try {
const scopeParams = activeView === 'all' ? { scope: 'all' } : {};
const rows = await api.get('/investissements', scopeParams);
setAllInv(rows);
} catch {}
}, [activeView]);
useEffect(() => { loadInv(); }, [loadInv, activeId]);
/* Raccourci clavier global Ctrl+K / Cmd+K */
useEffect(() => {
const h = e => {
if ((e.ctrlKey || e.metaKey) && e.key === 'k') {
e.preventDefault();
inputRef.current?.focus();
inputRef.current?.select();
}
};
document.addEventListener('keydown', h);
return () => document.removeEventListener('keydown', h);
}, []);
/* Fermeture au clic extérieur */
useEffect(() => {
const h = e => { if (!wrapRef.current?.contains(e.target)) setOpen(false); };
document.addEventListener('mousedown', h);
return () => document.removeEventListener('mousedown', h);
}, []);
/* Résultats filtrés */
const results = (() => {
const q = query.trim().toLowerCase();
if (!q) return [];
return allInv
.filter(r => r.nom_projet?.toLowerCase().includes(q) || r.plateforme_nom?.toLowerCase().includes(q))
.slice(0, 8);
})();
/* Synchronise l'ouverture du dropdown */
useEffect(() => {
setOpen(results.length > 0 && query.trim().length > 0);
setActiveIdx(-1);
}, [results.length, query]); /* eslint-disable-line */
const goTo = (inv) => {
setQuery(''); setOpen(false);
navigate(`/investissements/${inv.id}`);
};
const handleKeyDown = (e) => {
if (e.key === 'ArrowDown') { e.preventDefault(); setActiveIdx(i => Math.min(i + 1, results.length - 1)); }
if (e.key === 'ArrowUp') { e.preventDefault(); setActiveIdx(i => Math.max(i - 1, -1)); }
if (e.key === 'Enter') { e.preventDefault(); if (activeIdx >= 0) goTo(results[activeIdx]); else if (results.length === 1) goTo(results[0]); }
if (e.key === 'Escape') { setOpen(false); setQuery(''); inputRef.current?.blur(); }
};
const STATUT_LABELS = {
en_cours: 'en cours',
rembourse: 'remboursé',
en_retard: 'en retard',
procedure: 'procédure',
cloture: 'clôturé',
};
const statutColor = (s) => {
if (s === 'en_cours') return 'var(--b-en_cours-fg)';
if (s === 'rembourse') return 'var(--b-rembourse-fg)';
if (s === 'en_retard') return 'var(--b-en_retard-fg)';
if (s === 'procedure') return 'var(--b-procedure-fg)';
return 'var(--text-muted)';
};
return (
setQuery(e.target.value)}
onKeyDown={handleKeyDown}
placeholder="Rechercher un projet…"
autoComplete="off"
spellCheck="false"
/>
{query ? (
) : (
⌘K
)}
{open && (
{results.map((inv, i) => (
goTo(inv)}
onMouseEnter={() => setActiveIdx(i)}
>
{inv.nom_projet}
{inv.plateforme_nom}
·
{STATUT_LABELS[inv.statut] ?? inv.statut?.replace('_', ' ')}
{inv.montant_investi != null && (
<>
·
{inv.montant_investi.toLocaleString('fr-FR')} €
>
)}
))}
)}
);
}
/* ── Layout ─────────────────────────────────────────────────── */
function IconPlusCircle() {
return (
);
}
export default function Layout() {
const { sidebarCollapsed, toggleSidebar, displayMode, setDisplayMode } = useUi();
const navigate = useNavigate();
const [navIcons, setNavIcons] = useState({});
useEffect(() => {
api.get('/icons').then(rows => {
const m = {};
rows.forEach(r => { m[r.name] = r.filename; });
setNavIcons(m);
}).catch(() => {});
}, []);
return (
);
}