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,
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.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;

View file

@ -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">

View file

@ -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();
}
}
}

View file

@ -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
);