c56ef25546
Validación inline: al fallar un submit por campos required vacíos, el form los marca (label destructivo + mensaje debajo), no sólo un toast. MetaApp.form_errors + validate_required_fields. Secciones de formulario: FieldSpec.section agrupa campos bajo encabezados; abrir_form del CRM las usa. Campos condicionales y pulido puramente visual: scope-out conciente. El plan docs/nakui-erp-masterplan.md queda completo (7/7 fases). Tests verdes (meta-schema 16, meta-runtime 70, meta-form 8, nakui-ui 14); clippy limpio en las libs. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
272 lines
11 KiB
JSON
272 lines
11 KiB
JSON
{
|
|
"id": "crm",
|
|
"label": "CRM",
|
|
"description": "CRM conectado al módulo nakui-core 'crm': clientes, oportunidades que recorren un pipeline de ventas e interacciones. Los formularios «Abrir», «Mover» y «Registrar» disparan morfismos reales con validación del kernel.",
|
|
"nakui_module_dir": "../../../crates/modules/nakui/modules/crm",
|
|
"entities": [
|
|
{
|
|
"name": "Cliente",
|
|
"label": "Cliente",
|
|
"fields": [
|
|
{ "name": "id", "label": "ID", "kind": "text" },
|
|
{ "name": "nombre", "label": "Nombre", "kind": "text", "required": true },
|
|
{ "name": "email", "label": "Email", "kind": "text", "required": true },
|
|
{ "name": "empresa", "label": "Empresa", "kind": "text" }
|
|
]
|
|
},
|
|
{
|
|
"name": "Oportunidad",
|
|
"label": "Oportunidad",
|
|
"fields": [
|
|
{ "name": "id", "label": "ID", "kind": "text" },
|
|
{ "name": "cliente_id", "label": "Cliente", "kind": "entity_ref", "ref_entity": "Cliente" },
|
|
{ "name": "titulo", "label": "Título", "kind": "text" },
|
|
{ "name": "monto", "label": "Monto", "kind": "number" },
|
|
{ "name": "currency", "label": "Moneda", "kind": "text" },
|
|
{ "name": "etapa", "label": "Etapa", "kind": "text" },
|
|
{ "name": "timestamp", "label": "Fecha", "kind": "text" }
|
|
]
|
|
},
|
|
{
|
|
"name": "Interaccion",
|
|
"label": "Interacción",
|
|
"fields": [
|
|
{ "name": "id", "label": "ID", "kind": "text" },
|
|
{ "name": "cliente_id", "label": "Cliente", "kind": "entity_ref", "ref_entity": "Cliente" },
|
|
{ "name": "canal", "label": "Canal", "kind": "text" },
|
|
{ "name": "nota", "label": "Nota", "kind": "multiline" },
|
|
{ "name": "timestamp", "label": "Fecha", "kind": "text" }
|
|
]
|
|
}
|
|
],
|
|
"menu": [
|
|
{ "label": "Panorama", "view": "panorama", "icon": "📊" },
|
|
{ "label": "Clientes", "view": "cliente_list", "icon": "👤" },
|
|
{ "label": "+ Cliente", "view": "cliente_form", "icon": "✚" },
|
|
{ "label": "Oportunidades", "view": "oportunidad_list", "icon": "🎯" },
|
|
{ "label": "Abrir oportunidad", "view": "abrir_form", "icon": "✚" },
|
|
{ "label": "Mover oportunidad", "view": "mover_form", "icon": "⏩" },
|
|
{ "label": "Interacciones", "view": "interaccion_list", "icon": "💬" },
|
|
{ "label": "Registrar interacción", "view": "interaccion_form", "icon": "✚" }
|
|
],
|
|
"views": {
|
|
"panorama": {
|
|
"kind": "dashboard",
|
|
"title": "Panorama del CRM",
|
|
"cards": [
|
|
{ "label": "Clientes", "entity": "Cliente", "metric": { "kind": "count" } },
|
|
{ "label": "Oportunidades", "entity": "Oportunidad", "metric": { "kind": "count" } },
|
|
{
|
|
"label": "Pipeline (monto total)",
|
|
"entity": "Oportunidad",
|
|
"metric": { "kind": "sum", "field": "monto" },
|
|
"format": { "kind": "currency", "symbol": "$" }
|
|
},
|
|
{
|
|
"label": "Oportunidades ganadas",
|
|
"entity": "Oportunidad",
|
|
"metric": { "kind": "count" },
|
|
"filter": { "field": "etapa", "equals": "ganada" }
|
|
},
|
|
{
|
|
"label": "Monto ganado",
|
|
"entity": "Oportunidad",
|
|
"metric": { "kind": "sum", "field": "monto" },
|
|
"filter": { "field": "etapa", "equals": "ganada" },
|
|
"format": { "kind": "currency", "symbol": "$" }
|
|
},
|
|
{
|
|
"label": "Oportunidades por etapa",
|
|
"entity": "Oportunidad",
|
|
"metric": { "kind": "group_by", "field": "etapa" }
|
|
},
|
|
{
|
|
"label": "Interacciones por canal",
|
|
"entity": "Interaccion",
|
|
"metric": { "kind": "group_by", "field": "canal" }
|
|
}
|
|
]
|
|
},
|
|
"cliente_list": {
|
|
"kind": "list",
|
|
"title": "Clientes",
|
|
"entity": "Cliente",
|
|
"columns": [
|
|
{ "field": "nombre", "label": "Nombre", "weight": 2.0 },
|
|
{ "field": "email", "label": "Email", "weight": 2.5 },
|
|
{ "field": "empresa", "label": "Empresa", "weight": 2.0 }
|
|
],
|
|
"actions": [
|
|
{ "kind": "open_view", "view": "cliente_form", "label": "✚ Nuevo cliente" }
|
|
],
|
|
"search_in": ["nombre", "email", "empresa"],
|
|
"row_detail": "cliente_detail"
|
|
},
|
|
"cliente_form": {
|
|
"kind": "form",
|
|
"title": "Nuevo cliente",
|
|
"entity": "Cliente",
|
|
"fields": [
|
|
{ "name": "nombre", "label": "Nombre", "kind": "text", "required": true },
|
|
{ "name": "email", "label": "Email", "kind": "text", "required": true },
|
|
{ "name": "empresa", "label": "Empresa", "kind": "text" }
|
|
],
|
|
"on_submit": {
|
|
"kind": "seed_entity",
|
|
"entity": "Cliente",
|
|
"next_view": "cliente_list"
|
|
}
|
|
},
|
|
"oportunidad_list": {
|
|
"kind": "list",
|
|
"title": "Oportunidades",
|
|
"entity": "Oportunidad",
|
|
"columns": [
|
|
{ "field": "titulo", "label": "Título", "weight": 2.5 },
|
|
{ "field": "etapa", "label": "Etapa", "weight": 1.2 },
|
|
{ "field": "monto", "label": "Monto", "weight": 1.2, "format": { "kind": "currency", "symbol": "$" } },
|
|
{ "field": "cliente_id", "label": "Cliente", "weight": 2.0, "ref_entity": "Cliente" }
|
|
],
|
|
"actions": [
|
|
{ "kind": "open_view", "view": "abrir_form", "label": "✚ Abrir oportunidad" },
|
|
{ "kind": "open_view", "view": "mover_form", "label": "⏩ Mover etapa" }
|
|
],
|
|
"search_in": ["titulo", "etapa"],
|
|
"row_detail": "oportunidad_detail"
|
|
},
|
|
"abrir_form": {
|
|
"kind": "form",
|
|
"title": "Abrir oportunidad (morphism)",
|
|
"entity": "Oportunidad",
|
|
"fields": [
|
|
{ "name": "cliente_ref", "label": "Cliente", "kind": "entity_ref", "ref_entity": "Cliente", "required": true, "section": "Oportunidad", "help": "Click en un cliente de la lista para seleccionarlo." },
|
|
{ "name": "oportunidad_id", "label": "ID de la oportunidad", "kind": "auto_id", "section": "Oportunidad" },
|
|
{ "name": "titulo", "label": "Título", "kind": "text", "required": true, "section": "Oportunidad" },
|
|
{ "name": "monto", "label": "Monto", "kind": "number", "required": true, "default": "0", "section": "Importe y fecha" },
|
|
{ "name": "currency", "label": "Moneda", "kind": "text", "default": "USD", "section": "Importe y fecha" },
|
|
{ "name": "timestamp", "label": "Fecha ISO", "kind": "text", "required": true, "default": "2026-05-21T12:00:00Z", "section": "Importe y fecha" }
|
|
],
|
|
"on_submit": {
|
|
"kind": "morphism",
|
|
"name": "abrir_oportunidad",
|
|
"inputs": { "cliente": "cliente_ref" },
|
|
"params": ["oportunidad_id", "titulo", "monto", "currency", "timestamp"],
|
|
"next_view": "oportunidad_list"
|
|
}
|
|
},
|
|
"mover_form": {
|
|
"kind": "form",
|
|
"title": "Mover oportunidad (morphism)",
|
|
"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": "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": {
|
|
"kind": "morphism",
|
|
"name": "mover_oportunidad",
|
|
"inputs": { "oportunidad": "oportunidad_ref" },
|
|
"params": ["etapa", "timestamp"],
|
|
"next_view": "oportunidad_list"
|
|
}
|
|
},
|
|
"interaccion_list": {
|
|
"kind": "list",
|
|
"title": "Interacciones",
|
|
"entity": "Interaccion",
|
|
"columns": [
|
|
{ "field": "canal", "label": "Canal", "weight": 1.0 },
|
|
{ "field": "nota", "label": "Nota", "weight": 3.0 },
|
|
{ "field": "cliente_id", "label": "Cliente", "weight": 2.0, "ref_entity": "Cliente" },
|
|
{ "field": "timestamp", "label": "Fecha", "weight": 1.2 }
|
|
],
|
|
"actions": [
|
|
{ "kind": "open_view", "view": "interaccion_form", "label": "✚ Registrar interacción" }
|
|
],
|
|
"search_in": ["canal", "nota"]
|
|
},
|
|
"interaccion_form": {
|
|
"kind": "form",
|
|
"title": "Registrar interacción (morphism)",
|
|
"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": "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" }
|
|
],
|
|
"on_submit": {
|
|
"kind": "morphism",
|
|
"name": "registrar_interaccion",
|
|
"inputs": { "cliente": "cliente_ref" },
|
|
"params": ["interaccion_id", "canal", "nota", "timestamp"],
|
|
"next_view": "interaccion_list"
|
|
}
|
|
},
|
|
"cliente_detail": {
|
|
"kind": "detail",
|
|
"title": "Ficha del cliente",
|
|
"entity": "Cliente",
|
|
"fields": [
|
|
{ "field": "nombre", "label": "Nombre" },
|
|
{ "field": "email", "label": "Email" },
|
|
{ "field": "empresa", "label": "Empresa" }
|
|
],
|
|
"related": [
|
|
{
|
|
"title": "Oportunidades",
|
|
"entity": "Oportunidad",
|
|
"via_field": "cliente_id",
|
|
"columns": [
|
|
{ "field": "titulo", "label": "Título", "weight": 2.5 },
|
|
{ "field": "etapa", "label": "Etapa", "weight": 1.2 },
|
|
{ "field": "monto", "label": "Monto", "weight": 1.2, "format": { "kind": "currency", "symbol": "$" } }
|
|
]
|
|
},
|
|
{
|
|
"title": "Interacciones",
|
|
"entity": "Interaccion",
|
|
"via_field": "cliente_id",
|
|
"columns": [
|
|
{ "field": "canal", "label": "Canal", "weight": 1.0 },
|
|
{ "field": "nota", "label": "Nota", "weight": 3.0 },
|
|
{ "field": "timestamp", "label": "Fecha", "weight": 1.2 }
|
|
]
|
|
}
|
|
]
|
|
},
|
|
"oportunidad_detail": {
|
|
"kind": "detail",
|
|
"title": "Ficha de la oportunidad",
|
|
"entity": "Oportunidad",
|
|
"fields": [
|
|
{ "field": "titulo", "label": "Título" },
|
|
{ "field": "etapa", "label": "Etapa" },
|
|
{ "field": "monto", "label": "Monto", "format": { "kind": "currency", "symbol": "$" } },
|
|
{ "field": "currency", "label": "Moneda" },
|
|
{ "field": "cliente_id", "label": "Cliente", "ref_entity": "Cliente" },
|
|
{ "field": "timestamp", "label": "Fecha" }
|
|
]
|
|
}
|
|
}
|
|
}
|