/** * Registre unique pour étendre l'admin sans modifier le core. * * Trois types d'extensions : * - widget : une tuile du tableau de bord. Côté serveur on enregistre un fetcher * (registerWidgetFetcher), côté client le Composant (registerWidget). * - navItem : une entrée de la sidebar admin (section optionnelle pour grouper). * - page : un composant rendu sous /admin/. * * Les instances de module sont séparées entre le bundle serveur et le bundle * client de Next.js ; c'est attendu : les fetchers vivent côté serveur, les * Composants côté client. Les navItems et les pages sont enregistrés côté * neutre et visibles des deux côtés. */ const widgetFetchers = new Map(); // id -> async () => data const widgetComponents = new Map(); // id -> { Component, order } const navItems = new Map(); // id -> { id, label, icon, href, order, sectionId } const navSections = new Map(); // id -> { id, title, icon, order } const pages = new Map(); // slug -> { slug, Component, title? } // ---- Widgets --------------------------------------------------------------- export function registerWidgetFetcher(id, fetcher) { widgetFetchers.set(id, fetcher); } export function registerWidget({ id, Component, order = 0 }) { widgetComponents.set(id, { Component, order }); } export function getWidgets() { return [...widgetComponents.entries()] .map(([id, v]) => ({ id, ...v })) .sort((a, b) => a.order - b.order); } // Un fetcher qui échoue n'empêche pas les autres de produire leur donnée. export async function collectWidgetData() { const entries = [...widgetFetchers.entries()]; const results = await Promise.allSettled( entries.map(async ([id, fetch]) => [id, await fetch()]) ); const out = {}; for (const r of results) { if (r.status === 'fulfilled') { const [id, data] = r.value; out[id] = data; } } return out; } // ---- Navigation ------------------------------------------------------------ export function registerNavSection({ id, title, icon, order = 0 }) { navSections.set(id, { id, title, icon, order }); } export function registerNavItem({ id, label, icon, href, order = 0, sectionId = 'main' }) { navItems.set(id, { id, label, icon, href, order, sectionId }); } export function getNavSections() { return [...navSections.values()].sort((a, b) => a.order - b.order); } export function getNavItems() { return [...navItems.values()].sort((a, b) => a.order - b.order); } // ---- Pages ----------------------------------------------------------------- export function registerPage({ slug, Component, title }) { pages.set(slug, { slug, Component, title }); } export function getPage(slug) { return pages.get(slug); } export function getPages() { return [...pages.values()]; }