From 1511ca97d9563fcae1d5aa3361142d52a92e5dff Mon Sep 17 00:00:00 2001 From: Lizandro Guarnizo <77708265+lizandrogd@users.noreply.github.com> Date: Sat, 16 May 2026 21:36:59 -0500 Subject: [PATCH] up --- pkg/models/notif_config.go | 14 +++++ pkg/services/notif_dispatch.go | 13 +++-- resources/views/facturas.html | 36 +++++++------ resources/views/portal/proyecto.html | 76 ++++++++++++++++++--------- rest/controllers/portal_controller.go | 24 ++++++--- rest/routes/portal.go | 1 + 6 files changed, 114 insertions(+), 50 deletions(-) diff --git a/pkg/models/notif_config.go b/pkg/models/notif_config.go index d3f2e61..daa9a5d 100644 --- a/pkg/models/notif_config.go +++ b/pkg/models/notif_config.go @@ -116,3 +116,17 @@ func MarcarTodasLeidas(tipoUsuario string, usuarioID uint) error { Where("tipo_usuario = ? AND (usuario_id = ? OR usuario_id = 0)", tipoUsuario, usuarioID). Update("leida", true).Error } + +func CountUnreadFacturaNotifs(usuarioID uint) int64 { + var count int64 + app.Http.Database.DB.Model(&SistemaNotificacion{}). + Where("tipo_usuario = 'portal_user' AND usuario_id = ? AND icono = '🧾' AND leida = false", usuarioID). + Count(&count) + return count +} + +func MarcarNotifsFacturasLeidas(usuarioID uint) error { + return app.Http.Database.DB.Model(&SistemaNotificacion{}). + Where("tipo_usuario = 'portal_user' AND usuario_id = ? AND icono = '🧾'", usuarioID). + Update("leida", true).Error +} diff --git a/pkg/services/notif_dispatch.go b/pkg/services/notif_dispatch.go index ded5df3..11365f2 100644 --- a/pkg/services/notif_dispatch.go +++ b/pkg/services/notif_dispatch.go @@ -147,10 +147,15 @@ func DispatchFacturaSubida(factura *models.Factura) { clienteNombre = factura.Cliente.Nombre } + baseURL := getAppURL() + // Construir URL apuntando a la pestaΓ±a Facturas del proyecto (o dashboard si no tiene proyecto) + portalURL := baseURL + "/portal/dashboard" + if factura.ProyectoID != nil && factura.Proyecto != nil && factura.Proyecto.Slug != "" { + portalURL = baseURL + "/portal/proyecto/" + factura.Proyecto.Slug + "?tab=Facturas" + } + for _, u := range portalUsers { u := u - baseURL := getAppURL() - portalURL := baseURL + "/portal/dashboard" titulo := fmt.Sprintf("Nueva factura disponible: %s", factura.Numero) cuerpo := fmt.Sprintf("Factura %s por %s %.2f", factura.Numero, factura.Moneda, factura.Monto) @@ -169,8 +174,8 @@ func DispatchFacturaSubida(factura *models.Factura) { } if cfg.CanalTelegram && u.TelegramChatID != "" { ts := NewTelegramService() - msg := fmt.Sprintf("🧾 Nueva factura disponible\nNΓΊmero: %s\nMonto: %s %.2f", - factura.Numero, factura.Moneda, factura.Monto) + msg := fmt.Sprintf("🧾 Nueva factura disponible\nNΓΊmero: %s\nMonto: %s %.2f\n\nπŸ”— Ver en el portal", + factura.Numero, factura.Moneda, factura.Monto, portalURL) if err := ts.SendMessageWithToken(u.TelegramChatID, msg, getAdminBotToken()); err != nil { log.Printf("[Notif] Error telegram portal_user %d: %v", u.ID, err) } diff --git a/resources/views/facturas.html b/resources/views/facturas.html index 4806e8f..2664df5 100644 --- a/resources/views/facturas.html +++ b/resources/views/facturas.html @@ -49,23 +49,27 @@ - - - + +
+ + +
- - - + +
+ + +
diff --git a/resources/views/portal/proyecto.html b/resources/views/portal/proyecto.html index 9f3688e..55897b4 100644 --- a/resources/views/portal/proyecto.html +++ b/resources/views/portal/proyecto.html @@ -25,12 +25,15 @@
@@ -212,31 +215,32 @@
- {{ range .facturas }} -
-
-
- {{ if .Numero }}{{ .Numero }}{{ else }}Factura #{{ .ID }}{{ end }} - {{ .Estado }} + +

Sin facturas disponibles.

@@ -253,9 +257,19 @@ function portalProyecto() { loadingTickets: false, creandoTicket: false, ticketForm: { titulo:'', descripcion:'', prioridad:'media' }, + facturas: [], + facturasNoLeidas: 0, async init() { + // Soporte de URL param ?tab=Facturas + const params = new URLSearchParams(window.location.search); + const tabParam = params.get('tab'); + if (tabParam) this.activeTab = tabParam; await this.loadTickets(); + // Si la tab activa es Facturas, marcar como leΓ­das automΓ‘ticamente + if (this.activeTab === 'Facturas') { + this.marcarFacturasLeidas(); + } }, async loadTickets() { @@ -269,6 +283,12 @@ function portalProyecto() { _noLeidos: (t.mensajes||[]).filter(m => m.es_admin && !m.leido_portal).length, })); this.recalcNoLeidos(); + // Cargar facturas y conteo de no leΓ­das + this.facturasNoLeidas = r.data.facturasNoLeidas || 0; + this.facturas = (r.data.facturas||[]).map(f => ({ + ...f, + _nuevo: this.facturasNoLeidas > 0, + })); } catch(e){} finally { this.loadingTickets=false; } }, @@ -302,6 +322,14 @@ function portalProyecto() { this.recalcNoLeidos(); }, + async marcarFacturasLeidas() { + if (this.facturasNoLeidas > 0) { + this.facturasNoLeidas = 0; + this.facturas.forEach(f => { f._nuevo = false; }); + axios.post('/portal/mis-notifs/marcar-facturas').catch(()=>{}); + } + }, + async crearTicket() { this.creandoTicket=true; try { diff --git a/rest/controllers/portal_controller.go b/rest/controllers/portal_controller.go index eae4ef3..d8e0c62 100644 --- a/rest/controllers/portal_controller.go +++ b/rest/controllers/portal_controller.go @@ -173,17 +173,29 @@ func PortalGetProyectoData(c *fiber.Ctx) error { entregables, _ := models.GetEntregablesByProyecto(proy.ID, true) tickets, _ := models.GetTicketsByPortalUser(u.ID) facturas, _ := models.GetFacturasByCliente(proy.ClienteID, true) + facturasNoLeidas := models.CountUnreadFacturaNotifs(u.ID) return c.JSON(fiber.Map{ - "proyecto": proy, - "fases": fases, - "avances": avances, - "entregables": entregables, - "tickets": tickets, - "facturas": facturas, + "proyecto": proy, + "fases": fases, + "avances": avances, + "entregables": entregables, + "tickets": tickets, + "facturas": facturas, + "facturasNoLeidas": facturasNoLeidas, }) } +// PortalMarcarFacturasLeidas marca las notificaciones de facturas como leΓ­das. +func PortalMarcarFacturasLeidas(c *fiber.Ctx) error { + u := middlewares.PortalUserFromLocals(c) + if u == nil { + return c.Status(401).JSON(fiber.Map{"error": "no autenticado"}) + } + _ = models.MarcarNotifsFacturasLeidas(u.ID) + return c.JSON(fiber.Map{"ok": true}) +} + // PortalCrearTicket crea un nuevo ticket de soporte. func PortalCrearTicket(c *fiber.Ctx) error { u := middlewares.PortalUserFromLocals(c) diff --git a/rest/routes/portal.go b/rest/routes/portal.go index 64c4d0f..96d614d 100644 --- a/rest/routes/portal.go +++ b/rest/routes/portal.go @@ -35,6 +35,7 @@ func PortalRoutes(app fiber.Router) { portal.Get("/mis-notifs", controllers.PortalGetMisNotifs) portal.Put("/mis-notifs/:id/leida", controllers.PortalMarcarNotifLeida) portal.Post("/mis-notifs/marcar-todas", controllers.PortalMarcarTodasLeidas) + portal.Post("/mis-notifs/marcar-facturas", controllers.PortalMarcarFacturasLeidas) // Partner: recursos y comunicados (solo usuarios con rol partner) portal.Get("/partner-recursos", middlewares.PortalPartnerOnly(), controllers.PortalPartnerRecursos)