feat(nakui): Fase 1 del ERP — FieldKind Select + AutoId, seed inyecta id

Primera fase del plan maestro. La metainterfaz gana dos tipos de campo:
Select (chips de un conjunto cerrado, con options validadas) y AutoId
(UUID autogenerado read-only). NakuiBackend::seed inyecta el id de la
entity = clave del store. El módulo CRM los adopta: etapa/canal son
selects, los ids de idempotencia se autogeneran, el form de cliente ya
no pide id. Ningún formulario pide un UUID a mano.

Tests en meta-schema, meta-runtime y nakui-ui verdes.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
sergio
2026-05-21 18:55:13 +00:00
parent 0d1e378e42
commit 86d06da020
12 changed files with 442 additions and 210 deletions
+21 -5
View File
@@ -68,7 +68,6 @@
"title": "Nuevo cliente",
"entity": "Cliente",
"fields": [
{ "name": "id", "label": "ID interno", "kind": "text", "help": "Opcional; el runtime genera el UUID de la entity. Dejar vacío está bien." },
{ "name": "nombre", "label": "Nombre", "kind": "text", "required": true },
{ "name": "email", "label": "Email", "kind": "text", "required": true },
{ "name": "empresa", "label": "Empresa", "kind": "text" }
@@ -102,7 +101,7 @@
"entity": "Oportunidad",
"fields": [
{ "name": "cliente_ref", "label": "Cliente", "kind": "entity_ref", "ref_entity": "Cliente", "required": true, "help": "Click en un cliente de la lista para seleccionarlo." },
{ "name": "oportunidad_id", "label": "Oportunidad UUID", "kind": "text", "required": true, "help": "UUID nuevo por cada intento (idempotencia)." },
{ "name": "oportunidad_id", "label": "ID de la oportunidad", "kind": "auto_id" },
{ "name": "titulo", "label": "Título", "kind": "text", "required": true },
{ "name": "monto", "label": "Monto", "kind": "number", "required": true, "default": "0" },
{ "name": "currency", "label": "Moneda", "kind": "text", "default": "USD" },
@@ -122,7 +121,17 @@
"entity": "Oportunidad",
"fields": [
{ "name": "oportunidad_ref", "label": "Oportunidad", "kind": "entity_ref", "ref_entity": "Oportunidad", "required": true, "help": "Click en una oportunidad de la lista." },
{ "name": "etapa", "label": "Etapa destino", "kind": "text", "required": true, "help": "prospecto | calificado | propuesta | negociacion | ganada | perdida" },
{
"name": "etapa", "label": "Etapa destino", "kind": "select", "required": true,
"options": [
{ "value": "prospecto", "label": "Prospecto" },
{ "value": "calificado", "label": "Calificado" },
{ "value": "propuesta", "label": "Propuesta" },
{ "value": "negociacion", "label": "Negociación" },
{ "value": "ganada", "label": "Ganada" },
{ "value": "perdida", "label": "Perdida" }
]
},
{ "name": "timestamp", "label": "Fecha ISO", "kind": "text", "required": true, "default": "2026-05-21T12:00:00Z" }
],
"on_submit": {
@@ -154,8 +163,15 @@
"entity": "Interaccion",
"fields": [
{ "name": "cliente_ref", "label": "Cliente", "kind": "entity_ref", "ref_entity": "Cliente", "required": true, "help": "Click en un cliente de la lista." },
{ "name": "interaccion_id", "label": "Interacción UUID", "kind": "text", "required": true, "help": "UUID nuevo por cada intento (idempotencia)." },
{ "name": "canal", "label": "Canal", "kind": "text", "required": true, "help": "llamada | email | reunion" },
{ "name": "interaccion_id", "label": "ID de la interacción", "kind": "auto_id" },
{
"name": "canal", "label": "Canal", "kind": "select", "required": true,
"options": [
{ "value": "llamada", "label": "Llamada" },
{ "value": "email", "label": "Email" },
{ "value": "reunion", "label": "Reunión" }
]
},
{ "name": "nota", "label": "Nota", "kind": "multiline" },
{ "name": "timestamp", "label": "Fecha ISO", "kind": "text", "required": true, "default": "2026-05-21T12:00:00Z" }
],