Fixed some bugs. Added renew
This commit is contained in:
parent
2dd1d1f69c
commit
e062df4eb6
|
@ -141,4 +141,58 @@ public class StoreOrderService
|
|||
return await serviceService.Admin.Create(u, p,
|
||||
service => { service.RenewAt = DateTime.UtcNow.AddDays(duration); });
|
||||
}
|
||||
|
||||
public Task ValidateRenew(User u, Service s, int durationMultiplier)
|
||||
{
|
||||
using var scope = ServiceScopeFactory.CreateScope();
|
||||
var userRepo = scope.ServiceProvider.GetRequiredService<Repository<User>>();
|
||||
var serviceRepo = scope.ServiceProvider.GetRequiredService<Repository<Service>>();
|
||||
|
||||
var user = userRepo.Get().FirstOrDefault(x => x.Id == u.Id);
|
||||
|
||||
if(user == null)
|
||||
throw new DisplayException("Unsafe value detected. Please reload the page to proceed");
|
||||
|
||||
var service = serviceRepo
|
||||
.Get()
|
||||
.Include(x => x.Product)
|
||||
.Include(x => x.Owner)
|
||||
.FirstOrDefault(x => x.Id == s.Id);
|
||||
|
||||
if(service == null)
|
||||
throw new DisplayException("Unsafe value detected. Please reload the page to proceed");
|
||||
|
||||
var price = service.Product.Price * durationMultiplier;
|
||||
|
||||
if (user.Balance < price)
|
||||
throw new DisplayException("Order is too expensive");
|
||||
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
|
||||
public async Task Renew(User u, Service s, int durationMultiplier)
|
||||
{
|
||||
await ValidateRenew(u, s, durationMultiplier);
|
||||
|
||||
using var scope = ServiceScopeFactory.CreateScope();
|
||||
var serviceRepo = scope.ServiceProvider.GetRequiredService<Repository<Service>>();
|
||||
var transactionService = scope.ServiceProvider.GetRequiredService<TransactionService>();
|
||||
|
||||
var service = serviceRepo
|
||||
.Get()
|
||||
.Include(x => x.Product)
|
||||
.Include(x => x.Owner)
|
||||
.First(x => x.Id == s.Id);
|
||||
|
||||
var price = service.Product.Price * durationMultiplier;
|
||||
|
||||
// Calculate duration
|
||||
var duration = durationMultiplier * service.Product.Duration;
|
||||
|
||||
// Add transaction
|
||||
await transactionService.Add(u, -1 * price, $"Renewed service '{service.Nickname ?? $"Service {service.Id}"}' for {duration} days");
|
||||
|
||||
service.RenewAt = service.RenewAt.AddDays(duration);
|
||||
serviceRepo.Update(service);
|
||||
}
|
||||
}
|
|
@ -1,5 +1,6 @@
|
|||
using Moonlight.App.Database.Entities;
|
||||
using Moonlight.App.Database.Entities.Store;
|
||||
using Moonlight.App.Helpers;
|
||||
using Moonlight.App.Repositories;
|
||||
|
||||
namespace Moonlight.App.Services.Store;
|
||||
|
@ -28,6 +29,8 @@ public class TransactionService
|
|||
// We divide the call to ensure the transaction can be written to the database
|
||||
|
||||
user.Balance += amount;
|
||||
user.Balance = Math.Round(user.Balance, 2); // To prevent weird numbers
|
||||
|
||||
UserRepository.Update(user);
|
||||
|
||||
return Task.CompletedTask;
|
||||
|
|
|
@ -7,7 +7,7 @@
|
|||
|
||||
<SmartModal @ref="Modal" CssClasses="modal-dialog-centered modal-lg">
|
||||
<div class="modal-header">
|
||||
<h5 class="modal-title">Manage shared users</h5>
|
||||
<h5 class="modal-title fs-3">Manage shared users</h5>
|
||||
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
|
||||
</div>
|
||||
<div class="modal-body">
|
||||
|
|
|
@ -1,18 +1,21 @@
|
|||
@using Moonlight.App.Database.Entities.Store
|
||||
@using Moonlight.App.Services
|
||||
@using Moonlight.App.Services.ServiceManage
|
||||
@using Moonlight.App.Services.Store
|
||||
@using Moonlight.Shared.Components.Modals
|
||||
|
||||
@inject ConfigService ConfigService
|
||||
@inject ToastService ToastService
|
||||
@inject ServiceService ServiceService
|
||||
@inject StoreService StoreService
|
||||
@inject IdentityService IdentityService
|
||||
|
||||
@if (ShowDeletionScreen)
|
||||
{
|
||||
<div class="card">
|
||||
<div class="card-header">
|
||||
<h3 class="card-title align-items-start flex-column">
|
||||
<span class="card-label fw-bold text-dark fs-3">Do you really wan to delete @(Service.Nickname ?? $"Service {Service.Id}")</span>
|
||||
<span class="card-label fw-bold text-dark fs-3">Do you really want to delete @(Service.Nickname ?? $"Service {Service.Id}")</span>
|
||||
</h3>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
|
@ -22,12 +25,104 @@
|
|||
</div>
|
||||
<div class="card-footer p-3">
|
||||
<div class="btn-group w-100">
|
||||
<WButton OnClick="Delete" Text="Delete" CssClasses="btn btn-danger w-50 me-3" />
|
||||
<WButton OnClick="Delete" Text="Delete" CssClasses="btn btn-danger w-50 me-3"/>
|
||||
<button @onclick="() => SetShowDeletion(false)" class="btn btn-secondary w-50">Cancel</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
}
|
||||
else if (ShowRenewScreen)
|
||||
{
|
||||
<div class="card">
|
||||
<div class="card-header">
|
||||
<h3 class="card-title align-items-start flex-column">
|
||||
<span class="card-label fw-bold text-dark fs-3">Do you want to renew @(Service.Nickname ?? $"Service {Service.Id}")</span>
|
||||
</h3>
|
||||
</div>
|
||||
<div class="card-body fs-5">
|
||||
<div class="row mb-5">
|
||||
<div class="col-md-4 col-12">
|
||||
<a @onclick="() => SetDurationMultiplier(1)" @onclick:preventDefault href="#" class="card card-body bg-hover-secondary text-center @(DurationMultiplier == 1 ? "border border-info" : "")">
|
||||
<h6 class="fw-bold mb-0 align-middle">@(Service.Product.Duration * 1) days</h6>
|
||||
</a>
|
||||
</div>
|
||||
<div class="col-md-4 col-12">
|
||||
<a @onclick="() => SetDurationMultiplier(2)" @onclick:preventDefault href="#" class="card card-body bg-hover-secondary text-center @(DurationMultiplier == 2 ? "border border-info" : "")">
|
||||
<h6 class="fw-bold mb-0 align-middle">@(Service.Product.Duration * 2) days</h6>
|
||||
</a>
|
||||
</div>
|
||||
<div class="col-md-4 col-12">
|
||||
<a @onclick="() => SetDurationMultiplier(3)" @onclick:preventDefault href="#" class="card card-body bg-hover-secondary text-center @(DurationMultiplier == 3 ? "border border-info" : "")">
|
||||
<h6 class="fw-bold mb-0 align-middle">@(Service.Product.Duration * 3) days</h6>
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@{
|
||||
var actualPrice = Service.Product.Price * DurationMultiplier;
|
||||
var currency = ConfigService.Get().Store.Currency;
|
||||
}
|
||||
|
||||
<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) @(actualPrice)</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) @(Service.Product.Price)</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">@(Service.Product.Duration * DurationMultiplier) 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">Total</div>
|
||||
<div class="d-flex align-items-senter">
|
||||
<span class="fw-bold">@(currency) @(actualPrice)</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="mt-7">
|
||||
@if (!CanBeRenewed && !string.IsNullOrEmpty(ErrorMessage))
|
||||
{
|
||||
<div class="alert alert-warning bg-warning text-white text-center">
|
||||
@ErrorMessage
|
||||
</div>
|
||||
}
|
||||
</div>
|
||||
<div class="mt-5">
|
||||
@if (IsValidating)
|
||||
{
|
||||
<button class="btn btn-primary w-100 disabled" disabled="">
|
||||
<span class="spinner-border spinner-border-sm align-middle"></span>
|
||||
</button>
|
||||
}
|
||||
else
|
||||
{
|
||||
if (CanBeRenewed)
|
||||
{
|
||||
<WButton OnClick="Renew" Text="@($"Renew for {currency} {actualPrice}")" CssClasses="btn btn-primary w-100"/>
|
||||
}
|
||||
else
|
||||
{
|
||||
<button class="btn btn-primary w-100 disabled" disabled="">Order for @(currency) @(actualPrice)</button>
|
||||
}
|
||||
}
|
||||
</div>
|
||||
</div>
|
||||
<div class="card-footer p-3">
|
||||
<div class="btn-group w-100">
|
||||
<button @onclick="() => SetShowRenew(false)" class="btn btn-secondary">Cancel</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
}
|
||||
else
|
||||
{
|
||||
<div class="card">
|
||||
|
@ -63,13 +158,13 @@ else
|
|||
<button @onclick="() => ShareModal.Show()" class="btn btn-secondary w-50">Manage shares</button>
|
||||
</div>
|
||||
<div class="btn-group w-100">
|
||||
<button class="btn btn-warning w-50 me-3">Renew</button>
|
||||
<button @onclick="() => SetShowRenew(true)" class="btn btn-warning w-50 me-3">Renew</button>
|
||||
<button @onclick="() => SetShowDeletion(true)" class="btn btn-danger w-50">Delete</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<ManageServiceShareModal @ref="ShareModal" Service="Service" />
|
||||
|
||||
<ManageServiceShareModal @ref="ShareModal" Service="Service"/>
|
||||
}
|
||||
|
||||
|
||||
|
@ -78,19 +173,74 @@ else
|
|||
{
|
||||
[Parameter]
|
||||
public Service Service { get; set; }
|
||||
|
||||
|
||||
[Parameter]
|
||||
public Func<Task> OnChange { get; set; }
|
||||
|
||||
private bool ShowDeletionScreen = false;
|
||||
private bool ShowRenewScreen = false;
|
||||
private ManageServiceShareModal ShareModal;
|
||||
|
||||
private int DurationMultiplier = 1;
|
||||
private bool CanBeRenewed = false;
|
||||
private bool IsValidating = false;
|
||||
private string ErrorMessage = "";
|
||||
|
||||
private Task Revalidate()
|
||||
{
|
||||
IsValidating = true;
|
||||
InvokeAsync(StateHasChanged);
|
||||
|
||||
Task.Run(async () =>
|
||||
{
|
||||
try
|
||||
{
|
||||
await StoreService.Order.ValidateRenew(IdentityService.CurrentUser, Service, DurationMultiplier);
|
||||
CanBeRenewed = true;
|
||||
}
|
||||
catch (DisplayException e)
|
||||
{
|
||||
CanBeRenewed = false;
|
||||
ErrorMessage = e.Message;
|
||||
}
|
||||
|
||||
IsValidating = false;
|
||||
await InvokeAsync(StateHasChanged);
|
||||
});
|
||||
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
|
||||
private async Task Renew()
|
||||
{
|
||||
await StoreService.Order.Renew(IdentityService.CurrentUser, Service, DurationMultiplier);
|
||||
|
||||
await ToastService.Success("Successfully renewed service");
|
||||
await OnChange.Invoke();
|
||||
}
|
||||
|
||||
private async Task SetDurationMultiplier(int i)
|
||||
{
|
||||
DurationMultiplier = i;
|
||||
await Revalidate();
|
||||
}
|
||||
|
||||
private async Task SetShowDeletion(bool b)
|
||||
{
|
||||
ShowDeletionScreen = b;
|
||||
await InvokeAsync(StateHasChanged);
|
||||
}
|
||||
|
||||
|
||||
private async Task SetShowRenew(bool b)
|
||||
{
|
||||
ShowRenewScreen = b;
|
||||
|
||||
if (b) // Revalidate when the renew screen is shown
|
||||
await Revalidate();
|
||||
|
||||
await InvokeAsync(StateHasChanged);
|
||||
}
|
||||
|
||||
public async Task Delete()
|
||||
{
|
||||
await ServiceService.Admin.Delete(Service);
|
||||
|
@ -98,4 +248,4 @@ else
|
|||
await ToastService.Success("Successfully deleted service");
|
||||
await OnChange.Invoke();
|
||||
}
|
||||
}
|
||||
}
|
|
@ -36,17 +36,17 @@ TODO: Add 404 here
|
|||
</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" : "")">
|
||||
<a @onclick="() => SetDurationMultiplier(1)" @onclick:preventDefault href="#" class="card card-body bg-hover-secondary text-center @(DurationMultiplier == 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" : "")">
|
||||
<a @onclick="() => SetDurationMultiplier(2)" @onclick:preventDefault href="#" class="card card-body bg-hover-secondary text-center @(DurationMultiplier == 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" : "")">
|
||||
<a @onclick="() => SetDurationMultiplier(3)" @onclick:preventDefault href="#" class="card card-body bg-hover-secondary text-center @( DurationMultiplier == 3 ? "border border-info" : "")">
|
||||
<h4 class="fw-bold mb-0 align-middle">@(SelectedProduct.Duration * 3) days</h4>
|
||||
</a>
|
||||
</div>
|
||||
|
@ -69,7 +69,7 @@ TODO: Add 404 here
|
|||
</div>
|
||||
|
||||
@{
|
||||
var defaultPrice = SelectedProduct.Price * DurationMultiplicator;
|
||||
var defaultPrice = SelectedProduct.Price * DurationMultiplier;
|
||||
double actualPrice;
|
||||
|
||||
if (SelectedCoupon == null)
|
||||
|
@ -90,13 +90,13 @@ TODO: Add 404 here
|
|||
<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>
|
||||
<span class="fw-bold">@(currency) @(SelectedProduct.Price)</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>
|
||||
<span class="fw-bold">@(SelectedProduct.Duration * DurationMultiplier) days</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="separator my-4"></div>
|
||||
|
@ -161,7 +161,7 @@ TODO: Add 404 here
|
|||
|
||||
private Product? SelectedProduct;
|
||||
private Coupon? SelectedCoupon;
|
||||
private int DurationMultiplicator = 1;
|
||||
private int DurationMultiplier = 1;
|
||||
|
||||
private string CouponCode = "";
|
||||
|
||||
|
@ -169,9 +169,9 @@ TODO: Add 404 here
|
|||
private bool IsValidating = false;
|
||||
private string ErrorMessage = "";
|
||||
|
||||
private async Task SetDurationMultiplicator(int i)
|
||||
private async Task SetDurationMultiplier(int i)
|
||||
{
|
||||
DurationMultiplicator = i;
|
||||
DurationMultiplier = i;
|
||||
|
||||
await Revalidate();
|
||||
}
|
||||
|
@ -206,7 +206,7 @@ TODO: Add 404 here
|
|||
{
|
||||
try
|
||||
{
|
||||
await StoreService.Order.Validate(IdentityService.CurrentUser, SelectedProduct, 1, SelectedCoupon);
|
||||
await StoreService.Order.Validate(IdentityService.CurrentUser, SelectedProduct, DurationMultiplier, SelectedCoupon);
|
||||
CanBeOrdered = true;
|
||||
}
|
||||
catch (DisplayException e)
|
||||
|
@ -242,7 +242,7 @@ TODO: Add 404 here
|
|||
.Process(
|
||||
IdentityService.CurrentUser,
|
||||
SelectedProduct,
|
||||
DurationMultiplicator,
|
||||
DurationMultiplier,
|
||||
SelectedCoupon
|
||||
);
|
||||
|
||||
|
|
Loading…
Reference in a new issue