feat(nakui): metainterfaz declarativa + 6 modulos ERP estandar
Salto cualitativo: Nakui pasa de "library + demos + read-only viewer
del event log" a plataforma ERP con UI dirigida por datos. Cada
modulo de negocio se declara como un module.json (sin codigo Rust
nuevo) y el runtime GPUI lo carga dinamicamente: sidebar de menus,
listas con columnas configurables, formularios de alta.
3 entregables:
1. Crate nakui-ui-schema (datos puros): Module, View::List/Form,
FieldSpec con FieldKind {Text|Multiline|Number|Boolean|Date},
Action {OpenView|SeedEntity|Morphism}. Module::from_path,
Module::validate, load_modules_from_dir(dir). 6 tests unit + 4
integration.
2. Crate nakui-ui (binario GPUI): carga modulos desde
NAKUI_MODULES_DIR. Sidebar + main panel. List view con tabla
weighted; form view con campos labeled + submit que ejecuta
SeedEntity contra MemoryStore in-process compartido. Toast +
error banner. 6 tests unit.
3. 6 modulos demo en examples/nakui-modules/:
- customers (nombre, email, telefono, credito, notas)
- products (SKU, nombre, categoria, precio, stock)
- suppliers (razon social, ID fiscal, contacto, terminos pago)
- inventory_movements (fecha, tipo, SKU, cantidad, costo, motivo)
- sales_orders (numero, cliente, fechas, estado, totales)
- invoices (numero, cliente, fechas, totales, pagado, moneda)
Filosofia: UI como datos. Persistencia universal (MemoryStore hoy,
SurrealStore manana, sin tocar module.json). Schema primero, semantica
despues.
Activacion:
NAKUI_MODULES_DIR=examples/nakui-modules cargo run -p nakui-ui
Limitaciones conocidas (proximos iters):
- Inputs sin teclado (GPUI no lo trae nativo; integrar
yahweh-widget-text-input).
- Click handlers no propagan mutacion al estado (refactor con
cx.listener pendiente).
- Action::Morphism queda como TODO hasta cargar Manifest junto al
Module.
- Sin persistencia entre runs (wire con EventLog/SurrealStore para
cuando el daemon Nakui exista).
Tests: 16 totales nuevos. Lo que esto desbloquea: cualquiera puede
escribir un module.json para su dominio (pacientes, alumnos,
reservaciones) y aparece en la UI sin recompilar.
This commit is contained in:
@@ -0,0 +1,62 @@
|
||||
{
|
||||
"id": "suppliers",
|
||||
"label": "Proveedores",
|
||||
"description": "Proveedores que abastecen el catálogo.",
|
||||
"entities": [
|
||||
{
|
||||
"name": "supplier",
|
||||
"label": "Proveedor",
|
||||
"fields": [
|
||||
{ "name": "name", "label": "Razón social", "kind": "text", "required": true },
|
||||
{ "name": "tax_id", "label": "ID fiscal", "kind": "text", "required": true, "help": "RUT/RFC/NIT/EIN según país" },
|
||||
{ "name": "contact", "label": "Contacto", "kind": "text" },
|
||||
{ "name": "email", "label": "Email", "kind": "text" },
|
||||
{ "name": "phone", "label": "Teléfono", "kind": "text" },
|
||||
{ "name": "payment_terms_days", "label": "Términos de pago (días)", "kind": "number", "default": "30" },
|
||||
{ "name": "active", "label": "Activo", "kind": "boolean", "default": "true" }
|
||||
]
|
||||
}
|
||||
],
|
||||
"menu": [
|
||||
{ "label": "Listar", "view": "list", "icon": "🏭" },
|
||||
{ "label": "Nuevo", "view": "form", "icon": "✚" }
|
||||
],
|
||||
"views": {
|
||||
"list": {
|
||||
"kind": "list",
|
||||
"title": "Proveedores",
|
||||
"entity": "supplier",
|
||||
"columns": [
|
||||
{ "field": "name", "label": "Razón social", "weight": 2.5 },
|
||||
{ "field": "tax_id", "label": "ID fiscal", "weight": 1.2 },
|
||||
{ "field": "contact", "label": "Contacto", "weight": 1.5 },
|
||||
{ "field": "email", "label": "Email", "weight": 2.0 },
|
||||
{ "field": "payment_terms_days", "label": "Términos", "weight": 0.8 },
|
||||
{ "field": "active", "label": "Activo", "weight": 0.5 }
|
||||
],
|
||||
"actions": [
|
||||
{ "kind": "open_view", "view": "form", "label": "✚ Nuevo" }
|
||||
],
|
||||
"search_in": ["name", "tax_id", "contact", "email"]
|
||||
},
|
||||
"form": {
|
||||
"kind": "form",
|
||||
"title": "Nuevo proveedor",
|
||||
"entity": "supplier",
|
||||
"fields": [
|
||||
{ "name": "name", "label": "Razón social", "kind": "text", "required": true },
|
||||
{ "name": "tax_id", "label": "ID fiscal", "kind": "text", "required": true },
|
||||
{ "name": "contact", "label": "Persona de contacto", "kind": "text" },
|
||||
{ "name": "email", "label": "Email", "kind": "text" },
|
||||
{ "name": "phone", "label": "Teléfono", "kind": "text" },
|
||||
{ "name": "payment_terms_days", "label": "Términos de pago (días)", "kind": "number", "default": "30" },
|
||||
{ "name": "active", "label": "Activo", "kind": "boolean", "default": "true" }
|
||||
],
|
||||
"on_submit": {
|
||||
"kind": "seed_entity",
|
||||
"entity": "supplier",
|
||||
"next_view": "list"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user