Spaces:
Sleeping
Sleeping
| @model Frontend.Models.MembershipViewModel | |
| @{ | |
| ViewData["Title"] = "My Membership"; | |
| var hasActiveSub = Model.CurrentSubscription != null && Model.CurrentSubscription.IsActive; | |
| } | |
| <div class="space-y-10 animate-in fade-in slide-in-from-bottom-4 duration-700"> | |
| <!-- Header with Loyalty Summary --> | |
| <div class="flex flex-col md:flex-row md:items-center justify-between gap-6 bg-white p-8 rounded-2xl shadow-sm border border-slate-100"> | |
| <div> | |
| <h1 class="text-3xl font-extrabold text-slate-900 tracking-tight">Membership</h1> | |
| <p class="text-slate-500 mt-1">Manage your subscription and library benefits</p> | |
| </div> | |
| @if (Model.LoyaltyAccount != null) | |
| { | |
| <div class="flex items-center gap-4 bg-slate-50 px-6 py-4 rounded-xl border border-slate-200/60"> | |
| <div class="bg-amber-100 p-2.5 rounded-lg text-amber-600"> | |
| <svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><circle cx="8" cy="8" r="6"/><path d="M18.09 10.37A6 6 0 1 1 10.34 18"/><path d="M7 6h1v4"/><path d="m16.71 13.88.7.71-2.82 2.82"/></svg> | |
| </div> | |
| <div> | |
| <div class="text-xs font-bold text-slate-400 uppercase tracking-widest">Available Points</div> | |
| <div class="text-2xl font-black text-slate-900 leading-none">@Model.LoyaltyAccount.CurrentBalance.ToString("N0")</div> | |
| </div> | |
| </div> | |
| } | |
| </div> | |
| <div class="grid grid-cols-1 lg:grid-cols-3 gap-8"> | |
| <!-- Current Subscription Card --> | |
| <div class="lg:col-span-2 space-y-8"> | |
| <section> | |
| <h2 class="text-xl font-bold text-slate-900 mb-4 flex items-center gap-2"> | |
| <svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="text-slate-400"><path d="M3.85 8.62a4 4 0 0 1 4.78-4.77 4 4 0 0 1 6.74 0 4 4 0 0 1 4.78 4.78 4 4 0 0 1 0 6.74 4 4 0 0 1-4.77 4.78 4 4 0 0 1-6.75 0 4 4 0 0 1-4.78-4.77 4 4 0 0 1 0-6.76Z"/><path d="m9 12 2 2 4-4"/></svg> | |
| Active Plan | |
| </h2> | |
| @if (hasActiveSub) | |
| { | |
| <div class="relative overflow-hidden bg-slate-900 rounded-3xl p-8 text-white group shadow-xl"> | |
| <!-- Decorative element --> | |
| <div class="absolute -right-20 -top-20 w-64 h-64 bg-white/5 rounded-full blur-3xl group-hover:bg-white/10 transition-colors duration-500"></div> | |
| <div class="relative z-10"> | |
| <div class="flex justify-between items-start mb-10"> | |
| <div> | |
| <span class="inline-flex items-center px-3 py-1 rounded-full text-xs font-bold bg-green-500/20 text-green-400 border border-green-500/30 mb-3 uppercase tracking-wider">Active Status</span> | |
| <h3 class="text-4xl font-black tracking-tight">@Model.CurrentSubscription.MembershipType</h3> | |
| </div> | |
| <div class="h-14 w-14 bg-white/10 rounded-2xl flex items-center justify-center backdrop-blur-md"> | |
| <svg xmlns="http://www.w3.org/2000/svg" width="28" height="28" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><rect width="20" height="14" x="2" y="5" rx="2"/><line x1="2" x2="22" y1="10" y2="10"/></svg> | |
| </div> | |
| </div> | |
| <div class="grid grid-cols-2 gap-8 py-8 border-t border-white/10"> | |
| <div> | |
| <div class="text-slate-400 text-sm font-medium mb-1">Start Date</div> | |
| <div class="text-lg font-bold">@Model.CurrentSubscription.StartDate.ToString("MMM dd, yyyy")</div> | |
| </div> | |
| <div> | |
| <div class="text-slate-400 text-sm font-medium mb-1">Expiry Date</div> | |
| <div class="text-lg font-bold">@Model.CurrentSubscription.ExpiryDate.ToString("MMM dd, yyyy")</div> | |
| </div> | |
| </div> | |
| <div class="pt-6 flex items-center gap-2 text-slate-400 text-sm italic"> | |
| <svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><circle cx="12" cy="12" r="10"/><path d="M12 16v-4"/><path d="M12 8h.01"/></svg> | |
| Pro-tip: Maintain your subscription to earn loyalty points every month! | |
| </div> | |
| </div> | |
| </div> | |
| } | |
| else | |
| { | |
| <div class="bg-white border-2 border-dashed border-slate-200 rounded-3xl p-12 text-center"> | |
| <div class="bg-slate-50 w-16 h-16 rounded-full flex items-center justify-center mx-auto mb-4 text-slate-400"> | |
| <svg xmlns="http://www.w3.org/2000/svg" width="32" height="32" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><rect width="20" height="14" x="2" y="5" rx="2"/><line x1="2" x2="22" y1="10" y2="10"/></svg> | |
| </div> | |
| <h3 class="text-xl font-bold text-slate-900">No Active Subscription</h3> | |
| <p class="text-slate-500 mt-2 mb-8 max-w-xs mx-auto">Get a membership to borrow books and unlock premium library features.</p> | |
| <a href="#plans" class="btn-primary px-8 py-3 rounded-xl shadow-lg shadow-slate-900/10">Browse Plans</a> | |
| </div> | |
| } | |
| </section> | |
| <!-- Queued Memberships --> | |
| @if (Model.QueuedMemberships.Any()) | |
| { | |
| <section> | |
| <h2 class="text-xl font-bold text-slate-900 mb-4 flex items-center gap-2"> | |
| <svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="text-amber-500"><path d="M10 10 2.5 2.5"/><path d="M21.5 2.5 14 10"/><path d="m21.5 21.5-7.5-7.5"/><path d="M10 14 2.5 21.5"/><circle cx="12" cy="12" r="3"/></svg> | |
| Queued Memberships (@Model.QueuedMemberships.Count) | |
| </h2> | |
| <div class="space-y-3"> | |
| @foreach (var queued in Model.QueuedMemberships) | |
| { | |
| <div class="flex items-center justify-between bg-amber-50 border border-amber-100 p-5 rounded-2xl shadow-sm"> | |
| <div class="flex items-center gap-4"> | |
| <div class="h-12 w-12 bg-white rounded-xl flex items-center justify-center text-amber-600 shadow-sm border border-amber-100"> | |
| <svg xmlns="http://www.w3.org/2000/svg" width="22" height="22" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><rect width="20" height="14" x="2" y="5" rx="2"/><line x1="2" x2="22" y1="10" y2="10"/></svg> | |
| </div> | |
| <div> | |
| <div class="font-bold text-slate-900">@queued.MembershipType Membership</div> | |
| <div class="text-sm text-slate-500"> | |
| Activates on @queued.StartDate.ToString("MMM dd, yyyy") • Expires @queued.ExpiryDate.ToString("MMM dd, yyyy") | |
| </div> | |
| </div> | |
| </div> | |
| <div class="text-right"> | |
| <span class="inline-flex items-center px-3 py-1 rounded-full text-xs font-bold bg-amber-100 text-amber-700 border border-amber-200 uppercase tracking-wider">Queued</span> | |
| <div class="text-xs text-slate-400 mt-1">Will activate automatically</div> | |
| </div> | |
| </div> | |
| } | |
| </div> | |
| </section> | |
| } | |
| </div> | |
| <!-- Right Side Panel: Benefits --> | |
| <div class="space-y-8"> | |
| <div class="bg-white border border-slate-100 rounded-3xl p-8 shadow-sm"> | |
| <h3 class="text-lg font-bold text-slate-900 mb-6">Your Benefits</h3> | |
| <ul class="space-y-6"> | |
| @{ | |
| var currentPlan = Model.AvailableMemberships.FirstOrDefault(m => hasActiveSub && m.Type == Model.CurrentSubscription.MembershipType); | |
| } | |
| <li class="flex gap-4"> | |
| <div class="shrink-0 h-10 w-10 bg-indigo-50 text-indigo-600 rounded-xl flex items-center justify-center font-bold">@((currentPlan?.MaxBooks ?? 1))</div> | |
| <div> | |
| <div class="font-bold text-slate-900">Max Books</div> | |
| <div class="text-sm text-slate-500">Number of books you can borrow simultaneously.</div> | |
| </div> | |
| </li> | |
| <li class="flex gap-4"> | |
| <div class="shrink-0 h-10 w-10 bg-emerald-50 text-emerald-600 rounded-xl flex items-center justify-center font-bold">@((currentPlan?.BorrowingDays ?? 14))</div> | |
| <div> | |
| <div class="font-bold text-slate-900">Borrowing Days</div> | |
| <div class="text-sm text-slate-500">Duration you can keep each borrowed book.</div> | |
| </div> | |
| </li> | |
| <li class="flex gap-4"> | |
| <div class="shrink-0 h-10 w-10 bg-violet-50 text-violet-600 rounded-xl flex items-center justify-center"> | |
| <svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M4 19.5v-15A2.5 2.5 0 0 1 6.5 2H20v20H6.5a2.5 2.5 0 0 1 0-5H20"/></svg> | |
| </div> | |
| <div> | |
| <div class="font-bold text-slate-900">Digital Access</div> | |
| <div class="text-sm text-slate-500">Browse and search the full library catalog online.</div> | |
| </div> | |
| </li> | |
| </ul> | |
| <div class="mt-10 p-5 bg-slate-900 rounded-2xl text-white text-center"> | |
| <p class="text-sm font-medium mb-3">Want more books?</p> | |
| <a href="#plans" class="text-sm font-bold text-indigo-400 hover:text-indigo-300 transition-colors">Upgrade Plan →</a> | |
| </div> | |
| </div> | |
| </div> | |
| </div> | |
| <!-- Explore All Plans Section --> | |
| <section id="plans" class="pt-10"> | |
| <div class="text-center mb-10"> | |
| <h2 class="text-3xl font-black text-slate-900">Explore Membership Plans</h2> | |
| <p class="text-slate-500 mt-2">Find the perfect library experience for your reading habits</p> | |
| </div> | |
| <div class="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-4 gap-6"> | |
| @foreach (var plan in Model.AvailableMemberships) | |
| { | |
| var isCurrent = hasActiveSub && plan.Type == Model.CurrentSubscription.MembershipType; | |
| <div class="group relative bg-white border @(isCurrent ? "border-slate-900 ring-2 ring-slate-900 ring-offset-2" : "border-slate-200") rounded-3xl p-8 transition-all duration-300 hover:shadow-2xl hover:-translate-y-2"> | |
| @if (isCurrent) | |
| { | |
| <div class="absolute -top-4 left-1/2 -translate-x-1/2 bg-slate-900 text-white px-4 py-1 rounded-full text-[10px] font-black uppercase tracking-widest shadow-xl">Current Plan</div> | |
| } | |
| <div class="mb-8"> | |
| <h3 class="text-sm font-black text-slate-400 uppercase tracking-widest mb-2">@plan.Type</h3> | |
| <div class="flex items-baseline"> | |
| <span class="text-3xl font-black text-slate-900">@plan.Price.ToString("N0")</span> | |
| <span class="text-slate-500 font-bold ml-1 text-sm uppercase">MMK</span> | |
| </div> | |
| <div class="text-slate-400 text-xs font-bold mt-1">/ @(plan.DurationMonths == 1 ? "Month" : $"{plan.DurationMonths} Months")</div> | |
| </div> | |
| <ul class="space-y-4 mb-10"> | |
| <li class="flex items-center gap-3 text-sm"> | |
| <div class="h-5 w-5 rounded-full bg-slate-100 flex items-center justify-center text-slate-900"> | |
| <svg xmlns="http://www.w3.org/2000/svg" width="12" height="12" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="3" stroke-linecap="round" stroke-linejoin="round"><polyline points="20 6 9 17 4 12"/></svg> | |
| </div> | |
| <span class="text-slate-600 font-bold">@plan.MaxBooks Books limit</span> | |
| </li> | |
| <li class="flex items-center gap-3 text-sm"> | |
| <div class="h-5 w-5 rounded-full bg-slate-100 flex items-center justify-center text-slate-900"> | |
| <svg xmlns="http://www.w3.org/2000/svg" width="12" height="12" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="3" stroke-linecap="round" stroke-linejoin="round"><polyline points="20 6 9 17 4 12"/></svg> | |
| </div> | |
| <span class="text-slate-600 font-bold">@plan.BorrowingDays Days keep</span> | |
| </li> | |
| @if (!string.IsNullOrEmpty(plan.RewardId) && Model.RewardPointCosts.TryGetValue(plan.RewardId, out var pointCost)) | |
| { | |
| <li class="flex items-center gap-3 text-sm text-amber-600 font-black"> | |
| <div class="h-5 w-5 rounded-full bg-amber-100 flex items-center justify-center text-amber-600"> | |
| <svg xmlns="http://www.w3.org/2000/svg" width="12" height="12" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="3" stroke-linecap="round" stroke-linejoin="round"><circle cx="8" cy="8" r="6"/><path d="M18.09 10.37A6 6 0 1 1 10.34 18"/></svg> | |
| </div> | |
| <span>@pointCost.ToString("N0") Points</span> | |
| </li> | |
| } | |
| </ul> | |
| @if (isCurrent) | |
| { | |
| <button disabled class="w-full py-4 rounded-2xl bg-slate-100 text-slate-400 font-bold text-sm cursor-not-allowed">Active Now</button> | |
| } | |
| else | |
| { | |
| <div class="space-y-3"> | |
| <a href="#" class="block w-full py-4 rounded-2xl bg-white border-2 border-slate-900 text-slate-900 text-center font-bold text-sm transition-all duration-200 hover:bg-slate-900 hover:text-white">Pay Cash</a> | |
| @if (!string.IsNullOrEmpty(plan.RewardId) && Model.RewardPointCosts.ContainsKey(plan.RewardId)) | |
| { | |
| <button onclick="redeemMembership('@plan.RewardId', '@plan.Type', @(Model.RewardPointCosts[plan.RewardId]))" | |
| class="w-full py-4 rounded-2xl bg-amber-400 text-slate-900 font-black text-sm hover:bg-amber-500 transition-all shadow-lg shadow-amber-400/20"> | |
| Redeem with Points | |
| </button> | |
| } | |
| </div> | |
| } | |
| </div> | |
| } | |
| </div> | |
| </section> | |
| </div> | |
| <!-- Modal / Toast Scripts --> | |
| <script> | |
| async function redeemMembership(rewardId, planName, cost) { | |
| if (!confirm(`Are you sure you want to redeem ${planName} for ${cost.toLocaleString()} points?`)) { | |
| return; | |
| } | |
| try { | |
| const response = await fetch('/Membership/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> | |
| <style> | |
| body { | |
| background-color: #f8fafc; | |
| } | |
| </style> | |