Files
soft_usite/pkg/services/plantilla_preview_service.go
T
Lizandro GuarnizoandClaude Opus 5 86cdb2d368 feat(plantillas): ver cómo queda la plantilla mientras se edita
Se editaba HTML a ciegas: había que guardar y generar un documento real para
saber si estaba bien. Ahora el modal tiene un panel de vista previa al lado del
editor, que se actualiza mientras se escribe y se abre solo al editar una
plantilla o al importar una con IA.

Se ejecuta la plantilla de verdad contra datos de ejemplo, no se reemplaza
texto: es la única forma de que {{range .Items}} y los campos anidados se vean
como van a salir, y de que un error de sintaxis aparezca mientras se edita en
vez de al generar el PDF. Los datos de ejemplo salen de DatosBaseDocumento,
igual que en producción, para que la vista previa no muestre una cosa y el
documento otra.

El iframe va en sandbox sin scripts ni same-origin: el HTML lo escribe un
admin, pero no tiene por qué correr con los permisos del panel.

De paso, la ayuda de variables ofrecía {{.Cliente.Nit}}, que no existe en el
modelo — el campo es Documento.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-17 21:28:06 -05:00

68 lines
2.4 KiB
Go

package services
import (
"bytes"
"fmt"
"text/template"
"github.com/sujit-baniya/fiber-boilerplate/pkg/models"
)
// RenderizarPlantillaEjemplo ejecuta la plantilla con datos de muestra y
// devuelve el HTML resultante.
//
// Se ejecuta de verdad, no se hace un reemplazo de texto: es la única forma de
// que {{range .Items}} y los campos anidados se vean como van a salir, y de que
// un error de sintaxis aparezca mientras se edita y no al generar el documento.
func RenderizarPlantillaEjemplo(tipo, contenidoHTML string) (string, error) {
tmpl, err := template.New("preview").Parse(contenidoHTML)
if err != nil {
return "", fmt.Errorf("sintaxis inválida: %w", err)
}
var buf bytes.Buffer
if err := tmpl.Execute(&buf, DatosDeEjemploPlantilla(tipo)); err != nil {
return "", fmt.Errorf("no se pudo renderizar: %w", err)
}
return buf.String(), nil
}
// DatosDeEjemploPlantilla arma los mismos campos que le pasa cada generador
// real (ver CrearCotizacion, contrato_documento_service, cuenta_cobro), con
// valores inventados. Si esto se desincroniza de los generadores, la vista
// previa miente — por eso sale de DatosBaseDocumento, igual que en producción.
func DatosDeEjemploPlantilla(tipo string) map[string]interface{} {
cliente := &models.Cliente{
Nombre: "Acme S.A.S.",
Empresa: "Acme S.A.S.",
Documento: "900.123.456-7",
Email: "contacto@acme.com",
Telefono: "+57 300 123 4567",
}
extra := map[string]interface{}{
"Cliente": cliente,
"Alcance": "Desarrollo del sitio web institucional, con panel de administración y tres integraciones.",
"TipoProyecto": "Sitio web",
"Total": 4500000.0,
"Items": []ItemCotizacion{
{Descripcion: "Diseño de interfaz", Cantidad: 1, ValorUnitario: 1500000, Unidad: "servicio"},
{Descripcion: "Desarrollo frontend", Cantidad: 40, ValorUnitario: 50000, Unidad: "hora"},
{Descripcion: "Integración con pasarela de pagos", Cantidad: 1, ValorUnitario: 1000000, Unidad: "servicio"},
},
}
switch tipo {
case "contrato":
extra["Servicio"] = "Mantenimiento mensual del sitio"
extra["Periodicidad"] = "mensual"
case "acta":
extra["Proyecto"] = "Sitio web Acme"
extra["Entregables"] = "Sitio publicado, manual de uso y capacitación."
case "cuenta_cobro":
extra["Concepto"] = "Mantenimiento mensual — marzo"
extra["Numero"] = "CC-0042"
}
return DatosBaseDocumento(extra)
}