Fixed some bugs. Added renew

This commit is contained in:
Baumgartner Marcel
2023-10-23 13:27:49 +02:00
parent 2dd1d1f69c
commit e062df4eb6
5 changed files with 227 additions and 20 deletions

View File

@@ -141,4 +141,58 @@ public class StoreOrderService
return await serviceService.Admin.Create(u, p, return await serviceService.Admin.Create(u, p,
service => { service.RenewAt = DateTime.UtcNow.AddDays(duration); }); 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);
}
} }

View File

@@ -1,5 +1,6 @@
using Moonlight.App.Database.Entities; using Moonlight.App.Database.Entities;
using Moonlight.App.Database.Entities.Store; using Moonlight.App.Database.Entities.Store;
using Moonlight.App.Helpers;
using Moonlight.App.Repositories; using Moonlight.App.Repositories;
namespace Moonlight.App.Services.Store; 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 // We divide the call to ensure the transaction can be written to the database
user.Balance += amount; user.Balance += amount;
user.Balance = Math.Round(user.Balance, 2); // To prevent weird numbers
UserRepository.Update(user); UserRepository.Update(user);
return Task.CompletedTask; return Task.CompletedTask;

View File

@@ -7,7 +7,7 @@
<SmartModal @ref="Modal" CssClasses="modal-dialog-centered modal-lg"> <SmartModal @ref="Modal" CssClasses="modal-dialog-centered modal-lg">
<div class="modal-header"> <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> <button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
</div> </div>
<div class="modal-body"> <div class="modal-body">

View File

@@ -1,18 +1,21 @@
@using Moonlight.App.Database.Entities.Store @using Moonlight.App.Database.Entities.Store
@using Moonlight.App.Services @using Moonlight.App.Services
@using Moonlight.App.Services.ServiceManage @using Moonlight.App.Services.ServiceManage
@using Moonlight.App.Services.Store
@using Moonlight.Shared.Components.Modals @using Moonlight.Shared.Components.Modals
@inject ConfigService ConfigService @inject ConfigService ConfigService
@inject ToastService ToastService @inject ToastService ToastService
@inject ServiceService ServiceService @inject ServiceService ServiceService
@inject StoreService StoreService
@inject IdentityService IdentityService
@if (ShowDeletionScreen) @if (ShowDeletionScreen)
{ {
<div class="card"> <div class="card">
<div class="card-header"> <div class="card-header">
<h3 class="card-title align-items-start flex-column"> <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> </h3>
</div> </div>
<div class="card-body"> <div class="card-body">
@@ -28,6 +31,98 @@
</div> </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 else
{ {
<div class="card"> <div class="card">
@@ -63,7 +158,7 @@ else
<button @onclick="() => ShareModal.Show()" class="btn btn-secondary w-50">Manage shares</button> <button @onclick="() => ShareModal.Show()" class="btn btn-secondary w-50">Manage shares</button>
</div> </div>
<div class="btn-group w-100"> <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> <button @onclick="() => SetShowDeletion(true)" class="btn btn-danger w-50">Delete</button>
</div> </div>
</div> </div>
@@ -83,14 +178,69 @@ else
public Func<Task> OnChange { get; set; } public Func<Task> OnChange { get; set; }
private bool ShowDeletionScreen = false; private bool ShowDeletionScreen = false;
private bool ShowRenewScreen = false;
private ManageServiceShareModal ShareModal; 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) private async Task SetShowDeletion(bool b)
{ {
ShowDeletionScreen = b; ShowDeletionScreen = b;
await InvokeAsync(StateHasChanged); 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() public async Task Delete()
{ {
await ServiceService.Admin.Delete(Service); await ServiceService.Admin.Delete(Service);

View File

@@ -36,17 +36,17 @@ TODO: Add 404 here
</div> </div>
<div class="row mb-5"> <div class="row mb-5">
<div class="col-md-4 col-12"> <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> <h4 class="fw-bold mb-0 align-middle">@(SelectedProduct.Duration * 1) days</h4>
</a> </a>
</div> </div>
<div class="col-md-4 col-12"> <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> <h4 class="fw-bold mb-0 align-middle">@(SelectedProduct.Duration * 2) days</h4>
</a> </a>
</div> </div>
<div class="col-md-4 col-12"> <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> <h4 class="fw-bold mb-0 align-middle">@(SelectedProduct.Duration * 3) days</h4>
</a> </a>
</div> </div>
@@ -69,7 +69,7 @@ TODO: Add 404 here
</div> </div>
@{ @{
var defaultPrice = SelectedProduct.Price * DurationMultiplicator; var defaultPrice = SelectedProduct.Price * DurationMultiplier;
double actualPrice; double actualPrice;
if (SelectedCoupon == null) if (SelectedCoupon == null)
@@ -90,13 +90,13 @@ TODO: Add 404 here
<div class="d-flex flex-stack"> <div class="d-flex flex-stack">
<div class="text-gray-700 fw-semibold me-2">Renew</div> <div class="text-gray-700 fw-semibold me-2">Renew</div>
<div class="d-flex align-items-senter"> <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> </div>
<div class="d-flex flex-stack"> <div class="d-flex flex-stack">
<div class="text-gray-700 fw-semibold me-2">Duration</div> <div class="text-gray-700 fw-semibold me-2">Duration</div>
<div class="d-flex align-items-senter"> <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> </div>
<div class="separator my-4"></div> <div class="separator my-4"></div>
@@ -161,7 +161,7 @@ TODO: Add 404 here
private Product? SelectedProduct; private Product? SelectedProduct;
private Coupon? SelectedCoupon; private Coupon? SelectedCoupon;
private int DurationMultiplicator = 1; private int DurationMultiplier = 1;
private string CouponCode = ""; private string CouponCode = "";
@@ -169,9 +169,9 @@ TODO: Add 404 here
private bool IsValidating = false; private bool IsValidating = false;
private string ErrorMessage = ""; private string ErrorMessage = "";
private async Task SetDurationMultiplicator(int i) private async Task SetDurationMultiplier(int i)
{ {
DurationMultiplicator = i; DurationMultiplier = i;
await Revalidate(); await Revalidate();
} }
@@ -206,7 +206,7 @@ TODO: Add 404 here
{ {
try try
{ {
await StoreService.Order.Validate(IdentityService.CurrentUser, SelectedProduct, 1, SelectedCoupon); await StoreService.Order.Validate(IdentityService.CurrentUser, SelectedProduct, DurationMultiplier, SelectedCoupon);
CanBeOrdered = true; CanBeOrdered = true;
} }
catch (DisplayException e) catch (DisplayException e)
@@ -242,7 +242,7 @@ TODO: Add 404 here
.Process( .Process(
IdentityService.CurrentUser, IdentityService.CurrentUser,
SelectedProduct, SelectedProduct,
DurationMultiplicator, DurationMultiplier,
SelectedCoupon SelectedCoupon
); );