Yuyuqt
add: reward system
25f925e
Raw
History Blame Contribute Delete
6.24 kB
@model Frontend.Models.RewardsViewModel
@{
ViewData["Title"] = "Loyalty Rewards";
}
<div class="mb-10">
<div class="flex flex-col md:flex-row md:items-center justify-between gap-6">
<div>
<h1 class="text-3xl font-extrabold tracking-tight text-slate-900 border-b border-slate-100 pb-4 mb-2">Loyalty Rewards</h1>
<p class="text-slate-500 font-medium">Redeem your hard-earned points for exclusive library benefits.</p>
</div>
<div class="flex items-center gap-4 bg-amber-50 border border-amber-100 px-6 py-4 rounded-2xl shadow-sm">
<div class="p-2 bg-amber-100 rounded-lg text-amber-600">
<svg class="w-6 h-6" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M12 8v13m0-13V6a2 2 0 112 2h-2zm0 0V5.5A2.5 2.5 0 109.5 8H12zm-7 4h14M5 12a2 2 0 110-4h14a2 2 0 110 4M5 12v7a2 2 0 002 2h10a2 2 0 002-2v-7"></path></svg>
</div>
<div>
<p class="text-[10px] font-bold text-amber-600 uppercase tracking-widest mb-0.5">Your Balance</p>
<p class="text-2xl font-black text-slate-900">@(Model.Account?.CurrentBalance.ToString("N0") ?? "0") Points</p>
</div>
</div>
</div>
</div>
<div class="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-8">
@if (!Model.Rewards.Any())
{
<div class="col-span-full py-20 text-center bg-slate-50 rounded-3xl border-2 border-dashed border-slate-200">
<div class="text-slate-400 mb-4">
<svg class="w-16 h-16 mx-auto opacity-20" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="1" d="M20 7l-8-4-8 4m16 0l-8 4m8-4v10l-8 4m0-10L4 7m8 4v10M4 7v10l8 4"></path></svg>
</div>
<p class="text-slate-500 font-medium italic">No active rewards available at the moment. Check back soon!</p>
</div>
}
@foreach (var reward in Model.Rewards)
{
<div class="group relative card p-6 hover:shadow-2xl hover:-translate-y-1 transition-all duration-300 border-t-4 border-amber-400">
<div class="flex justify-between items-start mb-6">
<div class="p-3 bg-amber-50 rounded-xl text-amber-600 group-hover:bg-amber-400 group-hover:text-white transition-colors">
<svg class="w-6 h-6" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M5 3v4M3 5h4M6 17v4m-2-2h4m5-16l2.286 6.857L21 12l-5.714 2.143L13 21l-2.286-6.857L5 12l5.714-2.143L13 3z"></path></svg>
</div>
<div class="text-right">
<span class="text-3xl font-black text-slate-900">@reward.PointCost.ToString("N0")</span>
<span class="text-[10px] block font-bold text-slate-400 uppercase tracking-tighter">Points Required</span>
</div>
</div>
<h3 class="text-lg font-bold text-slate-900 mb-2 truncate">@reward.Name</h3>
<p class="text-sm text-slate-500 leading-relaxed mb-6 h-12 overflow-hidden line-clamp-2">@reward.Description</p>
<div class="flex items-center justify-between pt-6 border-t border-slate-50">
<div class="flex flex-col">
<span class="text-[10px] font-bold text-slate-400 uppercase tracking-widest">Stock</span>
<span class="text-sm font-bold text-slate-700">@reward.StockQuantity available</span>
</div>
<button onclick="redeemReward('@reward.Id', '@reward.Name', @reward.PointCost)"
class="bg-slate-900 text-white px-5 py-2.5 rounded-xl text-xs font-bold hover:bg-slate-800 hover:shadow-lg transition-all active:scale-95">
Redeem Reward
</button>
</div>
@if (reward.StockQuantity < 10)
{
<div class="absolute top-2 right-2 flex">
<span class="animate-pulse bg-rose-500 text-white text-[8px] font-black px-1.5 py-0.5 rounded uppercase">Limited</span>
</div>
}
</div>
}
</div>
<div class="mt-12 p-8 bg-slate-900 rounded-3xl text-white relative overflow-hidden">
<div class="relative z-10 flex flex-col md:flex-row items-center justify-between gap-8">
<div class="text-center md:text-left">
<h2 class="text-2xl font-bold mb-2">How to earn more points?</h2>
<p class="text-slate-400 max-w-md">Borrow books, subscribe to memberships, and return books on time to stack up your points and unlock even bigger rewards!</p>
</div>
<a asp-controller="Books" asp-action="Index" class="bg-amber-400 text-slate-900 px-8 py-4 rounded-2xl font-black hover:bg-amber-300 transition-all shadow-xl shadow-amber-400/20 whitespace-nowrap">
Browse Books Now
</a>
</div>
<!-- Decorative background element -->
<div class="absolute -right-10 -bottom-10 w-64 h-64 bg-amber-400/10 rounded-full blur-3xl"></div>
<div class="absolute -left-10 -top-10 w-48 h-48 bg-blue-400/5 rounded-full blur-3xl"></div>
</div>
<script>
async function redeemReward(rewardId, rewardName, cost) {
if (!confirm(`Are you sure you want to redeem ${rewardName} for ${cost.toLocaleString()} points?`)) {
return;
}
try {
const response = await fetch('/Borrowings/Redeem', {
method: 'POST',
headers: {
'Content-Type': 'application/x-www-form-urlencoded',
},
body: new URLSearchParams({ rewardId: rewardId })
});
const result = await response.json();
if (result.success) {
alert(result.message);
window.location.reload();
} else {
alert('Error: ' + result.message);
}
} catch (error) {
console.error('Redemption error:', error);
alert('An unexpected error occurred. Please try again.');
}
}
</script>