@page "/Rewards" @using BlazorFrontend.Services @using LibraryManagement.Shared.Models @inject LibraryApiClient ApiClient @inject IJSRuntime JS My Rewards

Loyalty Rewards

Earn points, redeem rewards, and track your loyalty journey.

@if (_account != null) {

Balance

@_account.CurrentBalance.ToString("N0")

Points

Tier

@_account.Tier

}
@if (_isLoading) {
} else { @if (_activeTab == "redeem") {
@if (!_availableRewards.Any()) {

No active rewards available. Check back soon!

} else {
@foreach (var reward in _availableRewards) { var canAfford = _account != null && _account.CurrentBalance >= reward.PointCost;
@reward.PointCost.ToString("N0") pts required

@reward.Name

@reward.Description

@reward.StockQuantity in stock @if (canAfford) { } else { Need more pts }
@if (reward.StockQuantity < 10) { Limited }
}
}

How to earn more points?

Borrow books, subscribe, and return on time to earn points!

Browse Books โ†’
} else if (_activeTab == "pending") {
@if (!_pendingRedemptions.Any()) {

No pending redemptions โ€” you're all clear!

} else {
@foreach (var r in _pendingRedemptions) {

@r.RewardName

Claimed @r.RedeemedAt.ToString("MMM dd, yyyy") ยท @r.PointCost.ToString("N0") pts

Awaiting Fulfillment
}
}
} else if (_activeTab == "history") {
@if (!_pointsHistory.Any()) {

No points history yet. Start borrowing books to earn points!

} else {
@foreach (var h in _pointsHistory.OrderByDescending(x => x.CreatedAt)) { var isPos = h.PointDelta >= 0; var label = PrettyEvent(h.EventKey, h.PointDelta); string? spentOn = h.RewardName; }
Date Type Details Spent on Points
@h.CreatedAt.ToString("MMM dd, yyyy") @label @h.Description @if (!string.IsNullOrWhiteSpace(spentOn)) { @spentOn } else { โ€” } @(isPos ? "+" : "")@h.PointDelta.ToString("N0")
}
} } @code { private string _activeTab = "redeem"; private bool _isLoading = true; private LoyaltyAccountDto? _account; private IEnumerable _availableRewards = Enumerable.Empty(); private IEnumerable _pendingRedemptions = Enumerable.Empty(); private IEnumerable _pointsHistory = Enumerable.Empty(); protected override async Task OnInitializedAsync() { await LoadDataAsync(); } private async Task LoadDataAsync() { _isLoading = true; try { _account = await ApiClient.GetMyLoyaltyAccountAsync(); _availableRewards = await ApiClient.GetActiveRewardsAsync(); _pendingRedemptions = await ApiClient.GetMyPendingRedemptionsAsync(); if (_account != null) { _pointsHistory = await ApiClient.GetPointsHistoryAsync(_account.AccountId); } } catch (Exception ex) { Console.WriteLine($"Error loading rewards: {ex.Message}"); } finally { _isLoading = false; } } private async Task RedeemAsync(LoyaltyRewardDto reward) { bool confirmed = await JS.InvokeAsync("confirm", $"Redeem \"{reward.Name}\" for {reward.PointCost:N0} points?"); if (!confirmed) return; var result = await ApiClient.ClaimRewardAsync(reward.Id); if (result.Success) { await JS.InvokeVoidAsync("showToast", result.Message, "success"); await LoadDataAsync(); } else { await JS.InvokeVoidAsync("showToast", result.Message, "error"); } } private string PrettyEvent(string? eventKey, double points) { var key = (eventKey ?? string.Empty).Trim().ToUpperInvariant(); return key switch { "RETURN" or "BOOK_RETURN" or "BORROW_RETURN" => "RETURN", "SIGNUP" or "REGISTER" => "SIGNUP", "SUBSCRIBE" or "SUBSCRIPTION" or "MEMBERSHIP" => "SUBSCRIBE", "REDEEM" or "REDEMPTION" or "CLAIM_REWARD" => "REDEEM", _ => points < 0 ? "SPENT" : (string.IsNullOrEmpty(key) ? "EARNED" : key) }; } }