Ir al contenido

Manifest y UI

El archivo manifest.json en la raíz del plugin declara sus metadatos y su interfaz de usuario. Se valida contra la interfaz PluginManifest del SDK.

interface PluginManifest {
name: string; // id del plugin (kebab-case)
version: string; // versión del propio plugin
description?: string;
author?: string;
logo?: string;
ui?: {
routes?: PluginRoute[];
menuItems?: PluginMenuItem[]; // deprecated
modules?: PluginModule[];
subTabs?: PluginSubTab[];
dashboardWidgets?: PluginDashboardWidget[];
themes?: PluginThemePreset[];
};
}

Ejemplo completo:

{
"name": "mi-plugin",
"version": "1.0.0",
"description": "Descripción corta del plugin",
"author": "Tu Nombre",
"ui": {
"modules": [
{
"id": "mi-plugin",
"label": "Mi plugin",
"icon": "Puzzle",
"subTabs": [
{ "label": "Panel", "path": "/plugin/mi-plugin", "icon": "LayoutDashboard" }
]
}
],
"routes": [
{
"path": "/plugin/mi-plugin",
"title": "Mi plugin",
"type": "custom",
"config": { "component": "ui/Page.tsx" }
}
]
}
}

Un módulo top-level: aparece como icono nuevo en el sidebar del ERP.

interface PluginModule {
id: string; // id único del módulo
label: string;
icon: string; // nombre de un icono de lucide-react (ej: "Tag", "Briefcase")
subTabs?: Array<Omit<PluginSubTab, 'moduleId'>>; // pestañas propias del módulo
}

Los iconos son nombres de lucide-react ("Gift", "Truck", "BarChart3"…).

Una pestaña horizontal inyectada en la topbar de un módulo existente (core o de otro plugin):

interface PluginSubTab {
moduleId: string; // dónde inyectar: home | inventory | sales | purchases |
// accounting | plugins | settings | <id de módulo de otro plugin>
label: string;
path: string;
icon?: string; // icono lucide-react, opcional
}
{
"ui": {
"subTabs": [
{ "moduleId": "sales", "label": "Fidelización", "path": "/plugin/fidelizacion", "icon": "Gift" }
]
}
}

Cada ruta asocia un path del frontend con una vista:

interface PluginRoute {
path: string;
title: string;
type: 'table' | 'form' | 'custom' | 'dashboard';
icon?: string;
config?: any; // para type "custom": { "component": "ui/Page.tsx" }
}
  • custom — renderiza un componente React del plugin. config.component es la ruta del archivo relativa a la raíz del plugin (ej. "ui/Page.tsx"), que debe hacer export default del componente.
  • table, form, dashboard — vistas generadas a partir de config.

Widgets inyectados en el Dashboard principal del ERP:

interface PluginDashboardWidget {
id: string;
title: string;
subtitle?: string;
component: string; // ruta relativa al componente, ej. "ui/MiWidget.tsx"
size?: 'sm' | 'md' | 'lg' | 'full'; // grid de 4 columnas: sm=1, md=2, lg=3, full=4 (default md)
order?: number; // menor = antes (default 100)
}

La alternativa programática es context.widgets.registerDashboard(widget) desde init(), con el mismo shape — útil si el widget depende de lógica (por ejemplo, registrarlo solo bajo cierta condición). Ver PluginContext.

Presets de tema que el plugin aporta al selector de Branding del ERP:

interface PluginThemePreset {
id: string;
label: string;
description?: string;
colorPrimary: string; // color CSS (hex, hsl...)
colorAccent: string;
themeMode: 'light' | 'dark';
}
{
"ui": {
"themes": [
{
"id": "mi-tema",
"label": "Mi tema",
"colorPrimary": "#7c3aed",
"colorAccent": "#f59e0b",
"themeMode": "dark"
}
]
}
}
/** @deprecated Usa `modules` o `subTabs`. Se mantiene por compatibilidad. */
interface PluginMenuItem {
label: string;
path: string;
icon: string;
}

Era la forma original de añadir entradas de menú. Los menuItems existentes se mapean automáticamente como sub-tabs del módulo Plugins. Migra a:

  • modules — si tu plugin merece su propio icono en el sidebar.
  • subTabs — si encaja como pestaña dentro de un módulo existente (sales, inventory…).