Files
midrandbooks/MidrandBookshop/Components/Pages/Account.razor
T
Khwezi Mngoma 8d2efbeb4a
continuous-integration/drone/pr Build is passing
Added legal pages, contact and abut us
Redesigned account, checkout
Added stock management design elements
2026-06-16 23:32:44 +02:00

240 lines
20 KiB
Plaintext

@page "/account"
@using Microsoft.AspNetCore.Components.Authorization
@inject NavigationManager Navigation
@rendermode InteractiveServer
@attribute [Authorize]
<div class="account-page-container py-5">
<header class="account-header mb-5">
<span class="text-uppercase font-monospace text-muted tracking-wider small d-block mb-1">Customer Dashboard</span>
<h1 class="account-main-title fw-bold">My Account</h1>
</header>
<div class="row g-5">
<div class="col-lg-3">
<div class="nav flex-column account-nav-stack gap-1" role="tablist">
<button class="nav-link active text-start d-flex align-items-center gap-2" data-bs-toggle="pill" data-bs-target="#orders" role="tab">
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M6 2L3 6v14a2 2 0 0 0 2 2h14a2 2 0 0 0 2-2V6l-3-4z"></path><line x1="3" y1="6" x2="21" y2="6"></line><path d="M16 10a4 4 0 0 1-8 0"></path></svg>
<span>Order History</span>
</button>
<button class="nav-link text-start d-flex align-items-center gap-2" data-bs-toggle="pill" data-bs-target="#shipping" role="tab">
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M21 10c0 7-9 13-9 13s-9-6-9-13a9 9 0 0 1 18 0z"></path><circle cx="12" cy="10" r="3"></circle></svg>
<span>Shipping Address</span>
</button>
<button class="nav-link text-start d-flex align-items-center gap-2" data-bs-toggle="pill" data-bs-target="#profile" role="tab">
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M20 21v-2a4 4 0 0 0-4-4H8a4 4 0 0 0-4 4v2"></path><circle cx="12" cy="7" r="4"></circle></svg>
<span>Profile Settings</span>
</button>
<hr class="my-3 opacity-10" />
<button class="nav-link nav-logout text-danger text-start d-flex align-items-center gap-2" @onclick="TriggerLogout">
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M9 21H5a2 2 0 0 1-2-2V5a2 2 0 0 1 2-2h4"></path><polyline points="16 17 21 12 16 7"></polyline><line x1="21" y1="12" x2="9" y2="12"></line></svg>
<span>Logout Account</span>
</button>
</div>
</div>
<div class="col-lg-9">
<AuthorizeView>
<Authorized>
<div class="tab-content account-panels-deck">
<div class="tab-pane fade show active" id="orders" role="tabpanel">
<div class="panel-card-wrapper mb-4">
<h5 class="panel-section-title fw-bold text-dark font-monospace text-uppercase tracking-wider mb-4">Order History</h5>
@if (orderHistory == null || !orderHistory.Any())
{
<div class="text-center py-5 border rounded-3 bg-light bg-opacity-50">
<p class="text-muted small mb-0">You haven't placed any orders with us yet.</p>
</div>
}
else
{
<div class="orders-stack d-flex flex-column gap-4">
@foreach (var order in orderHistory)
{
<div class="premium-order-card p-4 border rounded-3 bg-white shadow-sm">
<div class="d-flex justify-content-between align-items-start flex-wrap gap-3 pb-3 border-b-dashed mb-3">
<div>
<span class="font-monospace text-dark fw-bold d-block h6 mb-1">@order.OrderId</span>
<small class="text-muted d-block mb-1">Ordered on @order.OrderDate.ToString("dd MMMM yyyy")</small>
<small class="text-secondary d-flex align-items-center gap-1" style="font-size: 0.8rem;">
<svg width="12" height="12" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M21 10c0 7-9 13-9 13s-9-6-9-13a9 9 0 0 1 18 0z"></path><circle cx="12" cy="10" r="3"></circle></svg>
<span>Shipped to: <span class="fw-semibold text-dark">@order.ShippingAddressName</span></span>
</small>
</div>
<div class="text-md-end d-flex flex-column align-items-md-end gap-2">
<div class="d-flex align-items-center gap-1.5 flex-wrap justify-content-md-end">
<span class="badge status-badge-base @GetPaymentStatusClass(order.PaymentStatus)">
Pay: @order.PaymentStatus
</span>
<span class="badge status-badge-base @GetStatusClass(order.Status)">
Logistics: @order.Status
</span>
</div>
@if (order.PaymentStatus?.ToLower() == "paid")
{
<button class="btn btn-outline-dark btn-premium-sm font-monospace text-uppercase d-inline-flex align-items-center gap-1.5 py-1 px-2.5"
style="font-size: 0.7rem;"
@onclick="() => DownloadInvoice(order.OrderId)">
<svg width="12" height="12" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round"><path d="M21 15v4a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v4"></path><polyline points="7 10 12 15 17 10"></polyline><line x1="12" y1="15" x2="12" y2="3"></line></svg>
<span>Invoice</span>
</button>
}
</div>
</div>
<div class="order-manifest-details d-flex align-items-center justify-content-between gap-4 py-1">
<div class="item-meta">
<h6 class="text-dark fw-bold mb-0 small" style="line-height: 1.4;">@order.ProductTitle</h6>
</div>
<div class="item-value text-end flex-shrink-0">
<span class="font-monospace text-dark fw-bold d-block">R @order.Total.ToString("F2")</span>
<small class="text-muted extra-small font-monospace">VAT Inclusive</small>
</div>
</div>
</div>
}
</div>
}
</div>
</div>
<div class="tab-pane fade" id="shipping" role="tabpanel">
<div class="panel-card-wrapper mb-4">
<div class="d-flex justify-content-between align-items-baseline mb-4">
<h5 class="panel-section-title fw-bold text-dark font-monospace text-uppercase tracking-wider mb-0">Saved Addresses</h5>
@if (!showAddForm && editingAddress == null)
{
<button class="btn btn-outline-dark btn-premium-sm font-monospace text-uppercase" @onclick="() => showAddForm = true">
Add New Address
</button>
}
</div>
@if (showAddForm || editingAddress != null)
{
<div class="premium-interactive-form p-4 border rounded-3 mb-4 bg-light bg-opacity-20 animate-fade-in">
<h6 class="fw-bold text-dark mb-3">@(editingAddress != null ? "Modify Curated Address" : "Register Destination Address")</h6>
<div class="row g-3">
<div class="col-md-6">
<label class="form-label extra-small font-monospace text-uppercase text-muted">Address Name Label</label>
<input type="text" class="form-control premium-plaintext-field" placeholder="e.g., Home" @bind="newAddressName" />
</div>
<div class="col-md-6">
<label class="form-label extra-small font-monospace text-uppercase text-muted">Postal Routing Code</label>
<input type="text" class="form-control premium-plaintext-field" placeholder="e.g., 1685" @bind="newPostalCode" />
</div>
<div class="col-12">
<label class="form-label extra-small font-monospace text-uppercase text-muted">Street Address Lines</label>
<input type="text" class="form-control premium-plaintext-field" placeholder="e.g., 12 Main Road" @bind="newStreetAddress" />
</div>
<div class="col-md-12">
<label class="form-label extra-small font-monospace text-uppercase text-muted">City / Region</label>
<input type="text" class="form-control premium-plaintext-field" placeholder="e.g., Midrand" @bind="newCity" />
</div>
<div class="col-12 d-flex flex-wrap gap-4 py-2 border-y my-2 bg-white px-3 rounded border">
<div class="form-check d-flex align-items-center gap-2 m-0">
<input type="checkbox" class="form-check-input custom-box-tick m-0" id="isBillingCheck" @bind="isBilling" />
<label class="form-check-label context-clickable small fw-medium text-dark" for="isBillingCheck">Default Billing Endpoint</label>
</div>
<div class="form-check d-flex align-items-center gap-2 m-0">
<input type="checkbox" class="form-check-input custom-box-tick m-0" id="isShippingCheck" @bind="isShipping" />
<label class="form-check-label context-clickable small fw-medium text-dark" for="isShippingCheck">Default Fulfillment Endpoint</label>
</div>
</div>
<div class="col-12 d-flex justify-content-end gap-2 mt-3">
<button class="btn btn-clean-cancel font-monospace text-uppercase small" @onclick="CancelAddressActions">Cancel</button>
<button class="btn btn-dark px-4 py-2 text-uppercase font-monospace small" @onclick="SaveAddress">Save Address Details</button>
</div>
</div>
</div>
}
<div class="row g-4">
@foreach (var addr in savedAddresses)
{
<div class="col-md-6">
<div class="address-curated-card p-4 border rounded-3 position-relative d-flex flex-column h-100 bg-white @(addr.IsPrimary ? "border-dark shadow-sm" : "opacity-90")">
<div class="d-flex justify-content-between align-items-start mb-2">
<span class="fw-bold text-dark font-monospace tracking-wide text-uppercase" style="font-size: 0.82rem;">@addr.Name</span>
<div class="form-check d-flex align-items-center gap-1.5 p-0 m-0">
<input type="radio" class="form-check-input custom-box-tick m-0" name="primaryAddr" id="@($"primary-{addr.Id}")" checked="@addr.IsPrimary" @onchange="(e) => SetPrimary(addr, e)" />
<label class="form-check-label extra-small text-muted font-monospace text-uppercase context-clickable ms-1" for="@($"primary-{addr.Id}")">Primary</label>
</div>
</div>
<div class="address-body-text text-muted mb-4 mt-1 flex-grow-1" style="font-size: 0.88rem; line-height: 1.6;">
<span class="d-block text-dark fw-medium">@addr.Street</span>
<span class="d-block">@addr.City</span>
<span class="font-monospace text-secondary extra-small d-block mt-1">ZA-@addr.PostalCode</span>
</div>
<div class="address-metadata-badges d-flex flex-wrap gap-1 mb-3">
@if (addr.IsBilling)
{
<span class="badge bg-light text-secondary font-monospace tracking-wide border text-uppercase extra-small px-2 py-1">Billing</span>
}
@if (addr.IsShipping)
{
<span class="badge bg-light text-dark font-monospace tracking-wide border border-secondary text-uppercase extra-small px-2 py-1">Shipping</span>
}
</div>
<div class="address-actions-row border-top-dashed pt-3 d-flex gap-3 justify-content-end mt-auto">
<button class="btn-action-trigger text-uppercase font-monospace extra-small text-muted border-0 bg-transparent" @onclick="() => EditAddress(addr)">Edit</button>
<button class="btn-action-trigger text-uppercase font-monospace extra-small text-danger border-0 bg-transparent" @onclick="() => DeleteAddress(addr)">Delete</button>
</div>
</div>
</div>
}
</div>
</div>
</div>
<div class="tab-pane fade" id="profile" role="tabpanel">
<div class="panel-card-wrapper mb-4">
<h5 class="panel-section-title fw-bold text-dark font-monospace text-uppercase tracking-wider mb-4">Profile Settings</h5>
<div class="profile-hero-banner mb-4 d-flex align-items-center justify-content-between p-4 border rounded-3 bg-light bg-opacity-20 flex-wrap gap-3">
<div class="hero-text-content">
<div class="meta-tag font-monospace text-uppercase text-muted extra-small tracking-wider mb-1">Active Identity</div>
<h5 class="fw-bold text-dark mb-1 h6">@User?.Identity?.Name</h5>
<p class="text-muted small mb-0 font-monospace extra-small opacity-75">Secure Connection Authorized</p>
</div>
<span class="badge rounded-pill bg-success bg-opacity-10 text-success border border-success border-opacity-20 font-monospace px-3 py-1.5 small text-uppercase tracking-wide">
Verified
</span>
</div>
<div class="card p-5 text-center bg-white border rounded-3 shadow-sm my-4">
<div class="mb-4 text-muted opacity-40">
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" width="44" height="44" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round">
<rect x="3" y="11" width="18" height="11" rx="2" ry="2" />
<path d="M7 11V7a5 5 0 0 1 10 0v4" />
</svg>
</div>
<h5 class="fw-bold text-dark mb-2 h6">Centralized Identity Node Settings</h5>
<p class="text-muted small mx-auto mb-4" style="max-width: 480px; line-height: 1.5;">
For your structural protection, password alterations, account recovery preferences, cross-tenant factors, and core credential manifests are handled through our global Identity Node security layer.
</p>
<a href="https://sts.security.khongisa.co.za/Manage/Index?returnUrl=https://midrandbooks.co.za/account"
target="_blank"
rel="noopener noreferrer"
class="btn btn-dark rounded-pill px-4 py-2.5 btn-sm font-monospace text-uppercase tracking-wider d-inline-flex align-items-center gap-2 mx-auto"
style="font-size: 0.75rem;">
<span>Access Central Security Center</span>
<svg width="12" height="12" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round"><path d="M18 13v6a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2V8a2 2 0 0 1 2-2h6"></path><polyline points="15 3 21 3 21 9"></polyline><line x1="10" y1="14" x2="21" y2="3"></line></svg>
</a>
</div>
</div>
</div>
</div>
</Authorized>
</AuthorizeView>
</div>
</div>
</div>