Implemented order ui and validation. Added coupon handling
This commit is contained in:
230
Moonlight/Shared/Views/Store/Order.razor
Normal file
230
Moonlight/Shared/Views/Store/Order.razor
Normal file
@@ -0,0 +1,230 @@
|
||||
@page "/store/order/{slug}"
|
||||
|
||||
@using Moonlight.App.Services.Store
|
||||
@using Moonlight.App.Services
|
||||
@using Moonlight.App.Database.Entities.Store
|
||||
@using Moonlight.App.Repositories
|
||||
|
||||
@inject ConfigService ConfigService
|
||||
@inject StoreService StoreService
|
||||
@inject IdentityService IdentityService
|
||||
@inject AlertService AlertService
|
||||
@inject Repository<Product> ProductRepository
|
||||
@inject Repository<Coupon> CouponRepository
|
||||
|
||||
<LazyLoader Load="Load">
|
||||
@if (SelectedProduct == null)
|
||||
{
|
||||
@*
|
||||
TODO: Add 404 here
|
||||
*@
|
||||
}
|
||||
else
|
||||
{
|
||||
<div class="row">
|
||||
<div class="col-md-7 col-12">
|
||||
<div class="card mb-5">
|
||||
<div class="card-body">
|
||||
<h2 class="fs-2x fw-bold mb-10">@(SelectedProduct.Name)</h2>
|
||||
<p class="text-gray-400 fs-4 fw-semibold">
|
||||
@Formatter.FormatLineBreaks(SelectedProduct.Description)
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row mb-5">
|
||||
<div class="col-md-4 col-12">
|
||||
<a @onclick="() => SetDurationMultiplicator(1)" @onclick:preventDefault href="#" class="card card-body bg-hover-secondary text-center @(DurationMultiplicator == 1 ? "border border-info" : "")">
|
||||
<h4 class="fw-bold mb-0 align-middle">@(SelectedProduct.Duration * 1) days</h4>
|
||||
</a>
|
||||
</div>
|
||||
<div class="col-md-4 col-12">
|
||||
<a @onclick="() => SetDurationMultiplicator(2)" @onclick:preventDefault href="#" class="card card-body bg-hover-secondary text-center @(DurationMultiplicator == 2 ? "border border-info" : "")">
|
||||
<h4 class="fw-bold mb-0 align-middle">@(SelectedProduct.Duration * 2) days</h4>
|
||||
</a>
|
||||
</div>
|
||||
<div class="col-md-4 col-12">
|
||||
<a @onclick="() => SetDurationMultiplicator(3)" @onclick:preventDefault href="#" class="card card-body bg-hover-secondary text-center @(DurationMultiplicator == 3 ? "border border-info" : "")">
|
||||
<h4 class="fw-bold mb-0 align-middle">@(SelectedProduct.Duration * 3) days</h4>
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
<div class="card mb-5">
|
||||
<div class="card-body">
|
||||
<h3 class="fs-1x fw-bold mb-10">Apply coupon codes</h3>
|
||||
<div class="input-group">
|
||||
<input @bind="CouponCode" class="form-control form-control-solid" placeholder="Coupon code..."/>
|
||||
<button @onclick="ApplyCoupon" class="btn btn-primary">Apply</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-md-1 col-12"></div>
|
||||
<div class="col-md-3 col-12">
|
||||
<div class="card">
|
||||
<div class="card-header">
|
||||
<h3 class="card-title">Summary</h3>
|
||||
</div>
|
||||
|
||||
@{
|
||||
var defaultPrice = SelectedProduct.Price * DurationMultiplicator;
|
||||
double actualPrice;
|
||||
|
||||
if (SelectedCoupon == null)
|
||||
actualPrice = defaultPrice;
|
||||
else
|
||||
actualPrice = Math.Round(defaultPrice * SelectedCoupon.Percent / 100, 2);
|
||||
|
||||
var currency = ConfigService.Get().Store.Currency;
|
||||
}
|
||||
|
||||
<div class="card-body fs-5">
|
||||
<div class="d-flex flex-stack">
|
||||
<div class="text-gray-700 fw-semibold me-2">Today</div>
|
||||
<div class="d-flex align-items-senter">
|
||||
<span class="fw-bold">@(currency) @(defaultPrice)</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="d-flex flex-stack">
|
||||
<div class="text-gray-700 fw-semibold me-2">Renew</div>
|
||||
<div class="d-flex align-items-senter">
|
||||
<span class="fw-bold">@(currency) @(defaultPrice)</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="d-flex flex-stack">
|
||||
<div class="text-gray-700 fw-semibold me-2">Duration</div>
|
||||
<div class="d-flex align-items-senter">
|
||||
<span class="fw-bold">@(SelectedProduct.Duration * DurationMultiplicator) days</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="separator my-4"></div>
|
||||
<div class="d-flex flex-stack">
|
||||
<div class="text-gray-700 fw-semibold me-2">Discount</div>
|
||||
<div class="d-flex align-items-senter">
|
||||
<span class="fw-bold">@(SelectedCoupon?.Percent ?? 0)%</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="d-flex flex-stack">
|
||||
<div class="text-gray-700 fw-semibold me-2">Coupon</div>
|
||||
<div class="d-flex align-items-senter">
|
||||
<span class="fw-bold">@(SelectedCoupon?.Code ?? "None")</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="separator my-4"></div>
|
||||
<div class="d-flex flex-stack">
|
||||
<div class="text-gray-700 fw-semibold me-2">Total</div>
|
||||
<div class="d-flex align-items-senter">
|
||||
<span class="fw-bold">@(currency) @(actualPrice)</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="mt-7">
|
||||
@if (!CanBeOrdered && !string.IsNullOrEmpty(ErrorMessage))
|
||||
{
|
||||
<div class="alert alert-warning bg-warning text-white text-center">
|
||||
@ErrorMessage
|
||||
</div>
|
||||
}
|
||||
</div>
|
||||
</div>
|
||||
<div class="card-footer">
|
||||
@if (IsValidating)
|
||||
{
|
||||
<button class="btn btn-primary w-100 disabled" disabled="">
|
||||
<span class="spinner-border spinner-border-sm align-middle"></span>
|
||||
</button>
|
||||
}
|
||||
else
|
||||
{
|
||||
if (CanBeOrdered)
|
||||
{
|
||||
<button class="btn btn-primary w-100">Order for @(currency) @(actualPrice)</button>
|
||||
}
|
||||
else
|
||||
{
|
||||
<button class="btn btn-primary w-100 disabled" disabled="">Order for @(currency) @(actualPrice)</button>
|
||||
}
|
||||
}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-md-1 col-12"></div>
|
||||
</div>
|
||||
}
|
||||
</LazyLoader>
|
||||
|
||||
@code
|
||||
{
|
||||
[Parameter]
|
||||
public string Slug { get; set; }
|
||||
|
||||
private Product? SelectedProduct;
|
||||
private Coupon? SelectedCoupon;
|
||||
private int DurationMultiplicator = 1;
|
||||
|
||||
private string CouponCode = "";
|
||||
|
||||
private bool CanBeOrdered = false;
|
||||
private bool IsValidating = false;
|
||||
private string ErrorMessage = "";
|
||||
|
||||
private async Task SetDurationMultiplicator(int i)
|
||||
{
|
||||
DurationMultiplicator = i;
|
||||
|
||||
await Revalidate();
|
||||
}
|
||||
|
||||
private async Task ApplyCoupon()
|
||||
{
|
||||
SelectedCoupon = CouponRepository
|
||||
.Get()
|
||||
.FirstOrDefault(x => x.Code == CouponCode);
|
||||
|
||||
CouponCode = "";
|
||||
await InvokeAsync(StateHasChanged);
|
||||
|
||||
if (SelectedCoupon == null)
|
||||
{
|
||||
await AlertService.Error("", "Invalid coupon code entered");
|
||||
return;
|
||||
}
|
||||
|
||||
await Revalidate();
|
||||
}
|
||||
|
||||
private Task Revalidate()
|
||||
{
|
||||
if (SelectedProduct == null) // Prevent validating null
|
||||
return Task.CompletedTask;
|
||||
|
||||
IsValidating = true;
|
||||
InvokeAsync(StateHasChanged);
|
||||
|
||||
Task.Run(async () =>
|
||||
{
|
||||
try
|
||||
{
|
||||
await StoreService.Order.Validate(IdentityService.CurrentUser, SelectedProduct, 1, SelectedCoupon);
|
||||
CanBeOrdered = true;
|
||||
}
|
||||
catch (DisplayException e)
|
||||
{
|
||||
CanBeOrdered = false;
|
||||
ErrorMessage = e.Message;
|
||||
}
|
||||
|
||||
IsValidating = false;
|
||||
await InvokeAsync(StateHasChanged);
|
||||
});
|
||||
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
|
||||
private async Task Load(LazyLoader _)
|
||||
{
|
||||
SelectedProduct = ProductRepository
|
||||
.Get()
|
||||
.FirstOrDefault(x => x.Slug == Slug);
|
||||
|
||||
await Revalidate();
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user