Yuyuqt
add: blazor web assembly.
4433399
Raw
History Blame Contribute Delete
7.58 kB
@page "/notifications"
@using LibraryManagement.Shared.Models
@using Microsoft.AspNetCore.Authorization
@using Microsoft.AspNetCore.Components.Authorization
@using BlazorFrontend.Services
@inject LibraryApiClient ApiClient
@inject IJSRuntime JSRuntime
<PageTitle>Notifications</PageTitle>
<AuthorizeView>
<Authorized>
<div class="max-w-4xl mx-auto py-8 px-4 sm:px-6 lg:px-8">
<div class="mb-8 animate-in fade-in slide-in-from-top-4 duration-500 flex items-center justify-between">
<div>
<h1 class="text-3xl font-extrabold tracking-tight text-mystic-900">Notifications</h1>
<p class="text-mystic-500 font-medium mt-1">Stay updated with your library account activity.</p>
</div>
@if (_notifications != null && _notifications.Any(n => !n.IsRead))
{
<button @onclick="MarkAllAsRead" class="text-sm font-medium text-mystic-600 hover:text-mystic-900 bg-mystic-100 hover:bg-mystic-200 px-4 py-2 rounded-full transition-colors">
Mark all as read
</button>
}
</div>
<div class="space-y-4 animate-in fade-in slide-in-from-bottom-8 duration-700 delay-100">
@if (_isLoading)
{
<div class="flex justify-center py-12">
<div class="animate-spin rounded-full h-8 w-8 border-b-2 border-mystic-900"></div>
</div>
}
else if (_notifications == null || !_notifications.Any())
{
<div class="bg-white rounded-2xl p-12 text-center border border-mystic-200 shadow-sm">
<div class="w-16 h-16 bg-mystic-50 rounded-full flex items-center justify-center mx-auto mb-4">
<svg class="w-8 h-8 text-mystic-300" fill="none" viewBox="0 0 24 24" stroke="currentColor">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="1.5" d="M15 17h5l-1.405-1.405A2.032 2.032 0 0118 14.158V11a6.002 6.002 0 00-4-5.659V5a2 2 0 10-4 0v.341C7.67 6.165 6 8.388 6 11v3.159c0 .538-.214 1.055-.595 1.436L4 17h5m6 0v1a3 3 0 11-6 0v-1m6 0H9" />
</svg>
</div>
<h3 class="text-lg font-bold text-mystic-900 mb-1">No notifications yet</h3>
<p class="text-mystic-500 text-sm">When you get updates about your books or subscription, they'll show up here.</p>
</div>
}
else
{
@foreach (var notif in _notifications)
{
<div class="bg-white rounded-2xl p-5 border border-mystic-200 shadow-sm hover:shadow-md transition-shadow relative overflow-hidden group @(!notif.IsRead ? "bg-mystic-50/50" : "")">
@if (!notif.IsRead)
{
<div class="absolute left-0 top-0 bottom-0 w-1 bg-mystic-900"></div>
}
<div class="flex gap-4">
<div class="flex-shrink-0 mt-1">
@if (notif.Type == "Warning")
{
<div class="w-10 h-10 rounded-full bg-amber-100 flex items-center justify-center">
<svg class="w-5 h-5 text-amber-600" fill="none" viewBox="0 0 24 24" stroke="currentColor"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M12 9v2m0 4h.01m-6.938 4h13.856c1.54 0 2.502-1.667 1.732-3L13.732 4c-.77-1.333-2.694-1.333-3.464 0L3.34 16c-.77 1.333.192 3 1.732 3z" /></svg>
</div>
}
else if (notif.Type == "Info")
{
<div class="w-10 h-10 rounded-full bg-blue-100 flex items-center justify-center">
<svg class="w-5 h-5 text-blue-600" fill="none" viewBox="0 0 24 24" stroke="currentColor"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M13 16h-1v-4h-1m1-4h.01M21 12a9 9 0 11-18 0 9 9 0 0118 0z" /></svg>
</div>
}
else
{
<div class="w-10 h-10 rounded-full bg-mystic-100 flex items-center justify-center">
<svg class="w-5 h-5 text-mystic-600" fill="none" viewBox="0 0 24 24" stroke="currentColor"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M15 17h5l-1.405-1.405A2.032 2.032 0 0118 14.158V11a6.002 6.002 0 00-4-5.659V5a2 2 0 10-4 0v.341C7.67 6.165 6 8.388 6 11v3.159c0 .538-.214 1.055-.595 1.436L4 17h5m6 0v1a3 3 0 11-6 0v-1m6 0H9" /></svg>
</div>
}
</div>
<div class="flex-1">
<div class="flex items-start justify-between gap-4">
<div>
<h4 class="text-sm font-bold text-mystic-900 @(!notif.IsRead ? "font-extrabold" : "")">@notif.Title</h4>
<p class="text-sm text-mystic-600 mt-1 leading-relaxed">@notif.Message</p>
</div>
<span class="text-xs font-medium text-mystic-400 whitespace-nowrap">@notif.CreatedAt.ToString("MMM dd, HH:mm")</span>
</div>
@if (!string.IsNullOrEmpty(notif.ActionLink))
{
<div class="mt-3">
<a href="@notif.ActionLink" class="text-sm font-semibold text-mystic-900 hover:text-mystic-600 underline underline-offset-2">
@notif.ActionText
</a>
</div>
}
</div>
</div>
</div>
}
}
</div>
</div>
</Authorized>
<NotAuthorized>
<div class="max-w-4xl mx-auto py-12 px-4 text-center">
<h2 class="text-2xl font-bold text-mystic-900 mb-4">Please sign in to view notifications</h2>
<a href="/login" class="btn-primary">Sign In</a>
</div>
</NotAuthorized>
</AuthorizeView>
@code {
private List<NotificationDto> _notifications = new();
private bool _isLoading = true;
protected override async Task OnInitializedAsync()
{
await LoadNotifications();
}
private async Task LoadNotifications()
{
try
{
_isLoading = true;
StateHasChanged();
var results = await ApiClient.GetNotificationsAsync();
_notifications = results.ToList();
}
catch (Exception ex)
{
Console.WriteLine($"Error loading notifications: {ex.Message}");
_notifications = new List<NotificationDto>();
}
finally
{
_isLoading = false;
StateHasChanged();
}
}
private async Task MarkAllAsRead()
{
try
{
var success = await ApiClient.MarkNotificationsReadAsync();
if (success)
{
await LoadNotifications();
}
}
catch (Exception ex)
{
Console.WriteLine($"Error marking notifications as read: {ex.Message}");
}
}
}