using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;
using PowderCoating.Core.Interfaces;
using PowderCoating.Web.Extensions;
namespace PowderCoating.Web.Controllers;
[Authorize]
public class InAppNotificationsController : Controller
{
private readonly IUnitOfWork _unitOfWork;
private readonly ITenantContext _tenant;
public InAppNotificationsController(IUnitOfWork unitOfWork, ITenantContext tenant)
{
_unitOfWork = unitOfWork;
_tenant = tenant;
}
///
/// Displays the paginated notification history page (read and unread). SuperAdmins see only platform-level notifications (CompanyId 0) via IgnoreQueryFilters; regular users rely on the global query filter for tenant isolation.
///
public async Task Index(int pageNumber = 1, int pageSize = 25)
{
pageNumber = Math.Max(1, pageNumber);
pageSize = pageSize is 10 or 25 or 50 ? pageSize : 25;
var all = _tenant.IsPlatformAdmin()
? (await _unitOfWork.InAppNotifications.FindAsync(
n => !n.IsDeleted && n.CompanyId == 0, ignoreQueryFilters: true)).ToList()
: (await _unitOfWork.InAppNotifications.GetAllAsync()).ToList();
var totalCount = all.Count;
var tz = ViewBag.CompanyTimeZone as string;
var items = all
.OrderByDescending(n => n.CreatedAt)
.Skip((pageNumber - 1) * pageSize)
.Take(pageSize)
.Select(n => new
{
n.Id,
n.Title,
n.Message,
n.Link,
n.NotificationType,
n.IsRead,
n.ReadAt,
CreatedAt = n.CreatedAt
})
.ToList();
ViewBag.TotalCount = totalCount;
ViewBag.PageNumber = pageNumber;
ViewBag.PageSize = pageSize;
ViewBag.TotalPages = (int)Math.Ceiling((double)totalCount / pageSize);
return View(items);
}
///
/// AJAX endpoint that returns the 20 most recent notifications (read and unread) for the bell dropdown.
///
[HttpGet]
public async Task Recent()
{
var all = _tenant.IsPlatformAdmin()
? (await _unitOfWork.InAppNotifications.FindAsync(
n => !n.IsDeleted && n.CompanyId == 0, ignoreQueryFilters: true)).ToList()
: (await _unitOfWork.InAppNotifications.GetAllAsync()).ToList();
var tz = ViewBag.CompanyTimeZone as string;
var items = all
.OrderByDescending(n => n.CreatedAt)
.Take(20)
.Select(n => new { n.Id, n.Title, n.Message, n.Link, n.NotificationType, n.IsRead, n.CreatedAt })
.ToList();
var unreadCount = items.Count(n => !n.IsRead);
return Json(new { count = unreadCount, items = items.Select(n => new {
n.Id, n.Title, n.Message, n.Link, n.NotificationType, n.IsRead,
CreatedAt = n.CreatedAt.Tz(tz).ToString("MMM d, h:mm tt")
}) });
}
///
/// AJAX endpoint that returns only unread notifications (up to 20) plus an unread count for the bell badge.
///
[HttpGet]
public async Task Unread()
{
var items = _tenant.IsPlatformAdmin()
? (await _unitOfWork.InAppNotifications.FindAsync(
n => !n.IsDeleted && n.CompanyId == 0 && !n.IsRead, ignoreQueryFilters: true))
.OrderByDescending(n => n.CreatedAt).Take(20).ToList()
: (await _unitOfWork.InAppNotifications.FindAsync(n => !n.IsRead))
.OrderByDescending(n => n.CreatedAt).Take(20).ToList();
var tz = ViewBag.CompanyTimeZone as string;
return Json(new { count = items.Count, items = items.Select(n => new {
n.Id, n.Title, n.Message, n.Link, n.NotificationType,
CreatedAt = n.CreatedAt.Tz(tz).ToString("MMM d, h:mm tt")
}) });
}
///
/// Marks a single notification as read. Tenant isolation is enforced manually for SuperAdmins (CompanyId 0 filter) because IgnoreQueryFilters bypasses the global company filter.
///
[HttpPost]
public async Task MarkRead(int id)
{
var notification = _tenant.IsPlatformAdmin()
? await _unitOfWork.InAppNotifications.FirstOrDefaultAsync(
n => n.Id == id && n.CompanyId == 0 && !n.IsDeleted, ignoreQueryFilters: true)
: await _unitOfWork.InAppNotifications.GetByIdAsync(id);
if (notification == null) return NotFound();
notification.IsRead = true;
notification.ReadAt = DateTime.UtcNow;
notification.UpdatedAt = DateTime.UtcNow;
await _unitOfWork.CompleteAsync();
return Json(new { success = true });
}
///
/// Marks every unread notification as read for the current user's scope in a single SaveChanges call.
///
[HttpPost]
public async Task MarkAllRead()
{
var now = DateTime.UtcNow;
var unread = _tenant.IsPlatformAdmin()
? (await _unitOfWork.InAppNotifications.FindAsync(
n => !n.IsDeleted && n.CompanyId == 0 && !n.IsRead, ignoreQueryFilters: true)).ToList()
: (await _unitOfWork.InAppNotifications.FindAsync(n => !n.IsRead)).ToList();
foreach (var n in unread)
{
n.IsRead = true;
n.ReadAt = now;
n.UpdatedAt = now;
}
await _unitOfWork.CompleteAsync();
return Json(new { success = true, count = unread.Count });
}
}