diff --git a/Controllers/NotificationsController.cs b/Controllers/NotificationsController.cs index d842ae2..ccdff7d 100644 --- a/Controllers/NotificationsController.cs +++ b/Controllers/NotificationsController.cs @@ -1,5 +1,7 @@ +using ClosedXML.Excel; using line_gestao_api.Data; using line_gestao_api.Dtos; +using line_gestao_api.Models; using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Mvc; using Microsoft.EntityFrameworkCore; @@ -68,4 +70,160 @@ public class NotificationsController : ControllerBase return NoContent(); } + [HttpPatch("read-all")] + [HttpPatch("/notifications/read-all")] + public async Task MarkAllAsRead( + [FromQuery] string? filter, + [FromBody] NotificationSelectionRequest? request) + { + var utcNow = DateTime.UtcNow; + var query = ApplySelectionAndFilter(_db.Notifications, filter, request?.NotificationIds) + .Where(n => !n.Lida); + + await query.ExecuteUpdateAsync(updates => updates + .SetProperty(n => n.Lida, true) + .SetProperty(n => n.LidaEm, utcNow)); + + return NoContent(); + } + + [HttpGet("export")] + [HttpGet("/notifications/export")] + public async Task ExportNotifications([FromQuery] string? filter) + { + var query = ApplySelectionAndFilter(_db.Notifications.AsNoTracking(), filter, null); + return await ExportNotificationsAsync(query, filter); + } + + [HttpPost("export")] + [HttpPost("/notifications/export")] + public async Task ExportNotifications( + [FromQuery] string? filter, + [FromBody] NotificationSelectionRequest? request) + { + var query = ApplySelectionAndFilter(_db.Notifications.AsNoTracking(), filter, request?.NotificationIds); + return await ExportNotificationsAsync(query, filter); + } + + private async Task ExportNotificationsAsync(IQueryable query, string? filter) + { + var rows = await ( + from notification in query + join vigencia in _db.VigenciaLines.AsNoTracking() + on notification.VigenciaLineId equals vigencia.Id into vigencias + from vigencia in vigencias.DefaultIfEmpty() + orderby notification.ReferenciaData descending, notification.Data descending + select new NotificationExportRow( + notification.Linha ?? vigencia.Linha, + notification.Cliente ?? vigencia.Cliente, + notification.Usuario ?? vigencia.Usuario, + notification.ReferenciaData ?? vigencia.DtTerminoFidelizacao, + notification.Tipo)) + .ToListAsync(); + + using var workbook = new XLWorkbook(); + var worksheet = workbook.Worksheets.Add("Notificacoes"); + + var normalizedFilter = NormalizeFilter(filter); + var dateHeader = normalizedFilter switch + { + "vencidas" or "vencido" => "Data da Expiração", + "a-vencer" or "avencer" => "Data a Vencer", + _ => "Data de Referência" + }; + + var headers = new[] + { + "Número da Linha", + "Cliente", + "Usuário", + dateHeader, + "Status" + }; + + for (var i = 0; i < headers.Length; i++) + { + worksheet.Cell(1, i + 1).Value = headers[i]; + } + + var headerRange = worksheet.Range(1, 1, 1, headers.Length); + headerRange.Style.Font.Bold = true; + headerRange.Style.Fill.BackgroundColor = XLColor.LightGray; + headerRange.Style.Alignment.Horizontal = XLAlignmentHorizontalValues.Center; + + for (var i = 0; i < rows.Count; i++) + { + var row = rows[i]; + var rowIndex = i + 2; + worksheet.Cell(rowIndex, 1).Value = row.Linha ?? string.Empty; + worksheet.Cell(rowIndex, 2).Value = row.Cliente ?? string.Empty; + worksheet.Cell(rowIndex, 3).Value = row.Usuario ?? string.Empty; + worksheet.Cell(rowIndex, 4).Value = row.DataReferencia; + worksheet.Cell(rowIndex, 5).Value = row.Tipo; + } + + worksheet.Column(1).Width = 18; + worksheet.Column(2).Width = 26; + worksheet.Column(3).Width = 24; + worksheet.Column(4).Width = 20; + worksheet.Column(5).Width = 14; + + worksheet.Column(4).Style.DateFormat.Format = "dd/MM/yyyy"; + + worksheet.Columns().AdjustToContents(); + + using var stream = new MemoryStream(); + workbook.SaveAs(stream); + stream.Position = 0; + + var fileName = $"notificacoes-{DateTime.UtcNow:yyyyMMddHHmmss}.xlsx"; + return File( + stream.ToArray(), + "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet", + fileName); + } + + private static IQueryable ApplySelectionAndFilter( + IQueryable query, + string? filter, + IReadOnlyCollection? notificationIds) + { + query = ApplyFilter(query, filter); + + if (notificationIds is { Count: > 0 }) + { + query = query.Where(n => notificationIds.Contains(n.Id)); + } + + return query; + } + + private static IQueryable ApplyFilter(IQueryable query, string? filter) + { + var normalized = NormalizeFilter(filter); + return normalized switch + { + "a-vencer" or "avencer" => query.Where(n => n.Tipo == "AVencer"), + "vencidas" or "vencido" => query.Where(n => n.Tipo == "Vencido"), + _ => query + }; + } + + private static string? NormalizeFilter(string? filter) + { + return filter?.Trim().ToLowerInvariant(); + } + + private sealed record NotificationExportRow( + string? Linha, + string? Cliente, + string? Usuario, + DateTime? DataReferencia, + string Tipo); + + public sealed class NotificationSelectionRequest + { + public List? NotificationIds { get; set; } + } + }