|
1 | 1 | @page "/notifications" |
| 2 | +@using Aquiis.Professional.Core.Constants |
2 | 3 | @using Aquiis.Professional.Infrastructure.Services |
3 | 4 | @inject NotificationService NotificationService |
4 | 5 | @inject NavigationManager NavigationManager |
5 | 6 | @rendermode InteractiveServer |
| 7 | +@attribute [OrganizationAuthorize] |
6 | 8 | @namespace Aquiis.Professional.Features.Notifications.Pages |
7 | 9 |
|
8 | 10 | <PageTitle>Notification Center</PageTitle> |
|
16 | 18 | Here you can manage your notifications. |
17 | 19 | </p> |
18 | 20 | </div> |
19 | | - <button class="btn btn-secondary" @onclick="BackToDashboard"> |
20 | | - <i class="bi bi-arrow-left"></i> Back to Dashboard |
21 | | - </button> |
| 21 | + <div class="d-flex gap-2"> |
| 22 | + <button class="btn btn-outline-primary" @onclick="GoToPreferences"> |
| 23 | + <i class="bi bi-gear-fill"></i> Preferences |
| 24 | + </button> |
| 25 | + <button class="btn btn-secondary" @onclick="BackToDashboard"> |
| 26 | + <i class="bi bi-arrow-left"></i> Back to Dashboard |
| 27 | + </button> |
| 28 | + </div> |
| 29 | +</div> |
| 30 | + |
| 31 | +<!-- Search and Filters --> |
| 32 | +<div class="card mb-3"> |
| 33 | + <div class="card-body"> |
| 34 | + <div class="row g-3"> |
| 35 | + <!-- Search --> |
| 36 | + <div class="col-md-4"> |
| 37 | + <div class="input-group"> |
| 38 | + <span class="input-group-text"> |
| 39 | + <i class="bi bi-search"></i> |
| 40 | + </span> |
| 41 | + <input type="text" class="form-control" placeholder="Search notifications..." |
| 42 | + @bind="searchText" @bind:event="oninput" @bind:after="ApplyFilters" /> |
| 43 | + @if (!string.IsNullOrEmpty(searchText)) |
| 44 | + { |
| 45 | + <button class="btn btn-outline-secondary" @onclick="ClearSearch"> |
| 46 | + <i class="bi bi-x-lg"></i> |
| 47 | + </button> |
| 48 | + } |
| 49 | + </div> |
| 50 | + </div> |
| 51 | + |
| 52 | + <!-- Category Filter --> |
| 53 | + <div class="col-md-2"> |
| 54 | + <select class="form-select" @bind="filterCategory" @bind:after="ApplyFilters"> |
| 55 | + <option value="">All Categories</option> |
| 56 | + <option value="@NotificationConstants.Categories.Application">Application</option> |
| 57 | + <option value="@NotificationConstants.Categories.Document">Document</option> |
| 58 | + <option value="@NotificationConstants.Categories.Inspection">Inspection</option> |
| 59 | + <option value="@NotificationConstants.Categories.Lease">Lease</option> |
| 60 | + <option value="@NotificationConstants.Categories.Maintenance">Maintenance</option> |
| 61 | + <option value="@NotificationConstants.Categories.Message">Message</option> |
| 62 | + <option value="@NotificationConstants.Categories.Note">Note</option> |
| 63 | + <option value="@NotificationConstants.Categories.Payment">Payment</option> |
| 64 | + <option value="@NotificationConstants.Categories.Property">Property</option> |
| 65 | + <option value="@NotificationConstants.Categories.Report">Report</option> |
| 66 | + <option value="@NotificationConstants.Categories.Security">Security</option> |
| 67 | + <option value="@NotificationConstants.Categories.System">System</option> |
| 68 | + </select> |
| 69 | + </div> |
| 70 | + |
| 71 | + <!-- Type Filter --> |
| 72 | + <div class="col-md-2"> |
| 73 | + <select class="form-select" @bind="filterType" @bind:after="ApplyFilters"> |
| 74 | + <option value="">All Types</option> |
| 75 | + <option value="@NotificationConstants.Types.Error">Error</option> |
| 76 | + <option value="@NotificationConstants.Types.Info">Info</option> |
| 77 | + <option value="@NotificationConstants.Types.Success">Success</option> |
| 78 | + <option value="@NotificationConstants.Types.Warning">Warning</option> |
| 79 | + </select> |
| 80 | + </div> |
| 81 | + |
| 82 | + <!-- Status Filter --> |
| 83 | + <div class="col-md-2"> |
| 84 | + <select class="form-select" @bind="filterStatus" @bind:after="ApplyFilters"> |
| 85 | + <option value="">All Status</option> |
| 86 | + <option value="unread">Unread</option> |
| 87 | + <option value="read">Read</option> |
| 88 | + </select> |
| 89 | + </div> |
| 90 | + |
| 91 | + <!-- Mark All as Read --> |
| 92 | + <div class="col-md-2"> |
| 93 | + <button class="btn btn-success w-100" @onclick="MarkAllAsRead" |
| 94 | + disabled="@(!notifications.Any(n => !n.IsRead))"> |
| 95 | + <i class="bi bi-check-all"></i> Mark All Read |
| 96 | + </button> |
| 97 | + </div> |
| 98 | + </div> |
| 99 | + |
| 100 | + @if (HasActiveFilters()) |
| 101 | + { |
| 102 | + <div class="mt-2"> |
| 103 | + <button class="btn btn-sm btn-outline-secondary" @onclick="ClearAllFilters"> |
| 104 | + <i class="bi bi-x-circle"></i> Clear All Filters |
| 105 | + </button> |
| 106 | + <span class="ms-2 text-muted"> |
| 107 | + Showing @filteredNotifications.Count of @notifications.Count notifications |
| 108 | + </span> |
| 109 | + </div> |
| 110 | + } |
| 111 | + </div> |
22 | 112 | </div> |
23 | 113 |
|
24 | 114 | <div class="notification-list"> |
|
263 | 353 |
|
264 | 354 | @code { |
265 | 355 | private List<Notification> notifications = new List<Notification>(); |
| 356 | + private List<Notification> filteredNotifications = new List<Notification>(); |
266 | 357 | private List<Notification> sortedNotifications = new List<Notification>(); |
267 | 358 | private List<Notification> pagedNotifications = new List<Notification>(); |
268 | 359 |
|
269 | 360 | private Notification? selectedNotification; |
270 | 361 | private bool showMessageModal = false; |
271 | 362 |
|
272 | 363 | private string sortColumn = nameof(Notification.CreatedOn); |
273 | | - private bool sortAscending = true; |
| 364 | + private bool sortAscending = false; |
| 365 | + |
| 366 | + // Filter and search properties |
| 367 | + private string searchText = ""; |
| 368 | + private string filterCategory = ""; |
| 369 | + private string filterType = ""; |
| 370 | + private string filterStatus = ""; |
274 | 371 |
|
275 | 372 | private int currentPage = 1; |
276 | 373 | private int pageSize = 25; |
|
289 | 386 | notifications = await NotificationService.GetUnreadNotificationsAsync(); |
290 | 387 |
|
291 | 388 | notifications = new List<Notification>{ |
292 | | - new Notification { Id= Guid.NewGuid(), Title = "New message from John", Category = "Messages", Message = "Hey, can we meet tomorrow?", CreatedOn = DateTime.Now, IsRead = false }, |
293 | | - new Notification { Id= Guid.NewGuid(), Title = "Your report is ready", Category = "Reports", Message = "Your monthly report is now available.", CreatedOn = DateTime.Now.AddDays(-1), IsRead = false }, |
294 | | - new Notification { Id= Guid.NewGuid(), Title = "System maintenance scheduled", Category = "System", Message = "System maintenance is scheduled for tonight at 11 PM.", CreatedOn = DateTime.Now.AddDays(-5), IsRead = false }, |
295 | | - new Notification { Id= Guid.NewGuid(), Title = "New comment on your post", Category = "Comments", Message = "Alice commented on your post.", CreatedOn = DateTime.Now.AddDays(-2), IsRead = false }, |
296 | | - new Notification { Id= Guid.NewGuid(), Title = "Password will expire soon", Category = "Security", Message = "Your password will expire in 3 days.", CreatedOn = DateTime.Now.AddDays(-3), IsRead = false } |
| 389 | + new Notification { Id= Guid.NewGuid(), Title = "New message from John", Category = "Messages", Type = "Info", Message = "Hey, can we meet tomorrow?", CreatedOn = DateTime.Now, IsRead = false }, |
| 390 | + new Notification { Id= Guid.NewGuid(), Title = "Your report is ready", Category = "Reports", Type = "Success", Message = "Your monthly report is now available.", CreatedOn = DateTime.Now.AddDays(-1), IsRead = false }, |
| 391 | + new Notification { Id= Guid.NewGuid(), Title = "System maintenance scheduled", Category = "System", Type = "Warning", Message = "System maintenance is scheduled for tonight at 11 PM.", CreatedOn = DateTime.Now.AddDays(-5), IsRead = false }, |
| 392 | + new Notification { Id= Guid.NewGuid(), Title = "New comment on your post", Category = "Comments", Type = "Info", Message = "Alice commented on your post.", CreatedOn = DateTime.Now.AddDays(-2), IsRead = false }, |
| 393 | + new Notification { Id= Guid.NewGuid(), Title = "Password will expire soon", Category = "Security", Type = "Warning", Message = "Your password will expire in 3 days.", CreatedOn = DateTime.Now.AddDays(-3), IsRead = false } |
297 | 394 | }; |
| 395 | + |
| 396 | + filteredNotifications = notifications; |
298 | 397 | SortAndPaginateNotifications(); |
299 | | - |
300 | 398 | } |
301 | 399 |
|
302 | 400 | private void SortTable(string column) |
|
313 | 411 | SortAndPaginateNotifications(); |
314 | 412 | } |
315 | 413 |
|
| 414 | + private void ApplyFilters() |
| 415 | + { |
| 416 | + filteredNotifications = notifications.Where(n => |
| 417 | + { |
| 418 | + // Search filter |
| 419 | + if (!string.IsNullOrEmpty(searchText)) |
| 420 | + { |
| 421 | + var search = searchText.ToLower(); |
| 422 | + if (!n.Title.ToLower().Contains(search) && |
| 423 | + !n.Message.ToLower().Contains(search)) |
| 424 | + { |
| 425 | + return false; |
| 426 | + } |
| 427 | + } |
| 428 | + |
| 429 | + // Category filter |
| 430 | + if (!string.IsNullOrEmpty(filterCategory) && n.Category != filterCategory) |
| 431 | + { |
| 432 | + return false; |
| 433 | + } |
| 434 | + |
| 435 | + // Type filter |
| 436 | + if (!string.IsNullOrEmpty(filterType) && n.Type != filterType) |
| 437 | + { |
| 438 | + return false; |
| 439 | + } |
| 440 | + |
| 441 | + // Status filter |
| 442 | + if (!string.IsNullOrEmpty(filterStatus)) |
| 443 | + { |
| 444 | + if (filterStatus == "read" && !n.IsRead) |
| 445 | + return false; |
| 446 | + if (filterStatus == "unread" && n.IsRead) |
| 447 | + return false; |
| 448 | + } |
| 449 | + |
| 450 | + return true; |
| 451 | + }).ToList(); |
| 452 | + |
| 453 | + currentPage = 1; |
| 454 | + SortAndPaginateNotifications(); |
| 455 | + } |
| 456 | + |
| 457 | + private void ClearSearch() |
| 458 | + { |
| 459 | + searchText = ""; |
| 460 | + ApplyFilters(); |
| 461 | + } |
| 462 | + |
| 463 | + private void ClearAllFilters() |
| 464 | + { |
| 465 | + searchText = ""; |
| 466 | + filterCategory = ""; |
| 467 | + filterType = ""; |
| 468 | + filterStatus = ""; |
| 469 | + ApplyFilters(); |
| 470 | + } |
| 471 | + |
| 472 | + private bool HasActiveFilters() |
| 473 | + { |
| 474 | + return !string.IsNullOrEmpty(searchText) || |
| 475 | + !string.IsNullOrEmpty(filterCategory) || |
| 476 | + !string.IsNullOrEmpty(filterType) || |
| 477 | + !string.IsNullOrEmpty(filterStatus); |
| 478 | + } |
| 479 | + |
| 480 | + private async Task MarkAllAsRead() |
| 481 | + { |
| 482 | + foreach (var notification in notifications.Where(n => !n.IsRead)) |
| 483 | + { |
| 484 | + notification.IsRead = true; |
| 485 | + notification.ReadOn = DateTime.UtcNow; |
| 486 | + } |
| 487 | + await Task.CompletedTask; |
| 488 | + SortAndPaginateNotifications(); |
| 489 | + } |
| 490 | + |
316 | 491 | private void SortAndPaginateNotifications() |
317 | 492 | { |
| 493 | + // Use filtered notifications if filters are active |
| 494 | + var sourceList = HasActiveFilters() ? filteredNotifications : notifications; |
| 495 | + |
318 | 496 | // Sort |
319 | 497 | sortedNotifications = sortColumn switch |
320 | 498 | { |
321 | 499 | nameof(Notification.Title) => sortAscending |
322 | | - ? notifications.OrderBy(n => n.Title).ToList() |
323 | | - : notifications.OrderByDescending(n => n.Title).ToList(), |
| 500 | + ? sourceList.OrderBy(n => n.Title).ToList() |
| 501 | + : sourceList.OrderByDescending(n => n.Title).ToList(), |
324 | 502 | nameof(Notification.Category) => sortAscending |
325 | | - ? notifications.OrderBy(n => n.Category).ToList() |
326 | | - : notifications.OrderByDescending(n => n.Category).ToList(), |
| 503 | + ? sourceList.OrderBy(n => n.Category).ToList() |
| 504 | + : sourceList.OrderByDescending(n => n.Category).ToList(), |
327 | 505 | nameof(Notification.Message) => sortAscending |
328 | | - ? notifications.OrderBy(n => n.Message).ToList() |
329 | | - : notifications.OrderByDescending(n => n.Message).ToList(), |
| 506 | + ? sourceList.OrderBy(n => n.Message).ToList() |
| 507 | + : sourceList.OrderByDescending(n => n.Message).ToList(), |
330 | 508 | nameof(Notification.CreatedOn) => sortAscending |
331 | | - ? notifications.OrderBy(n => n.CreatedOn).ToList() |
332 | | - : notifications.OrderByDescending(n => n.CreatedOn).ToList(), |
333 | | - _ => notifications.OrderBy(n => n.CreatedOn).ToList() |
| 509 | + ? sourceList.OrderBy(n => n.CreatedOn).ToList() |
| 510 | + : sourceList.OrderByDescending(n => n.CreatedOn).ToList(), |
| 511 | + _ => sourceList.OrderByDescending(n => n.CreatedOn).ToList() |
334 | 512 | }; |
335 | 513 |
|
336 | 514 | // Paginate |
|
393 | 571 | NavigationManager.NavigateTo("/"); |
394 | 572 | } |
395 | 573 |
|
| 574 | + private void GoToPreferences() |
| 575 | + { |
| 576 | + NavigationManager.NavigateTo("/notifications/preferences"); |
| 577 | + } |
| 578 | + |
396 | 579 | // Modal Methods |
397 | 580 | private void OpenMessageModal(Guid id) |
398 | 581 | { |
|
0 commit comments