0106bc4ea0
BREAKING CHANGE: sup config now derives entries from package.json#exports and a server/client glob instead of manual lists; module structure follows flat + barrel convention with .server.js/.client.js runtime suffixes
85 lines
2.8 KiB
JavaScript
85 lines
2.8 KiB
JavaScript
/**
|
|
* 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/<slug>.
|
|
*
|
|
* 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()];
|
|
}
|