feat(umind): logo propio, plantillas de agente por rubro y página promocional

Logo. Hasta ahora no había: era el texto "uM" en un cuadrado, con tamaño y
esquinas distintas en cada pantalla. Ahora hay un mark — la "u" del nombre con
el punto de pulso integrado — probado a 16px, 20px y 32px sobre claro y oscuro,
que es donde se cae la mayoría de los logos. Reemplaza el texto en el portal,
el header, el sidebar del Studio y la promo, y va también de favicon.

Plantillas por rubro. Un agente recién creado no sabía nada, y escribir el
conocimiento desde cero frente a un campo en blanco es donde la mayoría
abandona. Al crear uno se puede elegir un punto de partida —tienda,
restaurante, consultorio, servicios, peluquería o la base genérica— y arranca
con sus notas ya escritas, listas para editar.

Los valores de ejemplo van entre corchetes a propósito, para que se vea que hay
que reemplazarlos: una nota que parezca dato real termina en boca del asistente.
Un test lo verifica nota por nota. Y la plantilla de salud abre con el límite —
el asistente no da consejo médico ni interpreta síntomas — porque es el único
rubro donde una respuesta inventada hace daño de verdad.

El catálogo vive en código, no en base: es contenido editorial que mejoramos
nosotros, no configuración que cada instalación toca por su lado. Las notas se
cargan en segundo plano porque cada una necesita sus embeddings, y hacer
esperar el alta sería castigar justo al que eligió plantilla.

Con las opciones nuevas el formulario pasaba de largo la pantalla y el botón de
guardar quedaba abajo del borde: el modal ahora tiene su propio scroll.

Y la página promocional de uMind queda servida en /umind.html.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
Lizandro Guarnizo
2026-08-19 18:21:46 -05:00
co-authored by Claude Opus 5
parent 3ea17b0980
commit af71b66bb8
17 changed files with 1797 additions and 38 deletions
@@ -198,6 +198,9 @@ type umindAgenteReq struct {
MensajeBienvenida string `json:"mensaje_bienvenida"`
Color string `json:"color"`
Activo bool `json:"activo"`
// PlantillaRubro precarga el conocimiento base de un rubro al crear el
// agente. Vacío = agente en blanco, como era antes.
PlantillaRubro string `json:"plantilla_rubro"`
}
func CreateUmindAgenteHandler(c *fiber.Ctx) error {
@@ -233,6 +236,14 @@ func CreateUmindAgenteHandler(c *fiber.Ctx) error {
if err := models.CreateUmindAgente(agente); err != nil {
return c.Status(fiber.StatusInternalServerError).JSON(fiber.Map{"error": err.Error()})
}
// El conocimiento de la plantilla se carga en segundo plano: cada nota
// necesita sus embeddings y el agente ya existe. Hacer esperar el alta por
// esto sería castigar justo al que eligió una plantilla.
if clave := strings.TrimSpace(req.PlantillaRubro); clave != "" {
go services.AplicarPlantillaRubro(agente.ID, clave)
}
return c.Status(fiber.StatusCreated).JSON(fiber.Map{"ok": true, "id": agente.ID, "site_key": agente.SiteKey})
}
@@ -1022,3 +1033,10 @@ func ActualizarUmindDocumentoHandler(c *fiber.Ctx) error {
}
return c.JSON(fiber.Map{"ok": true})
}
// GetPlantillasRubroHandler lista los puntos de partida disponibles para un
// agente nuevo.
// GET /umind/plantillas-rubro
func GetPlantillasRubroHandler(c *fiber.Ctx) error {
return c.JSON(fiber.Map{"items": services.ResumenPlantillasRubro()})
}