Files
midrandbooks/MidrandBooks/Components/Layout/CookieBanner.razor
T
2026-07-19 09:54:35 +02:00

166 lines
8.1 KiB
Plaintext

@namespace MidrandBooks.Components.Layout
@using Microsoft.AspNetCore.Components
@inject IJSRuntime JSRuntime
@if (ShowBanner || ShowPrivacyModal)
{
@if (ShowBanner)
{
<div id="cookie-acceptance-banner"
class="fixed bottom-4 left-4 z-40 max-w-sm w-[calc(100vw-2rem)] sm:w-[380px] bg-white/90 backdrop-blur-xl border border-slate-200/80 rounded-2xl p-4 shadow-xl flex flex-col gap-3 animate-in slide-in-from-bottom-8 fade-in duration-500 hover:shadow-slate-200/50 transition-all">
<!-- Accent Line -->
<div class="absolute top-0 left-6 right-6 h-0.5 bg-gradient-to-r from-amber-500/0 via-amber-500/60 to-amber-500/0"></div>
<div class="flex items-start gap-3">
<!-- Visual Icon -->
<div class="h-9 w-9 rounded-xl bg-amber-50 border border-amber-200/60 text-amber-600 flex items-center justify-center flex-shrink-0">
<svg class="h-5 w-5 animate-pulse" 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 2a10 10 0 1 0 10 10 4 4 0 0 1-5-5 4 4 0 0 1-5-5" />
<path d="M8.5 8.5v.01" />
<path d="M16 15.5v.01" />
<path d="M12 12v.01" />
<path d="M11 16v.01" />
<path d="M15 11v.01" />
</svg>
</div>
<div class="space-y-1">
<h4 class="text-xs font-sans font-bold text-slate-800 tracking-tight">
Cookie &amp; POPIA Protocol
</h4>
<p class="text-[11px] text-slate-600 leading-relaxed font-medium">
We utilize essential identifiers to facilitate secure electronic catalog licensing, sandboxed media playbacks, and cryptographic PayFast gateway handshake handoffs under <strong class="text-amber-600 font-bold">Lite Charms (Pty) Ltd</strong>.
</p>
</div>
<button @onclick="DismissBanner"
class="text-slate-400 hover:text-slate-600 transition-colors cursor-pointer p-0.5"
aria-label="Dismiss banner">
<svg class="h-4 w-4" fill="none" stroke="currentColor" viewBox="0 0 24 24" stroke-width="2">
<path stroke-linecap="round" stroke-linejoin="round" d="M6 18L18 6M6 6l12 12" />
</svg>
</button>
</div>
<!-- Action Links & Buttons -->
<div class="flex items-center justify-between mt-1 pt-2.5 border-t border-slate-100">
<button @onclick="OpenPrivacyModal"
class="text-[11px] font-semibold text-amber-600 hover:text-amber-700 underline cursor-pointer flex items-center gap-1 focus:outline-none">
<svg class="h-3.5 w-3.5" fill="none" stroke="currentColor" viewBox="0 0 24 24" stroke-width="2">
<path stroke-linecap="round" stroke-linejoin="round" d="M9 12l2 2 4-4m5.618-4.016A11.955 11.955 0 0112 2.944a11.955 11.955 0 01-8.618 3.04A12.02 12.02 0 003 9c0 5.591 3.824 10.29 9 11.622 5.176-1.332 9-6.03 9-11.622 0-1.042-.133-2.052-.382-3.016z" />
</svg>
Privacy Policy
</button>
<div class="flex items-center gap-2">
<button @onclick="AcceptCookies"
class="bg-slate-900 hover:bg-slate-800 text-white rounded-xl py-1.5 px-3.5 text-[10px] font-mono font-bold uppercase tracking-wider transition-all cursor-pointer shadow shadow-slate-900/10 hover:scale-[1.02] active:scale-[0.98]">
Accept All
</button>
</div>
</div>
</div>
}
@if (ShowPrivacyModal)
{
<div id="cookie-privacy-modal-overlay"
class="fixed inset-0 z-50 flex items-center justify-center p-4 bg-slate-950/80 backdrop-blur-md overflow-y-auto">
<div id="cookie-privacy-modal-content"
class="relative w-full max-w-5xl bg-slate-950 border border-white/10 rounded-3xl shadow-2xl overflow-hidden max-h-[90vh] flex flex-col animate-in zoom-in-95 duration-200">
<!-- Upper Highlight Bar -->
<div class="absolute top-0 left-0 right-0 h-1 bg-gradient-to-r from-amber-500 via-amber-400 to-amber-600" />
<!-- Modal Header -->
<div class="flex items-center justify-between px-6 py-4 border-b border-white/10 bg-slate-900/40 relative z-10 flex-shrink-0">
<div class="flex items-center gap-2 text-white">
<svg class="h-5 w-5 text-amber-400" fill="none" stroke="currentColor" viewBox="0 0 24 24" stroke-width="2">
<path stroke-linecap="round" stroke-linejoin="round" d="M9 12l2 2 4-4m5.618-4.016A11.955 11.955 0 0112 2.944a11.955 11.955 0 01-8.618 3.04A12.02 12.02 0 003 9c0 5.591 3.824 10.29 9 11.622 5.176-1.332 9-6.03 9-11.622 0-1.042-.133-2.052-.382-3.016z" />
</svg>
<span class="font-mono text-xs font-bold uppercase tracking-wider text-slate-300">
Secure Legal Document Portal
</span>
</div>
<button @onclick="ClosePrivacyModal"
class="p-1.5 rounded-xl border border-white/10 bg-white/5 text-slate-400 hover:text-white shadow-sm hover:scale-105 transition-all cursor-pointer"
aria-label="Close modal">
<svg class="h-4 w-4" fill="none" stroke="currentColor" viewBox="0 0 24 24" stroke-width="2">
<path stroke-linecap="round" stroke-linejoin="round" d="M6 18L18 6M6 6l12 12" />
</svg>
</button>
</div>
<!-- Modal Scrollable Body -->
<div class="flex-1 overflow-y-auto bg-slate-950/50 p-4 sm:p-6 md:p-8">
<div class="max-w-4xl mx-auto -my-10">
<MidrandBooks.Components.Privacy />
</div>
</div>
<!-- Modal Footer -->
<div class="px-6 py-4 border-t border-white/10 bg-slate-900/40 flex items-center justify-end flex-shrink-0">
<button @onclick="ClosePrivacyModal"
class="bg-white/5 hover:bg-white/10 text-slate-300 border border-white/10 rounded-xl py-2 px-5 text-xs font-mono font-bold uppercase tracking-wider transition-all cursor-pointer">
Close Document
</button>
</div>
</div>
</div>
}
}
@code {
private bool ShowBanner { get; set; } = false;
private bool ShowPrivacyModal { get; set; } = false;
protected override async Task OnAfterRenderAsync(bool firstRender)
{
if (firstRender)
{
try
{
var isAccepted = await JSRuntime.InvokeAsync<string>("localStorage.getItem", "midrandbooks_cookie_accepted");
if (isAccepted != "true")
{
await Task.Delay(1500);
ShowBanner = true;
StateHasChanged();
}
}
catch
{
// Fail gracefully if localStorage is blocked
}
}
}
private async Task AcceptCookies()
{
try
{
await JSRuntime.InvokeVoidAsync("localStorage.setItem", "midrandbooks_cookie_accepted", "true");
}
catch
{
// Fail gracefully
}
ShowBanner = false;
}
private void DismissBanner()
{
ShowBanner = false;
}
private void OpenPrivacyModal()
{
ShowPrivacyModal = true;
}
private void ClosePrivacyModal()
{
ShowPrivacyModal = false;
}
}