Added cart page
This commit is contained in:
@@ -0,0 +1,106 @@
|
||||
@page "/cart"
|
||||
|
||||
<div class="container py-5" style="max-width: 900px;">
|
||||
<div class="d-flex align-items-center justify-content-between mb-5">
|
||||
<h2 class="fw-bold m-0">Your Cart</h2>
|
||||
<a href="/" class="text-dark text-decoration-none small fw-bold tracking-widest">CONTINUE SHOPPING</a>
|
||||
</div>
|
||||
|
||||
@if (!CartItems.Any())
|
||||
{
|
||||
<div class="text-center py-5 border rounded-3 bg-white">
|
||||
<p class="text-muted mb-4">Your collection is currently empty.</p>
|
||||
<a href="/" class="btn btn-dark rounded-pill px-4">Browse Catalog</a>
|
||||
</div>
|
||||
}
|
||||
else
|
||||
{
|
||||
<div class="card border-0 shadow-sm p-4 mb-4">
|
||||
@foreach (var item in CartItems)
|
||||
{
|
||||
<div class="row align-items-center py-4 @(item != CartItems.Last() ? "border-bottom" : "")">
|
||||
<!-- Item Detail -->
|
||||
<div class="col-12 col-md-6 d-flex align-items-center gap-4">
|
||||
<div class="bg-light d-flex align-items-center justify-content-center" style="width: 70px; height: 95px;">
|
||||
<span class="text-muted" style="font-size: 0.5rem;">[COVER]</span>
|
||||
</div>
|
||||
<div>
|
||||
<h5 class="fw-bold mb-1">@item.Title</h5>
|
||||
<p class="text-muted small mb-0">by @item.Author</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Quantity -->
|
||||
<div class="col-6 col-md-3 d-flex justify-content-center">
|
||||
<div class="d-flex align-items-center border rounded-pill bg-light px-2">
|
||||
<button class="btn btn-sm border-0 text-dark" @onclick="() => ChangeQuantity(item, -1)">-</button>
|
||||
<span class="px-3 fw-bold">@item.Quantity</span>
|
||||
<button class="btn btn-sm border-0 text-dark" @onclick="() => ChangeQuantity(item, 1)">+</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Price & Remove -->
|
||||
<div class="col-6 col-md-3 text-end d-flex align-items-center justify-content-end gap-3">
|
||||
<span class="fw-bold">R @(item.Price * item.Quantity)</span>
|
||||
<button class="btn btn-sm p-1 text-muted" @onclick="() => RemoveFromCart(item)">
|
||||
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><line x1="18" y1="6" x2="6" y2="18"></line><line x1="6" y1="6" x2="18" y2="18"></line></svg>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
}
|
||||
</div>
|
||||
|
||||
<!-- Cart Totals Section -->
|
||||
<div class="row justify-content-end">
|
||||
<div class="col-md-5">
|
||||
<div class="bg-white p-4 rounded-3 border">
|
||||
<div class="d-flex justify-content-between mb-3">
|
||||
<span class="text-muted">Subtotal</span>
|
||||
<span class="fw-bold">R @Subtotal.ToString("F2")</span>
|
||||
</div>
|
||||
<div class="d-flex justify-content-between mb-4">
|
||||
<span class="text-muted">VAT (15%)</span>
|
||||
<span class="fw-bold">R @VatAmount.ToString("F2")</span>
|
||||
</div>
|
||||
<hr />
|
||||
<div class="d-flex justify-content-between align-items-center mb-4">
|
||||
<span class="fw-bold h5 mb-0">Total</span>
|
||||
<span class="fw-bold h4 mb-0">R @Total.ToString("F2")</span>
|
||||
</div>
|
||||
<a href="/checkout" class="btn btn-dark w-100 rounded-pill py-3">Proceed to Checkout</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
}
|
||||
</div>
|
||||
|
||||
@code {
|
||||
public class CartItem
|
||||
{
|
||||
public int Id { get; set; }
|
||||
public string Title { get; set; } = string.Empty;
|
||||
public string Author { get; set; } = string.Empty;
|
||||
public int Price { get; set; }
|
||||
public int Quantity { get; set; }
|
||||
}
|
||||
|
||||
private List<CartItem> CartItems = new()
|
||||
{
|
||||
new CartItem { Id = 1, Title = "Letters from M/M (Paris)", Author = "M/M Paris", Price = 720, Quantity = 1 },
|
||||
new CartItem { Id = 2, Title = "Daan Paans: Floating Signifiers", Author = "Daan Paans", Price = 540, Quantity = 1 },
|
||||
new CartItem { Id = 3, Title = "Album Architectures, Maputo", Author = "Guedes Archive", Price = 350, Quantity = 1 }
|
||||
};
|
||||
|
||||
// Computed Properties for Calculations
|
||||
private decimal Subtotal => CartItems.Sum(i => (decimal)i.Price * i.Quantity);
|
||||
private decimal VatAmount => Subtotal * 0.15m;
|
||||
private decimal Total => Subtotal + VatAmount;
|
||||
|
||||
private void ChangeQuantity(CartItem item, int delta)
|
||||
{
|
||||
item.Quantity += delta;
|
||||
if (item.Quantity <= 0) CartItems.Remove(item);
|
||||
}
|
||||
|
||||
private void RemoveFromCart(CartItem item) => CartItems.Remove(item);
|
||||
}
|
||||
@@ -0,0 +1,105 @@
|
||||
@page "/checkout"
|
||||
|
||||
<div class="container py-5">
|
||||
<h2 class="fw-bold mb-4">Checkout</h2>
|
||||
<div class="row g-5">
|
||||
|
||||
<!-- LEFT COLUMN: SHIPPING & CART -->
|
||||
<div class="col-lg-8">
|
||||
<!-- 1. Cart Items -->
|
||||
<div class="card border-0 shadow-sm p-4 mb-4">
|
||||
<h5 class="fw-bold mb-3">Your Items</h5>
|
||||
@foreach (var item in CartItems)
|
||||
{
|
||||
<div class="d-flex align-items-center justify-content-between pb-3 border-bottom mb-3">
|
||||
<div><h6 class="mb-0">@item.Title</h6><small class="text-muted">@item.Author</small></div>
|
||||
<div class="d-flex align-items-center gap-3">
|
||||
<div class="d-flex border rounded-pill">
|
||||
<button class="btn btn-sm px-2" @onclick="() => ChangeQuantity(item, -1)">-</button>
|
||||
<span class="px-2 pt-1">@item.Quantity</span>
|
||||
<button class="btn btn-sm px-2" @onclick="() => ChangeQuantity(item, 1)">+</button>
|
||||
</div>
|
||||
<button class="btn btn-sm text-danger" @onclick="() => RemoveFromCart(item)">Remove</button>
|
||||
</div>
|
||||
</div>
|
||||
}
|
||||
</div>
|
||||
|
||||
<!-- 2. Shipping Options -->
|
||||
<div class="card border-0 shadow-sm p-4 mb-4">
|
||||
<h5 class="fw-bold mb-3">Shipping Method</h5>
|
||||
<div class="form-check mb-2">
|
||||
<input class="form-check-input" type="radio" name="shipping" id="pickup"
|
||||
checked=@(ShippingCost == 0) @onclick="() => ShippingCost = 0">
|
||||
<label class="form-check-label" for="pickup">Pickup from Bookshop (Free)</label>
|
||||
</div>
|
||||
<div class="form-check">
|
||||
<input class="form-check-input" type="radio" name="shipping" id="delivery"
|
||||
checked=@(ShippingCost == 60) @onclick="() => ShippingCost = 60">
|
||||
<label class="form-check-label" for="delivery">Home Delivery (R60.00)</label>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 3. Address Fields -->
|
||||
<div class="card border-0 shadow-sm p-4">
|
||||
<h5 class="fw-bold mb-3">Shipping Address</h5>
|
||||
<div class="form-check mb-3">
|
||||
<input class="form-check-input" type="checkbox" id="sameAsBilling" @bind="IsSameAddress">
|
||||
<label class="form-check-label" for="sameAsBilling">Billing address same as shipping</label>
|
||||
</div>
|
||||
<!-- Add text inputs for address here, show/hide based on IsSameAddress -->
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- RIGHT COLUMN: STICKY SUMMARY -->
|
||||
<div class="col-lg-4">
|
||||
<div class="card border-0 shadow-sm p-4 sticky-top" style="top: 100px;">
|
||||
<h5 class="fw-bold mb-3">Order Summary</h5>
|
||||
<div class="d-flex justify-content-between mb-2"><span>Subtotal</span><span>R @Subtotal.ToString("F2")</span></div>
|
||||
<div class="d-flex justify-content-between mb-2"><span>VAT (15%)</span><span>R @VatAmount.ToString("F2")</span></div>
|
||||
<div class="d-flex justify-content-between mb-2"><span>Shipping</span><span>R @ShippingCost.ToString("F2")</span></div>
|
||||
<hr />
|
||||
<div class="d-flex justify-content-between mb-4">
|
||||
<span class="fw-bold">Total Due</span>
|
||||
<h4 class="fw-bold">R @((Subtotal + VatAmount + ShippingCost).ToString("F2"))</h4>
|
||||
</div>
|
||||
<button class="btn btn-dark w-100 py-3 rounded-pill">Complete Purchase</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@code {
|
||||
private decimal ShippingCost = 0;
|
||||
private bool IsSameAddress = true;
|
||||
|
||||
// Calculations
|
||||
private decimal Subtotal => CartItems.Sum(i => (decimal)i.Price * i.Quantity);
|
||||
private decimal VatAmount => Subtotal * 0.15m;
|
||||
|
||||
// Assuming your CartItems list is managed via a Service or cascading parameter
|
||||
// Here it is locally mocked for this example
|
||||
private List<CartItem> CartItems = new()
|
||||
{
|
||||
new CartItem { Id = 1, Title = "Letters from M/M (Paris)", Author = "M/M Paris", Price = 720, Quantity = 1 },
|
||||
new CartItem { Id = 2, Title = "Daan Paans: Floating Signifiers", Author = "Daan Paans", Price = 540, Quantity = 1 }
|
||||
};
|
||||
|
||||
private void ChangeQuantity(CartItem item, int delta)
|
||||
{
|
||||
item.Quantity += delta;
|
||||
if (item.Quantity <= 0) CartItems.Remove(item);
|
||||
}
|
||||
|
||||
private void RemoveFromCart(CartItem item) => CartItems.Remove(item);
|
||||
private int GetCartTotal() => CartItems.Sum(i => i.Price * i.Quantity);
|
||||
|
||||
public class CartItem
|
||||
{
|
||||
public int Id { get; set; }
|
||||
public string Title { get; set; } = "";
|
||||
public string Author { get; set; } = "";
|
||||
public int Price { get; set; }
|
||||
public int Quantity { get; set; }
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user