feat(portal): descarga del cronograma del proyecto en Excel

El cliente ya veía el roadmap en pantalla pero no tenía cómo pasárselo a
alguien que no entra al portal.

El .xlsx se genera con archive/zip + encoding/xml de la stdlib en vez de
sumar una dependencia de Excel para una sola pantalla. Un CSV renombrado
no servía: en Excel en español el separador y los acentos se rompen, y
esto es un archivo que el cliente le entrega a un tercero.

Detalles que hacen que Excel lo acepte y se lea bien: inline strings (sin
tabla sharedStrings, así ningún índice puede apuntar a la cadena
equivocada), encabezado en negrita y congelado, anchos de columna según
el contenido, y nombre de hoja saneado (Excel rechaza el archivo si pasa
de 31 caracteres o trae : \ / ? * [ ]).

El endpoint repite el chequeo de acceso de PortalProyecto: sin eso,
cualquiera con sesión de portal se bajaba el cronograma de otro cliente
adivinando el slug.

Verificado abriendo el archivo generado con openpyxl: 6 partes, CRC OK,
XML bien formado en todas, acentos y CJK intactos, negrita y panel
congelado donde corresponde.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
Lizandro Guarnizo
2026-08-14 22:12:45 -05:00
co-authored by Claude Sonnet 5
parent 5182c45d11
commit 62c644b815
5 changed files with 358 additions and 0 deletions
+1
View File
@@ -21,6 +21,7 @@ func PortalRoutes(app fiber.Router) {
portal.Get("/dashboard", controllers.PortalDashboard)
portal.Get("/proyecto/:slug", controllers.PortalProyecto)
portal.Get("/proyecto/:slug/cronograma.xlsx", controllers.PortalDescargarCronograma)
// API JSON para Alpine.js
portal.Get("/api/proyectos", controllers.PortalGetProyectos)